summaryrefslogtreecommitdiffstats
path: root/crawl-ref
diff options
context:
space:
mode:
Diffstat (limited to 'crawl-ref')
-rw-r--r--crawl-ref/docs/develop/level_design.txt56
-rw-r--r--crawl-ref/source/acr.cc6
-rw-r--r--crawl-ref/source/debug.cc7
-rw-r--r--crawl-ref/source/describe.cc2
-rw-r--r--crawl-ref/source/dungeon.cc14
-rw-r--r--crawl-ref/source/enum.h10
-rw-r--r--crawl-ref/source/mapdef.cc60
-rw-r--r--crawl-ref/source/mapdef.h9
-rw-r--r--crawl-ref/source/mon-act.cc4
-rw-r--r--crawl-ref/source/mon-cast.cc44
-rw-r--r--crawl-ref/source/mon-util.cc18
-rw-r--r--crawl-ref/source/monplace.cc126
-rw-r--r--crawl-ref/source/monspeak.cc2
-rw-r--r--crawl-ref/source/monster.cc38
-rw-r--r--crawl-ref/source/monster.h13
-rw-r--r--crawl-ref/source/monstuff.cc11
-rw-r--r--crawl-ref/source/xom.cc2
17 files changed, 289 insertions, 133 deletions
diff --git a/crawl-ref/docs/develop/level_design.txt b/crawl-ref/docs/develop/level_design.txt
index b7c1f2c728..cdeac74581 100644
--- a/crawl-ref/docs/develop/level_design.txt
+++ b/crawl-ref/docs/develop/level_design.txt
@@ -722,7 +722,61 @@ MONS: (list of monsters)
and then using the glyph '1' multiple times will result in multiple
"Durwent the Kobold"s).
- Monster names should be used very, very, very sparingly.
+ Monster names should be used very sparingly.
+
+ Overriding Monster Spells:
+ --------------------------
+ Monster spell sets can be overridden with a spells: tag,
+ used as follows:
+
+ MONS: goblin spells:throw_flame
+ MONS: ancient lich spells:symbol_of_torment;ice_storm;ice_storm
+
+ (a list of spell names, spaces replaced with underscores,
+ and names separated by ';' with no spaces around the ';' or
+ after the spell: prefix)
+
+ Monster spells currently use a limited spell-slot system,
+ with these slots:
+ 1. Bolt spell
+ 2. Enchantment
+ 3. Self-enchantment
+ 4. Misc(1)
+ 5. Misc(2)
+ 6. Emergency/escape
+
+ These slots are not hard and fast rules, but it is sometimes
+ useful to drop a spell in a specific slot, for instance the
+ emergency/escape slot:
+ MONS: hobgoblin spells:.;.;.;.;.;teleport_self
+
+ Spell names must exactly match the names in spl-data.h, with
+ spaces replaced by underscores. You may use "." or an empty
+ string to specify that a slot should be left empty. You can
+ force a spell-less monster with:
+ MONS: orc wizard spells:.
+ (although why you'd want to do this is open to question.)
+
+ If you define spells for a monster that cannot cast spells
+ normally, you may want to mark the monster as a real
+ spellcaster with 'actual_spells':
+ MONS: goblin spells:throw_flame actual_spells
+
+ Or as a priestly (divine) caster with 'priest_spells':
+ MONS: goblin spells:smiting priest_spells
+
+ Real spellcasters and priests can be silenced and will
+ trigger appropriate conducts (Trog will appreciate killing
+ spellcasters, Beogh will appreciate killing priests). If you
+ define spells without specifying 'actual_spells' or
+ 'priest_spells', and the monster cannot cast spells
+ normally, the spells will be treated as innate abilities, so
+ the monster can use these spells even when silenced.
+ Treating spells as innate abilities may produce odd casting
+ message (such as: "the rat throws fire at you"). If you find
+ the messages you get unsatisfactory, add suitable entries to
+ source/dat/database/monspell.txt.
+
COLOUR: . = green / blue:5 / red / none
COLOUR: allows you to attach explicit colours to any feature.
diff --git a/crawl-ref/source/acr.cc b/crawl-ref/source/acr.cc
index e6aad2d09b..8b876b1cb5 100644
--- a/crawl-ref/source/acr.cc
+++ b/crawl-ref/source/acr.cc
@@ -3557,9 +3557,6 @@ static bool _initialise(void)
// Set up the Lua interpreter for the dungeon builder.
init_dungeon_lua();
- // Read special levels and vaults.
- read_maps();
-
// Initialise internal databases.
databaseSystemInit();
@@ -3567,6 +3564,9 @@ static bool _initialise(void)
init_spell_name_cache();
init_spell_rarities();
+ // Read special levels and vaults.
+ read_maps();
+
cio_init();
// System initialisation stuff.
diff --git a/crawl-ref/source/debug.cc b/crawl-ref/source/debug.cc
index cfa01ca783..5d9cfe4bfb 100644
--- a/crawl-ref/source/debug.cc
+++ b/crawl-ref/source/debug.cc
@@ -470,16 +470,17 @@ void wizard_create_spec_monster_name()
if (!err.empty())
{
+ std::string newerr;
// Try for a partial match, but not if the user accidently entered
// only a few letters.
monster_type partial = get_monster_by_name(specs);
if (strlen(specs) >= 3 && partial != NON_MONSTER)
{
mlist.clear();
- err = mlist.add_mons(mons_type_name(partial, DESC_PLAIN));
+ newerr = mlist.add_mons(mons_type_name(partial, DESC_PLAIN));
}
- if (!err.empty())
+ if (!newerr.empty())
{
mpr(err.c_str());
return;
@@ -6764,7 +6765,7 @@ void debug_dump_mon(const monsters* mon, bool recurse)
}
fprintf(stderr, EOL);
- if (mons_class_flag(mon->type, M_SPELLCASTER))
+ if (mon->can_use_spells())
{
fprintf(stderr, "Spells:" EOL);
diff --git a/crawl-ref/source/describe.cc b/crawl-ref/source/describe.cc
index 612de87d5b..6d5b42bf67 100644
--- a/crawl-ref/source/describe.cc
+++ b/crawl-ref/source/describe.cc
@@ -2905,7 +2905,7 @@ void get_monster_db_desc(const monsters& mons, describe_info &inf,
inf.quote += quote2;
#if DEBUG_DIAGNOSTICS
- if (mons_class_flag( mons.type, M_SPELLCASTER ))
+ if (mons.can_use_spells())
{
const monster_spells &hspell_pass = mons.spells;
bool found_spell = false;
diff --git a/crawl-ref/source/dungeon.cc b/crawl-ref/source/dungeon.cc
index 8e8adeff8e..9c0bf853a3 100644
--- a/crawl-ref/source/dungeon.cc
+++ b/crawl-ref/source/dungeon.cc
@@ -4801,8 +4801,17 @@ int dgn_place_monster(mons_spec &mspec,
mg.flags |= MG_PERMIT_BANDS;
const int mindex = place_monster(mg, true);
- if (mindex != -1 && mspec.items.size() > 0)
- _dgn_give_mon_spec_items(mspec, mindex, mid, monster_level);
+ if (mindex != -1)
+ {
+ monsters &mons(menv[mindex]);
+ if (!mspec.items.empty())
+ _dgn_give_mon_spec_items(mspec, mindex, mid, monster_level);
+ if (mspec.explicit_spells)
+ mons.spells = mspec.spells;
+ mons.flags |= mspec.extra_monster_flags;
+ if (mons.is_priest() && mons.god == GOD_NO_GOD)
+ mons.god = GOD_NAMELESS;
+ }
return (mindex);
}
return (-1);
@@ -8484,4 +8493,3 @@ std::string dump_vault_maps()
}
return (out);
}
-
diff --git a/crawl-ref/source/enum.h b/crawl-ref/source/enum.h
index 02ad706fb2..c9b4d0e43a 100644
--- a/crawl-ref/source/enum.h
+++ b/crawl-ref/source/enum.h
@@ -2189,7 +2189,15 @@ enum monster_flag_type
MF_NAME_MASK = 0x30000,
MF_GOD_GIFT = 0x40000, // Is a god gift.
MF_FLEEING_FROM_SANCTUARY = 0x80000, // Is running away from player sanctuary
- MF_EXPLODE_KILL = 0x100000 // Is being killed with disintegration
+ MF_EXPLODE_KILL = 0x100000, // Is being killed with disintegration
+
+ // These are based on the flags in monster class, but can be set for
+ // monsters that are not normally spellcasters (in vaults).
+ MF_SPELLCASTER = 0x200000,
+ MF_ACTUAL_SPELLS = 0x400000, // Can use spells and is a spellcaster for
+ // Trog purposes.
+ MF_PRIEST = 0x800000 // Is a priest (divine spells)
+ // for the conduct.
};
// Adding slots breaks saves. YHBW.
diff --git a/crawl-ref/source/mapdef.cc b/crawl-ref/source/mapdef.cc
index 3f70e543d5..f12215acef 100644
--- a/crawl-ref/source/mapdef.cc
+++ b/crawl-ref/source/mapdef.cc
@@ -35,6 +35,7 @@
#include "place.h"
#include "random.h"
#include "religion.h"
+#include "spl-util.h"
#include "stuff.h"
#include "tags.h"
@@ -2337,6 +2338,41 @@ bool mons_list::check_mimic(const std::string &s, int *mid, bool *fix) const
return (true);
}
+void mons_list::parse_mons_spells(mons_spec &spec, const std::string &spells)
+{
+ spec.explicit_spells = true;
+ spec.extra_monster_flags |= MF_SPELLCASTER;
+ const std::vector<std::string> spell_names(split_string(";", spells));
+ if (spell_names.size() > NUM_MONSTER_SPELL_SLOTS)
+ {
+ error = make_stringf("Too many monster spells (max %d) in %s",
+ NUM_MONSTER_SPELL_SLOTS,
+ spells.c_str());
+ return;
+ }
+ for (unsigned i = 0, ssize = spell_names.size(); i < ssize; ++i)
+ {
+ const std::string spname(
+ lowercase_string(replace_all_of(spell_names[i], "_", " ")));
+ if (spname.empty() || spname == "." || spname == "none"
+ || spname == "no spell")
+ {
+ spec.spells[i] = SPELL_NO_SPELL;
+ }
+ else
+ {
+ const spell_type sp(spell_by_name(spname));
+ if (sp == SPELL_NO_SPELL)
+ {
+ error = make_stringf("Unknown spell name: '%s' in '%s'",
+ spname.c_str(), spells.c_str());
+ return;
+ }
+ spec.spells[i] = sp;
+ }
+ }
+}
+
mons_list::mons_spec_slot mons_list::parse_mons_spec(std::string spec)
{
mons_spec_slot slot;
@@ -2347,12 +2383,20 @@ mons_list::mons_spec_slot mons_list::parse_mons_spec(std::string spec)
for (int i = 0, ssize = specs.size(); i < ssize; ++i)
{
+ mons_spec mspec;
std::string s = specs[i];
+
+ std::string spells(strip_tag_prefix(s, "spells:"));
+ if (!spells.empty())
+ {
+ parse_mons_spells(mspec, spells);
+ if (!error.empty())
+ return (slot);
+ }
+
std::vector<std::string> parts = split_string(";", s);
std::string mon_str = parts[0];
- mons_spec mspec;
-
if (parts.size() > 2)
{
error = make_stringf("Too many semi-colons for '%s' spec.",
@@ -2388,6 +2432,18 @@ mons_list::mons_spec_slot mons_list::parse_mons_spec(std::string spec)
if (mspec.genweight == TAG_UNFOUND || mspec.genweight <= 0)
mspec.genweight = 10;
+ if (strip_tag(mon_str, "priest_spells"))
+ {
+ mspec.extra_monster_flags &= ~MF_ACTUAL_SPELLS;
+ mspec.extra_monster_flags |= MF_PRIEST;
+ }
+
+ if (strip_tag(mon_str, "actual_spells"))
+ {
+ mspec.extra_monster_flags &= ~MF_PRIEST;
+ mspec.extra_monster_flags |= MF_ACTUAL_SPELLS;
+ }
+
mspec.fix_mons = strip_tag(mon_str, "fix_mons");
mspec.generate_awake = strip_tag(mon_str, "generate_awake");
mspec.patrolling = strip_tag(mon_str, "patrolling");
diff --git a/crawl-ref/source/mapdef.h b/crawl-ref/source/mapdef.h
index 84fd1cce09..a94eaeab65 100644
--- a/crawl-ref/source/mapdef.h
+++ b/crawl-ref/source/mapdef.h
@@ -426,6 +426,7 @@ public:
item_spec get_item(int index);
size_t size() const { return items.size(); }
+ bool empty() const { return items.empty(); }
std::string add_item(const std::string &spec, bool fix = false);
std::string set_item(int index, const std::string &spec);
@@ -474,6 +475,10 @@ class mons_spec
item_list items;
std::string monname;
+ bool explicit_spells;
+ monster_spells spells;
+ unsigned long extra_monster_flags;
+
mons_spec(int id = RANDOM_MONSTER,
monster_type base = MONS_NO_MONSTER,
int num = 0,
@@ -482,7 +487,8 @@ class mons_spec
: mid(id), place(), monbase(base), attitude(ATT_HOSTILE), number(num),
quantity(1), genweight(gw), mlevel(ml), fix_mons(_fixmons),
generate_awake(awaken), patrolling(false), band(false),
- colour(BLACK), items(), monname("")
+ colour(BLACK), items(), monname(""), explicit_spells(false),
+ spells(), extra_monster_flags(0L)
{
}
};
@@ -532,6 +538,7 @@ private:
mons_spec get_zombified_monster(const std::string &name,
monster_type zomb) const;
mons_spec_slot parse_mons_spec(std::string spec);
+ void parse_mons_spells(mons_spec &slot, const std::string &spells);
mons_spec pick_monster(mons_spec_slot &slot);
int fix_demon(int id) const;
bool check_mimic(const std::string &s, int *mid, bool *fix) const;
diff --git a/crawl-ref/source/mon-act.cc b/crawl-ref/source/mon-act.cc
index d6ed4632a8..daa4a50577 100644
--- a/crawl-ref/source/mon-act.cc
+++ b/crawl-ref/source/mon-act.cc
@@ -167,8 +167,7 @@ static bool _swap_monsters(monsters* mover, monsters* moved)
static bool _do_mon_spell(monsters *monster, bolt &beem)
{
// Shapeshifters don't get spells.
- if (!mons_is_shapeshifter(monster)
- || !mons_class_flag(monster->type, M_ACTUAL_SPELLS))
+ if (!mons_is_shapeshifter(monster) || !monster->is_actual_spellcaster())
{
if (handle_mon_spell(monster, beem))
{
@@ -3634,4 +3633,3 @@ static spell_type _map_wand_to_mspell(int wand_type)
default: return SPELL_NO_SPELL;
}
}
-
diff --git a/crawl-ref/source/mon-cast.cc b/crawl-ref/source/mon-cast.cc
index c621237be5..e16d901f69 100644
--- a/crawl-ref/source/mon-cast.cc
+++ b/crawl-ref/source/mon-cast.cc
@@ -751,7 +751,7 @@ void setup_mons_cast(monsters *monster, bolt &pbolt, spell_type spell_cast)
if (targ->type != hog_type
&& mons_atts_aligned(monster->attitude, targ->attitude)
&& mons_power(hog_type) + random2(4) >= mons_power(targ->type)
- && (!mons_class_flag(targ->type, M_SPELLCASTER) || coinflip())
+ && (!targ->can_use_spells() || coinflip())
&& one_chance_in(++count))
{
target = i;
@@ -807,15 +807,6 @@ static spell_type _get_draconian_breath_spell( monsters *monster )
return (draco_breath);
}
-static bool _mon_has_spells(monsters *monster)
-{
- for (int i = 0; i < NUM_MONSTER_SPELL_SLOTS; ++i)
- if (monster->spells[i] != SPELL_NO_SPELL)
- return (true);
-
- return (false);
-}
-
static bool _is_emergency_spell(const monster_spells &msp, int spell)
{
// If the emergency spell appears early, it's probably not a dedicated
@@ -845,10 +836,10 @@ bool handle_mon_spell(monsters *monster, bolt &beem)
// form. If the new form has the SPELLCASTER flag, casting happens as
// normally, otherwise we need to enforce it, but it only happens with
// a 50% chance.
- const bool spellcasting_poly
- = !mons_class_flag(monster->type, M_SPELLCASTER)
- && mons_class_flag(monster->type, M_SPEAKS)
- && _mon_has_spells(monster);
+ const bool spellcasting_poly(
+ !monster->can_use_spells()
+ && mons_class_flag(monster->type, M_SPEAKS)
+ && monster->has_spells());
if (is_sanctuary(monster->pos()) && !mons_wont_attack(monster))
return (false);
@@ -856,9 +847,9 @@ bool handle_mon_spell(monsters *monster, bolt &beem)
// Yes, there is a logic to this ordering {dlb}:
if (monster->asleep()
|| monster->submerged()
- || !mons_class_flag(monster->type, M_SPELLCASTER)
- && !spellcasting_poly
- && draco_breath == SPELL_NO_SPELL)
+ || (!monster->can_use_spells()
+ && !spellcasting_poly
+ && draco_breath == SPELL_NO_SPELL))
{
return (false);
}
@@ -868,8 +859,8 @@ bool handle_mon_spell(monsters *monster, bolt &beem)
// monster is neither a priest nor a wizard, assume summons come
// from intrinsic abilities, in which case they'll also have the
// same god.
- const bool priest = mons_class_flag(monster->type, M_PRIEST);
- const bool wizard = mons_class_flag(monster->type, M_ACTUAL_SPELLS);
+ const bool priest = monster->is_priest();
+ const bool wizard = monster->is_actual_spellcaster();
god_type god = (priest || !(priest || wizard)) ? monster->god : GOD_NO_GOD;
if (silenced(monster->pos())
@@ -1471,8 +1462,8 @@ void mons_cast(monsters *monster, bolt &pbolt, spell_type spell_cast,
// monster is neither a priest nor a wizard, assume summons come
// from intrinsic abilities, in which case they'll also have the
// same god.
- const bool priest = mons_class_flag(monster->type, M_PRIEST);
- const bool wizard = mons_class_flag(monster->type, M_ACTUAL_SPELLS);
+ const bool priest = monster->is_priest();
+ const bool wizard = monster->is_actual_spellcaster();
god_type god = (priest || !(priest || wizard)) ? monster->god : GOD_NO_GOD;
switch (spell_cast)
@@ -2008,8 +1999,8 @@ void mons_cast_noise(monsters *monster, bolt &pbolt, spell_type spell_cast)
const unsigned int flags = get_spell_flags(real_spell);
- const bool priest = mons_class_flag(monster->type, M_PRIEST);
- const bool wizard = mons_class_flag(monster->type, M_ACTUAL_SPELLS);
+ const bool priest = monster->is_priest();
+ const bool wizard = monster->is_actual_spellcaster();
const bool innate = !(priest || wizard || no_silent)
|| (flags & SPFLAG_INNATE);
@@ -2354,10 +2345,3 @@ void mons_cast_noise(monsters *monster, bolt &pbolt, spell_type spell_cast)
mons_speaks_msg(monster, msg, chan);
}
}
-
-
-
-
-
-
-
diff --git a/crawl-ref/source/mon-util.cc b/crawl-ref/source/mon-util.cc
index d87a74fed9..35ebd6a443 100644
--- a/crawl-ref/source/mon-util.cc
+++ b/crawl-ref/source/mon-util.cc
@@ -1312,7 +1312,7 @@ int exper_value(const monsters *monster)
// These four are the original arguments.
const int mclass = monster->type;
const int mHD = monster->hit_dice;
- int maxhp = monster->max_hit_points;
+ int maxhp = monster->max_hit_points;
// Hacks to make merged slime creatures not worth so much exp. We
// will calculate the experience we would get for 1 blob, and then
@@ -1326,7 +1326,7 @@ int exper_value(const monsters *monster)
const int item_usage = mons_itemuse(monster);
// XXX: Shapeshifters can qualify here, even though they can't cast.
- const bool spellcaster = mons_class_flag(mclass, M_SPELLCASTER);
+ const bool spellcaster = monster->can_use_spells();
// Early out for no XP monsters.
if (mons_class_flag(mclass, M_NO_EXP_GAIN))
@@ -1453,11 +1453,6 @@ int exper_value(const monsters *monster)
return (x_val);
}
-void mons_load_spells(monsters *mon, mon_spellbook_type book)
-{
- mon->load_spells(book);
-}
-
monster_type random_draconian_monster_species()
{
const int num_drac = MONS_PALE_DRACONIAN - MONS_BLACK_DRACONIAN + 1;
@@ -1660,7 +1655,8 @@ void define_monster(monsters &mons)
mons.experience = 0L;
mons.colour = col;
- mons_load_spells(&mons, spells);
+ mons.load_spells(spells);
+ mons.bind_spell_flags();
// Reset monster enchantments.
mons.enchantments.clear();
@@ -2687,7 +2683,7 @@ static bool _ms_ranged_spell(spell_type monspell, bool attack_only = false,
bool mons_is_magic_user( const monsters *mon )
{
- if (mons_class_flag(mon->type, M_ACTUAL_SPELLS))
+ if (mon->is_actual_spellcaster())
{
for (int i = 0; i < NUM_MONSTER_SPELL_SLOTS; ++i)
if (mon->spells[i] != SPELL_NO_SPELL)
@@ -2727,7 +2723,7 @@ bool mons_has_los_attack(const monsters *mon)
if (mons_has_los_ability(mclass))
return (true);
- if (mons_class_flag(mclass, M_SPELLCASTER))
+ if (mon->can_use_spells())
{
for (int i = 0; i < NUM_MONSTER_SPELL_SLOTS; i++)
if (_ms_los_spell(mon->spells[i]))
@@ -2746,7 +2742,7 @@ bool mons_has_ranged_spell(const monsters *mon, bool attack_only,
if (mons_has_los_ability(mclass))
return (true);
- if (mons_class_flag(mclass, M_SPELLCASTER))
+ if (mon->can_use_spells())
{
for (int i = 0; i < NUM_MONSTER_SPELL_SLOTS; i++)
if (_ms_ranged_spell(mon->spells[i], attack_only, ench_too))
diff --git a/crawl-ref/source/monplace.cc b/crawl-ref/source/monplace.cc
index 4e520decf3..33850d50c5 100644
--- a/crawl-ref/source/monplace.cc
+++ b/crawl-ref/source/monplace.cc
@@ -965,7 +965,7 @@ int place_monster(mgen_data mg, bool force_pos)
if (band_size > 1)
menv[id].flags |= MF_BAND_MEMBER;
- const bool priest = mons_class_flag(mon->type, M_PRIEST);
+ const bool priest = mon->is_priest();
mgen_data band_template = mg;
// (5) For each band monster, loop call to place_monster_aux().
@@ -1054,12 +1054,13 @@ static int _place_monster_aux(const mgen_data &mg,
return (-1);
}
+ monsters &mons(menv[id]);
// Now, actually create the monster. (Wheeee!)
- menv[id].type = mg.cls;
- menv[id].base_monster = mg.base_type;
- menv[id].number = mg.number;
+ mons.type = mg.cls;
+ mons.base_monster = mg.base_type;
+ mons.number = mg.number;
- menv[id].moveto(fpos);
+ mons.moveto(fpos);
// Link monster into monster grid.
mgrd(fpos) = id;
@@ -1073,8 +1074,8 @@ static int _place_monster_aux(const mgen_data &mg,
// Is it a god gift?
if (mg.god != GOD_NO_GOD)
{
- menv[id].god = mg.god;
- menv[id].flags |= MF_GOD_GIFT;
+ mons.god = mg.god;
+ mons.flags |= MF_GOD_GIFT;
}
// Not a god gift, give priestly monsters a god.
else if (mons_class_flag(mg.cls, M_PRIEST))
@@ -1082,20 +1083,17 @@ static int _place_monster_aux(const mgen_data &mg,
switch (mons_genus(mg.cls))
{
case MONS_ORC:
- menv[id].god = GOD_BEOGH;
+ mons.god = GOD_BEOGH;
break;
case MONS_JELLY:
- menv[id].god = GOD_JIYVA;
+ mons.god = GOD_JIYVA;
break;
case MONS_MUMMY:
case MONS_DRACONIAN:
case MONS_ELF:
- menv[id].god = GOD_NAMELESS;
- break;
+ // [ds] Vault defs can request priest monsters of unusual types.
default:
- mprf(MSGCH_ERROR, "ERROR: Invalid monster priest '%s'",
- menv[id].name(DESC_PLAIN, true).c_str());
- ASSERT(false);
+ mons.god = GOD_NAMELESS;
break;
}
}
@@ -1103,34 +1101,34 @@ static int _place_monster_aux(const mgen_data &mg,
else if (mons_genus(mg.cls) == MONS_ORC)
{
if (!one_chance_in(7))
- menv[id].god = GOD_BEOGH;
+ mons.god = GOD_BEOGH;
}
// The royal jelly belongs to Jiyva.
else if (mg.cls == MONS_ROYAL_JELLY)
- menv[id].god = GOD_JIYVA;
+ mons.god = GOD_JIYVA;
// Angels and Daevas belong to TSO, but 1 out of 7 in the Abyss are
// adopted by Xom.
else if (mons_class_holiness(mg.cls) == MH_HOLY)
{
if (mg.level_type != LEVEL_ABYSS || !one_chance_in(7))
- menv[id].god = GOD_SHINING_ONE;
+ mons.god = GOD_SHINING_ONE;
else
- menv[id].god = GOD_XOM;
+ mons.god = GOD_XOM;
}
// 6 out of 7 demons in the Abyss belong to Lugonu.
else if (mons_class_holiness(mg.cls) == MH_DEMONIC)
{
if (mg.level_type == LEVEL_ABYSS && !one_chance_in(7))
- menv[id].god = GOD_LUGONU;
+ mons.god = GOD_LUGONU;
}
// If the caller requested a specific colour for this monster, apply
// it now.
if (mg.colour != BLACK)
- menv[id].colour = mg.colour;
+ mons.colour = mg.colour;
if (mg.mname != "")
- menv[id].mname = mg.mname;
+ mons.mname = mg.mname;
// The return of Boris is now handled in monster_die(). Not setting
// this for Boris here allows for multiple Borises in the dungeon at
@@ -1139,16 +1137,16 @@ static int _place_monster_aux(const mgen_data &mg,
you.unique_creatures[mg.cls] = true;
if (mons_class_flag(mg.cls, M_INVIS))
- menv[id].add_ench(ENCH_INVIS);
+ mons.add_ench(ENCH_INVIS);
if (mons_class_flag(mg.cls, M_CONFUSED))
- menv[id].add_ench(ENCH_CONFUSION);
+ mons.add_ench(ENCH_CONFUSION);
if (mg.cls == MONS_SHAPESHIFTER)
- menv[id].add_ench(ENCH_SHAPESHIFTER);
+ mons.add_ench(ENCH_SHAPESHIFTER);
if (mg.cls == MONS_GLOWING_SHAPESHIFTER)
- menv[id].add_ench(ENCH_GLOWING_SHAPESHIFTER);
+ mons.add_ench(ENCH_GLOWING_SHAPESHIFTER);
if (mg.cls == MONS_TOADSTOOL)
{
@@ -1157,29 +1155,29 @@ static int _place_monster_aux(const mgen_data &mg,
// spawning mushrooms in the same place over and over. Aside
// from that, the value is slightly randomised to avoid
// simultaneous die-offs of mushroom rings.
- menv[id].add_ench(ENCH_SLOWLY_DYING);
+ mons.add_ench(ENCH_SLOWLY_DYING);
}
if (mg.cls == MONS_BALLISTOMYCETE)
{
// This enchantment causes giant spore production.
- menv[id].add_ench(ENCH_SPORE_PRODUCTION);
+ mons.add_ench(ENCH_SPORE_PRODUCTION);
}
- if (monster_can_submerge(&menv[id], grd(fpos)) && !one_chance_in(5))
- menv[id].add_ench(ENCH_SUBMERGED);
+ if (monster_can_submerge(&mons, grd(fpos)) && !one_chance_in(5))
+ mons.add_ench(ENCH_SUBMERGED);
- menv[id].flags |= MF_JUST_SUMMONED;
+ mons.flags |= MF_JUST_SUMMONED;
// Don't leave shifters in their starting shape.
if (mg.cls == MONS_SHAPESHIFTER || mg.cls == MONS_GLOWING_SHAPESHIFTER)
{
no_messages nm;
- monster_polymorph(&menv[id], RANDOM_MONSTER);
+ monster_polymorph(&mons, RANDOM_MONSTER);
// It's not actually a known shapeshifter if it happened to be
// placed in LOS of the player.
- menv[id].flags &= ~MF_KNOWN_MIMIC;
+ mons.flags &= ~MF_KNOWN_MIMIC;
}
// dur should always be 1-6 for monsters that can be abjured.
@@ -1192,16 +1190,16 @@ static int _place_monster_aux(const mgen_data &mg,
// Dancing weapons *always* have a weapon. Fail to create them
// otherwise.
- const item_def* wpn = menv[id].weapon();
+ const item_def* wpn = mons.weapon();
if (!wpn)
{
- menv[id].destroy_inventory();
- menv[id].reset();
+ mons.destroy_inventory();
+ mons.reset();
mgrd(fpos) = NON_MONSTER;
return (-1);
}
else
- menv[id].colour = wpn->colour;
+ mons.colour = wpn->colour;
}
else if (mons_class_itemuse(mg.cls) >= MONUSE_STARTING_EQUIPMENT)
{
@@ -1210,13 +1208,13 @@ static int _place_monster_aux(const mgen_data &mg,
if (mons_class_wields_two_weapons(mg.cls))
give_item(id, mg.power, summoned);
- unwind_var<int> save_speedinc(menv[id].speed_increment);
- menv[id].wield_melee_weapon(false);
+ unwind_var<int> save_speedinc(mons.speed_increment);
+ mons.wield_melee_weapon(false);
}
// Give manticores 8 to 16 spike volleys.
if (mg.cls == MONS_MANTICORE)
- menv[id].number = 8 + random2(9);
+ mons.number = 8 + random2(9);
if (mg.cls == MONS_SLIME_CREATURE)
{
@@ -1224,21 +1222,21 @@ static int _place_monster_aux(const mgen_data &mg,
{
// Boost HP to what it would have been if it had grown this
// big by merging.
- menv[id].hit_points *= mg.number;
- menv[id].max_hit_points *= mg.number;
+ mons.hit_points *= mg.number;
+ mons.max_hit_points *= mg.number;
}
}
// Set attitude, behaviour and target.
- menv[id].attitude = ATT_HOSTILE;
- menv[id].behaviour = mg.behaviour;
+ mons.attitude = ATT_HOSTILE;
+ mons.behaviour = mg.behaviour;
// Statues cannot sleep (nor wander but it means they are a bit
// more aware of the player than they'd be otherwise).
if (mons_is_statue(mg.cls))
- menv[id].behaviour = BEH_WANDER;
+ mons.behaviour = BEH_WANDER;
- menv[id].foe_memory = 0;
+ mons.foe_memory = 0;
// Setting attitude will always make the monster wander...
// If you want sleeping hostiles, use BEH_SLEEP since the default
@@ -1246,52 +1244,52 @@ static int _place_monster_aux(const mgen_data &mg,
if (mg.behaviour > NUM_BEHAVIOURS)
{
if (mg.behaviour == BEH_FRIENDLY)
- menv[id].attitude = ATT_FRIENDLY;
+ mons.attitude = ATT_FRIENDLY;
if (mg.behaviour == BEH_GOOD_NEUTRAL)
- menv[id].attitude = ATT_GOOD_NEUTRAL;
+ mons.attitude = ATT_GOOD_NEUTRAL;
if (mg.behaviour == BEH_NEUTRAL)
- menv[id].attitude = ATT_NEUTRAL;
+ mons.attitude = ATT_NEUTRAL;
if (mg.behaviour == BEH_STRICT_NEUTRAL)
- menv[id].attitude = ATT_STRICT_NEUTRAL;
+ mons.attitude = ATT_STRICT_NEUTRAL;
- menv[id].behaviour = BEH_WANDER;
+ mons.behaviour = BEH_WANDER;
}
if (summoned)
{
- menv[id].mark_summoned(mg.abjuration_duration, true,
+ mons.mark_summoned(mg.abjuration_duration, true,
mg.summon_type);
}
- menv[id].foe = mg.foe;
+ mons.foe = mg.foe;
// Initialise (very) ugly things and pandemonium demons.
- if (menv[id].type == MONS_UGLY_THING
- || menv[id].type == MONS_VERY_UGLY_THING)
+ if (mons.type == MONS_UGLY_THING
+ || mons.type == MONS_VERY_UGLY_THING)
{
ghost_demon ghost;
- ghost.init_ugly_thing(menv[id].type == MONS_VERY_UGLY_THING, false,
+ ghost.init_ugly_thing(mons.type == MONS_VERY_UGLY_THING, false,
mg.colour);
- menv[id].set_ghost(ghost, false);
- menv[id].uglything_init();
+ mons.set_ghost(ghost, false);
+ mons.uglything_init();
}
- else if (menv[id].type == MONS_PANDEMONIUM_DEMON)
+ else if (mons.type == MONS_PANDEMONIUM_DEMON)
{
ghost_demon ghost;
ghost.init_random_demon();
- menv[id].set_ghost(ghost);
- menv[id].pandemon_init();
+ mons.set_ghost(ghost);
+ mons.pandemon_init();
}
- mark_interesting_monst(&menv[id], mg.behaviour);
+ mark_interesting_monst(&mons, mg.behaviour);
- if (you.can_see(&menv[id]))
- handle_seen_interrupt(&menv[id]);
+ if (you.can_see(&mons))
+ handle_seen_interrupt(&mons);
if (crawl_state.arena)
- arena_placed_monster(&menv[id]);
+ arena_placed_monster(&mons);
return (id);
}
diff --git a/crawl-ref/source/monspeak.cc b/crawl-ref/source/monspeak.cc
index 17f41b05da..6ac5e4544c 100644
--- a/crawl-ref/source/monspeak.cc
+++ b/crawl-ref/source/monspeak.cc
@@ -340,7 +340,7 @@ static std::string _player_ghost_speak_str(const monsters *monster,
static bool _polyd_can_speak(const monsters* monster)
{
// Wizard and priest monsters can always speak.
- if (mons_class_flag(monster->type, M_ACTUAL_SPELLS | M_PRIEST))
+ if (monster->is_actual_spellcaster() || monster->is_priest())
return (true);
// Silent or non-sentient monsters can't use the original speech.
diff --git a/crawl-ref/source/monster.cc b/crawl-ref/source/monster.cc
index 4770e2fe9b..66c38c3277 100644
--- a/crawl-ref/source/monster.cc
+++ b/crawl-ref/source/monster.cc
@@ -658,6 +658,18 @@ bool monsters::has_spell_of_type(unsigned disciplines) const
return (false);
}
+void monsters::bind_spell_flags()
+{
+ // Bind spellcaster / priest flags from the base type. These may be
+ // overridden by vault defs for individual monsters.
+ if (mons_class_flag(type, M_SPELLCASTER))
+ flags |= MF_SPELLCASTER;
+ if (mons_class_flag(type, M_ACTUAL_SPELLS))
+ flags |= MF_ACTUAL_SPELLS;
+ if (mons_class_flag(type, M_PRIEST))
+ flags |= MF_PRIEST;
+}
+
static bool _needs_ranged_attack(const monsters *mon)
{
// Prevent monsters that have conjurations from grabbing missiles.
@@ -2680,6 +2692,15 @@ void monsters::banish(const std::string &)
monster_die(this, KILL_RESET, NON_MONSTER);
}
+bool monsters::has_spells() const
+{
+ for (int i = 0; i < NUM_MONSTER_SPELL_SLOTS; ++i)
+ if (spells[i] != SPELL_NO_SPELL)
+ return (true);
+
+ return (false);
+}
+
bool monsters::has_spell(spell_type spell) const
{
for (int i = 0; i < NUM_MONSTER_SPELL_SLOTS; ++i)
@@ -3597,6 +3618,21 @@ bool monsters::has_multitargeting() const
|| type == MONS_ELECTRIC_GOLEM);
}
+bool monsters::can_use_spells() const
+{
+ return (flags & MF_SPELLCASTER);
+}
+
+bool monsters::is_priest() const
+{
+ return (flags & MF_PRIEST);
+}
+
+bool monsters::is_actual_spellcaster() const
+{
+ return (flags & MF_ACTUAL_SPELLS);
+}
+
bool monsters::has_ench(enchant_type ench) const
{
return (enchantments.find(ench) != enchantments.end());
@@ -5705,5 +5741,3 @@ void mon_enchant::set_duration(const monsters *mons, const mon_enchant *added)
if (duration > maxduration)
maxduration = duration;
}
-
-
diff --git a/crawl-ref/source/monster.h b/crawl-ref/source/monster.h
index 9c7b2fcee3..537320b614 100644
--- a/crawl-ref/source/monster.h
+++ b/crawl-ref/source/monster.h
@@ -136,6 +136,16 @@ public:
bool has_hydra_multi_attack() const;
bool has_multitargeting() const;
+ // Has the 'spellcaster' flag (may not actually have any spells)
+ bool can_use_spells() const;
+
+ // Has the 'priest' flag.
+ bool is_priest() const;
+
+ // Is an actual wizard-type spellcaster, i.e. Trog will dislike it, it
+ // can be silenced, etc.
+ bool is_actual_spellcaster() const;
+
bool has_ench(enchant_type ench) const;
bool has_ench(enchant_type ench, enchant_type ench2) const;
mon_enchant get_ench(enchant_type ench,
@@ -319,6 +329,7 @@ public:
bool backlit(bool check_haloed = true) const;
bool haloed() const;
+ bool has_spells() const;
bool has_spell(spell_type spell) const;
bool has_attack_flavour(int flavour) const;
@@ -369,6 +380,8 @@ public:
bool do_shaft();
bool has_spell_of_type(unsigned) const;
+ void bind_spell_flags();
+
private:
void init_with(const monsters &mons);
void swap_slots(mon_inv_type a, mon_inv_type b);
diff --git a/crawl-ref/source/monstuff.cc b/crawl-ref/source/monstuff.cc
index 3115ca8bb0..00181a28ba 100644
--- a/crawl-ref/source/monstuff.cc
+++ b/crawl-ref/source/monstuff.cc
@@ -1564,7 +1564,7 @@ int monster_die(monsters *monster, killer_type killer,
}
// Beogh hates priests of other gods.
- if (mons_class_flag(monster->type, M_PRIEST))
+ if (monster->is_priest())
{
did_god_conduct(DID_KILL_PRIEST,
monster->hit_dice, true, monster);
@@ -2324,8 +2324,8 @@ bool monster_polymorph(monsters *monster, monster_type targetc,
monster_spells spl = monster->spells;
const bool need_save_spells
= (!name.empty()
- && (!mons_class_flag(monster->type, M_SPELLCASTER)
- || mons_class_flag(monster->type, M_ACTUAL_SPELLS)));
+ && (!monster->can_use_spells()
+ || monster->is_actual_spellcaster()));
// deal with mons_sec
monster->type = targetc;
@@ -2345,8 +2345,7 @@ bool monster_polymorph(monsters *monster, monster_type targetc,
// swamp drake he'll breathe fumes and, if polymorphed further,
// won't remember his spells anymore.
if (need_save_spells
- && (!mons_class_flag(monster->type, M_SPELLCASTER)
- || mons_class_flag(monster->type, M_ACTUAL_SPELLS)))
+ && (!monster->can_use_spells() || monster->is_actual_spellcaster()))
{
monster->spells = spl;
}
@@ -2899,7 +2898,7 @@ monsters *choose_random_monster_on_level(int weight,
if (prefer_named && mon->is_named())
mon_weight++;
- if (prefer_priest && mons_class_flag(mon->type, M_PRIEST))
+ if (prefer_priest && mon->is_priest())
mon_weight++;
if (x_chance_in_y(mon_weight, (weight += mon_weight)))
diff --git a/crawl-ref/source/xom.cc b/crawl-ref/source/xom.cc
index 17bd18b9f0..9fd5def20c 100644
--- a/crawl-ref/source/xom.cc
+++ b/crawl-ref/source/xom.cc
@@ -962,7 +962,7 @@ static bool _choose_chaos_upgrade(const monsters* mon)
// Beogh presumably doesn't want Xom messing with his orcs, even if
// it would give them a better weapon.
if (mons_species(mon->type) == MONS_ORC
- && (mons_class_flag(mon->type, M_PRIEST) || coinflip()))
+ && (mon->is_priest() || coinflip()))
{
return (false);
}