Get rid of an old known buglist entry C342-46 > Cleric cast lightning doesn't blind as other lightning does
782 lines
21 KiB
C
782 lines
21 KiB
C
/* SCCS Id: @(#)mcastu.c 3.4 2003/01/08 */
|
|
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
|
|
/* NetHack may be freely redistributed. See license for details. */
|
|
|
|
#include "hack.h"
|
|
|
|
/* monster mage spells */
|
|
#define MGC_PSI_BOLT 0
|
|
#define MGC_CURE_SELF 1
|
|
#define MGC_HASTE_SELF 2
|
|
#define MGC_STUN_YOU 3
|
|
#define MGC_DISAPPEAR 4
|
|
#define MGC_WEAKEN_YOU 5
|
|
#define MGC_DESTRY_ARMR 6
|
|
#define MGC_CURSE_ITEMS 7
|
|
#define MGC_AGGRAVATION 8
|
|
#define MGC_SUMMON_MONS 9
|
|
#define MGC_CLONE_WIZ 10
|
|
#define MGC_DEATH_TOUCH 11
|
|
|
|
/* monster cleric spells */
|
|
#define CLC_OPEN_WOUNDS 0
|
|
#define CLC_CURE_SELF 1
|
|
#define CLC_CONFUSE_YOU 2
|
|
#define CLC_PARALYZE 3
|
|
#define CLC_BLIND_YOU 4
|
|
#define CLC_INSECTS 5
|
|
#define CLC_CURSE_ITEMS 6
|
|
#define CLC_LIGHTNING 7
|
|
#define CLC_FIRE_PILLAR 8
|
|
#define CLC_GEYSER 9
|
|
|
|
STATIC_DCL void FDECL(cursetxt,(struct monst *,BOOLEAN_P));
|
|
STATIC_DCL int FDECL(choose_magic_spell, (int));
|
|
STATIC_DCL int FDECL(choose_clerical_spell, (int));
|
|
STATIC_DCL void FDECL(cast_wizard_spell,(struct monst *, int,int));
|
|
STATIC_DCL void FDECL(cast_cleric_spell,(struct monst *, int,int));
|
|
STATIC_DCL boolean FDECL(is_undirected_spell,(unsigned int,int));
|
|
STATIC_DCL boolean FDECL(spell_would_be_useless,(struct monst *,unsigned int,int));
|
|
|
|
extern const char * const flash_types[]; /* from zap.c */
|
|
|
|
/* feedback when frustrated monster couldn't cast a spell */
|
|
STATIC_OVL
|
|
void
|
|
cursetxt(mtmp, undirected)
|
|
struct monst *mtmp;
|
|
boolean undirected;
|
|
{
|
|
if (canseemon(mtmp) && couldsee(mtmp->mx, mtmp->my)) {
|
|
const char *point_msg; /* spellcasting monsters are impolite */
|
|
|
|
if (undirected)
|
|
point_msg = "all around, then curses";
|
|
else if ((Invis && !perceives(mtmp->data) &&
|
|
(mtmp->mux != u.ux || mtmp->muy != u.uy)) ||
|
|
(youmonst.m_ap_type == M_AP_OBJECT &&
|
|
youmonst.mappearance == STRANGE_OBJECT) ||
|
|
u.uundetected)
|
|
point_msg = "and curses in your general direction";
|
|
else if (Displaced && (mtmp->mux != u.ux || mtmp->muy != u.uy))
|
|
point_msg = "and curses at your displaced image";
|
|
else
|
|
point_msg = "at you, then curses";
|
|
|
|
pline("%s points %s.", Monnam(mtmp), point_msg);
|
|
} else if ((!(moves % 4) || !rn2(4))) {
|
|
if (!Deaf) Norep("You hear a mumbled curse.");
|
|
}
|
|
}
|
|
|
|
/* convert a level based random selection into a specific mage spell;
|
|
inappropriate choices will be screened out by spell_would_be_useless() */
|
|
STATIC_OVL int
|
|
choose_magic_spell(spellval)
|
|
int spellval;
|
|
{
|
|
switch (spellval) {
|
|
case 22:
|
|
case 21:
|
|
case 20:
|
|
return MGC_DEATH_TOUCH;
|
|
case 19:
|
|
case 18:
|
|
return MGC_CLONE_WIZ;
|
|
case 17:
|
|
case 16:
|
|
case 15:
|
|
return MGC_SUMMON_MONS;
|
|
case 14:
|
|
case 13:
|
|
return MGC_AGGRAVATION;
|
|
case 12:
|
|
case 11:
|
|
case 10:
|
|
return MGC_CURSE_ITEMS;
|
|
case 9:
|
|
case 8:
|
|
return MGC_DESTRY_ARMR;
|
|
case 7:
|
|
case 6:
|
|
return MGC_WEAKEN_YOU;
|
|
case 5:
|
|
case 4:
|
|
return MGC_DISAPPEAR;
|
|
case 3:
|
|
return MGC_STUN_YOU;
|
|
case 2:
|
|
return MGC_HASTE_SELF;
|
|
case 1:
|
|
return MGC_CURE_SELF;
|
|
case 0:
|
|
default:
|
|
return MGC_PSI_BOLT;
|
|
}
|
|
}
|
|
|
|
/* convert a level based random selection into a specific cleric spell */
|
|
STATIC_OVL int
|
|
choose_clerical_spell(spellnum)
|
|
int spellnum;
|
|
{
|
|
switch (spellnum) {
|
|
case 13:
|
|
return CLC_GEYSER;
|
|
case 12:
|
|
return CLC_FIRE_PILLAR;
|
|
case 11:
|
|
return CLC_LIGHTNING;
|
|
case 10:
|
|
case 9:
|
|
return CLC_CURSE_ITEMS;
|
|
case 8:
|
|
return CLC_INSECTS;
|
|
case 7:
|
|
case 6:
|
|
return CLC_BLIND_YOU;
|
|
case 5:
|
|
case 4:
|
|
return CLC_PARALYZE;
|
|
case 3:
|
|
case 2:
|
|
return CLC_CONFUSE_YOU;
|
|
case 1:
|
|
return CLC_CURE_SELF;
|
|
case 0:
|
|
default:
|
|
return CLC_OPEN_WOUNDS;
|
|
}
|
|
}
|
|
|
|
/* return values:
|
|
* 1: successful spell
|
|
* 0: unsuccessful spell
|
|
*/
|
|
int
|
|
castmu(mtmp, mattk, thinks_it_foundyou, foundyou)
|
|
register struct monst *mtmp;
|
|
register struct attack *mattk;
|
|
boolean thinks_it_foundyou;
|
|
boolean foundyou;
|
|
{
|
|
int dmg, ml = mtmp->m_lev;
|
|
int ret;
|
|
int spellnum = 0;
|
|
|
|
/* Three cases:
|
|
* -- monster is attacking you. Search for a useful spell.
|
|
* -- monster thinks it's attacking you. Search for a useful spell,
|
|
* without checking for undirected. If the spell found is directed,
|
|
* it fails with cursetxt() and loss of mspec_used.
|
|
* -- monster isn't trying to attack. Select a spell once. Don't keep
|
|
* searching; if that spell is not useful (or if it's directed),
|
|
* return and do something else.
|
|
* Since most spells are directed, this means that a monster that isn't
|
|
* attacking casts spells only a small portion of the time that an
|
|
* attacking monster does.
|
|
*/
|
|
if ((mattk->adtyp == AD_SPEL || mattk->adtyp == AD_CLRC) && ml) {
|
|
int cnt = 40;
|
|
|
|
do {
|
|
spellnum = rn2(ml);
|
|
if (mattk->adtyp == AD_SPEL)
|
|
spellnum = choose_magic_spell(spellnum);
|
|
else
|
|
spellnum = choose_clerical_spell(spellnum);
|
|
/* not trying to attack? don't allow directed spells */
|
|
if (!thinks_it_foundyou) {
|
|
if (!is_undirected_spell(mattk->adtyp, spellnum) ||
|
|
spell_would_be_useless(mtmp, mattk->adtyp, spellnum)) {
|
|
if (foundyou)
|
|
impossible("spellcasting monster found you and doesn't know it?");
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
} while(--cnt > 0 &&
|
|
spell_would_be_useless(mtmp, mattk->adtyp, spellnum));
|
|
if (cnt == 0) return 0;
|
|
}
|
|
|
|
/* monster unable to cast spells? */
|
|
if(mtmp->mcan || mtmp->mspec_used || !ml) {
|
|
cursetxt(mtmp, is_undirected_spell(mattk->adtyp, spellnum));
|
|
return(0);
|
|
}
|
|
|
|
if (mattk->adtyp == AD_SPEL || mattk->adtyp == AD_CLRC) {
|
|
mtmp->mspec_used = 10 - mtmp->m_lev;
|
|
if (mtmp->mspec_used < 2) mtmp->mspec_used = 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. */
|
|
if (!foundyou && thinks_it_foundyou &&
|
|
!is_undirected_spell(mattk->adtyp, spellnum)) {
|
|
pline("%s casts a spell at %s!",
|
|
canseemon(mtmp) ? Monnam(mtmp) : "Something",
|
|
levl[mtmp->mux][mtmp->muy].typ == WATER
|
|
? "empty water" : "thin air");
|
|
return(0);
|
|
}
|
|
|
|
nomul(0);
|
|
if(rn2(ml*10) < (mtmp->mconf ? 100 : 20)) { /* fumbled attack */
|
|
if (canseemon(mtmp) && !Deaf)
|
|
pline_The("air crackles around %s.", mon_nam(mtmp));
|
|
return(0);
|
|
}
|
|
if (canspotmon(mtmp) || !is_undirected_spell(mattk->adtyp, spellnum)) {
|
|
pline("%s casts a spell%s!",
|
|
canspotmon(mtmp) ? Monnam(mtmp) : "Something",
|
|
is_undirected_spell(mattk->adtyp, spellnum) ? "" :
|
|
(Invisible && !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");
|
|
}
|
|
|
|
/*
|
|
* As these are spells, the damage is related to the level
|
|
* of the monster casting the spell.
|
|
*/
|
|
if (!foundyou) {
|
|
dmg = 0;
|
|
if (mattk->adtyp != AD_SPEL && mattk->adtyp != AD_CLRC) {
|
|
impossible(
|
|
"%s casting non-hand-to-hand version of hand-to-hand spell %d?",
|
|
Monnam(mtmp), mattk->adtyp);
|
|
return(0);
|
|
}
|
|
} else if (mattk->damd)
|
|
dmg = d((int)((ml/2) + mattk->damn), (int)mattk->damd);
|
|
else dmg = d((int)((ml/2) + 1), 6);
|
|
if (Half_spell_damage) dmg = (dmg+1) / 2;
|
|
|
|
ret = 1;
|
|
|
|
switch (mattk->adtyp) {
|
|
|
|
case AD_FIRE:
|
|
pline("You're enveloped in flames.");
|
|
if(Fire_resistance) {
|
|
shieldeff(u.ux, u.uy);
|
|
pline("But you resist the effects.");
|
|
dmg = 0;
|
|
}
|
|
burn_away_slime();
|
|
break;
|
|
case AD_COLD:
|
|
pline("You're covered in frost.");
|
|
if(Cold_resistance) {
|
|
shieldeff(u.ux, u.uy);
|
|
pline("But you resist the effects.");
|
|
dmg = 0;
|
|
}
|
|
break;
|
|
case AD_MAGM:
|
|
You("are hit by a shower of missiles!");
|
|
if(Antimagic) {
|
|
shieldeff(u.ux, u.uy);
|
|
pline_The("missiles bounce off!");
|
|
dmg = 0;
|
|
} else dmg = d((int)mtmp->m_lev/2 + 1,6);
|
|
break;
|
|
case AD_SPEL: /* wizard spell */
|
|
case AD_CLRC: /* clerical spell */
|
|
{
|
|
if (mattk->adtyp == AD_SPEL)
|
|
cast_wizard_spell(mtmp, dmg, spellnum);
|
|
else
|
|
cast_cleric_spell(mtmp, dmg, spellnum);
|
|
dmg = 0; /* done by the spell casting functions */
|
|
break;
|
|
}
|
|
}
|
|
if(dmg) mdamageu(mtmp, dmg);
|
|
return(ret);
|
|
}
|
|
|
|
/* monster wizard and cleric spellcasting functions */
|
|
/*
|
|
If dmg is zero, then the monster is not casting at you.
|
|
If the monster is intentionally not casting at you, we have previously
|
|
called spell_would_be_useless() and spellnum should always be a valid
|
|
undirected spell.
|
|
If you modify either of these, be sure to change is_undirected_spell()
|
|
and spell_would_be_useless().
|
|
*/
|
|
STATIC_OVL
|
|
void
|
|
cast_wizard_spell(mtmp, dmg, spellnum)
|
|
struct monst *mtmp;
|
|
int dmg;
|
|
int spellnum;
|
|
{
|
|
if (dmg == 0 && !is_undirected_spell(AD_SPEL, spellnum)) {
|
|
impossible("cast directed wizard spell (%d) with dmg=0?", spellnum);
|
|
return;
|
|
}
|
|
|
|
switch (spellnum) {
|
|
case MGC_DEATH_TOUCH:
|
|
pline("Oh no, %s's using the touch of death!", mhe(mtmp));
|
|
if (nonliving(youmonst.data) || is_demon(youmonst.data)) {
|
|
You("seem no deader than before.");
|
|
} else if (!Antimagic && rn2(mtmp->m_lev) > 12) {
|
|
if (Hallucination) {
|
|
You("have an out of body experience.");
|
|
} else {
|
|
killer.format = KILLED_BY_AN;
|
|
Strcpy(killer.name, "touch of death");
|
|
done(DIED);
|
|
}
|
|
} else {
|
|
if (Antimagic) shieldeff(u.ux, u.uy);
|
|
pline("Lucky for you, it didn't work!");
|
|
}
|
|
dmg = 0;
|
|
break;
|
|
case MGC_CLONE_WIZ:
|
|
if (mtmp->iswiz && context.no_of_wizards == 1) {
|
|
pline("Double Trouble...");
|
|
clonewiz();
|
|
dmg = 0;
|
|
} else
|
|
impossible("bad wizard cloning?");
|
|
break;
|
|
case MGC_SUMMON_MONS:
|
|
{
|
|
int count;
|
|
|
|
count = nasty(mtmp); /* summon something nasty */
|
|
if (mtmp->iswiz)
|
|
verbalize("Destroy the thief, my pet%s!", plur(count));
|
|
else {
|
|
const char *mappear =
|
|
(count == 1) ? "A monster appears" : "Monsters appear";
|
|
|
|
/* messages not quite right if plural monsters created but
|
|
only a single monster is seen */
|
|
if (Invisible && !perceives(mtmp->data) &&
|
|
(mtmp->mux != u.ux || mtmp->muy != u.uy))
|
|
pline("%s around a spot near you!", mappear);
|
|
else if (Displaced && (mtmp->mux != u.ux || mtmp->muy != u.uy))
|
|
pline("%s around your displaced image!", mappear);
|
|
else
|
|
pline("%s from nowhere!", mappear);
|
|
}
|
|
dmg = 0;
|
|
break;
|
|
}
|
|
case MGC_AGGRAVATION:
|
|
You_feel("that monsters are aware of your presence.");
|
|
aggravate();
|
|
dmg = 0;
|
|
break;
|
|
case MGC_CURSE_ITEMS:
|
|
You_feel("as if you need some help.");
|
|
rndcurse();
|
|
dmg = 0;
|
|
break;
|
|
case MGC_DESTRY_ARMR:
|
|
if (Antimagic) {
|
|
shieldeff(u.ux, u.uy);
|
|
pline("A field of force surrounds you!");
|
|
} else if (!destroy_arm(some_armor(&youmonst))) {
|
|
Your("skin itches.");
|
|
}
|
|
dmg = 0;
|
|
break;
|
|
case MGC_WEAKEN_YOU: /* drain strength */
|
|
if (Antimagic) {
|
|
shieldeff(u.ux, u.uy);
|
|
You_feel("momentarily weakened.");
|
|
} else {
|
|
You("suddenly feel weaker!");
|
|
dmg = mtmp->m_lev - 6;
|
|
if (Half_spell_damage) dmg = (dmg + 1) / 2;
|
|
losestr(rnd(dmg));
|
|
if (u.uhp < 1)
|
|
done_in_by(mtmp);
|
|
}
|
|
dmg = 0;
|
|
break;
|
|
case MGC_DISAPPEAR: /* makes self invisible */
|
|
if (!mtmp->minvis && !mtmp->invis_blkd) {
|
|
if (canseemon(mtmp))
|
|
pline("%s suddenly %s!", Monnam(mtmp),
|
|
!See_invisible ? "disappears" : "becomes transparent");
|
|
mon_set_minvis(mtmp);
|
|
dmg = 0;
|
|
} else
|
|
impossible("no reason for monster to cast disappear spell?");
|
|
break;
|
|
case MGC_STUN_YOU:
|
|
if (Antimagic || Free_action) {
|
|
shieldeff(u.ux, u.uy);
|
|
if (!Stunned)
|
|
You_feel("momentarily disoriented.");
|
|
make_stunned(1L, FALSE);
|
|
} else {
|
|
You(Stunned ? "struggle to keep your balance." : "reel...");
|
|
dmg = d(ACURR(A_DEX) < 12 ? 6 : 4, 4);
|
|
if (Half_spell_damage) dmg = (dmg + 1) / 2;
|
|
make_stunned(HStun + dmg, FALSE);
|
|
}
|
|
dmg = 0;
|
|
break;
|
|
case MGC_HASTE_SELF:
|
|
mon_adjust_speed(mtmp, 1, (struct obj *)0);
|
|
dmg = 0;
|
|
break;
|
|
case MGC_CURE_SELF:
|
|
if (mtmp->mhp < mtmp->mhpmax) {
|
|
if (canseemon(mtmp))
|
|
pline("%s looks better.", Monnam(mtmp));
|
|
/* note: player healing does 6d4; this used to do 1d8 */
|
|
if ((mtmp->mhp += d(3,6)) > mtmp->mhpmax)
|
|
mtmp->mhp = mtmp->mhpmax;
|
|
dmg = 0;
|
|
}
|
|
break;
|
|
case MGC_PSI_BOLT:
|
|
/* prior to 3.4.0 Antimagic was setting the damage to 1--this
|
|
made the spell virtually harmless to players with magic res. */
|
|
if (Antimagic) {
|
|
shieldeff(u.ux, u.uy);
|
|
dmg = (dmg + 1) / 2;
|
|
}
|
|
if (dmg <= 5)
|
|
You("get a slight %sache.", body_part(HEAD));
|
|
else if (dmg <= 10)
|
|
Your("brain is on fire!");
|
|
else if (dmg <= 20)
|
|
Your("%s suddenly aches painfully!", body_part(HEAD));
|
|
else
|
|
Your("%s suddenly aches very painfully!", body_part(HEAD));
|
|
break;
|
|
default:
|
|
impossible("mcastu: invalid magic spell (%d)", spellnum);
|
|
dmg = 0;
|
|
break;
|
|
}
|
|
|
|
if (dmg) mdamageu(mtmp, dmg);
|
|
}
|
|
|
|
STATIC_OVL
|
|
void
|
|
cast_cleric_spell(mtmp, dmg, spellnum)
|
|
struct monst *mtmp;
|
|
int dmg;
|
|
int spellnum;
|
|
{
|
|
if (dmg == 0 && !is_undirected_spell(AD_CLRC, spellnum)) {
|
|
impossible("cast directed cleric spell (%d) with dmg=0?", spellnum);
|
|
return;
|
|
}
|
|
|
|
switch (spellnum) {
|
|
case CLC_GEYSER:
|
|
/* this is physical damage (force not heat),
|
|
* not magical damage or fire damage
|
|
*/
|
|
pline("A sudden geyser slams into you from nowhere!");
|
|
dmg = d(8, 6);
|
|
if (Half_physical_damage) dmg = (dmg + 1) / 2;
|
|
break;
|
|
case CLC_FIRE_PILLAR:
|
|
pline("A pillar of fire strikes all around you!");
|
|
if (Fire_resistance) {
|
|
shieldeff(u.ux, u.uy);
|
|
dmg = 0;
|
|
} else
|
|
dmg = d(8, 6);
|
|
if (Half_spell_damage) dmg = (dmg + 1) / 2;
|
|
burn_away_slime();
|
|
(void) burnarmor(&youmonst);
|
|
destroy_item(SCROLL_CLASS, AD_FIRE);
|
|
destroy_item(POTION_CLASS, AD_FIRE);
|
|
destroy_item(SPBOOK_CLASS, AD_FIRE);
|
|
(void) burn_floor_paper(u.ux, u.uy, TRUE, FALSE);
|
|
break;
|
|
case CLC_LIGHTNING:
|
|
{
|
|
boolean reflects;
|
|
|
|
pline("A bolt of lightning strikes down at you from above!");
|
|
reflects = ureflects("It bounces off your %s%s.", "");
|
|
if (reflects || Shock_resistance) {
|
|
shieldeff(u.ux, u.uy);
|
|
dmg = 0;
|
|
if (reflects)
|
|
break;
|
|
} else
|
|
dmg = d(8, 6);
|
|
if (Half_spell_damage) dmg = (dmg + 1) / 2;
|
|
destroy_item(WAND_CLASS, AD_ELEC);
|
|
destroy_item(RING_CLASS, AD_ELEC);
|
|
(void) flashburn((long)rnd(100));
|
|
break;
|
|
}
|
|
case CLC_CURSE_ITEMS:
|
|
You_feel("as if you need some help.");
|
|
rndcurse();
|
|
dmg = 0;
|
|
break;
|
|
case CLC_INSECTS:
|
|
{
|
|
/* Try for insects, and if there are none
|
|
left, go for (sticks to) snakes. -3. */
|
|
struct permonst *pm = mkclass(S_ANT,0);
|
|
struct monst *mtmp2 = (struct monst *)0;
|
|
char let = (pm ? S_ANT : S_SNAKE);
|
|
boolean success;
|
|
int i;
|
|
coord bypos;
|
|
int quan;
|
|
|
|
quan = (mtmp->m_lev < 2) ? 1 : rnd((int)mtmp->m_lev / 2);
|
|
if (quan < 3) quan = 3;
|
|
success = pm ? TRUE : FALSE;
|
|
for (i = 0; i <= quan; i++) {
|
|
if (!enexto(&bypos, mtmp->mux, mtmp->muy, mtmp->data))
|
|
break;
|
|
if ((pm = mkclass(let,0)) != 0 &&
|
|
(mtmp2 = makemon(pm, bypos.x, bypos.y, NO_MM_FLAGS)) != 0) {
|
|
success = TRUE;
|
|
mtmp2->msleeping = mtmp2->mpeaceful = mtmp2->mtame = 0;
|
|
set_malign(mtmp2);
|
|
}
|
|
}
|
|
/* Not quite right:
|
|
* -- message doesn't always make sense for unseen caster (particularly
|
|
* the first message)
|
|
* -- message assumes plural monsters summoned (non-plural should be
|
|
* very rare, unlike in nasty())
|
|
* -- message assumes plural monsters seen
|
|
*/
|
|
if (!success)
|
|
pline("%s casts at a clump of sticks, but nothing happens.",
|
|
Monnam(mtmp));
|
|
else if (let == S_SNAKE)
|
|
pline("%s transforms a clump of sticks into snakes!",
|
|
Monnam(mtmp));
|
|
else if (Invisible && !perceives(mtmp->data) &&
|
|
(mtmp->mux != u.ux || mtmp->muy != u.uy))
|
|
pline("%s summons insects around a spot near you!",
|
|
Monnam(mtmp));
|
|
else if (Displaced && (mtmp->mux != u.ux || mtmp->muy != u.uy))
|
|
pline("%s summons insects around your displaced image!",
|
|
Monnam(mtmp));
|
|
else
|
|
pline("%s summons insects!", Monnam(mtmp));
|
|
dmg = 0;
|
|
break;
|
|
}
|
|
case CLC_BLIND_YOU:
|
|
/* note: resists_blnd() doesn't apply here */
|
|
if (!Blinded) {
|
|
int num_eyes = eyecount(youmonst.data);
|
|
pline("Scales cover your %s!",
|
|
(num_eyes == 1) ?
|
|
body_part(EYE) : makeplural(body_part(EYE)));
|
|
make_blinded(Half_spell_damage ? 100L : 200L, FALSE);
|
|
if (!Blind) Your(vision_clears);
|
|
dmg = 0;
|
|
} else
|
|
impossible("no reason for monster to cast blindness spell?");
|
|
break;
|
|
case CLC_PARALYZE:
|
|
if (Antimagic || Free_action) {
|
|
shieldeff(u.ux, u.uy);
|
|
if (multi >= 0)
|
|
You("stiffen briefly.");
|
|
nomul(-1);
|
|
} else {
|
|
if (multi >= 0)
|
|
You("are frozen in place!");
|
|
dmg = 4 + (int)mtmp->m_lev;
|
|
if (Half_spell_damage) dmg = (dmg + 1) / 2;
|
|
nomul(-dmg);
|
|
}
|
|
dmg = 0;
|
|
break;
|
|
case CLC_CONFUSE_YOU:
|
|
if (Antimagic) {
|
|
shieldeff(u.ux, u.uy);
|
|
You_feel("momentarily dizzy.");
|
|
} else {
|
|
boolean oldprop = !!Confusion;
|
|
|
|
dmg = (int)mtmp->m_lev;
|
|
if (Half_spell_damage) dmg = (dmg + 1) / 2;
|
|
make_confused(HConfusion + dmg, TRUE);
|
|
if (Hallucination)
|
|
You_feel("%s!", oldprop ? "trippier" : "trippy");
|
|
else
|
|
You_feel("%sconfused!", oldprop ? "more " : "");
|
|
}
|
|
dmg = 0;
|
|
break;
|
|
case CLC_CURE_SELF:
|
|
if (mtmp->mhp < mtmp->mhpmax) {
|
|
if (canseemon(mtmp))
|
|
pline("%s looks better.", Monnam(mtmp));
|
|
/* note: player healing does 6d4; this used to do 1d8 */
|
|
if ((mtmp->mhp += d(3,6)) > mtmp->mhpmax)
|
|
mtmp->mhp = mtmp->mhpmax;
|
|
dmg = 0;
|
|
}
|
|
break;
|
|
case CLC_OPEN_WOUNDS:
|
|
if (Antimagic) {
|
|
shieldeff(u.ux, u.uy);
|
|
dmg = (dmg + 1) / 2;
|
|
}
|
|
if (dmg <= 5)
|
|
Your("skin itches badly for a moment.");
|
|
else if (dmg <= 10)
|
|
pline("Wounds appear on your body!");
|
|
else if (dmg <= 20)
|
|
pline("Severe wounds appear on your body!");
|
|
else
|
|
Your("body is covered with painful wounds!");
|
|
break;
|
|
default:
|
|
impossible("mcastu: invalid clerical spell (%d)", spellnum);
|
|
dmg = 0;
|
|
break;
|
|
}
|
|
|
|
if (dmg) mdamageu(mtmp, dmg);
|
|
}
|
|
|
|
STATIC_DCL
|
|
boolean
|
|
is_undirected_spell(adtyp, spellnum)
|
|
unsigned int adtyp;
|
|
int spellnum;
|
|
{
|
|
if (adtyp == AD_SPEL) {
|
|
switch (spellnum) {
|
|
case MGC_CLONE_WIZ:
|
|
case MGC_SUMMON_MONS:
|
|
case MGC_AGGRAVATION:
|
|
case MGC_DISAPPEAR:
|
|
case MGC_HASTE_SELF:
|
|
case MGC_CURE_SELF:
|
|
return TRUE;
|
|
default:
|
|
break;
|
|
}
|
|
} else if (adtyp == AD_CLRC) {
|
|
switch (spellnum) {
|
|
case CLC_INSECTS:
|
|
case CLC_CURE_SELF:
|
|
return TRUE;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/* Some spells are useless under some circumstances. */
|
|
STATIC_DCL
|
|
boolean
|
|
spell_would_be_useless(mtmp, adtyp, spellnum)
|
|
struct monst *mtmp;
|
|
unsigned int adtyp;
|
|
int spellnum;
|
|
{
|
|
/* Some spells don't require the player to really be there and can be cast
|
|
* by the monster when you're invisible, yet still shouldn't be cast when
|
|
* the monster doesn't even think you're there.
|
|
* This check isn't quite right because it always uses your real position.
|
|
* We really want something like "if the monster could see mux, muy".
|
|
*/
|
|
boolean mcouldseeu = couldsee(mtmp->mx, mtmp->my);
|
|
|
|
if (adtyp == AD_SPEL) {
|
|
/* aggravate monsters, etc. won't be cast by peaceful monsters */
|
|
if (mtmp->mpeaceful && (spellnum == MGC_AGGRAVATION ||
|
|
spellnum == MGC_SUMMON_MONS || spellnum == MGC_CLONE_WIZ))
|
|
return TRUE;
|
|
/* haste self when already fast */
|
|
if (mtmp->permspeed == MFAST && spellnum == MGC_HASTE_SELF)
|
|
return TRUE;
|
|
/* invisibility when already invisible */
|
|
if ((mtmp->minvis || mtmp->invis_blkd) && spellnum == MGC_DISAPPEAR)
|
|
return TRUE;
|
|
/* peaceful monster won't cast invisibility if you can't see invisible,
|
|
same as when monsters drink potions of invisibility. This doesn't
|
|
really make a lot of sense, but lets the player avoid hitting
|
|
peaceful monsters by mistake */
|
|
if (mtmp->mpeaceful && !See_invisible && spellnum == MGC_DISAPPEAR)
|
|
return TRUE;
|
|
/* healing when already healed */
|
|
if (mtmp->mhp == mtmp->mhpmax && spellnum == MGC_CURE_SELF)
|
|
return TRUE;
|
|
/* don't summon monsters if it doesn't think you're around */
|
|
if (!mcouldseeu && (spellnum == MGC_SUMMON_MONS ||
|
|
(!mtmp->iswiz && spellnum == MGC_CLONE_WIZ)))
|
|
return TRUE;
|
|
if ((!mtmp->iswiz || context.no_of_wizards > 1)
|
|
&& spellnum == MGC_CLONE_WIZ)
|
|
return TRUE;
|
|
} else if (adtyp == AD_CLRC) {
|
|
/* summon insects/sticks to snakes won't be cast by peaceful monsters */
|
|
if (mtmp->mpeaceful && spellnum == CLC_INSECTS)
|
|
return TRUE;
|
|
/* healing when already healed */
|
|
if (mtmp->mhp == mtmp->mhpmax && spellnum == CLC_CURE_SELF)
|
|
return TRUE;
|
|
/* don't summon insects if it doesn't think you're around */
|
|
if (!mcouldseeu && spellnum == CLC_INSECTS)
|
|
return TRUE;
|
|
/* blindness spell on blinded player */
|
|
if (Blinded && spellnum == CLC_BLIND_YOU)
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/* convert 1..10 to 0..9; add 10 for second group (spell casting) */
|
|
#define ad_to_typ(k) (10 + (int)k - 1)
|
|
|
|
int
|
|
buzzmu(mtmp, mattk) /* monster uses spell (ranged) */
|
|
register struct monst *mtmp;
|
|
register struct attack *mattk;
|
|
{
|
|
/* don't print constant stream of curse messages for 'normal'
|
|
spellcasting monsters at range */
|
|
if (mattk->adtyp > AD_SPC2)
|
|
return(0);
|
|
|
|
if (mtmp->mcan) {
|
|
cursetxt(mtmp, FALSE);
|
|
return(0);
|
|
}
|
|
if(lined_up(mtmp) && rn2(3)) {
|
|
nomul(0);
|
|
if(mattk->adtyp && (mattk->adtyp < 11)) { /* no cf unsigned >0 */
|
|
if(canseemon(mtmp))
|
|
pline("%s zaps you with a %s!", Monnam(mtmp),
|
|
flash_types[ad_to_typ(mattk->adtyp)]);
|
|
buzz(-ad_to_typ(mattk->adtyp), (int)mattk->damn,
|
|
mtmp->mx, mtmp->my, sgn(tbx), sgn(tby));
|
|
} else impossible("Monster spell %d cast", mattk->adtyp-1);
|
|
}
|
|
return(1);
|
|
}
|
|
|
|
/*mcastu.c*/
|