Monster spells now use spell_type instead of the old mon_spell_type.
Fixed buggy behaviour when banished from Labyrinth.
DGL_WHEREIS was not including current time, fixed.
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@1275 c06c8d41-db1a-0410-9941-cceddc491573
DDU4A3JGN5IUIPP5IASOODKPR2WBHSDSV4FITZ6HNXNSXXQACWAQC
O5A2BYFCOHSSDRZH3VAS2OYEJ2LBSZYNIQ4AB3BKZ2AWTNYLJ2JQC
ER4KHDZQ4VBEZKOUJIEUM5HBPWFGBDSB5KE56HG4UE2X6WN266QQC
IC6N445KSNOOEJNPWBGEWYMGOLNLJ2QGZWS2R3K6EQSCEQXDUFTQC
B62ICMDTN5V7R7RBL4JALFVKEMVOOVLRSJASRNYS6CGFWBEEF5JQC
BPSH3LUDUTXMK2QZKSMFDTBNDD6HPILMCBLZNMCRLZZQKPS5QOQQC
OC4O235ESV5UO3LREKILJWWIXYFVTYGF2RXQEB5AWPJUKLF5T3YAC
W4LFYTIOCEWMSU45KP2YAVH7PCTGHTM7LUCGZNEEHW2WL43FZBKQC
LS3DAZVRDCXVN2BKBC6RGCKO3R43Z7HKG4GXJWLBK4AKBL2G6QDQC
OP6CTAKWCAU64JXQ3USQYR5E5IFHQHNCACII5UMVRXUTZXJQOAZAC
FXQ3ZOBEJ6JC65JLPKUHOIRM62HUURI2OJZ5QZRG33QMR6C6YRIQC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
7KWDC7XFNMBLSUO2HISIROBINZBX5T67LJEEXTAORXW2YZ7VWFGAC
RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC
NQMXQ6OQVUSC7Y7F7IL252QW4A5JED224EECNHWAM4ZZYVNY745AC
VIFRP3HZEONFR6PQRYZYM3YUEJOQ7T4F5CZY4MN4YJMB23FMU7XAC
4JNV6RTRY2UR3PZFMCNPRGCBSUAB7QTXYJCSYBIUAFMCCYVGN6PQC
SDLKLUNFGVKDS55DDJZCBAVIB7NL3RRYPTACAY65SCUQKV6APFSAC
74LQ7JXVLAFSHLI7LCBKFX47CNTYSKGUQSXNX5FCIUIGCC2JTR3QC
43ZTEB57FU7KE5EVMYWZONNVJBZCGF3JEAJZIY25LC4LGE65PG5QC
X5WLJCJVW55SXZVP7IKP7ADCJIGNKN4PKAXFECVR6TNK7XSMZR7QC
KEYK3CH5J46U6TTOKTWRNMYTZXMQXFVEAZUC4ZQ4QCOSJHVIBDRQC
Y46M2XO74VYDTBTFFUUCI275UGELTXUXS4GEIBBXCY5USQKJ5O6AC
NW74NALMBEWIKEOBIXA65RQULHS6M4GZ4S5IWDMEGWUAAAY7CQNQC
FBKPAWAUTA2CEAKFYJ7O3B3LU7N4JOVZBGEDFUUCCRXT5N7PJ22AC
QDTVLBRGHDTRUVT7I3O72K6TMOYAUSAJBZUHGOEFU2RKJNUPWZSQC
IPQ63XIUPHFMCQOZZAVSGCJOZFDRDWZTUUJSAUMARNDUFLBEMYIAC
7AMQN7MITMXBNVDAK5VOXTQ4TZIAOD6ZLOFJG7GQMBTY23Y2BKSAC
MSQI3TH6T62JAXQGLL52QZCWAMC372TGB6ZNNRDGUGMJKBNNV2VAC
ABPRQBBNMOXUJJNZXAJBIIUULMCB23MKAM5T3M5YFXDORJ3IY3JAC
QUXTRAANLFJWXMRAANUIXUU2AELFIZUBNAKGZZIVTSKX6NUMWGTQC
R22TTMI6WXWULC7ODKFF3QCB7MOTETQQ6IR4BUCUPOCQKQNCTT5AC
TR4NPGNO5QNNRJNVMNSUEO5QLT37HCXXDOBKXCB5XWXRQNAJ5SHAC
CAHE52HL2ZGRJPBYZ3DS4BVKUD2XC7N3SG25TGG7JGHGJDST4P3QC
CQO4TZ3Z2LL6TRJGO4SXVFUNDTAEVPUVVPO52SJLTJ73RCXBEIJQC
6L4EP4ZRWWYLT55PD5KTTJON5J2JB5VV5MWNHF5VPZQZ5BKEYZ4QC
MBBPLL4SZUB3JUUYQYLZW7S5OXRCEGJX3WWADOQXGHWQ7BIKCY5QC
int id;
const char *title;
unsigned int disciplines; // bitfield
unsigned int flags; // bitfield
unsigned int level;
const char *target_prompt;
int id;
const char *title;
unsigned int disciplines; // bitfield
unsigned int flags; // bitfield
unsigned int level;
const char *target_prompt;
// If a monster is casting this, does it need a tracer?
bool ms_needs_tracer;
// The spell can be used no matter what the monster's foe is.
bool ms_utility;
// can only use up to PLYRSPELLDATASIZE _MINUS ONE_, or the
// last entry tries to set plyrspell_list[SPELL_NO_SPELL]
// can only use up to SPELLDATASIZE _MINUS ONE_, or the
// last entry tries to set spell_list[SPELL_NO_SPELL]
NULL
NULL,
true
},
{
SPELL_HELLFIRE_BURST, "Hellfire Burst",
SPTYP_CONJURATION | SPTYP_FIRE,
SPFLAG_DIR_OR_TARGET | SPFLAG_UNHOLY,
9,
NULL,
false,
false
},
{
SPELL_VAMPIRE_SUMMON, "Vampire Summon",
SPTYP_SUMMONING,
SPFLAG_UNHOLY,
3,
NULL,
false,
false
},
{
SPELL_BRAIN_FEED, "Brain Feed",
SPTYP_NECROMANCY,
SPFLAG_UNHOLY,
3,
NULL,
false,
false
},
{
SPELL_FAKE_RAKSHASA_SUMMON, "Rakshasa Summon",
SPTYP_SUMMONING | SPTYP_NECROMANCY,
SPFLAG_UNHOLY,
3,
NULL,
false
},
{
SPELL_STEAM_BALL, "Steam Ball",
SPTYP_CONJURATION | SPTYP_FIRE,
SPFLAG_DIR_OR_TARGET,
4,
NULL,
true
},
{
SPELL_SUMMON_UFETUBUS, "Summon Ufetubus",
SPTYP_SUMMONING,
SPFLAG_UNHOLY,
4,
NULL,
false,
false
},
{
SPELL_SUMMON_BEAST, "Summon Beast",
SPTYP_SUMMONING,
SPFLAG_NONE,
4,
NULL,
false
},
{
SPELL_ENERGY_BOLT, "Energy Bolt",
SPTYP_CONJURATION,
SPFLAG_DIR_OR_TARGET,
4,
NULL,
true
},
{
SPELL_POISON_SPLASH, "Poison Splash",
SPTYP_POISON,
SPFLAG_DIR_OR_TARGET,
2,
NULL,
true
},
{
SPELL_SUMMON_UNDEAD, "Summon Undead",
SPTYP_SUMMONING | SPTYP_NECROMANCY,
SPFLAG_NONE,
7,
NULL,
false,
false,
},
{
SPELL_CANTRIP, "Cantrip",
SPTYP_NONE,
SPFLAG_NONE,
1,
NULL,
false,
false
},
{
SPELL_QUICKSILVER_BOLT, "Quicksilver Bolt",
SPTYP_CONJURATION,
SPFLAG_DIR_OR_TARGET,
5,
NULL,
true
},
{
SPELL_METAL_SPLINTERS, "Metal Splinters",
SPTYP_CONJURATION,
SPFLAG_DIR_OR_TARGET,
5,
NULL,
true
},
{
SPELL_MIASMA, "Miasma",
SPTYP_CONJURATION | SPTYP_NECROMANCY,
SPFLAG_DIR_OR_TARGET,
6,
NULL,
true
},
{
SPELL_SUMMON_DRAKES, "Summon Drakes",
SPTYP_SUMMONING,
SPFLAG_NONE,
6,
NULL,
false,
false
},
{
SPELL_BLINK_OTHER, "Blink Other",
SPTYP_TRANSLOCATION,
SPFLAG_NONE,
2,
NULL,
true
static bool mons_abjured(monsters *monster, bool nearby)
{
const bool friendly = mons_friendly(monster);
if (nearby && monster_abjuration(friendly, 1, true) > 0
&& coinflip())
{
monster_abjuration( friendly, monster->hit_dice * 10, false );
return (true);
}
return (false);
}
static monster_type pick_random_wraith()
{
static monster_type wraiths[] =
{
MONS_WRAITH, MONS_FREEZING_WRAITH, MONS_SHADOW_WRAITH
};
void mons_cast(struct monsters *monster, struct bolt &pbolt, int spell_cast)
return wraiths[ random2(sizeof(wraiths) / sizeof(*wraiths)) ];
}
static monster_type pick_horrible_thing()
{
return (one_chance_in(4)? MONS_TENTACLED_MONSTROSITY
: MONS_ABOMINATION_LARGE);
}
static monster_type pick_undead_summon()
{
int summonik = MONS_PROGRAM_BUG;
// FIXME: This is ridiculous.
do
{
summonik = random2(241); // hmmmm ... {dlb}
}
while (mons_class_holiness(summonik) != MH_UNDEAD);
return static_cast<monster_type>(summonik);
}
static void do_high_level_summon(monsters *monster, bool monsterNearby,
monster_type (*mpicker)(),
int nsummons)
{
if (mons_abjured(monster, monsterNearby))
return;
const int duration = cap_int(2 + monster->hit_dice / 5, 6);
for (int i = 0; i < nsummons; ++i)
{
const monster_type which_mons = mpicker();
if (which_mons == MONS_PROGRAM_BUG)
continue;
create_monster( which_mons, duration,
SAME_ATTITUDE(monster), monster->x, monster->y,
monster->foe, 250 );
}
}
void mons_cast(monsters *monster, bolt &pbolt, spell_type spell_cast)
case MS_SUMMON_SMALL_MAMMALS:
case MS_VAMPIRE_SUMMON:
if ( spell_cast == MS_SUMMON_SMALL_MAMMALS )
case SPELL_SUMMON_SMALL_MAMMAL:
case SPELL_VAMPIRE_SUMMON:
if ( spell_cast == SPELL_SUMMON_SMALL_MAMMAL )
case MS_LEVEL_SUMMON: // summon anything appropriate for level
if (!mons_friendly(monster) && monsterNearby
&& monster_abjuration(1, true) > 0 && coinflip())
{
monster_abjuration( monster->hit_dice * 10, false );
case SPELL_SHADOW_CREATURES: // summon anything appropriate for level
if (mons_abjured(monster, monsterNearby))
case MS_SUMMON_DEMON: // class 2-4 demons
if (!mons_friendly(monster) && monsterNearby
&& monster_abjuration(1, true) > 0 && coinflip())
{
monster_abjuration(monster->hit_dice * 10, false);
case SPELL_SUMMON_DEMON: // class 2-4 demons
if (mons_abjured(monster, monsterNearby))
case MS_SUMMON_MUSHROOMS: // Summon swarms of icky crawling fungi.
if (!mons_friendly(monster) && monsterNearby
&& monster_abjuration(1, true) > 0 && coinflip())
{
monster_abjuration( monster->hit_dice * 10, false );
case SPELL_SUMMON_ICE_BEAST:
create_monster( MONS_ICE_BEAST, 5, SAME_ATTITUDE(monster),
monster->x, monster->y, monster->foe, 250 );
return;
case SPELL_SUMMON_MUSHROOMS: // Summon swarms of icky crawling fungi.
if (mons_abjured(monster, monsterNearby))
case MS_SUMMON_UNDEAD: // summon undead around player
if (!mons_friendly(monster) && monsterNearby
&& monster_abjuration(1, true) > 0 && coinflip())
{
monster_abjuration( monster->hit_dice * 10, false );
return;
}
case SPELL_SUMMON_WRAITHS:
do_high_level_summon(monster, monsterNearby, pick_random_wraith,
random_range(3, 6));
return;
sumcount2 = 2 + random2(2) + random2( monster->hit_dice / 4 + 1 );
duration = cap_int(2 + monster->hit_dice / 5, 6);
for (sumcount = 0; sumcount < sumcount2; sumcount++)
{
do
{
summonik = random2(241); // hmmmm ... {dlb}
}
while (mons_class_holiness(summonik) != MH_UNDEAD);
case SPELL_SUMMON_HORRIBLE_THINGS:
do_high_level_summon(monster, monsterNearby, pick_horrible_thing,
random_range(3, 5));
return;
create_monster(summonik, duration, SAME_ATTITUDE(monster),
monster->x, monster->y, monster->foe, 250);
}
case SPELL_SUMMON_UNDEAD: // summon undead around player
do_high_level_summon(monster, monsterNearby, pick_undead_summon,
2 + random2(2)
+ random2( monster->hit_dice / 4 + 1 ));
case MS_SUMMON_DEMON_GREATER:
if (!mons_friendly(monster) && !monsterNearby &&
monster_abjuration(1, true) > 0 && coinflip())
{
monster_abjuration(monster->hit_dice * 10, false);
case SPELL_SUMMON_GREATER_DEMON:
if (mons_abjured(monster, monsterNearby))
case MS_SUMMON_DRAKES:
if (!mons_friendly( monster ) && !monsterNearby &&
monster_abjuration( 1, true ) > 0 && coinflip())
{
monster_abjuration( monster->hit_dice * 10, false );
case SPELL_SUMMON_DRAKES:
if (mons_abjured(monster, monsterNearby))
case MS_SUMMON_SMALL_MAMMALS:
case MS_VAMPIRE_SUMMON:
case MS_LEVEL_SUMMON: // summon anything appropriate for level
case MS_FAKE_RAKSHASA_SUMMON:
case MS_SUMMON_DEMON:
case MS_ANIMATE_DEAD:
case MS_SUMMON_DEMON_LESSER:
case MS_SUMMON_UFETUBUS:
case MS_SUMMON_BEAST: // Geryon
case MS_SUMMON_UNDEAD: // summon undead around player
case MS_SUMMON_MUSHROOMS:
case MS_SUMMON_DRAKES:
case MS_TORMENT:
case MS_SUMMON_DEMON_GREATER:
case MS_CANTRIP:
case MS_BERSERK_RAGE:
case MS_MIGHT:
case SPELL_SUMMON_SMALL_MAMMAL:
case SPELL_VAMPIRE_SUMMON:
case SPELL_SHADOW_CREATURES: // summon anything appropriate for level
case SPELL_FAKE_RAKSHASA_SUMMON:
case SPELL_SUMMON_DEMON:
case SPELL_ANIMATE_DEAD:
case SPELL_CALL_IMP:
case SPELL_SUMMON_UFETUBUS:
case SPELL_SUMMON_BEAST: // Geryon
case SPELL_SUMMON_UNDEAD: // summon undead around player
case SPELL_SUMMON_ICE_BEAST:
case SPELL_SUMMON_MUSHROOMS:
case SPELL_SUMMON_DRAKES:
case SPELL_SUMMON_HORRIBLE_THINGS:
case SPELL_SUMMON_WRAITHS:
case SPELL_SYMBOL_OF_TORMENT:
case SPELL_SUMMON_GREATER_DEMON:
case SPELL_CANTRIP:
case SPELL_BERSERKER_RAGE:
if (spell_cast == MS_HASTE
|| spell_cast == MS_INVIS
|| spell_cast == MS_HEAL || spell_cast == MS_TELEPORT)
if (spell_cast == SPELL_HASTE
|| spell_cast == SPELL_INVISIBILITY
|| spell_cast == SPELL_LESSER_HEALING
|| spell_cast == SPELL_TELEPORT_SELF)
case MS_SHOCK:
case SPELL_FREEZING_CLOUD:
beam.name = "freezing blast";
beam.range = 5;
beam.rangeMax = 12;
beam.damage = dice_def( 2, 9 + power / 11 );
beam.colour = WHITE;
beam.type = SYM_ZAP;
beam.thrower = KILL_MON;
beam.flavour = BEAM_COLD;
beam.hit = 17 + power / 25;
beam.is_beam = true;
beam.is_big_cloud = true;
break;
case SPELL_SHOCK:
case MS_PURPLE_BLAST: // purple bang thing
beam.colour = LIGHTMAGENTA;
beam.name = "orb of energy";
beam.range = 6;
beam.rangeMax = 10;
beam.damage = dice_def( 3, 10 + power / 15 );
beam.hit = 20 + power / 25;
beam.type = SYM_ZAP;
beam.thrower = KILL_MON_MISSILE;
beam.flavour = BEAM_MMISSILE;
beam.is_beam = false;
beam.is_explosion = true;
break;
case MS_ENERGY_BOLT: // eye of devastation
case SPELL_ENERGY_BOLT: // eye of devastation
static bool mons_announce_cast(monsters *monster, bool nearby,
spell_type spell_cast,
spell_type draco_breath)
{
if (nearby) // handle monsters within range of player
{
if (monster->type == MONS_GERYON)
{
if (silenced(monster->x, monster->y))
return (false);
simple_monster_message( monster, " winds a great silver horn.",
MSGCH_MONSTER_SPELL );
}
else if (mons_is_demon( monster->type ))
{
simple_monster_message( monster, " gestures.",
MSGCH_MONSTER_SPELL );
}
else
{
switch (monster->type)
{
default:
if (spell_cast == draco_breath)
{
if (!simple_monster_message(monster, " breathes.",
MSGCH_MONSTER_SPELL))
{
if (!silenced(monster->x, monster->y)
&& !silenced(you.x_pos, you.y_pos))
{
mpr("You hear a roar.", MSGCH_SOUND);
}
}
break;
}
if (silenced(monster->x, monster->y))
return (false);
if (mons_class_flag(monster->type, M_PRIEST))
{
switch (random2(3))
{
case 0:
simple_monster_message( monster,
" prays.",
MSGCH_MONSTER_SPELL );
break;
case 1:
simple_monster_message( monster,
" mumbles some strange prayers.",
MSGCH_MONSTER_SPELL );
break;
case 2:
default:
simple_monster_message( monster,
" utters an invocation.",
MSGCH_MONSTER_SPELL );
break;
}
}
else
{
switch (random2(3))
{
case 0:
// XXX: could be better, chosen to match the
// ones in monspeak.cc... has the problem
// that it doesn't suggest a vocal component. -- bwr
if (player_monster_visible(monster))
simple_monster_message( monster,
" gestures wildly.",
MSGCH_MONSTER_SPELL );
break;
case 1:
simple_monster_message( monster,
" mumbles some strange words.",
MSGCH_MONSTER_SPELL );
break;
case 2:
default:
simple_monster_message( monster,
" casts a spell.",
MSGCH_MONSTER_SPELL );
break;
}
}
break;
case MONS_BALL_LIGHTNING:
monster->hit_points = -1;
break;
case MONS_STEAM_DRAGON:
case MONS_MOTTLED_DRAGON:
case MONS_STORM_DRAGON:
case MONS_GOLDEN_DRAGON:
case MONS_SHADOW_DRAGON:
case MONS_SWAMP_DRAGON:
case MONS_SWAMP_DRAKE:
case MONS_DEATH_DRAKE:
case MONS_HELL_HOG:
case MONS_SERPENT_OF_HELL:
case MONS_QUICKSILVER_DRAGON:
case MONS_IRON_DRAGON:
if (!simple_monster_message(monster, " breathes.",
MSGCH_MONSTER_SPELL))
{
if (!silenced(monster->x, monster->y)
&& !silenced(you.x_pos, you.y_pos))
{
mpr("You hear a roar.", MSGCH_SOUND);
}
}
break;
case MONS_VAPOUR:
monster->add_ench(ENCH_SUBMERGED);
break;
case MONS_BRAIN_WORM:
case MONS_ELECTRIC_GOLEM:
case MONS_ICE_STATUE:
// These don't show any signs that they're casting a spell.
break;
case MONS_GREAT_ORB_OF_EYES:
case MONS_SHINING_EYE:
case MONS_EYE_OF_DEVASTATION:
simple_monster_message(monster, " gazes.", MSGCH_MONSTER_SPELL);
break;
case MONS_GIANT_ORANGE_BRAIN:
simple_monster_message(monster, " pulsates.",
MSGCH_MONSTER_SPELL);
break;
case MONS_NAGA:
case MONS_NAGA_WARRIOR:
simple_monster_message(monster, " spits poison.",
MSGCH_MONSTER_SPELL);
break;
}
}
}
else // handle far-away monsters
{
if (monster->type == MONS_GERYON
&& !silenced(you.x_pos, you.y_pos))
{
mpr("You hear a weird and mournful sound.", MSGCH_SOUND);
}
}
return (true);
}
simple_monster_message( monster, " winds a great silver horn.",
MSGCH_MONSTER_SPELL );
}
else if (mons_is_demon( monster->type ))
{
simple_monster_message( monster, " gestures.",
MSGCH_MONSTER_SPELL );
}
else
{
switch (monster->type)
{
default:
if (spell_cast == draco_breath)
{
if (!simple_monster_message(monster, " breathes.",
MSGCH_MONSTER_SPELL))
{
if (!silenced(monster->x, monster->y)
&& !silenced(you.x_pos, you.y_pos))
{
mpr("You hear a roar.", MSGCH_SOUND);
}
}
break;
}
if (silenced(monster->x, monster->y))
return (false);
if (mons_class_flag(monster->type, M_PRIEST))
{
switch (random2(3))
{
case 0:
simple_monster_message( monster,
" prays.",
MSGCH_MONSTER_SPELL );
break;
case 1:
simple_monster_message( monster,
" mumbles some strange prayers.",
MSGCH_MONSTER_SPELL );
break;
case 2:
default:
simple_monster_message( monster,
" utters an invocation.",
MSGCH_MONSTER_SPELL );
break;
}
}
else
{
switch (random2(3))
{
case 0:
// XXX: could be better, chosen to match the
// ones in monspeak.cc... has the problem
// that it doesn't suggest a vocal component. -- bwr
if (player_monster_visible(monster))
simple_monster_message( monster,
" gestures wildly.",
MSGCH_MONSTER_SPELL );
break;
case 1:
simple_monster_message( monster,
" mumbles some strange words.",
MSGCH_MONSTER_SPELL );
break;
case 2:
default:
simple_monster_message( monster,
" casts a spell.",
MSGCH_MONSTER_SPELL );
break;
}
}
break;
case MONS_BALL_LIGHTNING:
monster->hit_points = -1;
break;
case MONS_STEAM_DRAGON:
case MONS_MOTTLED_DRAGON:
case MONS_STORM_DRAGON:
case MONS_GOLDEN_DRAGON:
case MONS_SHADOW_DRAGON:
case MONS_SWAMP_DRAGON:
case MONS_SWAMP_DRAKE:
case MONS_DEATH_DRAKE:
case MONS_HELL_HOG:
case MONS_SERPENT_OF_HELL:
case MONS_QUICKSILVER_DRAGON:
case MONS_IRON_DRAGON:
if (!simple_monster_message(monster, " breathes.",
MSGCH_MONSTER_SPELL))
{
if (!silenced(monster->x, monster->y)
&& !silenced(you.x_pos, you.y_pos))
{
mpr("You hear a roar.", MSGCH_SOUND);
}
}
break;
case MONS_VAPOUR:
monster->add_ench(ENCH_SUBMERGED);
break;
case MONS_BRAIN_WORM:
case MONS_ELECTRIC_GOLEM:
// These don't show any signs that they're casting a spell.
break;
case MONS_GREAT_ORB_OF_EYES:
case MONS_SHINING_EYE:
case MONS_EYE_OF_DEVASTATION:
simple_monster_message(monster, " gazes.", MSGCH_MONSTER_SPELL);
break;
case MONS_GIANT_ORANGE_BRAIN:
simple_monster_message(monster, " pulsates.",
MSGCH_MONSTER_SPELL);
break;
case MONS_NAGA:
case MONS_NAGA_WARRIOR:
simple_monster_message(monster, " spits poison.",
MSGCH_MONSTER_SPELL);
break;
}
}
}
else // handle far-away monsters
{
if (monster->type == MONS_GERYON
&& !silenced(you.x_pos, you.y_pos))
{
mpr("You hear a weird and mournful sound.", MSGCH_SOUND);
}
}
if (!mons_announce_cast(monster, monsterNearby,
spell_cast, draco_breath))
return (false);
bool ms_useful_fleeing_out_of_sight( struct monsters *mon, int monspell );
bool ms_waste_of_time( struct monsters *mon, int monspell );
bool ms_low_hitpoint_cast( struct monsters *mon, int monspell );
bool ms_useful_fleeing_out_of_sight( const monsters *mon, spell_type monspell );
bool ms_waste_of_time( const monsters *mon, spell_type monspell );
bool ms_low_hitpoint_cast( const monsters *mon, spell_type monspell );
};
#if DEBUG_DIAGNOSTICS
static const char *monster_spell_name[] = {
"Magic Missile",
"Throw Flame",
"Throw Frost",
"Paralysis",
"Slow",
"Haste",
"Confuse",
"Venom Bolt",
"Fire Bolt",
"Cold Bolt",
"Lightning Bolt",
"Invisibility",
"Fireball",
"Heal",
"Teleport",
"Teleport Other",
"Blink",
"Crystal Spear",
"Dig",
"Negative Bolt",
"Hellfire Burst",
"Vampire Summon",
"Orb Energy",
"Brain Feed",
"Level Summon",
"Fake Rakshasa Summon",
"Steam Ball",
"Summon Demon",
"Animate Dead",
"Pain",
"Smite",
"Sticky Flame",
"Poison Blast",
"Summon Demon Lesser",
"Summon Ufetubus",
"Purple Blast",
"Summon Beast",
"Energy Bolt",
"Sting",
"Iron Bolt",
"Stone Arrow",
"Poison Splash",
"Summon Undead",
"Mutation",
"Cantrip",
"Disintegrate",
"Marsh Gas",
"Quicksilver Bolt",
"Torment",
"Hellfire",
"Metal Splinters",
"Summon Demon Greater",
"Banishment",
"Controlled Blink",
"Control Undead",
"Miasma",
"Summon Drakes",
"Blink Other",
"Dispel Undead",
"Hellfrost",
"Poison Arrow",
"Summon Small Mammals",
"Summon Mushrooms",
"Ice Bolt",
"Magma",
"Shock",
"Berserk Rage",
"Might"
case MS_LIGHTNING_BOLT:
case MS_NEGATIVE_BOLT:
case MS_VENOM_BOLT:
case MS_STICKY_FLAME:
case MS_DISINTEGRATE:
case MS_SUMMON_DEMON_GREATER:
case MS_BANISHMENT:
case MS_CRYSTAL_SPEAR:
case MS_IRON_BOLT:
case MS_TELEPORT:
case MS_TELEPORT_OTHER:
case SPELL_LIGHTNING_BOLT:
case SPELL_BOLT_OF_DRAINING:
case SPELL_VENOM_BOLT:
case SPELL_STICKY_FLAME:
case SPELL_DISINTEGRATE:
case SPELL_SUMMON_GREATER_DEMON:
case SPELL_BANISHMENT:
case SPELL_LEHUDIBS_CRYSTAL_SPEAR:
case SPELL_BOLT_OF_IRON:
case SPELL_TELEPORT_SELF:
case SPELL_TELEPORT_OTHER:
bool requires = false;
switch(monspell)
{
case MS_BANISHMENT:
case MS_COLD_BOLT:
case MS_ICE_BOLT:
case MS_SHOCK:
case MS_MAGMA:
case MS_CONFUSE:
case MS_CRYSTAL_SPEAR:
case MS_DISINTEGRATE:
case MS_ENERGY_BOLT:
case MS_FIRE_BOLT:
case MS_FIREBALL:
case MS_FLAME:
case MS_FROST:
case MS_HELLFIRE:
case MS_IRON_BOLT:
case MS_LIGHTNING_BOLT:
case MS_MARSH_GAS:
case MS_MIASMA:
case MS_METAL_SPLINTERS:
case MS_MMISSILE:
case MS_NEGATIVE_BOLT:
case MS_ORB_ENERGY:
case MS_PAIN:
case MS_PARALYSIS:
case MS_POISON_BLAST:
case MS_POISON_ARROW:
case MS_POISON_SPLASH:
case MS_QUICKSILVER_BOLT:
case MS_SLOW:
case MS_STEAM_BALL:
case MS_STICKY_FLAME:
case MS_STING:
case MS_STONE_ARROW:
case MS_TELEPORT_OTHER:
case MS_VENOM_BOLT:
requires = true;
break;
// self-niceties and direct effects
case MS_ANIMATE_DEAD:
case MS_BLINK:
case MS_BRAIN_FEED:
case MS_DIG:
case MS_FAKE_RAKSHASA_SUMMON:
case MS_HASTE:
case MS_HEAL:
case MS_HELLFIRE_BURST:
case MS_INVIS:
case MS_LEVEL_SUMMON:
case MS_MUTATION:
case MS_SMITE:
case MS_SUMMON_BEAST:
case MS_SUMMON_DEMON_LESSER:
case MS_SUMMON_DEMON:
case MS_SUMMON_DEMON_GREATER:
case MS_SUMMON_UFETUBUS:
case MS_TELEPORT:
case MS_TORMENT:
case MS_SUMMON_SMALL_MAMMALS:
case MS_VAMPIRE_SUMMON:
case MS_CANTRIP:
case MS_BERSERK_RAGE:
// meaningless, but sure, why not?
case MS_NO_SPELL:
break;
default:
break;
}
return (requires);
return (spell_needs_tracer(monspell));
bool nasty = true;
switch(monspell)
{
// self-niceties/summonings
case MS_ANIMATE_DEAD:
case MS_BLINK:
case MS_DIG:
case MS_FAKE_RAKSHASA_SUMMON:
case MS_HASTE:
case MS_HEAL:
case MS_INVIS:
case MS_LEVEL_SUMMON:
case MS_SUMMON_BEAST:
case MS_SUMMON_DEMON_LESSER:
case MS_SUMMON_DEMON:
case MS_SUMMON_DEMON_GREATER:
case MS_SUMMON_UFETUBUS:
case MS_TELEPORT:
case MS_SUMMON_SMALL_MAMMALS:
case MS_VAMPIRE_SUMMON:
nasty = false;
break;
case MS_BRAIN_FEED:
case MS_HELLFIRE_BURST:
case MS_MUTATION:
case MS_SMITE:
case MS_TORMENT:
case MS_BERSERK_RAGE:
// meaningless, but sure, why not?
case MS_NO_SPELL:
break;
default:
break;
}
return (nasty);
return (spell_needs_foe(monspell));
case MS_SUMMON_SMALL_MAMMALS:
case MS_VAMPIRE_SUMMON:
case MS_SUMMON_UFETUBUS:
case MS_FAKE_RAKSHASA_SUMMON:
case MS_LEVEL_SUMMON:
case MS_SUMMON_DEMON:
case MS_SUMMON_DEMON_LESSER:
case MS_SUMMON_BEAST:
case MS_SUMMON_UNDEAD:
case MS_SUMMON_MUSHROOMS:
case MS_SUMMON_DEMON_GREATER:
if (one_chance_in(10)) // only summon friends some of the time
default:
if ((get_spell_flags(monspell) & SPTYP_SUMMONING) && one_chance_in(4))
case MS_SUMMON_SMALL_MAMMALS:
case MS_VAMPIRE_SUMMON:
case MS_SUMMON_UFETUBUS:
case MS_FAKE_RAKSHASA_SUMMON:
if (!targ_adj)
default:
if (!targ_adj && (get_spell_flags(monspell) & SPTYP_SUMMONING))
MS_NEGATIVE_BOLT,
MS_ANIMATE_DEAD,
MS_SUMMON_UNDEAD,
MS_FROST,
MS_CRYSTAL_SPEAR,
MS_SUMMON_UNDEAD },
SPELL_BOLT_OF_DRAINING,
SPELL_ANIMATE_DEAD,
SPELL_SUMMON_UNDEAD,
SPELL_THROW_FROST,
SPELL_LEHUDIBS_CRYSTAL_SPEAR,
SPELL_SUMMON_UNDEAD },
MS_HELLFIRE_BURST,
MS_HELLFIRE_BURST,
MS_NO_SPELL,
MS_HELLFIRE_BURST,
MS_HELLFIRE_BURST,
MS_HELLFIRE_BURST },
SPELL_HELLFIRE_BURST,
SPELL_HELLFIRE_BURST,
SPELL_NO_SPELL,
SPELL_HELLFIRE_BURST,
SPELL_HELLFIRE_BURST,
SPELL_HELLFIRE_BURST },
MS_POISON_ARROW,
MS_POISON_BLAST,
MS_NO_SPELL,
MS_VENOM_BOLT,
MS_SUMMON_DEMON_LESSER,
MS_BLINK },
SPELL_POISON_ARROW,
SPELL_POISONOUS_CLOUD,
SPELL_NO_SPELL,
SPELL_VENOM_BOLT,
SPELL_CALL_IMP,
SPELL_BLINK },
MS_SUMMON_DEMON_GREATER,
MS_IRON_BOLT,
MS_SUMMON_DEMON,
MS_LIGHTNING_BOLT,
MS_HELLFIRE,
MS_SUMMON_DEMON_GREATER },
SPELL_SUMMON_GREATER_DEMON,
SPELL_BOLT_OF_IRON,
SPELL_SUMMON_DEMON,
SPELL_LIGHTNING_BOLT,
SPELL_HELLFIRE,
SPELL_SUMMON_GREATER_DEMON },
MS_POISON_ARROW,
MS_SLOW,
MS_SUMMON_DEMON,
MS_NEGATIVE_BOLT,
MS_SUMMON_DEMON,
MS_NO_SPELL },
SPELL_POISON_ARROW,
SPELL_SLOW,
SPELL_SUMMON_DEMON,
SPELL_BOLT_OF_DRAINING,
SPELL_SUMMON_DEMON,
SPELL_NO_SPELL },
MS_SUMMON_DEMON,
MS_BANISHMENT,
MS_SUMMON_DEMON,
MS_SUMMON_DEMON_GREATER,
MS_SUMMON_DEMON_LESSER,
MS_TELEPORT },
SPELL_SUMMON_DEMON,
SPELL_BANISHMENT,
SPELL_SUMMON_DEMON,
SPELL_SUMMON_GREATER_DEMON,
SPELL_CALL_IMP,
SPELL_TELEPORT_SELF },
MS_LIGHTNING_BOLT,
MS_CRYSTAL_SPEAR,
MS_BLINK,
MS_IRON_BOLT,
MS_POISON_ARROW,
MS_TELEPORT },
SPELL_LIGHTNING_BOLT,
SPELL_LEHUDIBS_CRYSTAL_SPEAR,
SPELL_BLINK,
SPELL_BOLT_OF_IRON,
SPELL_POISON_ARROW,
SPELL_TELEPORT_SELF },
MS_SUMMON_DEMON_LESSER,
MS_SUMMON_DEMON,
MS_CANTRIP,
MS_SUMMON_DEMON_LESSER,
MS_SUMMON_DEMON,
MS_CANTRIP }, // this should be cute -- bwr
SPELL_CALL_IMP,
SPELL_SUMMON_DEMON,
SPELL_CANTRIP,
SPELL_CALL_IMP,
SPELL_SUMMON_DEMON,
SPELL_CANTRIP }, // this should be cute -- bwr
MS_VENOM_BOLT,
MS_ORB_ENERGY,
MS_HASTE,
MS_POISON_ARROW,
MS_TELEPORT_OTHER,
MS_TELEPORT },
SPELL_VENOM_BOLT,
SPELL_ISKENDERUNS_MYSTIC_BLAST,
SPELL_HASTE,
SPELL_POISON_ARROW,
SPELL_TELEPORT_OTHER,
SPELL_TELEPORT_SELF },
MS_NO_SPELL, // this line: splist[x] = ghost.values[x + 14] -- dlb
MS_NO_SPELL,
MS_NO_SPELL,
MS_NO_SPELL,
MS_NO_SPELL,
MS_NO_SPELL },
SPELL_NO_SPELL, // this line: splist[x] = ghost.values[x + 14] -- dlb
SPELL_NO_SPELL,
SPELL_NO_SPELL,
SPELL_NO_SPELL,
SPELL_NO_SPELL,
SPELL_NO_SPELL },
MS_QUICKSILVER_BOLT,
MS_QUICKSILVER_BOLT,
MS_NO_SPELL,
MS_QUICKSILVER_BOLT,
MS_QUICKSILVER_BOLT,
MS_NO_SPELL },
SPELL_QUICKSILVER_BOLT,
SPELL_QUICKSILVER_BOLT,
SPELL_NO_SPELL,
SPELL_QUICKSILVER_BOLT,
SPELL_QUICKSILVER_BOLT,
SPELL_NO_SPELL },
MS_NO_SPELL,
MS_SUMMON_DRAKES,
MS_SUMMON_DRAKES,
MS_NO_SPELL,
MS_NO_SPELL,
MS_SUMMON_DRAKES },
SPELL_NO_SPELL,
SPELL_SUMMON_DRAKES,
SPELL_SUMMON_DRAKES,
SPELL_NO_SPELL,
SPELL_NO_SPELL,
SPELL_SUMMON_DRAKES },
MS_BANISHMENT,
MS_BLINK_OTHER,
MS_BLINK,
MS_NO_SPELL,
MS_BLINK_OTHER,
MS_CONTROLLED_BLINK },
SPELL_BANISHMENT,
SPELL_BLINK_OTHER,
SPELL_BLINK,
SPELL_NO_SPELL,
SPELL_BLINK_OTHER,
SPELL_CONTROLLED_BLINK },
MS_SUMMON_UNDEAD,
MS_SUMMON_MUSHROOMS, // fungal theme
MS_SUMMON_MUSHROOMS,
MS_TORMENT,
MS_SUMMON_UNDEAD,
MS_TORMENT },
SPELL_SUMMON_UNDEAD,
SPELL_SUMMON_MUSHROOMS, // fungal theme
SPELL_SUMMON_MUSHROOMS,
SPELL_SYMBOL_OF_TORMENT,
SPELL_SUMMON_UNDEAD,
SPELL_SYMBOL_OF_TORMENT },
MS_PARALYSIS,
MS_CONFUSE,
MS_BERSERK_RAGE,
MS_NO_SPELL,
MS_NO_SPELL,
MS_BERSERK_RAGE },
SPELL_PARALYSE,
SPELL_CONFUSE,
SPELL_BERSERKER_RAGE,
SPELL_NO_SPELL,
SPELL_NO_SPELL,
SPELL_BERSERKER_RAGE },
{ MST_ICE_STATUE,
SPELL_BOLT_OF_COLD,
SPELL_ICE_BOLT,
SPELL_NO_SPELL,
SPELL_FREEZING_CLOUD,
SPELL_SUMMON_ICE_BEAST,
SPELL_SUMMON_ICE_BEAST },
MONUSE_NOTHING, SIZE_LARGE
}
,
{
MONS_ICE_STATUE, '8', LIGHTBLUE, "ice statue",
M_SPELLCASTER,
MR_RES_POISON | MR_VUL_FIRE | MR_RES_COLD | MR_RES_ELEC,
0, 10, MONS_CLAY_GOLEM, MONS_ICE_STATUE, MH_NONLIVING, MAG_IMMUNE,
{ {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} },
{ 8, 0, 0, 140 },
12, 1, 10, 7, MST_ICE_STATUE, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_HIGH,
// [ds] Descending into the Labyrinth increments your_level. Going
// downstairs from a labyrinth implies that you've been banished (or been
// sent to Pandemonium somehow). Decrementing your_level here is needed
// to fix this buggy sequence: D:n -> Labyrinth -> Abyss -> D:(n+1).
if (you.level_type == LEVEL_LABYRINTH)
you.your_level--;
case SPELL_TELEPORT_SELF:
return (MS_TELEPORT);
case SPELL_ICE_BOLT:
return (MS_ICE_BOLT);
case SPELL_SHOCK:
return (MS_SHOCK);
case SPELL_BOLT_OF_MAGMA:
return (MS_MAGMA);
case SPELL_MAGIC_DART:
return (MS_MMISSILE);
case SPELL_FIREBALL:
case SPELL_DELAYED_FIREBALL:
return (MS_FIREBALL);
case SPELL_DIG:
return (MS_DIG);
case SPELL_BOLT_OF_FIRE:
return (MS_FIRE_BOLT);
case SPELL_BOLT_OF_COLD:
return (MS_COLD_BOLT);
case SPELL_LIGHTNING_BOLT:
return (MS_LIGHTNING_BOLT);
case SPELL_POLYMORPH_OTHER:
return (MS_MUTATION);
case SPELL_SLOW:
return (MS_SLOW);
case SPELL_HASTE:
return (MS_HASTE);
case SPELL_PARALYZE:
return (MS_PARALYSIS);
case SPELL_CONFUSE:
return (MS_CONFUSE);
case SPELL_INVISIBILITY:
return (MS_INVIS);
case SPELL_THROW_FLAME:
return (MS_FLAME);
case SPELL_THROW_FROST:
return (MS_FROST);
return (MS_BLINK); /* approximate */
/* case FREEZING_CLOUD: return ; no freezing/mephitic cloud yet
case MEPHITIC_CLOUD: return ; */
case SPELL_VENOM_BOLT:
return (MS_VENOM_BOLT);
case SPELL_POISON_ARROW:
return (MS_POISON_ARROW);
case SPELL_TELEPORT_OTHER:
return (MS_TELEPORT_OTHER);
case SPELL_SUMMON_SMALL_MAMMAL:
return (MS_SUMMON_SMALL_MAMMALS);
case SPELL_BOLT_OF_DRAINING:
return (MS_NEGATIVE_BOLT);
case SPELL_LEHUDIBS_CRYSTAL_SPEAR:
return (MS_CRYSTAL_SPEAR);
case SPELL_BLINK:
return (MS_BLINK);
case SPELL_ISKENDERUNS_MYSTIC_BLAST:
return (MS_ORB_ENERGY);
case SPELL_SUMMON_HORRIBLE_THINGS:
return (MS_LEVEL_SUMMON); /* approximate */
case SPELL_SHADOW_CREATURES:
return (MS_LEVEL_SUMMON); /* approximate */
case SPELL_ANIMATE_DEAD:
return (MS_ANIMATE_DEAD);
case SPELL_PAIN:
return (MS_PAIN);
case SPELL_SUMMON_WRAITHS:
return (MS_SUMMON_UNDEAD); /* approximate */
case SPELL_STICKY_FLAME:
return (MS_STICKY_FLAME);
case SPELL_CALL_IMP:
return (MS_SUMMON_DEMON_LESSER);
case SPELL_BANISHMENT:
return (MS_BANISHMENT);
case SPELL_STING:
return (MS_STING);
case SPELL_SUMMON_DEMON:
return (MS_SUMMON_DEMON);
return (SPELL_BLINK); /* approximate */
return (MS_SUMMON_DEMON_LESSER);
case SPELL_SUMMON_GREATER_DEMON:
return (MS_SUMMON_DEMON_GREATER);
case SPELL_BOLT_OF_IRON:
return (MS_IRON_BOLT);
case SPELL_STONE_ARROW:
return (MS_STONE_ARROW);
case SPELL_DISINTEGRATE:
return (MS_DISINTEGRATE);
return (SPELL_CALL_IMP);
// XXX: someday merge these into SPELL_
// If changing this list, keep mon-util.cc spell names in sync.
enum mon_spell_type
{
MS_MMISSILE, // 0
MS_FLAME,
MS_FROST,
MS_PARALYSIS,
MS_SLOW,
MS_HASTE, // 5
MS_CONFUSE, // 6 - do not deprecate!!! 13jan2000 {dlb}
MS_VENOM_BOLT,
MS_FIRE_BOLT,
MS_COLD_BOLT,
MS_LIGHTNING_BOLT, // 10
MS_INVIS,
MS_FIREBALL,
MS_HEAL,
MS_TELEPORT,
MS_TELEPORT_OTHER, // 15
MS_BLINK,
MS_CRYSTAL_SPEAR,
MS_DIG,
MS_NEGATIVE_BOLT,
MS_HELLFIRE_BURST, // 20
MS_VAMPIRE_SUMMON,
MS_ORB_ENERGY,
MS_BRAIN_FEED,
MS_LEVEL_SUMMON,
MS_FAKE_RAKSHASA_SUMMON, // 25
MS_STEAM_BALL,
MS_SUMMON_DEMON,
MS_ANIMATE_DEAD,
MS_PAIN,
MS_SMITE, // 30
MS_STICKY_FLAME,
MS_POISON_BLAST,
MS_SUMMON_DEMON_LESSER,
MS_SUMMON_UFETUBUS,
MS_PURPLE_BLAST, // 35
MS_SUMMON_BEAST, // MS_GERYON was not descriptive - renamed 13jan2000 {dlb}
MS_ENERGY_BOLT,
MS_STING,
MS_IRON_BOLT,
MS_STONE_ARROW, // 40
MS_POISON_SPLASH,
MS_SUMMON_UNDEAD,
MS_MUTATION, // 43
MS_CANTRIP,
MS_DISINTEGRATE, // 45
MS_MARSH_GAS,
MS_QUICKSILVER_BOLT,
MS_TORMENT,
MS_HELLFIRE,
MS_METAL_SPLINTERS, // 50
MS_SUMMON_DEMON_GREATER, // [foo]_1 was confusing - renamed 13jan2000 {dlb}
MS_BANISHMENT,
MS_CONTROLLED_BLINK,
MS_CONTROL_UNDEAD,
MS_MIASMA, // 55
MS_SUMMON_DRAKES,
MS_BLINK_OTHER,
MS_DISPEL_UNDEAD,
MS_HELLFROST,
MS_POISON_ARROW, // 60
MS_SUMMON_SMALL_MAMMALS,
MS_SUMMON_MUSHROOMS,
MS_ICE_BOLT,
MS_MAGMA,
MS_SHOCK,
MS_BERSERK_RAGE,
MS_MIGHT,
MS_MAKHLEB_MINOR_DESTRUCTION,
MS_MAKHLEB_MAJOR_DESTRUCTION,
// XXX: before adding more monster versions of player spells we should
// consider merging the two lists into one and just having monsters
// fail to implement the ones that are impractical.
NUM_MONSTER_SPELLS,
MS_NO_SPELL = 100
};
SPELL_EXCRUCIATING_WOUNDS, // 204 (be wary of 209/210, see below)
SPELL_EXCRUCIATING_WOUNDS,
// Mostly monster-only spells after this point:
SPELL_HELLFIRE_BURST, // 205
SPELL_VAMPIRE_SUMMON,
SPELL_BRAIN_FEED,
SPELL_FAKE_RAKSHASA_SUMMON,
SPELL_STEAM_BALL,
SPELL_SUMMON_UFETUBUS, // 210
SPELL_SUMMON_BEAST,
SPELL_ENERGY_BOLT,
SPELL_POISON_SPLASH,
SPELL_SUMMON_UNDEAD,
SPELL_CANTRIP, // 215
SPELL_QUICKSILVER_BOLT,
SPELL_METAL_SPLINTERS,
SPELL_MIASMA,
SPELL_SUMMON_DRAKES,
SPELL_BLINK_OTHER, // 220
SPELL_SUMMON_MUSHROOMS,
char *itname = (char*) item_name( item, DESC_PLAIN );
char *item_runed = strstr( strlwr(itname), strlwr((char*) "runed") );
char *heav_runed = strstr( strlwr(itname), strlwr((char*) "heavily") );
std::string itname = item_name( item, DESC_PLAIN );
lowercase(itname);
const bool item_runed = itname.find(" runed ") != std::string::npos;
const bool heav_runed = itname.find(" heavily ") != std::string::npos;