diff options
Diffstat (limited to 'crawl-ref/source/mon-info.cc')
-rw-r--r-- | crawl-ref/source/mon-info.cc | 305 |
1 files changed, 305 insertions, 0 deletions
diff --git a/crawl-ref/source/mon-info.cc b/crawl-ref/source/mon-info.cc new file mode 100644 index 0000000000..99677d1108 --- /dev/null +++ b/crawl-ref/source/mon-info.cc @@ -0,0 +1,305 @@ +#include "AppHdr.h" + +#include "mon-info.h" + +#include "fight.h" +#include "misc.h" +#include "mon-util.h" +#include "monster.h" +#include "religion.h" + +#include <sstream> + +enum monster_info_brands +{ + MB_STABBABLE, + MB_DISTRACTED, + MB_BERSERK +}; + +monster_info::monster_info(const monsters *m) + : m_mon(m), m_attitude(ATT_HOSTILE), m_difficulty(0), + m_brands(0), m_fullname(true) +{ + // XXX: this doesn't take into account ENCH_NEUTRAL, but that's probably + // a bug for mons_attitude, not this. + // XXX: also, mons_attitude_type should be sorted hostile/neutral/friendly; + // will break saves a little bit though. + m_attitude = mons_attitude(m); + + int mtype = m->type; + if (mtype == MONS_RAKSHASA_FAKE) + mtype = MONS_RAKSHASA; + + // Currently, difficulty is defined as "average hp". + m_difficulty = mons_difficulty(mtype); + + if (mons_looks_stabbable(m)) m_brands |= (1 << MB_STABBABLE); + if (mons_looks_distracted(m)) m_brands |= (1 << MB_DISTRACTED); + if (m->has_ench(ENCH_BERSERK)) m_brands |= (1 << MB_BERSERK); +} + +// Needed because gcc 4.3 sort does not like comparison functions that take +// more than 2 arguments. +bool monster_info::less_than_wrapper(const monster_info& m1, + const monster_info& m2) +{ + return monster_info::less_than(m1, m2, true); +} + +// Sort monsters by (in that order): attitude, difficulty, type, brand +bool monster_info::less_than(const monster_info& m1, + const monster_info& m2, bool zombified) +{ + if (m1.m_attitude < m2.m_attitude) + return (true); + else if (m1.m_attitude > m2.m_attitude) + return (false); + + int m1type = m1.m_mon->type; + int m2type = m2.m_mon->type; + + // Don't differentiate real rakshasas from fake ones. + if (m1type == MONS_RAKSHASA_FAKE) + m1type = MONS_RAKSHASA; + if (m2type == MONS_RAKSHASA_FAKE) + m2type = MONS_RAKSHASA; + + // Force plain but different coloured draconians to be treated like the + // same sub-type. + if (!zombified && m1type >= MONS_DRACONIAN + && m1type <= MONS_PALE_DRACONIAN + && m2type >= MONS_DRACONIAN + && m2type <= MONS_PALE_DRACONIAN) + { + return (false); + } + + // By descending difficulty + if (m1.m_difficulty > m2.m_difficulty) + return (true); + else if (m1.m_difficulty < m2.m_difficulty) + return (false); + + // Force mimics of different types to be treated like the same one. + if (mons_is_mimic(m1type) && mons_is_mimic(m2type)) + return (false); + + if (m1type < m2type) + return (true); + else if (m1type > m2type) + return (false); + + // Never distinguish between dancing weapons. + // The above checks guarantee that *both* monsters are of this type. + if (m1type == MONS_DANCING_WEAPON) + return (false); + + if (zombified) + { + // Because of the type checks above, if one of the two is zombified, so + // is the other, and of the same type. + if (mons_is_zombified(m1.m_mon) + && m1.m_mon->base_monster < m2.m_mon->base_monster) + { + return (true); + } + + // Both monsters are hydras or hydra zombies, sort by number of heads. + if (m1.m_mon->has_hydra_multi_attack() + && m1.m_mon->number > m2.m_mon->number) + { + return (true); + } + } + + if (m1.m_fullname && m2.m_fullname || m1type == MONS_PLAYER_GHOST) + return (m1.m_mon->name(DESC_PLAIN) < m1.m_mon->name(DESC_PLAIN)); + +#if 0 // for now, sort brands together. + // By descending brands, so no brands sorts to the end + if (m1.m_brands > m2.m_brands) + return (true); + else if (m1.m_brands < m2.m_brands) + return (false); +#endif + + return (false); +} + +static std::string _verbose_info(const monsters* m) +{ + if (mons_is_caught(m)) + return (" (caught)"); + + if (mons_behaviour_perceptible(m)) + { + if (mons_is_petrified(m)) + return(" (petrified)"); + if (mons_is_paralysed(m)) + return(" (paralysed)"); + if (mons_is_petrifying(m)) + return(" (petrifying)"); + if (mons_is_confused(m)) + return(" (confused)"); + if (mons_is_fleeing(m)) + return(" (fleeing)"); + if (mons_is_sleeping(m)) + { + if (mons_holiness(m) == MH_UNDEAD + || mons_holiness(m) == MH_NONLIVING + || mons_holiness(m) == MH_PLANT) + { + return(" (dormant)"); + } + else + return(" (sleeping)"); + } + if (mons_is_wandering(m) && !mons_is_batty(m) + && !(m->attitude == ATT_STRICT_NEUTRAL)) + { + // Labeling strictly neutral monsters as fellow slimes is more important. + return(" (wandering)"); + } + if (m->foe == MHITNOT && !mons_is_batty(m) && !mons_neutral(m) + && !mons_friendly(m)) + { + return (" (unaware)"); + } + } + + if (m->has_ench(ENCH_STICKY_FLAME)) + return (" (burning)"); + + if (m->has_ench(ENCH_ROT)) + return (" (rotting)"); + + if (m->has_ench(ENCH_INVIS)) + return (" (invisible)"); + + return (""); +} + +void monster_info::to_string(int count, std::string& desc, + int& desc_color) const +{ + std::ostringstream out; + + if (count == 1) + { + if (mons_is_mimic(m_mon->type)) + out << mons_type_name(m_mon->type, DESC_PLAIN); + else + out << m_mon->full_name(DESC_PLAIN); + } + else + { + // Don't pluralise uniques, ever. Multiple copies of the same unique + // are unlikely in the dungeon currently, but quite common in the + // arena. This prevens "4 Gra", etc. {due} + if (mons_is_unique(m_mon->type)) + { + out << count << " " + << m_mon->name(DESC_PLAIN); + } + // Don't differentiate between dancing weapons, mimics, (very) + // ugly things or draconians of different types. + else if (m_fullname + && m_mon->type != MONS_DANCING_WEAPON + && mons_genus(m_mon->type) != MONS_DRACONIAN + && m_mon->type != MONS_UGLY_THING + && m_mon->type != MONS_VERY_UGLY_THING + && !mons_is_mimic(m_mon->type) + && m_mon->mname.empty()) + { + out << count << " " + << pluralise(m_mon->name(DESC_PLAIN)); + } + else if (m_mon->type >= MONS_DRACONIAN + && m_mon->type <= MONS_PALE_DRACONIAN) + { + out << count << " " + << pluralise(mons_type_name(MONS_DRACONIAN, DESC_PLAIN)); + } + else + { + out << count << " " + << pluralise(mons_type_name(m_mon->type, DESC_PLAIN)); + } + } + +#if DEBUG_DIAGNOSTICS + out << " av" << m_difficulty << " " + << m_mon->hit_points << "/" << m_mon->max_hit_points; +#endif + + if (count == 1) + { + if (m_mon->has_ench(ENCH_BERSERK)) + out << " (berserk)"; + else if (Options.verbose_monster_pane) + out << _verbose_info(m_mon); + else if (mons_looks_stabbable(m_mon)) + out << " (resting)"; + else if (mons_looks_distracted(m_mon)) + out << " (distracted)"; + else if (m_mon->has_ench(ENCH_INVIS)) + out << " (invisible)"; + } + + // Friendliness + switch (m_attitude) + { + case ATT_FRIENDLY: + //out << " (friendly)"; + desc_color = GREEN; + break; + case ATT_GOOD_NEUTRAL: + case ATT_NEUTRAL: + //out << " (neutral)"; + desc_color = BROWN; + break; + case ATT_STRICT_NEUTRAL: + out << " (fellow slime)"; + desc_color = BROWN; + break; + case ATT_HOSTILE: + // out << " (hostile)"; + desc_color = LIGHTGREY; + break; + } + + // Evilness of attacking + switch (m_attitude) + { + case ATT_NEUTRAL: + case ATT_HOSTILE: + if (count == 1 && you.religion == GOD_SHINING_ONE + && !tso_unchivalric_attack_safe_monster(m_mon) + && is_unchivalric_attack(&you, m_mon)) + { + desc_color = Options.evil_colour; + } + break; + default: + break; + } + + desc = out.str(); +} + +void get_monster_info(std::vector<monster_info>& mons) +{ + std::vector<monsters*> visible = get_nearby_monsters(); + for (unsigned int i = 0; i < visible.size(); i++) + { + if (Options.target_zero_exp + || !mons_class_flag( visible[i]->type, M_NO_EXP_GAIN ) + || visible[i]->type == MONS_KRAKEN_TENTACLE) + { + mons.push_back(monster_info(visible[i])); + } + } + std::sort(mons.begin(), mons.end(), monster_info::less_than_wrapper); +} + |