1 /* SCCS Id: @(#)mcastu.c 3.4 2003/01/08 */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
7 /* monster mage spells */
9 #define MGC_CURE_SELF 1
10 #define MGC_HASTE_SELF 2
11 #define MGC_STUN_YOU 3
12 #define MGC_DISAPPEAR 4
13 #define MGC_WEAKEN_YOU 5
14 #define MGC_DESTRY_ARMR 6
15 #define MGC_CURSE_ITEMS 7
16 #define MGC_AGGRAVATION 8
17 #define MGC_SUMMON_MONS 9
18 #define MGC_CLONE_WIZ 10
19 #define MGC_DEATH_TOUCH 11
21 /* monster cleric spells */
22 #define CLC_OPEN_WOUNDS 0
23 #define CLC_CURE_SELF 1
24 #define CLC_CONFUSE_YOU 2
25 #define CLC_PARALYZE 3
26 #define CLC_BLIND_YOU 4
28 #define CLC_CURSE_ITEMS 6
29 #define CLC_LIGHTNING 7
30 #define CLC_FIRE_PILLAR 8
33 STATIC_DCL void FDECL(cursetxt,(struct monst *,BOOLEAN_P));
34 STATIC_DCL int FDECL(choose_magic_spell, (int));
35 STATIC_DCL int FDECL(choose_clerical_spell, (int));
36 STATIC_DCL void FDECL(cast_wizard_spell,(struct monst *, int,int));
37 STATIC_DCL void FDECL(cast_cleric_spell,(struct monst *, int,int));
38 STATIC_DCL boolean FDECL(is_undirected_spell,(unsigned int,int));
39 STATIC_DCL boolean FDECL(spell_would_be_useless,(struct monst *,unsigned int,int));
43 extern const char * const flash_types[]; /* from zap.c */
45 /* feedback when frustrated monster couldn't cast a spell */
48 cursetxt(mtmp, undirected)
52 if (canseemon(mtmp) && couldsee(mtmp->mx, mtmp->my)) {
53 const char *point_msg; /* spellcasting monsters are impolite */
56 point_msg = "all around, then curses";
57 else if ((Invis && !perceives(mtmp->data) &&
58 (mtmp->mux != u.ux || mtmp->muy != u.uy)) ||
59 (youmonst.m_ap_type == M_AP_OBJECT &&
60 youmonst.mappearance == STRANGE_OBJECT) ||
62 point_msg = "and curses in your general direction";
63 else if (Displaced && (mtmp->mux != u.ux || mtmp->muy != u.uy))
64 point_msg = "and curses at your displaced image";
66 point_msg = "at you, then curses";
68 pline("%s points %s.", Monnam(mtmp), point_msg);
69 } else if ((!(moves % 4) || !rn2(4))) {
70 if (flags.soundok) Norep("You hear a mumbled curse.");
77 /* convert a level based random selection into a specific mage spell;
78 inappropriate choices will be screened out by spell_would_be_useless() */
80 choose_magic_spell(spellval)
87 return MGC_DEATH_TOUCH;
94 return MGC_SUMMON_MONS;
97 return MGC_AGGRAVATION;
101 return MGC_CURSE_ITEMS;
104 return MGC_DESTRY_ARMR;
107 return MGC_WEAKEN_YOU;
110 return MGC_DISAPPEAR;
114 return MGC_HASTE_SELF;
116 return MGC_CURE_SELF;
123 /* convert a level based random selection into a specific cleric spell */
125 choose_clerical_spell(spellnum)
132 return CLC_FIRE_PILLAR;
134 return CLC_LIGHTNING;
137 return CLC_CURSE_ITEMS;
142 return CLC_BLIND_YOU;
148 return CLC_CONFUSE_YOU;
150 return CLC_CURE_SELF;
153 return CLC_OPEN_WOUNDS;
158 * 1: successful spell
159 * 0: unsuccessful spell
162 castmu(mtmp, mattk, thinks_it_foundyou, foundyou)
163 register struct monst *mtmp;
164 register struct attack *mattk;
165 boolean thinks_it_foundyou;
168 int dmg, ml = mtmp->m_lev;
173 * -- monster is attacking you. Search for a useful spell.
174 * -- monster thinks it's attacking you. Search for a useful spell,
175 * without checking for undirected. If the spell found is directed,
176 * it fails with cursetxt() and loss of mspec_used.
177 * -- monster isn't trying to attack. Select a spell once. Don't keep
178 * searching; if that spell is not useful (or if it's directed),
179 * return and do something else.
180 * Since most spells are directed, this means that a monster that isn't
181 * attacking casts spells only a small portion of the time that an
182 * attacking monster does.
184 if ((mattk->adtyp == AD_SPEL || mattk->adtyp == AD_CLRC) && ml) {
189 if (mattk->adtyp == AD_SPEL)
190 spellnum = choose_magic_spell(spellnum);
192 spellnum = choose_clerical_spell(spellnum);
193 /* not trying to attack? don't allow directed spells */
194 if (!thinks_it_foundyou) {
195 if (!is_undirected_spell(mattk->adtyp, spellnum) ||
196 spell_would_be_useless(mtmp, mattk->adtyp, spellnum)) {
198 impossible("spellcasting monster found you and doesn't know it?");
204 spell_would_be_useless(mtmp, mattk->adtyp, spellnum));
205 if (cnt == 0) return 0;
208 /* monster unable to cast spells? */
209 if(mtmp->mcan || mtmp->mspec_used || !ml) {
210 cursetxt(mtmp, is_undirected_spell(mattk->adtyp, spellnum));
214 if (mattk->adtyp == AD_SPEL || mattk->adtyp == AD_CLRC) {
215 mtmp->mspec_used = 10 - mtmp->m_lev;
216 if (mtmp->mspec_used < 2) mtmp->mspec_used = 2;
219 /* monster can cast spells, but is casting a directed spell at the
220 wrong place? If so, give a message, and return. Do this *after*
221 penalizing mspec_used. */
222 if (!foundyou && thinks_it_foundyou &&
223 !is_undirected_spell(mattk->adtyp, spellnum)) {
224 pline("%s casts a spell at %s!",
225 canseemon(mtmp) ? Monnam(mtmp) : "Something",
226 levl[mtmp->mux][mtmp->muy].typ == WATER
227 ? "empty water" : "thin air");
232 if(rn2(ml*10) < (mtmp->mconf ? 100 : 20)) { /* fumbled attack */
233 if (canseemon(mtmp) && flags.soundok)
234 pline_The("air crackles around %s.", mon_nam(mtmp));
237 if (canspotmon(mtmp) || !is_undirected_spell(mattk->adtyp, spellnum)) {
238 pline("%s casts a spell%s!",
239 canspotmon(mtmp) ? Monnam(mtmp) : "Something",
240 is_undirected_spell(mattk->adtyp, spellnum) ? "" :
241 (Invisible && !perceives(mtmp->data) &&
242 (mtmp->mux != u.ux || mtmp->muy != u.uy)) ?
243 " at a spot near you" :
244 (Displaced && (mtmp->mux != u.ux || mtmp->muy != u.uy)) ?
245 " at your displaced image" :
250 * As these are spells, the damage is related to the level
251 * of the monster casting the spell.
255 if (mattk->adtyp != AD_SPEL && mattk->adtyp != AD_CLRC) {
257 "%s casting non-hand-to-hand version of hand-to-hand spell %d?",
258 Monnam(mtmp), mattk->adtyp);
261 } else if (mattk->damd)
262 dmg = d((int)((ml/2) + mattk->damn), (int)mattk->damd);
263 else dmg = d((int)((ml/2) + 1), 6);
264 if (Half_spell_damage) dmg = (dmg+1) / 2;
268 switch (mattk->adtyp) {
271 pline("You're enveloped in flames.");
272 if(Fire_resistance) {
273 shieldeff(u.ux, u.uy);
274 pline("But you resist the effects.");
280 pline("You're covered in frost.");
281 if(Cold_resistance) {
282 shieldeff(u.ux, u.uy);
283 pline("But you resist the effects.");
288 You("are hit by a shower of missiles!");
290 shieldeff(u.ux, u.uy);
291 pline_The("missiles bounce off!");
293 } else dmg = d((int)mtmp->m_lev/2 + 1,6);
295 case AD_SPEL: /* wizard spell */
296 case AD_CLRC: /* clerical spell */
298 if (mattk->adtyp == AD_SPEL)
299 cast_wizard_spell(mtmp, dmg, spellnum);
301 cast_cleric_spell(mtmp, dmg, spellnum);
302 dmg = 0; /* done by the spell casting functions */
306 if(dmg) mdamageu(mtmp, dmg);
310 /* monster wizard and cleric spellcasting functions */
312 If dmg is zero, then the monster is not casting at you.
313 If the monster is intentionally not casting at you, we have previously
314 called spell_would_be_useless() and spellnum should always be a valid
316 If you modify either of these, be sure to change is_undirected_spell()
317 and spell_would_be_useless().
321 cast_wizard_spell(mtmp, dmg, spellnum)
326 if (dmg == 0 && !is_undirected_spell(AD_SPEL, spellnum)) {
327 impossible("cast directed wizard spell (%d) with dmg=0?", spellnum);
332 case MGC_DEATH_TOUCH:
333 pline("Oh no, %s's using the touch of death!", mhe(mtmp));
334 if (nonliving(youmonst.data) || is_demon(youmonst.data)) {
335 You("seem no deader than before.");
336 } else if (!Antimagic && rn2(mtmp->m_lev) > 12) {
338 You("have an out of body experience.");
340 killer_format = KILLED_BY_AN;
341 killer = "touch of death";
345 if (Antimagic) shieldeff(u.ux, u.uy);
346 pline("Lucky for you, it didn't work!");
351 if (mtmp->iswiz && flags.no_of_wizards == 1) {
352 pline("Double Trouble...");
356 impossible("bad wizard cloning?");
358 case MGC_SUMMON_MONS:
362 count = nasty(mtmp); /* summon something nasty */
364 verbalize("Destroy the thief, my pet%s!", plur(count));
366 const char *mappear =
367 (count == 1) ? "A monster appears" : "Monsters appear";
369 /* messages not quite right if plural monsters created but
370 only a single monster is seen */
371 if (Invisible && !perceives(mtmp->data) &&
372 (mtmp->mux != u.ux || mtmp->muy != u.uy))
373 pline("%s around a spot near you!", mappear);
374 else if (Displaced && (mtmp->mux != u.ux || mtmp->muy != u.uy))
375 pline("%s around your displaced image!", mappear);
377 pline("%s from nowhere!", mappear);
382 case MGC_AGGRAVATION:
383 You_feel("that monsters are aware of your presence.");
387 case MGC_CURSE_ITEMS:
388 You_feel("as if you need some help.");
392 case MGC_DESTRY_ARMR:
394 shieldeff(u.ux, u.uy);
395 pline("A field of force surrounds you!");
396 } else if (!destroy_arm(some_armor(&youmonst))) {
397 Your("skin itches.");
401 case MGC_WEAKEN_YOU: /* drain strength */
403 shieldeff(u.ux, u.uy);
404 You_feel("momentarily weakened.");
406 You("suddenly feel weaker!");
407 dmg = mtmp->m_lev - 6;
408 if (Half_spell_damage) dmg = (dmg + 1) / 2;
415 case MGC_DISAPPEAR: /* makes self invisible */
416 if (!mtmp->minvis && !mtmp->invis_blkd) {
418 pline("%s suddenly %s!", Monnam(mtmp),
419 !See_invisible ? "disappears" : "becomes transparent");
420 mon_set_minvis(mtmp);
423 impossible("no reason for monster to cast disappear spell?");
426 if (Antimagic || Free_action) {
427 shieldeff(u.ux, u.uy);
429 You_feel("momentarily disoriented.");
430 make_stunned(1L, FALSE);
432 You(Stunned ? "struggle to keep your balance." : "reel...");
433 dmg = d(ACURR(A_DEX) < 12 ? 6 : 4, 4);
434 if (Half_spell_damage) dmg = (dmg + 1) / 2;
435 make_stunned(HStun + dmg, FALSE);
440 mon_adjust_speed(mtmp, 1, (struct obj *)0);
444 if (mtmp->mhp < mtmp->mhpmax) {
446 pline("%s looks better.", Monnam(mtmp));
447 /* note: player healing does 6d4; this used to do 1d8 */
448 if ((mtmp->mhp += d(3,6)) > mtmp->mhpmax)
449 mtmp->mhp = mtmp->mhpmax;
454 /* prior to 3.4.0 Antimagic was setting the damage to 1--this
455 made the spell virtually harmless to players with magic res. */
457 shieldeff(u.ux, u.uy);
461 You("get a slight %sache.", body_part(HEAD));
463 Your("brain is on fire!");
465 Your("%s suddenly aches painfully!", body_part(HEAD));
467 Your("%s suddenly aches very painfully!", body_part(HEAD));
470 impossible("mcastu: invalid magic spell (%d)", spellnum);
475 if (dmg) mdamageu(mtmp, dmg);
480 cast_cleric_spell(mtmp, dmg, spellnum)
485 if (dmg == 0 && !is_undirected_spell(AD_CLRC, spellnum)) {
486 impossible("cast directed cleric spell (%d) with dmg=0?", spellnum);
492 /* this is physical damage, not magical damage */
493 pline("A sudden geyser slams into you from nowhere!");
495 if (Half_physical_damage) dmg = (dmg + 1) / 2;
497 case CLC_FIRE_PILLAR:
498 pline("A pillar of fire strikes all around you!");
499 if (Fire_resistance) {
500 shieldeff(u.ux, u.uy);
504 if (Half_spell_damage) dmg = (dmg + 1) / 2;
506 (void) burnarmor(&youmonst);
507 destroy_item(SCROLL_CLASS, AD_FIRE);
508 destroy_item(POTION_CLASS, AD_FIRE);
509 destroy_item(SPBOOK_CLASS, AD_FIRE);
510 (void) burn_floor_paper(u.ux, u.uy, TRUE, FALSE);
516 pline("A bolt of lightning strikes down at you from above!");
517 reflects = ureflects("It bounces off your %s%s.", "");
518 if (reflects || Shock_resistance) {
519 shieldeff(u.ux, u.uy);
525 if (Half_spell_damage) dmg = (dmg + 1) / 2;
526 destroy_item(WAND_CLASS, AD_ELEC);
527 destroy_item(RING_CLASS, AD_ELEC);
530 case CLC_CURSE_ITEMS:
531 You_feel("as if you need some help.");
537 /* Try for insects, and if there are none
538 left, go for (sticks to) snakes. -3. */
539 struct permonst *pm = mkclass(S_ANT,0);
540 struct monst *mtmp2 = (struct monst *)0;
541 char let = (pm ? S_ANT : S_SNAKE);
547 quan = (mtmp->m_lev < 2) ? 1 : rnd((int)mtmp->m_lev / 2);
548 if (quan < 3) quan = 3;
549 success = pm ? TRUE : FALSE;
550 for (i = 0; i <= quan; i++) {
551 if (!enexto(&bypos, mtmp->mux, mtmp->muy, mtmp->data))
553 if ((pm = mkclass(let,0)) != 0 &&
554 (mtmp2 = makemon(pm, bypos.x, bypos.y, NO_MM_FLAGS)) != 0) {
556 mtmp2->msleeping = mtmp2->mpeaceful = mtmp2->mtame = 0;
561 * -- message doesn't always make sense for unseen caster (particularly
563 * -- message assumes plural monsters summoned (non-plural should be
564 * very rare, unlike in nasty())
565 * -- message assumes plural monsters seen
568 pline("%s casts at a clump of sticks, but nothing happens.",
570 else if (let == S_SNAKE)
571 pline("%s transforms a clump of sticks into snakes!",
573 else if (Invisible && !perceives(mtmp->data) &&
574 (mtmp->mux != u.ux || mtmp->muy != u.uy))
575 pline("%s summons insects around a spot near you!",
577 else if (Displaced && (mtmp->mux != u.ux || mtmp->muy != u.uy))
578 pline("%s summons insects around your displaced image!",
581 pline("%s summons insects!", Monnam(mtmp));
586 /* note: resists_blnd() doesn't apply here */
588 int num_eyes = eyecount(youmonst.data);
589 pline("Scales cover your %s!",
591 body_part(EYE) : makeplural(body_part(EYE)));
592 make_blinded(Half_spell_damage ? 100L : 200L, FALSE);
593 if (!Blind) Your(vision_clears);
596 impossible("no reason for monster to cast blindness spell?");
599 if (Antimagic || Free_action) {
600 shieldeff(u.ux, u.uy);
602 You("stiffen briefly.");
606 You("are frozen in place!");
607 dmg = 4 + (int)mtmp->m_lev;
608 if (Half_spell_damage) dmg = (dmg + 1) / 2;
613 case CLC_CONFUSE_YOU:
615 shieldeff(u.ux, u.uy);
616 You_feel("momentarily dizzy.");
618 boolean oldprop = !!Confusion;
620 dmg = (int)mtmp->m_lev;
621 if (Half_spell_damage) dmg = (dmg + 1) / 2;
622 make_confused(HConfusion + dmg, TRUE);
624 You_feel("%s!", oldprop ? "trippier" : "trippy");
626 You_feel("%sconfused!", oldprop ? "more " : "");
631 if (mtmp->mhp < mtmp->mhpmax) {
633 pline("%s looks better.", Monnam(mtmp));
634 /* note: player healing does 6d4; this used to do 1d8 */
635 if ((mtmp->mhp += d(3,6)) > mtmp->mhpmax)
636 mtmp->mhp = mtmp->mhpmax;
640 case CLC_OPEN_WOUNDS:
642 shieldeff(u.ux, u.uy);
646 Your("skin itches badly for a moment.");
648 pline("Wounds appear on your body!");
650 pline("Severe wounds appear on your body!");
652 Your("body is covered with painful wounds!");
655 impossible("mcastu: invalid clerical spell (%d)", spellnum);
660 if (dmg) mdamageu(mtmp, dmg);
665 is_undirected_spell(adtyp, spellnum)
669 if (adtyp == AD_SPEL) {
672 case MGC_SUMMON_MONS:
673 case MGC_AGGRAVATION:
681 } else if (adtyp == AD_CLRC) {
693 /* Some spells are useless under some circumstances. */
696 spell_would_be_useless(mtmp, adtyp, spellnum)
701 /* Some spells don't require the player to really be there and can be cast
702 * by the monster when you're invisible, yet still shouldn't be cast when
703 * the monster doesn't even think you're there.
704 * This check isn't quite right because it always uses your real position.
705 * We really want something like "if the monster could see mux, muy".
707 boolean mcouldseeu = couldsee(mtmp->mx, mtmp->my);
709 if (adtyp == AD_SPEL) {
710 /* aggravate monsters, etc. won't be cast by peaceful monsters */
711 if (mtmp->mpeaceful && (spellnum == MGC_AGGRAVATION ||
712 spellnum == MGC_SUMMON_MONS || spellnum == MGC_CLONE_WIZ))
714 /* haste self when already fast */
715 if (mtmp->permspeed == MFAST && spellnum == MGC_HASTE_SELF)
717 /* invisibility when already invisible */
718 if ((mtmp->minvis || mtmp->invis_blkd) && spellnum == MGC_DISAPPEAR)
720 /* peaceful monster won't cast invisibility if you can't see invisible,
721 same as when monsters drink potions of invisibility. This doesn't
722 really make a lot of sense, but lets the player avoid hitting
723 peaceful monsters by mistake */
724 if (mtmp->mpeaceful && !See_invisible && spellnum == MGC_DISAPPEAR)
726 /* healing when already healed */
727 if (mtmp->mhp == mtmp->mhpmax && spellnum == MGC_CURE_SELF)
729 /* don't summon monsters if it doesn't think you're around */
730 if (!mcouldseeu && (spellnum == MGC_SUMMON_MONS ||
731 (!mtmp->iswiz && spellnum == MGC_CLONE_WIZ)))
733 if ((!mtmp->iswiz || flags.no_of_wizards > 1)
734 && spellnum == MGC_CLONE_WIZ)
736 } else if (adtyp == AD_CLRC) {
737 /* summon insects/sticks to snakes won't be cast by peaceful monsters */
738 if (mtmp->mpeaceful && spellnum == CLC_INSECTS)
740 /* healing when already healed */
741 if (mtmp->mhp == mtmp->mhpmax && spellnum == CLC_CURE_SELF)
743 /* don't summon insects if it doesn't think you're around */
744 if (!mcouldseeu && spellnum == CLC_INSECTS)
746 /* blindness spell on blinded player */
747 if (Blinded && spellnum == CLC_BLIND_YOU)
756 /* convert 1..10 to 0..9; add 10 for second group (spell casting) */
757 #define ad_to_typ(k) (10 + (int)k - 1)
760 buzzmu(mtmp, mattk) /* monster uses spell (ranged) */
761 register struct monst *mtmp;
762 register struct attack *mattk;
764 /* don't print constant stream of curse messages for 'normal'
765 spellcasting monsters at range */
766 if (mattk->adtyp > AD_SPC2)
770 cursetxt(mtmp, FALSE);
773 if(lined_up(mtmp) && rn2(3)) {
775 if(mattk->adtyp && (mattk->adtyp < 11)) { /* no cf unsigned >0 */
777 pline("%s zaps you with a %s!", Monnam(mtmp),
778 flash_types[ad_to_typ(mattk->adtyp)]);
779 buzz(-ad_to_typ(mattk->adtyp), (int)mattk->damn,
780 mtmp->mx, mtmp->my, sgn(tbx), sgn(tby));
781 } else impossible("Monster spell %d cast", mattk->adtyp-1);