I plan to use monster_info to get monster data out to the player safely via Lua.
2BGXBH5OMEFAX2XTS2OWIRODUGW6V2FJR4QRJIQTV4C2WIJI22BQC CI7MCWE7NO6WUAPIZNOFVXN37TKVQWVZATJZZMAV3OW2DH3HCCBAC JESCFNNBKC7BW3XYLJLAF6RLMP5VCXIUUXQUGEZBF4FYPNMEGNJAC ETJXWTOUQEK2BECTSNQOQN2GQO4U5GMSCEIOVNW4KY7J3CLEUWCAC JQK4F4RTKVQWIF5YKFPH7IHF7UREBOY2NOFQ7J3O44KYPCNHNL5AC JR2RAQ523LOWNDYJNK6AZVKI6WVMI622PIV72XWOVZYPXPUKSQWAC K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC SKWBAGSAB625IIN4UP3NCPRX2H3KCPC2LULHS2A7JVRLO3EUBJDAC 7X4LFGBYTBK5LKZH7HPYBUHHMHWFAT3NPKAMJHK54YL2ZO5ZIK2AC NO2HRD7RZS42S55UG4FQ5EFQDY6WYWKGIXHUKYLWF36HHE3VZ7WAC IQFLSXLOKMSMM65BL7XOEI5ZP55WKZ7BFBOIA44AMTPNJ7DAQXBQC QSRRV725GCNDOMIDS36NYAA65MOLDMAZSENVSZ3PNOLBFSGHH7BQC WEFNLSQ7HRLNHIKFH46FIBBZNAORSXGOVIJWZGVVEHL36MICAUAAC VZSAQXCHOUUAREGQBTIJVXOXVIUNACE74SHTBONNOXINDFK4FDCAC TE3QYFTQB2N6RHYG6VULGNUZ7EN6MZ5IDITC7KWD75UHPG7A2YAAC ZNMT5CZHP2FC4HTLNA7KYEDGFBXSCUE5QHJOALVPE6RDPHSEDXRQC HAM54HXIO2245W6REO4RZDY2QMIH476AWWJSMYAMSYYNEBBJSHWAC YXWZYOBUFR4EHBSKXCFGZNK7NIM7IPE2GMZYIXEJTUPDDC3BIEDAC 6XSOB55JCEWQK7OANHLEIPOIUGH32ZEB35A7XLBKSFJ5YH5GCZ4AC N6J4ANT3WFBQYTFDPZFVF2PWS2S7CK4GHHX4UV3JW4VWAKETZR5AC F2YWDQUXULLS2WKLVMNEJKEGSGG2MPEREVI2RKH7ROS2YYKKMXHAC HT2Z5ZWY5G2QVHXCKFDQT62FRNGPRRD3HHJ2UULBWRTMAJICUDGAC S34LKQDIQJLIWVIPASOJBBZ6ZCXDHP5KPS7TRBZJSCDRVNCLK6UAC EXZ3JJJIBWKHQ7XSJPC6P3LOKAALMJHUX3CGIL6FO5KPNTFMLE4QC 7NRMOLVOLPSF4KW23GZOO4X6BMGSMBE3IDMO4WOKBZXPYCLRZIXQC GJBUM2B6VECYVSAWUQAG64MDJ4MUHTACSTDDVPEKRDBGBX4HPQLQC 5WVUTEZLEZEML54CKPR6GACQBYY3EMVNXMLJOREN6SSEUZGC47AQC 4ZSVHDY23VI2FMLU5Z2CJ35SAPDQRREM6RABZ4GEILWI7NRULQNAC 5B5DP5S6A6LQMKZYVLQAEMHQZWFWYDHPCKQGRNSCNNYIBQYZ6BIQC KVPP3CYPEFADQCL5Y56ELSUBA47SFNAJNCSVCSOK2GF67DHVMBCQC HM6NOS7BN5665KWIFGBXOHZIDMRSVCESN72GMGI6NVBFZGCF3L6QC TR6KNC3N4EXHAFJWYCQJ6QJOQRVVE5VJAMENA4DUZWE5XDGQQF4QC UVGFF3QPZKZNMWFDDANQRBTVOROTA4TZFXGYDPV4GQXZKHZY2EXAC U6OTXM3JN7SGPVIGQ5F6NR2I7J5V7KOWFQ7AVNNRQQDNLBEDMYFQC BGEMR37KHW34PY6TU4WR7YBXFARYB6UEXAEXORTK7RKFXCBXN5UAC KQLAGI6OMH5UB2UUG6BPJH46DV2B3THBQVSZIZHGROCON57GTLUQC GQF26J3LYUS35FFHOV4N26QHSJIA5GXMNBXVVKDGX7YMFBFJK4LQC ZR4ALQ5F2ETOD3GXMBYXSN6P75DPDGI3C275A7W5XHYI5TGPHNUQC APWL24FMDRVA6NXFFCGNP3Z3FJJ6WFSRWO4ESHEOVT7Z5JGXPUQQC UL7XFKMUX3WIU4O2LZANK4ECJ654UZPDBFGNXUEYZYOLKBYBCG6AC MQ62OAMLGJVRW2QIL4PAZRAU6PC52ZVGY2FCOBIY6IWGQIHMU5CAC HKQTMQVLLOBG2VO47TUGSTQALA3D2YLMEVADXXYNR4RGGKD3F2ZAC RZP5Z6JM74GVX2M6L2JOFRJ5VT2XGFHBNRDESVHSKF66PQKIW32QC // Monster info used by the pane; precomputes some data// to help with sorting and rendering.class monster_pane_info{public:static bool less_than(const monster_pane_info& m1,const monster_pane_info& m2, bool zombified = true);static bool less_than_wrapper(const monster_pane_info& m1,const monster_pane_info& m2);
}monster_pane_info::monster_pane_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);// [ds] XXX: Kill the magic numbers.if (mons_looks_stabbable(m)) m_brands |= 1;if (mons_looks_distracted(m)) m_brands |= 2;if (m->has_ench(ENCH_BERSERK)) m_brands |= 4;}// Needed because gcc 4.3 sort does not like comparison functions that take// more than 2 arguments.bool monster_pane_info::less_than_wrapper(const monster_pane_info& m1,const monster_pane_info& m2){return monster_pane_info::less_than(m1, m2, true);}// Sort monsters by (in that order): attitude, difficulty, type, brandbool monster_pane_info::less_than(const monster_pane_info& m1,const monster_pane_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 difficultyif (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 endif (m1.m_brands > m2.m_brands)return (true);else if (m1.m_brands < m2.m_brands)return (false);#endifreturn (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)");}elsereturn(" (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 ("");
if (count == 1){if (mons_is_mimic(m_mon->type))out << mons_type_name(m_mon->type, DESC_PLAIN);elseout << 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_DIAGNOSTICSout << " av" << m_difficulty << " "<< m_mon->hit_points << "/" << m_mon->max_hit_points;#endifif (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)";}// Friendlinessswitch (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 attackingswitch (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_pane_info(std::vector<monster_pane_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_pane_info(visible[i]));}}std::sort(mons.begin(), mons.end(), monster_pane_info::less_than_wrapper);}
std::vector<monster_pane_info> mons;get_monster_pane_info(mons);std::sort(mons.begin(), mons.end(), monster_pane_info::less_than_wrapper);
std::vector<monster_info> mons;get_monster_info(mons);std::sort(mons.begin(), mons.end(), monster_info::less_than_wrapper);
#ifndef MON_INFO_H#define MON_INFO_H// Monster info used by the pane; precomputes some data// to help with sorting and rendering.class monster_info{public:static bool less_than(const monster_info& m1,const monster_info& m2, bool zombified = true);static bool less_than_wrapper(const monster_info& m1,const monster_info& m2);monster_info(const monsters* m);void to_string(int count, std::string& desc, int& desc_color) const;const monsters* m_mon;mon_attitude_type m_attitude;int m_difficulty;int m_brands;bool m_fullname;};void get_monster_info(std::vector<monster_info>& mons);#endif
#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, brandbool 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 difficultyif (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 endif (m1.m_brands > m2.m_brands)return (true);else if (m1.m_brands < m2.m_brands)return (false);#endifreturn (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)");}elsereturn(" (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);elseout << 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_DIAGNOSTICSout << " av" << m_difficulty << " "<< m_mon->hit_points << "/" << m_mon->max_hit_points;#endifif (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)";}// Friendlinessswitch (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 attackingswitch (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);}
// Get monsters via the monster_pane_info, sorted by difficulty.std::vector<monster_pane_info> mons;get_monster_pane_info(mons);std::sort(mons.begin(), mons.end(), monster_pane_info::less_than_wrapper);
// Get monsters via the monster_info, sorted by difficulty.std::vector<monster_info> mons;get_monster_info(mons);std::sort(mons.begin(), mons.end(), monster_info::less_than_wrapper);