diff --git a/include/mcastu.h b/include/mcastu.h index fd59871c9..dd1e37420 100644 --- a/include/mcastu.h +++ b/include/mcastu.h @@ -6,30 +6,30 @@ #define MCF_HOSTILE 0x0004 /* cast by hostile monsters only */ #if defined(MCASTU_ENUM) -#define MONSPELL(def, flags) MCAST_##def +#define MONSPELL(def, lvl, flags) MCAST_##def #elif defined(MCASTU_INIT) -#define MONSPELL(def, flags) flags +#define MONSPELL(def, lvl, flags) { lvl, flags } #endif -MONSPELL(PSI_BOLT, MCF_HOSTILE|MCF_SIGHT), -MONSPELL(OPEN_WOUNDS, MCF_HOSTILE|MCF_SIGHT), -MONSPELL(LIGHTNING, MCF_HOSTILE|MCF_SIGHT), -MONSPELL(FIRE_PILLAR, MCF_HOSTILE|MCF_SIGHT), -MONSPELL(GEYSER, MCF_HOSTILE|MCF_SIGHT), -MONSPELL(DEATH_TOUCH, MCF_HOSTILE|MCF_SIGHT), -MONSPELL(CURE_SELF, MCF_INDIRECT), -MONSPELL(HASTE_SELF, MCF_INDIRECT), -MONSPELL(DISAPPEAR, MCF_INDIRECT), -MONSPELL(AGGRAVATION, MCF_INDIRECT|MCF_HOSTILE|MCF_SIGHT), -MONSPELL(STUN_YOU, MCF_HOSTILE|MCF_SIGHT), -MONSPELL(WEAKEN_YOU, MCF_HOSTILE|MCF_SIGHT), -MONSPELL(CONFUSE_YOU, MCF_HOSTILE|MCF_SIGHT), -MONSPELL(PARALYZE, MCF_HOSTILE|MCF_SIGHT), -MONSPELL(BLIND_YOU, MCF_HOSTILE|MCF_SIGHT), -MONSPELL(DESTRY_ARMR, MCF_HOSTILE|MCF_SIGHT), -MONSPELL(CURSE_ITEMS, MCF_HOSTILE|MCF_SIGHT), -MONSPELL(INSECTS, MCF_HOSTILE|MCF_INDIRECT|MCF_SIGHT), -MONSPELL(SUMMON_MONS, MCF_HOSTILE|MCF_INDIRECT|MCF_SIGHT), -MONSPELL(CLONE_WIZ, MCF_HOSTILE|MCF_INDIRECT|MCF_SIGHT), +MONSPELL(PSI_BOLT, 0, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(OPEN_WOUNDS, 0, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(CURE_SELF, 1, MCF_INDIRECT), +MONSPELL(HASTE_SELF, 2, MCF_INDIRECT), +MONSPELL(CONFUSE_YOU, 2, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(STUN_YOU, 3, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(DISAPPEAR, 4, MCF_INDIRECT), +MONSPELL(PARALYZE, 4, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(BLIND_YOU, 6, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(WEAKEN_YOU, 6, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(DESTRY_ARMR, 8, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(INSECTS, 8, MCF_HOSTILE|MCF_INDIRECT|MCF_SIGHT), +MONSPELL(CURSE_ITEMS, 10, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(LIGHTNING, 11, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(FIRE_PILLAR, 12, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(GEYSER, 13, MCF_HOSTILE|MCF_SIGHT), +MONSPELL(AGGRAVATION, 13, MCF_INDIRECT|MCF_HOSTILE|MCF_SIGHT), +MONSPELL(SUMMON_MONS, 15, MCF_HOSTILE|MCF_INDIRECT|MCF_SIGHT), +MONSPELL(CLONE_WIZ, 18, MCF_HOSTILE|MCF_INDIRECT|MCF_SIGHT), +MONSPELL(DEATH_TOUCH, 20, MCF_HOSTILE|MCF_SIGHT), #undef MONSPELL diff --git a/src/mcastu.c b/src/mcastu.c index 0d8af9d68..34d839906 100644 --- a/src/mcastu.c +++ b/src/mcastu.c @@ -11,15 +11,32 @@ enum mcast_spells { }; #undef MCASTU_ENUM +struct _mcast_data { + int level; + int flags; +}; + #define MCASTU_INIT -static int mcast_flags[] = { +static struct _mcast_data mcast_data[] = { #include "mcastu.h" }; #undef MCASTU_INIT +/* spell lists for specific monster casters */ +/* the spells in the list should be in ascending level order */ +static int mon_cleric_spells[] = { + MCAST_OPEN_WOUNDS, MCAST_CURE_SELF, MCAST_CONFUSE_YOU, MCAST_PARALYZE, + MCAST_BLIND_YOU, MCAST_INSECTS, MCAST_CURSE_ITEMS, MCAST_LIGHTNING, + MCAST_FIRE_PILLAR, MCAST_GEYSER +}; +static int mon_wizard_spells[] = { + MCAST_PSI_BOLT, MCAST_CURE_SELF, MCAST_HASTE_SELF, MCAST_STUN_YOU, + MCAST_DISAPPEAR, MCAST_WEAKEN_YOU, MCAST_DESTRY_ARMR, MCAST_CURSE_ITEMS, + MCAST_AGGRAVATION, MCAST_SUMMON_MONS, MCAST_CLONE_WIZ, MCAST_DEATH_TOUCH +}; + staticfn void cursetxt(struct monst *, boolean); -staticfn int choose_magic_spell(struct monst *); -staticfn int choose_clerical_spell(struct monst *); +staticfn int choose_monster_spell(struct monst *, int); staticfn int m_cure_self(struct monst *, int); staticfn void mcast_death_touch(struct monst *); staticfn void mcast_clone_wiz(struct monst *); @@ -67,107 +84,42 @@ cursetxt(struct monst *mtmp, boolean undirected) } } -/* convert a level-based random selection into a specific mage spell; - inappropriate choices will be screened out by spell_would_be_useless() */ +/* choose a spell for monster to cast */ staticfn int -choose_magic_spell(struct monst *mtmp) +choose_monster_spell(struct monst *mtmp, int adtyp) { - int spellval = rn2(mtmp->m_lev); + int *list = NULL; + int i, spellval, len = 0; + int maxlev; - /* for 3.4.3 and earlier, val greater than 22 selected default spell */ - while (spellval > 24 && rn2(25)) - spellval = rn2(spellval); + /* which spell list to use? */ + if (adtyp == AD_SPEL) { + list = mon_wizard_spells; + len = SIZE(mon_wizard_spells); + } else if (adtyp == AD_CLRC) { + list = mon_cleric_spells; + len = SIZE(mon_cleric_spells); + } - switch (spellval) { - case 24: - case 23: - if (Antimagic || Hallucination) - return MCAST_PSI_BOLT; - FALLTHROUGH; - /*FALLTHRU*/ - case 22: - case 21: - case 20: - return MCAST_DEATH_TOUCH; - case 19: - case 18: - return MCAST_CLONE_WIZ; - case 17: - case 16: - case 15: - return MCAST_SUMMON_MONS; - case 14: - case 13: - return MCAST_AGGRAVATION; - case 12: - case 11: - case 10: - return MCAST_CURSE_ITEMS; - case 9: - case 8: - return MCAST_DESTRY_ARMR; - case 7: - case 6: - return MCAST_WEAKEN_YOU; - case 5: - case 4: - return MCAST_DISAPPEAR; - case 3: - return MCAST_STUN_YOU; - case 2: - return MCAST_HASTE_SELF; - case 1: - return MCAST_CURE_SELF; - case 0: - default: + if (!list || len < 1) return MCAST_PSI_BOLT; - } -} -/* convert a level-based random selection into a specific cleric spell */ -staticfn int -choose_clerical_spell(struct monst *mtmp) -{ - int spellnum = rn2(mtmp->m_lev); + /* max spell level in this monster spell list */ + maxlev = mcast_data[list[len - 1]].level; - /* for 3.4.3 and earlier, num greater than 13 selected the default spell - */ - while (spellnum > 15 && rn2(16)) - spellnum = rn2(spellnum); + /* which level spell to cast? */ + spellval = rn2(mtmp->m_lev); + if (spellval > maxlev && rn2(maxlev)) + spellval = rn2(maxlev); - switch (spellnum) { - case 15: - case 14: - if (rn2(3)) - return MCAST_OPEN_WOUNDS; - FALLTHROUGH; - /*FALLTHRU*/ - case 13: - return MCAST_GEYSER; - case 12: - return MCAST_FIRE_PILLAR; - case 11: - return MCAST_LIGHTNING; - case 10: - case 9: - return MCAST_CURSE_ITEMS; - case 8: - return MCAST_INSECTS; - case 7: - case 6: - return MCAST_BLIND_YOU; - case 5: - case 4: - return MCAST_PARALYZE; - case 3: - case 2: - return MCAST_CONFUSE_YOU; - case 1: - return MCAST_CURE_SELF; - case 0: - default: - return MCAST_OPEN_WOUNDS; - } + /* find the highest spell in the list we could cast */ + for (i = len-1; i >= 0; i--) + if (mcast_data[list[i]].level <= spellval + && !spell_would_be_useless(mtmp, list[i])) + return list[i]; + + /* or return the first spell in the list */ + return list[0]; } /* return values: @@ -201,10 +153,7 @@ castmu( int cnt = 40; do { - if (mattk->adtyp == AD_SPEL) - spellnum = choose_magic_spell(mtmp); - else - spellnum = choose_clerical_spell(mtmp); + spellnum = choose_monster_spell(mtmp, mattk->adtyp); /* not trying to attack? don't allow directed spells */ if (!thinks_it_foundyou) { if (!is_undirected_spell(spellnum) @@ -950,7 +899,7 @@ mcast_spell(struct monst *mtmp, int dmg, int spellnum) staticfn boolean is_undirected_spell(int spellnum) { - if ((mcast_flags[spellnum] & MCF_INDIRECT) != 0) + if ((mcast_data[spellnum].flags & MCF_INDIRECT) != 0) return TRUE; return FALSE; } @@ -967,13 +916,13 @@ spell_would_be_useless(struct monst *mtmp, int spellnum) */ /* spell is only cast by hostile monsters */ - if ((mcast_flags[spellnum] & MCF_HOSTILE) != 0) { + if ((mcast_data[spellnum].flags & MCF_HOSTILE) != 0) { if (mtmp->mpeaceful) return TRUE; } /* spell needs the monster to see hero */ - if ((mcast_flags[spellnum] & MCF_SIGHT) != 0) { + if ((mcast_data[spellnum].flags & MCF_SIGHT) != 0) { boolean mcouldseeu = couldsee(mtmp->mx, mtmp->my); if (!mcouldseeu) @@ -981,6 +930,14 @@ spell_would_be_useless(struct monst *mtmp, int spellnum) } switch (spellnum) { + case MCAST_DEATH_TOUCH: + if ((Antimagic || Hallucination) && !rn2(2)) + return TRUE; + break; + case MCAST_GEYSER: + if (!rn2(5)) + return TRUE; + break; case MCAST_CLONE_WIZ: /* only the Wizard is allowed to clone himself */ if (!mtmp->iswiz || svc.context.no_of_wizards > 1)