diff options
Diffstat (limited to 'crawl-ref')
-rw-r--r-- | crawl-ref/docs/develop/level_design.txt | 56 | ||||
-rw-r--r-- | crawl-ref/source/acr.cc | 6 | ||||
-rw-r--r-- | crawl-ref/source/debug.cc | 7 | ||||
-rw-r--r-- | crawl-ref/source/describe.cc | 2 | ||||
-rw-r--r-- | crawl-ref/source/dungeon.cc | 14 | ||||
-rw-r--r-- | crawl-ref/source/enum.h | 10 | ||||
-rw-r--r-- | crawl-ref/source/mapdef.cc | 60 | ||||
-rw-r--r-- | crawl-ref/source/mapdef.h | 9 | ||||
-rw-r--r-- | crawl-ref/source/mon-act.cc | 4 | ||||
-rw-r--r-- | crawl-ref/source/mon-cast.cc | 44 | ||||
-rw-r--r-- | crawl-ref/source/mon-util.cc | 18 | ||||
-rw-r--r-- | crawl-ref/source/monplace.cc | 126 | ||||
-rw-r--r-- | crawl-ref/source/monspeak.cc | 2 | ||||
-rw-r--r-- | crawl-ref/source/monster.cc | 38 | ||||
-rw-r--r-- | crawl-ref/source/monster.h | 13 | ||||
-rw-r--r-- | crawl-ref/source/monstuff.cc | 11 | ||||
-rw-r--r-- | crawl-ref/source/xom.cc | 2 |
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); } |