diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 427fbbc5d..80adc4c06 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -530,6 +530,10 @@ prediscovered weapons adjustmens: only knights and samurai know polearms; if the move counter ever reaches 1000000000, end the game knights get no metal armor penalty for clerical spells change touch of death from instadeath to maxhp reduction and damage +report cause of death due to touch of death as "killed by the touch of death + inflicted by " instead of just "killed by a touch of death" +report cause of death due to wand/spell/breath as "killed by + zapped/cast/exhaled by " instead of just "killed by " dying from being level-drained below level 1 killed hero without saying so and jumped straight to "do you want your possessions identified?" conflict will now consider your charisma and requires line of sight diff --git a/include/decl.h b/include/decl.h index 3a5edf54d..edb34eed7 100644 --- a/include/decl.h +++ b/include/decl.h @@ -793,6 +793,9 @@ struct instance_globals_b { /* pickup.c */ boolean bucx_filter; + /* zap.c */ + struct monst *buzzer; /* zapper/caster/breather who initiates buzz() */ + boolean havestate; unsigned long magic; /* validate that structure layout is preserved */ }; diff --git a/include/extern.h b/include/extern.h index 549376858..2c7afdc18 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1322,6 +1322,7 @@ extern boolean usmellmon(struct permonst *); extern int castmu(struct monst *, struct attack *, boolean, boolean); extern void touch_of_death(struct monst *); +extern char *death_inflicted_by(char *, const char *, struct monst *); extern int buzzmu(struct monst *, struct attack *); /* ### mdlib.c ### */ diff --git a/include/hack.h b/include/hack.h index c7c33f40d..6639680a8 100644 --- a/include/hack.h +++ b/include/hack.h @@ -677,12 +677,12 @@ enum getobj_callback_returns { #define BZ_U_SPELL(bztyp) (10 + (bztyp)) /* hero breathing as a monster */ #define BZ_U_BREATH(bztyp) (20 + (bztyp)) +/* monster shooting a wand */ +#define BZ_M_WAND(bztyp) (-0 - (bztyp)) /* monster casting a spell */ #define BZ_M_SPELL(bztyp) (-10 - (bztyp)) /* monster breathing */ #define BZ_M_BREATH(bztyp) (-20 - (bztyp)) -/* monster shooting a wand */ -#define BZ_M_WAND(bztyp) (-30 - (bztyp)) /* * option setting restrictions diff --git a/src/decl.c b/src/decl.c index a0714791f..dbac7bb68 100644 --- a/src/decl.c +++ b/src/decl.c @@ -252,6 +252,9 @@ const struct instance_globals_b g_init_b = { UNDEFINED_PTR, /* bbubbles */ /* pickup.c */ FALSE, /* bucx_filter */ + /* zap.c */ + NULL, /* buzzer -- monst that zapped/cast/breathed to initiate buzz() */ + TRUE, /* havestate*/ IVMAGIC /* b_magic to validate that structure layout has been preserved */ }; diff --git a/src/mcastu.c b/src/mcastu.c index a97fcd04a..90ae9f73d 100644 --- a/src/mcastu.c +++ b/src/mcastu.c @@ -39,7 +39,6 @@ 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); @@ -375,7 +374,7 @@ touch_of_death(struct monst *mtmp) } /* give a reason for death by some monster spells */ -static char * +char * death_inflicted_by( char *outbuf, /* assumed big enough; pm_names are short */ const char *deathreason, /* cause of death */ @@ -909,7 +908,7 @@ spell_would_be_useless(struct monst *mtmp, unsigned int adtyp, int spellnum) /* monster uses spell (ranged) */ int -buzzmu(register struct monst *mtmp, register struct attack *mattk) +buzzmu(struct monst *mtmp, struct attack *mattk) { /* don't print constant stream of curse messages for 'normal' spellcasting monsters at range */ @@ -925,8 +924,10 @@ buzzmu(register struct monst *mtmp, register struct attack *mattk) if (canseemon(mtmp)) pline("%s zaps you with a %s!", Monnam(mtmp), flash_str(BZ_OFS_AD(mattk->adtyp), FALSE)); - buzz(BZ_M_SPELL(BZ_OFS_AD(mattk->adtyp)), (int) mattk->damn, mtmp->mx, - mtmp->my, sgn(gt.tbx), sgn(gt.tby)); + gb.buzzer = mtmp; + buzz(BZ_M_SPELL(BZ_OFS_AD(mattk->adtyp)), (int) mattk->damn, + mtmp->mx, mtmp->my, sgn(gt.tbx), sgn(gt.tby)); + gb.buzzer = 0; return MM_HIT; } return MM_MISS; diff --git a/src/mthrowu.c b/src/mthrowu.c index 0207d52e0..7e73a7d1c 100644 --- a/src/mthrowu.c +++ b/src/mthrowu.c @@ -927,8 +927,10 @@ breamm(struct monst* mtmp, struct attack* mattk, struct monst* mtarg) if (canseemon(mtmp)) pline("%s breathes %s!", Monnam(mtmp), breathwep_name(typ)); + gb.buzzer = mtmp; dobuzz(BZ_M_BREATH(BZ_OFS_AD(typ)), (int) mattk->damn, mtmp->mx, mtmp->my, sgn(gt.tbx), sgn(gt.tby), utarget); + gb.buzzer = 0; nomul(0); /* breath runs out sometimes. Also, give monster some * cunning; don't breath if the target fell asleep. diff --git a/src/muse.c b/src/muse.c index eb95611c6..22789d654 100644 --- a/src/muse.c +++ b/src/muse.c @@ -1659,19 +1659,27 @@ use_offensive(struct monst *mtmp) if (oseen) makeknown(otmp->otyp); gm.m_using = TRUE; + gc.current_wand = otmp; + gb.buzzer = mtmp; buzz(BZ_M_WAND(BZ_OFS_WAN(otmp->otyp)), (otmp->otyp == WAN_MAGIC_MISSILE) ? 2 : 6, mtmp->mx, mtmp->my, sgn(mtmp->mux - mtmp->mx), sgn(mtmp->muy - mtmp->my)); + gb.buzzer = 0; + gc.current_wand = 0; gm.m_using = FALSE; return (DEADMONSTER(mtmp)) ? 1 : 2; case MUSE_FIRE_HORN: case MUSE_FROST_HORN: mplayhorn(mtmp, otmp, FALSE); gm.m_using = TRUE; + gb.buzzer = mtmp; + gc.current_wand = otmp; /* needed by zhitu() */ buzz(BZ_M_WAND(BZ_OFS_AD((otmp->otyp == FROST_HORN) ? AD_COLD : AD_FIRE)), rn1(6, 6), mtmp->mx, mtmp->my, sgn(mtmp->mux - mtmp->mx), sgn(mtmp->muy - mtmp->my)); + gb.buzzer = 0; + gc.current_wand = 0; gm.m_using = FALSE; return (DEADMONSTER(mtmp)) ? 1 : 2; case MUSE_WAN_TELEPORTATION: diff --git a/src/music.c b/src/music.c index b6c6179b1..a51ee8257 100644 --- a/src/music.c +++ b/src/music.c @@ -615,12 +615,15 @@ do_improvisation(struct obj* instr) losehp(damage, buf, KILLED_BY); /* fire or frost damage */ } } else { - int type = BZ_OFS_AD((instr->otyp == FROST_HORN) ? AD_COLD : AD_FIRE); + int type = BZ_OFS_AD((instr->otyp == FROST_HORN) ? AD_COLD + : AD_FIRE); if (!Blind) pline("A %s blasts out of the horn!", flash_str(type, FALSE)); Hero_playnotes(obj_to_instr(&itmp), improvisation, 50); + gc.current_wand = instr; ubuzz(BZ_U_WAND(type), rn1(6, 6)); + gc.current_wand = 0; } makeknown(instr->otyp); break; diff --git a/src/priest.c b/src/priest.c index 12471408c..1e737035c 100644 --- a/src/priest.c +++ b/src/priest.c @@ -753,9 +753,11 @@ in_your_sanctuary( void ghod_hitsu(struct monst *priest) { + struct mkroom *troom; + struct monst *oldbuzzer; + struct obj *oldcurrwand; coordxy x, y, ax, ay; int roomno = (int) temple_occupied(u.urooms); - struct mkroom *troom; if (!roomno || !has_shrine(priest)) return; @@ -818,8 +820,14 @@ ghod_hitsu(struct monst *priest) break; } - buzz(BZ_M_SPELL(BZ_OFS_AD(AD_ELEC)), 6, x, y, sgn(gt.tbx), - sgn(gt.tby)); /* bolt of lightning */ + /* bolt of lightning cast by unspecified monster */ + oldcurrwand = gc.current_wand; + gc.current_wand = 0; + oldbuzzer = gb.buzzer; + gb.buzzer = 0; + buzz(BZ_M_SPELL(BZ_OFS_AD(AD_ELEC)), 6, x, y, sgn(gt.tbx), sgn(gt.tby)); + gb.buzzer = oldbuzzer; + gc.current_wand = oldcurrwand; exercise(A_WIS, FALSE); } diff --git a/src/timeout.c b/src/timeout.c index a4b7e74d4..865704164 100644 --- a/src/timeout.c +++ b/src/timeout.c @@ -1721,9 +1721,11 @@ do_storms(void) if (count < 100) { dirx = rn2(3) - 1; diry = rn2(3) - 1; - if (dirx != 0 || diry != 0) - buzz(-15, /* "monster" LIGHTNING spell */ - 8, x, y, dirx, diry); + if (dirx != 0 || diry != 0) { + /* BZ_M_SPELL(BZ_OFS_AD(AD_ELEC)): monster LIGHTNING spell */ + gb.buzzer = 0; /* unspecified attacker */ + buzz(BZ_M_SPELL(BZ_OFS_AD(AD_ELEC)), 8, x, y, dirx, diry); + } } } diff --git a/src/zap.c b/src/zap.c index 22b1581ff..f50214fcc 100644 --- a/src/zap.c +++ b/src/zap.c @@ -52,25 +52,31 @@ static void wishcmdassist(int); #define M_IN_WATER(ptr) \ ((ptr)->mlet == S_EEL || amphibious(ptr) || is_swimmer(ptr)) -static const char are_blinded_by_the_flash[] = - "are blinded by the flash!"; +static const char are_blinded_by_the_flash[] = "are blinded by the flash!"; -static const char *const flash_types[] = - { - "magic missile", /* Wands must be 0-9 */ - "bolt of fire", "bolt of cold", "sleep ray", "death ray", - "bolt of lightning", "", "", "", "", +/* + * FIXME: + * flash_types[0] for wand of magic missile is ambiguous. + * A positive index means zapped/cast/breathed by hero. + * A negative index means zapped/cast/breathed by a monster. + * Since abs(-0)==abs(0), there's no way to tell who zapped a wand of + * magic missile by just checking the index. + */ +static const char *const flash_types[] = { + "magic missile", /* Wands must be 0-9 */ + "bolt of fire", "bolt of cold", "sleep ray", "death ray", + "bolt of lightning", "", "", "", "", - "magic missile", /* Spell equivalents must be 10-19 */ - "fireball", "cone of cold", "sleep ray", "finger of death", - "bolt of lightning", /* there is no spell, used for retribution */ - "", "", "", "", + "magic missile", /* Spell equivalents must be 10-19 */ + "fireball", "cone of cold", "sleep ray", "finger of death", + "bolt of lightning", /* there is no spell, used for retribution */ + "", "", "", "", - "blast of missiles", /* Dragon breath equivalents 20-29*/ - "blast of fire", "blast of frost", "blast of sleep gas", - "blast of disintegration", "blast of lightning", - "blast of poison gas", "blast of acid", "", "" - }; + "blast of missiles", /* Dragon breath equivalents 20-29*/ + "blast of fire", "blast of frost", "blast of sleep gas", + "blast of disintegration", "blast of lightning", + "blast of poison gas", "blast of acid", "", "" +}; /* * Recognizing unseen wands by zapping: in 3.4.3 and earlier, zapping @@ -2854,10 +2860,10 @@ ubreatheu(struct attack *mattk) /* light damages hero in gremlin form */ int -lightdamage(struct obj *obj, /* item making light (fake book if spell) */ - boolean ordinary, /* wand/camera zap vs wand destruction */ - int amt) /* pseudo-damage used to determine blindness - duration */ +lightdamage( + struct obj *obj, /* item making light (fake book if spell) */ + boolean ordinary, /* wand/camera zap vs wand destruction */ + int amt) /* pseudo-damage used to determine blindness duration */ { char buf[BUFSZ]; const char *how; @@ -3300,7 +3306,8 @@ weffects(struct obj *obj) else if (otyp >= SPE_MAGIC_MISSILE && otyp <= SPE_FINGER_OF_DEATH) ubuzz(BZ_U_SPELL(BZ_OFS_SPE(otyp)), u.ulevel / 2 + 1); else if (otyp >= WAN_MAGIC_MISSILE && otyp <= WAN_LIGHTNING) - ubuzz(BZ_U_WAND(BZ_OFS_WAN(otyp)), (otyp == WAN_MAGIC_MISSILE) ? 2 : 6); + ubuzz(BZ_U_WAND(BZ_OFS_WAN(otyp)), + (otyp == WAN_MAGIC_MISSILE) ? 2 : 6); else impossible("weffects: unexpected spell or wand"); disclose = TRUE; @@ -4058,7 +4065,10 @@ zhitm( } static void -zhitu(int type, int nd, const char *fltxt, coordxy sx, coordxy sy) +zhitu( + int type, int nd, + const char *fltxt, + coordxy sx, coordxy sy) { int dam = 0, abstyp = abs(type); @@ -4197,11 +4207,47 @@ zhitu(int type, int nd, const char *fltxt, coordxy sx, coordxy sy) break; } - /* Half_spell_damage protection yields half-damage for wands & spells, - including hero's own ricochets; breath attacks do full damage */ - if (dam && Half_spell_damage && !(abstyp >= 20 && abstyp <= 29)) - dam = (dam + 1) / 2; - losehp(dam, fltxt, KILLED_BY_AN); + /* + * 3.7: when fatal, this used to yield "Killed by ." without any + * information about who was responsible. Now 'buzzer' is used to try + * to supply "zapped/cast/breathed by [imitating ]." + * + * Room for improvement: there is no monster available when player is + * hit by divine lighting or by Plane of Air thunderstorm so cause of + * death remains "killed by a bolt of lightning" w/o extra explanation. + * + * Wand of death, spell of finger of death, and disintegration breath + * don't use this routine so don't include 'inflicted by'. + */ + { + char kbuf[BUFSZ]; + struct obj *otmp = gc.current_wand; + /* fire horn and frost horn get handled as wands by caller */ + const char *verb = (abstyp < 10) /* wand */ + ? ((otmp && otmp->oclass == TOOL_CLASS) ? "played" + : "zapped") + : (abstyp < 20) ? "cast" + : (abstyp < 30) ? "exhaled" + : "imagined"; /* should never happen */ + + if (type < 0 || (type == 0 && gb.buzzer != 0)) { + /* if gb.buzzer is Null, kbuf[] will end up with just */ + (void) death_inflicted_by(kbuf, fltxt, gb.buzzer); + /* change "death inflicted by mon" to "death by mon" */ + if (gb.buzzer) + (void) strsubst(kbuf, "inflicted", verb); + } else { + /* FIXME: "zapped by herself" is suitable for a rebound; + "zapped at herself" would be better if player explicitly + targeted hero */ + Sprintf(kbuf, "%s %s by %sself", fltxt, verb, uhim()); + } + /* Half_spell_damage protection yields half-damage for wands & spells, + including hero's own ricochets; breath attacks do full damage */ + if (dam && Half_spell_damage && abstyp < 20) + dam = (dam + 1) / 2; + losehp(dam, kbuf, KILLED_BY_AN); + } return; } @@ -4210,10 +4256,10 @@ zhitu(int type, int nd, const char *fltxt, coordxy sx, coordxy sy) * at position x,y; return the number of objects burned */ int -burn_floor_objects(coordxy x, coordxy y, - boolean give_feedback, /* caller needs to decide about - visibility checks */ - boolean u_caused) +burn_floor_objects( + coordxy x, coordxy y, + boolean give_feedback, /* caller needs to decide about visibility checks */ + boolean u_caused) { struct obj *obj, *obj2; long i, scrquan, delquan; @@ -4288,9 +4334,10 @@ zap_hit(int ac, } static void -disintegrate_mon(struct monst *mon, - int type, /* hero vs other */ - const char *fltxt) +disintegrate_mon( + struct monst *mon, + int type, /* hero vs other */ + const char *fltxt) { struct obj *otmp, *otmp2, *m_amulet = mlifesaver(mon); @@ -4428,7 +4475,8 @@ dobuzz( if (type >= 0) mon->mstrategy &= ~STRAT_WAITMASK; buzzmonst: - gn.notonhead = (mon->mx != gb.bhitpos.x || mon->my != gb.bhitpos.y); + gn.notonhead = (mon->mx != gb.bhitpos.x + || mon->my != gb.bhitpos.y); if (zap_hit(find_mac(mon), spell_type)) { if (mon_reflects(mon, (char *) 0)) { if (cansee(mon->mx, mon->my)) { @@ -4667,9 +4715,9 @@ melt_ice(coordxy x, coordxy y, const char *msg) * permanent instead. */ void -start_melt_ice_timeout(coordxy x, coordxy y, - long min_time) /* 's old melt timeout (deleted by - time we get here) */ +start_melt_ice_timeout( + coordxy x, coordxy y, + long min_time) /* 's old melt timeout (deleted by time we get here) */ { int when; long where;