More mcastu code reorg

Instead of using two separate functions with switch-cases for
wizard and clerical spell lists, define the spell lists
as arrays and use a single function to pick a spell
from the lists.

Adds levels to the monster spells, using the switch-case values,
with some minor fudging.
This commit is contained in:
Pasi Kallinen
2026-04-01 13:17:05 +03:00
parent c8e97527f1
commit 3917f5493d
2 changed files with 82 additions and 125 deletions

View File

@@ -6,30 +6,30 @@
#define MCF_HOSTILE 0x0004 /* cast by hostile monsters only */ #define MCF_HOSTILE 0x0004 /* cast by hostile monsters only */
#if defined(MCASTU_ENUM) #if defined(MCASTU_ENUM)
#define MONSPELL(def, flags) MCAST_##def #define MONSPELL(def, lvl, flags) MCAST_##def
#elif defined(MCASTU_INIT) #elif defined(MCASTU_INIT)
#define MONSPELL(def, flags) flags #define MONSPELL(def, lvl, flags) { lvl, flags }
#endif #endif
MONSPELL(PSI_BOLT, MCF_HOSTILE|MCF_SIGHT), MONSPELL(PSI_BOLT, 0, MCF_HOSTILE|MCF_SIGHT),
MONSPELL(OPEN_WOUNDS, MCF_HOSTILE|MCF_SIGHT), MONSPELL(OPEN_WOUNDS, 0, MCF_HOSTILE|MCF_SIGHT),
MONSPELL(LIGHTNING, MCF_HOSTILE|MCF_SIGHT), MONSPELL(CURE_SELF, 1, MCF_INDIRECT),
MONSPELL(FIRE_PILLAR, MCF_HOSTILE|MCF_SIGHT), MONSPELL(HASTE_SELF, 2, MCF_INDIRECT),
MONSPELL(GEYSER, MCF_HOSTILE|MCF_SIGHT), MONSPELL(CONFUSE_YOU, 2, MCF_HOSTILE|MCF_SIGHT),
MONSPELL(DEATH_TOUCH, MCF_HOSTILE|MCF_SIGHT), MONSPELL(STUN_YOU, 3, MCF_HOSTILE|MCF_SIGHT),
MONSPELL(CURE_SELF, MCF_INDIRECT), MONSPELL(DISAPPEAR, 4, MCF_INDIRECT),
MONSPELL(HASTE_SELF, MCF_INDIRECT), MONSPELL(PARALYZE, 4, MCF_HOSTILE|MCF_SIGHT),
MONSPELL(DISAPPEAR, MCF_INDIRECT), MONSPELL(BLIND_YOU, 6, MCF_HOSTILE|MCF_SIGHT),
MONSPELL(AGGRAVATION, MCF_INDIRECT|MCF_HOSTILE|MCF_SIGHT), MONSPELL(WEAKEN_YOU, 6, MCF_HOSTILE|MCF_SIGHT),
MONSPELL(STUN_YOU, MCF_HOSTILE|MCF_SIGHT), MONSPELL(DESTRY_ARMR, 8, MCF_HOSTILE|MCF_SIGHT),
MONSPELL(WEAKEN_YOU, MCF_HOSTILE|MCF_SIGHT), MONSPELL(INSECTS, 8, MCF_HOSTILE|MCF_INDIRECT|MCF_SIGHT),
MONSPELL(CONFUSE_YOU, MCF_HOSTILE|MCF_SIGHT), MONSPELL(CURSE_ITEMS, 10, MCF_HOSTILE|MCF_SIGHT),
MONSPELL(PARALYZE, MCF_HOSTILE|MCF_SIGHT), MONSPELL(LIGHTNING, 11, MCF_HOSTILE|MCF_SIGHT),
MONSPELL(BLIND_YOU, MCF_HOSTILE|MCF_SIGHT), MONSPELL(FIRE_PILLAR, 12, MCF_HOSTILE|MCF_SIGHT),
MONSPELL(DESTRY_ARMR, MCF_HOSTILE|MCF_SIGHT), MONSPELL(GEYSER, 13, MCF_HOSTILE|MCF_SIGHT),
MONSPELL(CURSE_ITEMS, MCF_HOSTILE|MCF_SIGHT), MONSPELL(AGGRAVATION, 13, MCF_INDIRECT|MCF_HOSTILE|MCF_SIGHT),
MONSPELL(INSECTS, MCF_HOSTILE|MCF_INDIRECT|MCF_SIGHT), MONSPELL(SUMMON_MONS, 15, MCF_HOSTILE|MCF_INDIRECT|MCF_SIGHT),
MONSPELL(SUMMON_MONS, MCF_HOSTILE|MCF_INDIRECT|MCF_SIGHT), MONSPELL(CLONE_WIZ, 18, MCF_HOSTILE|MCF_INDIRECT|MCF_SIGHT),
MONSPELL(CLONE_WIZ, MCF_HOSTILE|MCF_INDIRECT|MCF_SIGHT), MONSPELL(DEATH_TOUCH, 20, MCF_HOSTILE|MCF_SIGHT),
#undef MONSPELL #undef MONSPELL

View File

@@ -11,15 +11,32 @@ enum mcast_spells {
}; };
#undef MCASTU_ENUM #undef MCASTU_ENUM
struct _mcast_data {
int level;
int flags;
};
#define MCASTU_INIT #define MCASTU_INIT
static int mcast_flags[] = { static struct _mcast_data mcast_data[] = {
#include "mcastu.h" #include "mcastu.h"
}; };
#undef MCASTU_INIT #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 void cursetxt(struct monst *, boolean);
staticfn int choose_magic_spell(struct monst *); staticfn int choose_monster_spell(struct monst *, int);
staticfn int choose_clerical_spell(struct monst *);
staticfn int m_cure_self(struct monst *, int); staticfn int m_cure_self(struct monst *, int);
staticfn void mcast_death_touch(struct monst *); staticfn void mcast_death_touch(struct monst *);
staticfn void mcast_clone_wiz(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; /* choose a spell for monster to cast */
inappropriate choices will be screened out by spell_would_be_useless() */
staticfn int 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 */ /* which spell list to use? */
while (spellval > 24 && rn2(25)) if (adtyp == AD_SPEL) {
spellval = rn2(spellval); 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) { if (!list || len < 1)
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:
return MCAST_PSI_BOLT; return MCAST_PSI_BOLT;
}
}
/* convert a level-based random selection into a specific cleric spell */ /* max spell level in this monster spell list */
staticfn int maxlev = mcast_data[list[len - 1]].level;
choose_clerical_spell(struct monst *mtmp)
{
int spellnum = rn2(mtmp->m_lev);
/* for 3.4.3 and earlier, num greater than 13 selected the default spell /* which level spell to cast? */
*/ spellval = rn2(mtmp->m_lev);
while (spellnum > 15 && rn2(16)) if (spellval > maxlev && rn2(maxlev))
spellnum = rn2(spellnum); spellval = rn2(maxlev);
switch (spellnum) { /* find the highest spell in the list we could cast */
case 15: for (i = len-1; i >= 0; i--)
case 14: if (mcast_data[list[i]].level <= spellval
if (rn2(3)) && !spell_would_be_useless(mtmp, list[i]))
return MCAST_OPEN_WOUNDS; return list[i];
FALLTHROUGH;
/*FALLTHRU*/ /* or return the first spell in the list */
case 13: return list[0];
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;
}
} }
/* return values: /* return values:
@@ -201,10 +153,7 @@ castmu(
int cnt = 40; int cnt = 40;
do { do {
if (mattk->adtyp == AD_SPEL) spellnum = choose_monster_spell(mtmp, mattk->adtyp);
spellnum = choose_magic_spell(mtmp);
else
spellnum = choose_clerical_spell(mtmp);
/* not trying to attack? don't allow directed spells */ /* not trying to attack? don't allow directed spells */
if (!thinks_it_foundyou) { if (!thinks_it_foundyou) {
if (!is_undirected_spell(spellnum) if (!is_undirected_spell(spellnum)
@@ -950,7 +899,7 @@ mcast_spell(struct monst *mtmp, int dmg, int spellnum)
staticfn boolean staticfn boolean
is_undirected_spell(int spellnum) is_undirected_spell(int spellnum)
{ {
if ((mcast_flags[spellnum] & MCF_INDIRECT) != 0) if ((mcast_data[spellnum].flags & MCF_INDIRECT) != 0)
return TRUE; return TRUE;
return FALSE; return FALSE;
} }
@@ -967,13 +916,13 @@ spell_would_be_useless(struct monst *mtmp, int spellnum)
*/ */
/* spell is only cast by hostile monsters */ /* 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) if (mtmp->mpeaceful)
return TRUE; return TRUE;
} }
/* spell needs the monster to see hero */ /* 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); boolean mcouldseeu = couldsee(mtmp->mx, mtmp->my);
if (!mcouldseeu) if (!mcouldseeu)
@@ -981,6 +930,14 @@ spell_would_be_useless(struct monst *mtmp, int spellnum)
} }
switch (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: case MCAST_CLONE_WIZ:
/* only the Wizard is allowed to clone himself */ /* only the Wizard is allowed to clone himself */
if (!mtmp->iswiz || svc.context.no_of_wizards > 1) if (!mtmp->iswiz || svc.context.no_of_wizards > 1)