Skip to content

Commit

Permalink
secondary damage for monster spell attacks
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
PatR committed Jan 16, 2024
1 parent a5c7fed commit 6b8079a
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 18 deletions.
1 change: 1 addition & 0 deletions include/extern.h
Expand Up @@ -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);
Expand Down
64 changes: 46 additions & 18 deletions src/mcastu.c
Expand Up @@ -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;
}

Expand All @@ -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");
}

/*
Expand All @@ -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.");
Expand All @@ -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.");
Expand All @@ -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!");
Expand All @@ -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 */
Expand All @@ -334,7 +351,7 @@ castmu(
dmg = 0; /* done by the spell casting functions */
break;
}
}
} /* switch */
if (dmg)
mdamageu(mtmp, dmg);
return ret;
Expand Down Expand Up @@ -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!");
Expand All @@ -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;
Expand All @@ -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;
}
Expand Down
36 changes: 36 additions & 0 deletions src/zap.c
Expand Up @@ -5302,6 +5302,42 @@ zap_over_floor(
return rangemod;
}

/* monster has cast flames or frost at target on <x,y>; 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 <u.ux,u.uy> */
{
/* "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! */
Expand Down

0 comments on commit 6b8079a

Please sign in to comment.