From 6b8079a16f425aa80239b0b1009ec98a70af56ff Mon Sep 17 00:00:00 2001 From: PatR Date: Tue, 16 Jan 2024 14:01:38 -0800 Subject: [PATCH] secondary damage for monster spell attacks Have monster spells | "shower of missiles" (AT_MAGC+AD_MAGM: Angels, Yeenoghu) scuff an engraving at the hero's spot if there is one, | "frost" (AT_MAGC+AD_COLD: only Asmodeus) freeze water and lava terrain, | "flames" (AT_MAGC+AD_FIRE: moot, no monster has this attack) burn items on the floor at the hero's spot and melt ice terrain, | "pillar of flame" (AT_MAGC+AD_CLRC+randomly chosen clerical spell) which already burns floor items, melt ice too, and | "lightning" (same casters as pillar of flame) give a tiny chance of destroying iron bars. The chance to hit bars is low and the hero has to be targeted while located on an iron bars spot so probably won't happen before the sun burns out, but only needed one extra line of code. Only the first two have been thoroughly tested. --- include/extern.h | 1 + src/mcastu.c | 64 ++++++++++++++++++++++++++++++++++-------------- src/zap.c | 36 +++++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 18 deletions(-) diff --git a/include/extern.h b/include/extern.h index c2e3566cd9..756a3145a1 100644 --- a/include/extern.h +++ b/include/extern.h @@ -3756,6 +3756,7 @@ extern void start_melt_ice_timeout(coordxy, coordxy, long); extern void melt_ice_away(union any *, long) NONNULLARG1; extern int zap_over_floor(coordxy, coordxy, int, boolean *, boolean, short) NONNULLARG4; +extern void mon_spell_hits_spot(struct monst *, int, coordxy x, coordxy y); extern void fracture_rock(struct obj *) NONNULLARG1; extern boolean break_statue(struct obj *) NONNULLARG1; extern int u_adtyp_resistance_obj(int); diff --git a/src/mcastu.c b/src/mcastu.c index a2bf587ada..c840211bb9 100644 --- a/src/mcastu.c +++ b/src/mcastu.c @@ -233,15 +233,22 @@ castmu( mtmp->mspec_used = (int) ((mtmp->m_lev < 8) ? (10 - mtmp->m_lev) : 2); } - /* monster can cast spells, but is casting a directed spell at the - wrong place? If so, give a message, and return. Do this *after* - penalizing mspec_used. */ + /* Monster can cast spells, but is casting a directed spell at the + * wrong place? If so, give a message, and return. + * Do this *after* penalizing mspec_used. + * + * FIXME? + * Shouldn't wall of lava have a case similar to wall of water? + * And should cold damage hit water or lava instead of missing + * even when the caster has targeted the wrong spot? Likewise + * for fire mis-aimed at ice. + */ if (!foundyou && thinks_it_foundyou && !is_undirected_spell(mattk->adtyp, spellnum)) { pline_xy(mtmp->mx, mtmp->my, "%s casts a spell at %s!", - canseemon(mtmp) ? Monnam(mtmp) : "Something", - is_waterwall(mtmp->mux,mtmp->muy) ? "empty water" - : "thin air"); + canseemon(mtmp) ? Monnam(mtmp) : "Something", + is_waterwall(mtmp->mux, mtmp->muy) ? "empty water" + : "thin air"); return M_ATTK_MISS; } @@ -256,16 +263,14 @@ castmu( } if (canspotmon(mtmp) || !is_undirected_spell(mattk->adtyp, spellnum)) { pline_xy(mtmp->mx, mtmp->my, "%s casts a spell%s!", - canspotmon(mtmp) ? Monnam(mtmp) : "Something", - is_undirected_spell(mattk->adtyp, spellnum) - ? "" - : (Invis && !perceives(mtmp->data) - && (mtmp->mux != u.ux || mtmp->muy != u.uy)) - ? " at a spot near you" - : (Displaced - && (mtmp->mux != u.ux || mtmp->muy != u.uy)) - ? " at your displaced image" - : " at you"); + canspotmon(mtmp) ? Monnam(mtmp) : "Something", + is_undirected_spell(mattk->adtyp, spellnum) ? "" + : (Invis && !perceives(mtmp->data) + && !u_at(mtmp->mux, mtmp->muy)) + ? " at a spot near you" + : (Displaced && !u_at(mtmp->mux, mtmp->muy)) + ? " at your displaced image" + : " at you"); } /* @@ -288,6 +293,10 @@ castmu( dmg = (dmg + 1) / 2; ret = M_ATTK_HIT; + /* + * FIXME: none of these hit the steed when hero is riding, nor do + * they inflict damage on carried items. + */ switch (mattk->adtyp) { case AD_FIRE: pline("You're enveloped in flames."); @@ -300,6 +309,8 @@ castmu( monstunseesu(M_SEEN_FIRE); } burn_away_slime(); + /* burn up flammable items on the floor, melt ice terrain */ + mon_spell_hits_spot(mtmp, AD_FIRE, u.ux, u.uy); break; case AD_COLD: pline("You're covered in frost."); @@ -311,6 +322,10 @@ castmu( } else { monstunseesu(M_SEEN_COLD); } + /* freeze water or lava terrain */ + /* FIXME: mon_spell_hits_spot() uses zap_over_floor(); unlike with + * fire, it does not target susceptible floor items with cold */ + mon_spell_hits_spot(mtmp, AD_COLD, u.ux, u.uy); break; case AD_MAGM: You("are hit by a shower of missiles!"); @@ -323,6 +338,8 @@ castmu( dmg = d((int) mtmp->m_lev / 2 + 1, 6); monstunseesu(M_SEEN_MAGR); } + /* shower of magic missiles scuffs an engraving */ + mon_spell_hits_spot(mtmp, AD_MAGM, u.ux, u.uy); break; case AD_SPEL: /* wizard spell */ case AD_CLRC: /* clerical spell */ @@ -334,7 +351,7 @@ castmu( dmg = 0; /* done by the spell casting functions */ break; } - } + } /* switch */ if (dmg) mdamageu(mtmp, dmg); return ret; @@ -618,6 +635,10 @@ cast_cleric_spell(struct monst *mtmp, int dmg, int spellnum) dmg = d(8, 6); if (Half_physical_damage) dmg = (dmg + 1) / 2; +#if 0 /* since inventory items aren't affected, don't include this */ + /* make floor items wet */ + water_damage_chain(level.objects[u.ux][u.uy], TRUE); +#endif break; case CLC_FIRE_PILLAR: pline("A pillar of fire strikes all around you!"); @@ -637,7 +658,8 @@ cast_cleric_spell(struct monst *mtmp, int dmg, int spellnum) destroy_item(POTION_CLASS, AD_FIRE); destroy_item(SPBOOK_CLASS, AD_FIRE); ignite_items(gi.invent); - (void) burn_floor_objects(u.ux, u.uy, TRUE, FALSE); + /* burn up flammable items on the floor, melt ice terrain */ + mon_spell_hits_spot(mtmp, AD_FIRE, u.ux, u.uy); break; case CLC_LIGHTNING: { boolean reflects; @@ -662,6 +684,12 @@ cast_cleric_spell(struct monst *mtmp, int dmg, int spellnum) dmg = (dmg + 1) / 2; destroy_item(WAND_CLASS, AD_ELEC); destroy_item(RING_CLASS, AD_ELEC); + /* lightning might destroy iron bars if hero is on such a spot; + reflection protects terrain here [execution won't get here due + to 'if (reflects) break' above] but hero resistance doesn't; + do this before maybe blinding the hero via flashburn() */ + mon_spell_hits_spot(mtmp, AD_ELEC, u.ux, u.uy); + /* blind hero; no effect if already blind */ (void) flashburn((long) rnd(100)); break; } diff --git a/src/zap.c b/src/zap.c index 09553e182f..5184cfd2ca 100644 --- a/src/zap.c +++ b/src/zap.c @@ -5302,6 +5302,42 @@ zap_over_floor( return rangemod; } +/* monster has cast flames or frost at target on ; called by mcastu() */ +void +mon_spell_hits_spot( + struct monst *caster UNUSED, + int adtyp, /* canonical damage type */ + coordxy x, coordxy y) /* so far, only used for targeting */ +{ + /* "shower of missiles" or [hypothetical] "acid rain" attack: + thoroughly clobber an engraving (unless its type makes it be + scuff-protected); zap_over_floor() doesn't handle this */ + if (adtyp == AD_MAGM || adtyp == AD_ACID) { + struct engr *ep = engr_at(x, y); + char *etext = ep ? ep->engr_txt[actual_text] : NULL; + + if (etext) + wipe_engr_at(x, y, (int) strlen(etext) + d(6, 6), TRUE); + /* hero and player will still remember prior text until the spot + is re-examined (lookhere or move off and back on) */ + } + + /* hit items and/or terrain; only matters for AD_FIRE and AD_COLD but + accept any basic damage type that zap_over_floor() might handle */ + if (adtyp >= AD_MAGM && adtyp <= AD_ACID) { + boolean shopdummy = FALSE; /* zap_over_floor() requires this even + * though it's only used when zapdmgtyp + * is non-negative (hero's fault) */ + int zt_typ = adtyp - 1, /* convert AD_xxxx to ZT_xxxx */ + zapdmgtyp = -ZT_SPELL(zt_typ); /* damage is from monster spell */ + + (void) zap_over_floor(x, y, zapdmgtyp, &shopdummy, TRUE, 0); + } else { + impossible("Unsupported damage type (%d) for mon_spell_hits_spot.", + adtyp); + } +} + /* fractured by pick-axe or wand of striking or by vault guard */ void fracture_rock(struct obj *obj) /* no texts here! */