diff --git a/include/extern.h b/include/extern.h index c2e3566cd..756a3145a 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 a2bf587ad..c840211bb 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 09553e182..5184cfd2c 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! */