1 /**
2  * @file
3  * @brief Misc monster related functions.
4 **/
5 
6 #include "AppHdr.h"
7 
8 #include "mon-util.h"
9 
10 #include <algorithm>
11 #include <cmath>
12 #include <sstream>
13 
14 #include "act-iter.h"
15 #include "areas.h"
16 #include "artefact.h"
17 #include "attitude-change.h"
18 #include "beam.h"
19 #include "cloud.h"
20 #include "colour.h"
21 #include "coordit.h"
22 #include "database.h"
23 #include "delay.h"
24 #include "dgn-overview.h"
25 #include "directn.h"
26 #include "dungeon.h"
27 #include "english.h"
28 #include "env.h"
29 #include "tile-env.h"
30 #include "errors.h"
31 #include "fight.h"
32 #include "files.h"
33 #include "fprop.h"
34 #include "ghost.h"
35 #include "god-item.h"
36 #include "god-passive.h"
37 #include "item-name.h"
38 #include "item-prop.h"
39 #include "items.h"
40 #include "libutil.h"
41 #include "mapmark.h"
42 #include "message.h"
43 #include "mgen-data.h"
44 #include "mon-abil.h"
45 #include "mon-behv.h"
46 #include "mon-book.h"
47 #include "mon-death.h"
48 #include "mon-explode.h"
49 #include "mon-place.h"
50 #include "mon-poly.h"
51 #include "mon-tentacle.h"
52 #include "mutant-beast.h"
53 #include "notes.h"
54 #include "options.h"
55 #include "random.h"
56 #include "reach-type.h"
57 #include "religion.h"
58 #include "showsymb.h"
59 #include "species.h"
60 #include "spl-util.h"
61 #include "state.h"
62 #include "stringutil.h"
63 #include "tag-version.h"
64 #include "terrain.h"
65 #include "rltiles/tiledef-player.h"
66 #include "tilepick.h"
67 #include "tileview.h"
68 #include "timed-effects.h"
69 #include "traps.h"
70 #include "unicode.h"
71 #include "unwind.h"
72 
73 static FixedVector < int, NUM_MONSTERS > mon_entry;
74 
75 struct mon_display
76 {
77     char32_t glyph;
78     colour_t colour;
79 
mon_displaymon_display80     mon_display(unsigned gly = 0, unsigned col = 0)
81        : glyph(gly), colour(col) { }
82 };
83 
84 static mon_display monster_symbols[NUM_MONSTERS];
85 
86 static bool initialised_randmons = false;
87 static vector<monster_type> monsters_by_habitat[NUM_HABITATS];
88 static vector<monster_type> species_by_habitat[NUM_HABITATS];
89 
90 #include "mon-spell.h"
91 #include "mon-data.h"
92 
93 #define MONDATASIZE ARRAYSZ(mondata)
94 
95 static int _mons_exp_mod(monster_type mclass);
96 
97 // Macro that saves some typing, nothing more.
98 #define smc get_monster_data(mc)
99 // ASSERT(smc) was getting really old
100 #define ASSERT_smc()                                                    \
101     do {                                                                \
102         if (!get_monster_data(mc))                                      \
103             die("bogus mc (no monster data): %s (%d)",                  \
104                 mons_type_name(mc, DESC_PLAIN).c_str(), mc);            \
105     } while (false)
106 
107 /* ******************** BEGIN PUBLIC FUNCTIONS ******************** */
108 
monster_class_flies(monster_type mc)109 bool monster_class_flies(monster_type mc)
110 {
111     return mons_class_flag(mc, M_FLIES);
112 }
113 
monster_inherently_flies(const monster & mons)114 bool monster_inherently_flies(const monster &mons)
115 {
116     // check both so spectral humans and zombified dragons both fly
117     return monster_class_flies(mons.type)
118         || monster_class_flies(mons_base_type(mons))
119         || mons_is_ghost_demon(mons.type) && mons.ghost && mons.ghost->flies
120         || mons.has_facet(BF_BAT);
121 }
122 
_grid2habitat(dungeon_feature_type grid)123 static habitat_type _grid2habitat(dungeon_feature_type grid)
124 {
125     if (feat_is_watery(grid))
126         return HT_WATER;
127 
128     switch (grid)
129     {
130     case DNGN_LAVA:
131         return HT_LAVA;
132     case DNGN_FLOOR:
133     default:
134         return HT_LAND;
135     }
136 }
137 
habitat2grid(habitat_type ht)138 dungeon_feature_type habitat2grid(habitat_type ht)
139 {
140     switch (ht)
141     {
142     case HT_WATER:
143         return DNGN_DEEP_WATER;
144     case HT_LAVA:
145         return DNGN_LAVA;
146     case HT_LAND:
147     case HT_AMPHIBIOUS:
148     case HT_AMPHIBIOUS_LAVA:
149     default:
150         return DNGN_FLOOR;
151     }
152 }
153 
_initialise_randmons()154 static void _initialise_randmons()
155 {
156     for (int i = 0; i < NUM_HABITATS; ++i)
157     {
158         set<monster_type> tmp_species;
159         const dungeon_feature_type grid = habitat2grid(habitat_type(i));
160 
161         for (monster_type mt = MONS_0; mt < NUM_MONSTERS; ++mt)
162         {
163             if (invalid_monster_type(mt))
164                 continue;
165 
166             if (monster_habitable_grid(mt, grid))
167                 monsters_by_habitat[i].push_back(mt);
168 
169             const monster_type species = mons_species(mt);
170             if (monster_habitable_grid(species, grid))
171                 tmp_species.insert(species);
172 
173         }
174 
175         for (auto type : tmp_species)
176             species_by_habitat[i].push_back(type);
177     }
178     initialised_randmons = true;
179 }
180 
random_monster_at_grid(const coord_def & p,bool species)181 monster_type random_monster_at_grid(const coord_def& p, bool species)
182 {
183     if (!initialised_randmons)
184         _initialise_randmons();
185 
186     const habitat_type ht = _grid2habitat(env.grid(p));
187     const vector<monster_type> &valid_mons = species ? species_by_habitat[ht]
188                                                      : monsters_by_habitat[ht];
189 
190     ASSERT(!valid_mons.empty());
191     return valid_mons.empty() ? MONS_PROGRAM_BUG
192                               : valid_mons[ random2(valid_mons.size()) ];
193 }
194 
195 typedef map<string, monster_type> mon_name_map;
196 static mon_name_map Mon_Name_Cache;
197 
init_mon_name_cache()198 void init_mon_name_cache()
199 {
200     if (!Mon_Name_Cache.empty())
201         return;
202 
203     for (const monsterentry &me : mondata)
204     {
205         string name = me.name;
206         lowercase(name);
207 
208         const int          mtype = me.mc;
209         const monster_type mon   = monster_type(mtype);
210 
211         // Deal sensibly with duplicate entries; refuse or allow the
212         // insert, depending on which should take precedence. Some
213         // uniques of multiple forms can get away with this, though.
214         if (Mon_Name_Cache.count(name))
215         {
216             if (mon == MONS_PLAYER_SHADOW
217                 || mon == MONS_BAI_SUZHEN_DRAGON
218                 || mon != MONS_SERPENT_OF_HELL
219                    && mons_species(mon) == MONS_SERPENT_OF_HELL)
220             {
221                 // Keep previous entry.
222                 continue;
223             }
224             else
225                 die("Un-handled duplicate monster name: %s", name.c_str());
226         }
227 
228         Mon_Name_Cache[name] = mon;
229     }
230 }
231 
_mon_entry_name(size_t idx)232 static const char *_mon_entry_name(size_t idx)
233 {
234     return mondata[idx].name;
235 }
236 
get_monster_by_name(string name,bool substring)237 monster_type get_monster_by_name(string name, bool substring)
238 {
239     if (name.empty())
240         return MONS_PROGRAM_BUG;
241 
242     lowercase(name);
243 
244     if (!substring)
245     {
246         if (monster_type *mc = map_find(Mon_Name_Cache, name))
247             return *mc;
248         return MONS_PROGRAM_BUG;
249     }
250 
251     size_t idx = find_earliest_match(name, (size_t) 0, ARRAYSZ(mondata),
252                                      always_true<size_t>, _mon_entry_name);
253     return idx == ARRAYSZ(mondata) ? MONS_PROGRAM_BUG
254                                    : (monster_type) mondata[idx].mc;
255 }
256 
init_monsters()257 void init_monsters()
258 {
259     // First, fill static array with dummy values. {dlb}
260     mon_entry.init(-1);
261 
262     // Next, fill static array with location of entry in mondata[]. {dlb}:
263     for (unsigned int i = 0; i < MONDATASIZE; ++i)
264         mon_entry[mondata[i].mc] = i;
265 
266     // Finally, monsters yet with dummy entries point to TTTSNB(tm). {dlb}:
267     for (int &entry : mon_entry)
268         if (entry == -1)
269             entry = mon_entry[MONS_PROGRAM_BUG];
270 
271     init_monster_symbols();
272 }
273 
init_monster_symbols()274 void init_monster_symbols()
275 {
276     map<unsigned, monster_type> base_mons;
277     for (monster_type mc = MONS_0; mc < NUM_MONSTERS; ++mc)
278     {
279         mon_display &md = monster_symbols[mc];
280         if (const monsterentry *me = get_monster_data(mc))
281         {
282             md.glyph  = me->basechar;
283             md.colour = me->colour;
284             auto it = base_mons.find(md.glyph);
285             if (it == base_mons.end() || it->first == MONS_PROGRAM_BUG)
286                 base_mons[md.glyph] = mc;
287         }
288     }
289 
290     // Let those follow the feature settings, unless specifically overridden.
291     monster_symbols[MONS_ANIMATED_TREE].glyph = get_feat_symbol(DNGN_TREE);
292     for (monster_type mc = MONS_0; mc < NUM_MONSTERS; ++mc)
293         if (get_monster_data(mc)->genus == MONS_STATUE)
294             monster_symbols[mc].glyph = get_feat_symbol(DNGN_GRANITE_STATUE);
295 
296     // Validate all glyphs, even those which didn't come from an override.
297     for (monster_type i = MONS_PROGRAM_BUG; i < NUM_MONSTERS; ++i)
298         if (wcwidth(monster_symbols[i].glyph) != 1)
299             monster_symbols[i].glyph = mons_base_char(i);
300 }
301 
set_resist(resists_t & all,mon_resist_flags res,int lev)302 void set_resist(resists_t &all, mon_resist_flags res, int lev)
303 {
304     if (res > MR_LAST_MULTI)
305     {
306         ASSERT_RANGE(lev, 0, 2);
307         if (lev)
308             all |= res;
309         else
310             all &= ~res;
311         return;
312     }
313 
314     ASSERT_RANGE(lev, -3, 5);
315     all = (all & ~(res * 7)) | (res * (lev & 7));
316 }
317 
get_mons_class_ac(monster_type mc)318 int get_mons_class_ac(monster_type mc)
319 {
320     const monsterentry *me = get_monster_data(mc);
321     return me ? me->AC : get_monster_data(MONS_PROGRAM_BUG)->AC;
322 }
323 
get_mons_class_ev(monster_type mc)324 int get_mons_class_ev(monster_type mc)
325 {
326     const monsterentry *me = get_monster_data(mc);
327     return me ? me->ev : get_monster_data(MONS_PROGRAM_BUG)->ev;
328 }
329 
_apply_holiness_resists(resists_t resists,mon_holy_type mh)330 static resists_t _apply_holiness_resists(resists_t resists, mon_holy_type mh)
331 {
332     // Undead and non-living beings get full poison resistance.
333     if (mh & (MH_UNDEAD | MH_NONLIVING))
334         resists = (resists & ~(MR_RES_POISON * 7)) | (MR_RES_POISON * 3);
335 
336     // Everything but natural creatures have full rNeg. Set here for the
337     // benefit of the monster_info constructor. If you change this, also
338     // change monster::res_negative_energy.
339     if (!(mh & MH_NATURAL))
340         resists = (resists & ~(MR_RES_NEG * 7)) | (MR_RES_NEG * 3);
341 
342     if (mh & (MH_UNDEAD | MH_DEMONIC | MH_PLANT | MH_NONLIVING))
343         resists |= MR_RES_TORMENT;
344 
345     return resists;
346 }
347 
348 /**
349  * What special resistances does the given mutant beast facet provide?
350  *
351  * @param facet     The beast_facet in question, e.g. BF_FIRE.
352  * @return          A bitfield of resists corresponding to the given facet;
353  *                  e.g. MR_RES_FIRE for BF_FIRE.
354  */
_beast_facet_resists(beast_facet facet)355 static resists_t _beast_facet_resists(beast_facet facet)
356 {
357     static const map<beast_facet, resists_t> resists = {
358         { BF_STING, MR_RES_POISON },
359         { BF_FIRE,  MR_RES_FIRE },
360         { BF_SHOCK, MR_RES_ELEC },
361         { BF_OX,    MR_RES_COLD },
362     };
363 
364     return lookup(resists, facet, 0);
365 }
366 
get_mons_class_resists(monster_type mc)367 resists_t get_mons_class_resists(monster_type mc)
368 {
369     const monsterentry *me = get_monster_data(mc);
370     const resists_t resists = me ? me->resists
371                                  : get_monster_data(MONS_PROGRAM_BUG)->resists;
372     // Don't apply fake holiness resists.
373     if (mons_is_sensed(mc))
374         return resists;
375 
376     // Assumes that, when a monster's holiness differs from other monsters
377     // of the same type, that only adds resistances, never removes them.
378     // Currently the only such case is MF_FAKE_UNDEAD.
379     return _apply_holiness_resists(resists, mons_class_holiness(mc));
380 }
381 
get_mons_resists(const monster & m)382 resists_t get_mons_resists(const monster& m)
383 {
384     const monster& mon = get_tentacle_head(m);
385 
386     resists_t resists = get_mons_class_resists(mon.type);
387 
388     if (mons_is_ghost_demon(mon.type))
389         resists |= mon.ghost->resists;
390 
391     if (mons_genus(mon.type) == MONS_DRACONIAN
392             && mon.type != MONS_DRACONIAN
393         || mon.type == MONS_TIAMAT
394         || mons_genus(mon.type) == MONS_DEMONSPAWN
395             && mon.type != MONS_DEMONSPAWN)
396     {
397         monster_type subspecies = draco_or_demonspawn_subspecies(mon);
398         if (subspecies != mon.type)
399             resists |= get_mons_class_resists(subspecies);
400     }
401 
402     if (mon.props.exists(MUTANT_BEAST_FACETS))
403         for (auto facet : mon.props[MUTANT_BEAST_FACETS].get_vector())
404             resists |= _beast_facet_resists((beast_facet)facet.get_int());
405 
406     // This is set from here in case they're undead due to the
407     // MF_FAKE_UNDEAD flag. See the comment in get_mons_class_resists.
408     return _apply_holiness_resists(resists, mon.holiness());
409 }
410 
get_mons_resist(const monster & mon,mon_resist_flags res)411 int get_mons_resist(const monster& mon, mon_resist_flags res)
412 {
413     return get_resist(get_mons_resists(mon), res);
414 }
415 
416 // Returns true if the monster successfully resists this attempt to poison it.
monster_resists_this_poison(const monster & mons,bool force)417 bool monster_resists_this_poison(const monster& mons, bool force)
418 {
419     const int res = mons.res_poison();
420     if (res >= 3)
421         return true;
422     if (!force && res >= 1 && x_chance_in_y(2, 3))
423         return true;
424     return false;
425 }
426 
monster_at(const coord_def & pos)427 monster* monster_at(const coord_def &pos)
428 {
429     if (!in_bounds(pos))
430         return nullptr;
431 
432     const int mindex = env.mgrid(pos);
433     if (mindex == NON_MONSTER)
434         return nullptr;
435 
436     ASSERT(mindex <= MAX_MONSTERS);
437     return &env.mons[mindex];
438 }
439 
440 /// Are any of the bits set?
mons_class_flag(monster_type mc,monclass_flags_t bits)441 bool mons_class_flag(monster_type mc, monclass_flags_t bits)
442 {
443     const monsterentry * const me = get_monster_data(mc);
444     return me && (me->bitfields & bits);
445 }
446 
wearing(equipment_type slot,int sub_type,bool calc_unid) const447 int monster::wearing(equipment_type slot, int sub_type, bool calc_unid) const
448 {
449     int ret = 0;
450     const item_def *item = 0;
451 
452     if (!alive())
453         return 0;
454 
455     switch (slot)
456     {
457     case EQ_WEAPON:
458     case EQ_STAFF:
459         {
460             const mon_inv_type end = mons_wields_two_weapons(*this)
461                                      ? MSLOT_ALT_WEAPON : MSLOT_WEAPON;
462 
463             for (int i = MSLOT_WEAPON; i <= end; i = i + 1)
464             {
465                 item = mslot_item((mon_inv_type) i);
466                 if (item && item->base_type == (slot == EQ_WEAPON ? OBJ_WEAPONS
467                                                                   : OBJ_STAVES)
468                     && item->sub_type == sub_type
469                     // Weapon subtypes are always known, staves not.
470                     && (slot == EQ_WEAPON || calc_unid
471                         || item_type_known(*item)))
472                 {
473                     ret++;
474                 }
475             }
476         }
477         break;
478 
479     case EQ_ALL_ARMOUR:
480     case EQ_CLOAK:
481     case EQ_HELMET:
482     case EQ_GLOVES:
483     case EQ_BOOTS:
484     case EQ_SHIELD:
485         item = mslot_item(MSLOT_SHIELD);
486         if (item && item->is_type(OBJ_ARMOUR, sub_type))
487             ret++;
488         // Don't check MSLOT_ARMOUR for EQ_SHIELD
489         if (slot == EQ_SHIELD)
490             break;
491         // intentional fall-through
492     case EQ_BODY_ARMOUR:
493         item = mslot_item(MSLOT_ARMOUR);
494         if (item && item->is_type(OBJ_ARMOUR, sub_type))
495             ret++;
496         break;
497 
498     case EQ_AMULET:
499     case EQ_RINGS:
500     case EQ_RINGS_PLUS:
501         item = mslot_item(MSLOT_JEWELLERY);
502         if (item && item->is_type(OBJ_JEWELLERY, sub_type)
503             && (calc_unid || item_type_known(*item)))
504         {
505             if (slot == EQ_RINGS_PLUS)
506                 ret += item->plus;
507             else
508                 ret++;
509         }
510         break;
511     default:
512         die("invalid slot %d for monster::wearing()", slot);
513     }
514     return ret;
515 }
516 
wearing_ego(equipment_type slot,int special,bool calc_unid) const517 int monster::wearing_ego(equipment_type slot, int special, bool calc_unid) const
518 {
519     int ret = 0;
520     const item_def *item = 0;
521 
522     if (!alive())
523         return 0;
524 
525     switch (slot)
526     {
527     case EQ_WEAPON:
528         {
529             const mon_inv_type end = mons_wields_two_weapons(*this)
530                                      ? MSLOT_ALT_WEAPON : MSLOT_WEAPON;
531 
532             for (int i = MSLOT_WEAPON; i <= end; i++)
533             {
534                 item = mslot_item((mon_inv_type) i);
535                 if (item && item->base_type == OBJ_WEAPONS
536                     && get_weapon_brand(*item) == special
537                     && (calc_unid || item_type_known(*item)))
538                 {
539                     ret++;
540                 }
541             }
542         }
543         break;
544 
545     case EQ_ALL_ARMOUR:
546     case EQ_CLOAK:
547     case EQ_HELMET:
548     case EQ_GLOVES:
549     case EQ_BOOTS:
550     case EQ_SHIELD:
551         item = mslot_item(MSLOT_SHIELD);
552         if (item && item->base_type == OBJ_ARMOUR
553             && get_armour_ego_type(*item) == special
554             && (calc_unid || item_type_known(*item)))
555         {
556             ret++;
557         }
558         // Don't check MSLOT_ARMOUR for EQ_SHIELD
559         if (slot == EQ_SHIELD)
560             break;
561         // intentional fall-through
562     case EQ_BODY_ARMOUR:
563         item = mslot_item(MSLOT_ARMOUR);
564         if (item && item->base_type == OBJ_ARMOUR
565             && get_armour_ego_type(*item) == special
566             && (calc_unid || item_type_known(*item)))
567         {
568             ret++;
569         }
570         break;
571 
572     case EQ_AMULET:
573     case EQ_STAFF:
574     case EQ_RINGS:
575     case EQ_RINGS_PLUS:
576         // No egos.
577         break;
578 
579     default:
580         die("invalid slot %d for monster::wearing_ego()", slot);
581     }
582     return ret;
583 }
584 
scan_artefacts(artefact_prop_type ra_prop,bool,vector<const item_def * > * matches) const585 int monster::scan_artefacts(artefact_prop_type ra_prop, bool /*calc_unid*/,
586                             vector<const item_def *> *matches) const
587 {
588     UNUSED(matches); //TODO: implement this when it will be required somewhere
589 
590     if (!alive())
591         return 0;
592 
593     int ret = 0;
594 
595     // TODO: do we really want to prevent randarts from working for zombies?
596     if (mons_itemuse(*this) >= MONUSE_STARTING_EQUIPMENT)
597     {
598         const int weap      = inv[MSLOT_WEAPON];
599         const int second    = inv[MSLOT_ALT_WEAPON]; // Two-headed ogres, etc.
600         const int armour    = inv[MSLOT_ARMOUR];
601         const int shld      = inv[MSLOT_SHIELD];
602         const int jewellery = inv[MSLOT_JEWELLERY];
603 
604         if (weap != NON_ITEM && env.item[weap].base_type == OBJ_WEAPONS
605             && is_artefact(env.item[weap]))
606         {
607             ret += artefact_property(env.item[weap], ra_prop);
608         }
609 
610         if (second != NON_ITEM && env.item[second].base_type == OBJ_WEAPONS
611             && is_artefact(env.item[second]) && mons_wields_two_weapons(*this))
612         {
613             ret += artefact_property(env.item[second], ra_prop);
614         }
615 
616         if (armour != NON_ITEM && env.item[armour].base_type == OBJ_ARMOUR
617             && is_artefact(env.item[armour]))
618         {
619             ret += artefact_property(env.item[armour], ra_prop);
620         }
621 
622         if (shld != NON_ITEM && env.item[shld].base_type == OBJ_ARMOUR
623             && is_artefact(env.item[shld]))
624         {
625             ret += artefact_property(env.item[shld], ra_prop);
626         }
627 
628         if (jewellery != NON_ITEM && env.item[jewellery].base_type == OBJ_JEWELLERY
629             && is_artefact(env.item[jewellery]))
630         {
631             ret += artefact_property(env.item[jewellery], ra_prop);
632         }
633     }
634 
635     return ret;
636 }
637 
holiness_by_name(string name)638 mon_holy_type holiness_by_name(string name)
639 {
640     lowercase(name);
641     for (const auto bit : mon_holy_type::range())
642     {
643         if (name == holiness_name(mon_holy_type::exponent(bit)))
644             return mon_holy_type::exponent(bit);
645     }
646     return MH_NONE;
647 }
648 
holiness_name(mon_holy_type_flags which_holiness)649 const char * holiness_name(mon_holy_type_flags which_holiness)
650 {
651     switch (which_holiness)
652     {
653     case MH_HOLY:
654         return "holy";
655     case MH_NATURAL:
656         return "natural";
657     case MH_UNDEAD:
658         return "undead";
659     case MH_DEMONIC:
660         return "demonic";
661     case MH_NONLIVING:
662         return "nonliving";
663     case MH_PLANT:
664         return "plant";
665     case MH_EVIL:
666         return "evil";
667     default:
668         return "bug";
669     }
670 }
671 
holiness_description(mon_holy_type holiness)672 string holiness_description(mon_holy_type holiness)
673 {
674     string description = "";
675     for (const auto bit : mon_holy_type::range())
676     {
677         if (holiness & bit)
678         {
679             if (!description.empty())
680                 description += ",";
681             description += holiness_name(bit);
682         }
683     }
684     return description;
685 }
686 
mons_class_holiness(monster_type mc)687 mon_holy_type mons_class_holiness(monster_type mc)
688 {
689     ASSERT_smc();
690     return smc->holiness;
691 }
692 
mons_class_is_stationary(monster_type mc)693 bool mons_class_is_stationary(monster_type mc)
694 {
695     return mons_class_flag(mc, M_STATIONARY);
696 }
697 
mons_class_is_draconic(monster_type mc)698 bool mons_class_is_draconic(monster_type mc)
699 {
700     switch (mons_genus(mc))
701     {
702         case MONS_DRAGON:
703         case MONS_DRAKE:
704         case MONS_DRACONIAN:
705             return true;
706         default:
707             return false;
708     }
709 }
710 
711 /**
712  * Can killing this class of monster ever reward xp?
713  *
714  * This answers whether any agent could receive XP for killing a monster of
715  * this class. Monsters that fail this have M_NO_EXP_GAIN set.
716  * @param mc       The monster type
717  * @param indirect If true this will count monsters that are parts of a parent
718  *                 monster as xp rewarding even if the parts themselves don't
719  *                 reward xp (e.g. tentacles).
720  * @returns True if killing a monster of this class could reward xp, false
721  * otherwise.
722  */
mons_class_gives_xp(monster_type mc,bool indirect)723 bool mons_class_gives_xp(monster_type mc, bool indirect)
724 {
725     return !mons_class_flag(mc, M_NO_EXP_GAIN)
726         || (indirect && mons_is_tentacle_or_tentacle_segment(mc));
727 }
728 
729 /**
730  * Can killing this monster reward xp to the given actor?
731  *
732  * This answers whether the player or a monster could ever receive XP for
733  * killing the monster, assuming an appropriate kill_type.
734  * @param mon      The monster.
735  * @param agent    The actor who would be responsible for the kill.
736  * @returns True if killing the monster will reward the agent with xp, false
737  * otherwise.
738  */
mons_gives_xp(const monster & victim,const actor & agent)739 bool mons_gives_xp(const monster& victim, const actor& agent)
740 {
741     const bool mon_killed_friend
742         = agent.is_monster() && mons_aligned(&victim, &agent);
743     return !victim.is_summoned()                   // no summons
744         && !victim.has_ench(ENCH_ABJ)              // not-really-summons
745         && !victim.has_ench(ENCH_FAKE_ABJURATION)  // no animated remains
746         && mons_class_gives_xp(victim.type)        // class must reward xp
747         && !testbits(victim.flags, MF_WAS_NEUTRAL) // no neutral monsters
748         && !testbits(victim.flags, MF_NO_REWARD)   // no reward for no_reward
749         && !mon_killed_friend;
750 }
751 
mons_class_is_threatening(monster_type mo)752 bool mons_class_is_threatening(monster_type mo)
753 {
754     return !mons_class_flag(mo, M_NO_THREAT);
755 }
756 
mons_is_threatening(const monster & mons)757 bool mons_is_threatening(const monster& mons)
758 {
759     return mons_class_is_threatening(mons.type) || mons_is_active_ballisto(mons);
760 }
761 
mons_class_is_fragile(monster_type mc)762 bool mons_class_is_fragile(monster_type mc)
763 {
764     return mons_class_flag(mc, M_FRAGILE);
765 }
766 
mons_is_fragile(const monster & mons)767 bool mons_is_fragile(const monster& mons)
768 {
769     return mons_class_is_fragile(mons.type);
770 }
771 
772 /**
773  * Is this an active ballistomycete?
774  *
775  * @param mon             The monster
776  * @returns True if the monster is an active ballistomycete, false otherwise.
777  */
mons_is_active_ballisto(const monster & mon)778 bool mons_is_active_ballisto(const monster& mon)
779 {
780     return mon.type == MONS_BALLISTOMYCETE && mon.ballisto_activity;
781 }
782 
783 /**
784  * Is this monster class firewood?
785  *
786  * Firewood monsters are harmless stationary monsters than don't give xp. These
787  * are worthless obstacles: not to be attacked by default, but may be cut down
788  * to get to target even if coaligned.
789  * @param mc The monster type
790  * @returns True if the monster class is firewood, false otherwise.
791  */
mons_class_is_firewood(monster_type mc)792 bool mons_class_is_firewood(monster_type mc)
793 {
794     return mons_class_is_stationary(mc)
795            && !mons_class_is_test(mc)
796            && mons_class_flag(mc, M_NO_THREAT)
797            && !mons_is_tentacle_or_tentacle_segment(mc);
798 }
799 
800 /**
801  * Is this a test monster? Mainly used for exempting these from various checks
802  * like firewood, etc.
803  */
mons_class_is_test(monster_type mc)804 bool mons_class_is_test(monster_type mc)
805 {
806     return mc == MONS_TEST_SPAWNER || mc == MONS_TEST_STATUE
807         || mc == MONS_TEST_BLOB;
808 }
809 
810 /**
811  * Is this monster firewood?
812  *
813  * Firewood monsters are stationary monsters than don't give xp.
814  * @param mon             The monster
815  * @returns True if the monster is firewood, false otherwise.
816  */
mons_is_firewood(const monster & mon)817 bool mons_is_firewood(const monster& mon)
818 {
819     return mons_class_is_firewood(mon.type);
820 }
821 
822 // "body" in a purely grammatical sense.
mons_has_body(const monster & mon)823 bool mons_has_body(const monster& mon)
824 {
825     if (mon.type == MONS_FLYING_SKULL
826         || mons_species(mon.type) == MONS_CURSE_SKULL // including Murray
827         || mon.type == MONS_CURSE_TOE
828         || mon.type == MONS_DEATH_COB
829         || mon.type == MONS_ANIMATED_ARMOUR
830         || mons_class_is_animated_weapon(mon.type)
831         || mons_is_tentacle_or_tentacle_segment(mon.type))
832     {
833         return false;
834     }
835 
836     switch (mons_base_char(mon.type))
837     {
838     case 'P':
839     case 'v':
840     case 'G':
841     case '*':
842     case 'J':
843         return false;
844     }
845 
846     return true;
847 }
848 
mons_has_flesh(const monster & mon)849 bool mons_has_flesh(const monster& mon)
850 {
851     if (mon.is_skeletal() || mon.is_insubstantial())
852         return false;
853 
854     // Dictionary says:
855     // 1. (12) flesh -- (the soft tissue of the body of a vertebrate:
856     //    mainly muscle tissue and fat)
857     // 3. pulp, flesh -- (a soft moist part of a fruit)
858     // yet I exclude sense 3 anyway but include arthropods and molluscs.
859     return !(mon.holiness() & (MH_PLANT | MH_NONLIVING))
860            && mons_genus(mon.type) != MONS_FLOATING_EYE
861            && mons_genus(mon.type) != MONS_GLOWING_ORANGE_BRAIN
862            && mons_genus(mon.type) != MONS_JELLY
863            && mon.type != MONS_DEATH_COB; // plant!
864 }
865 
866 // Difference in speed between monster and the player for Cheibriados'
867 // purposes. This is the speed difference disregarding the player's
868 // slow status.
cheibriados_monster_player_speed_delta(const monster & mon)869 int cheibriados_monster_player_speed_delta(const monster& mon)
870 {
871     // Ignore the Slow effect.
872     unwind_var<int> ignore_slow(you.duration[DUR_SLOW], 0);
873     const int pspeed = 1000 / (player_movement_speed() * player_speed());
874     dprf("Your delay: %d, your speed: %d, mon speed: %d",
875         player_movement_speed(), pspeed, mon.speed);
876     return mon.speed - pspeed;
877 }
878 
cheibriados_thinks_mons_is_fast(const monster & mon)879 bool cheibriados_thinks_mons_is_fast(const monster& mon)
880 {
881     return cheibriados_monster_player_speed_delta(mon) > 0;
882 }
883 
mons_is_projectile(monster_type mc)884 bool mons_is_projectile(monster_type mc)
885 {
886     return mons_class_flag(mc, M_PROJECTILE);
887 }
888 
mons_is_projectile(const monster & mon)889 bool mons_is_projectile(const monster& mon)
890 {
891     return mons_is_projectile(mon.type);
892 }
893 
894 // Conjuration or Hexes. Summoning and Necromancy make the monster a creature
895 // at least in some degree, golems have a chem granting them that.
mons_is_object(monster_type mc)896 bool mons_is_object(monster_type mc)
897 {
898     return mons_is_conjured(mc)
899            || mc == MONS_TWISTER
900            // unloading seeds helps the species
901            || mc == MONS_BALLISTOMYCETE_SPORE
902            || mc == MONS_LURKING_HORROR
903            || mc == MONS_DANCING_WEAPON
904            || mc == MONS_LIGHTNING_SPIRE;
905 }
906 
mons_has_blood(monster_type mc)907 bool mons_has_blood(monster_type mc)
908 {
909     return mons_class_flag(mc, M_COLD_BLOOD)
910            || mons_class_flag(mc, M_WARM_BLOOD);
911 }
912 
mons_is_sensed(monster_type mc)913 bool mons_is_sensed(monster_type mc)
914 {
915     return mc == MONS_SENSED
916            || mc == MONS_SENSED_FRIENDLY
917            || mc == MONS_SENSED_TRIVIAL
918            || mc == MONS_SENSED_EASY
919            || mc == MONS_SENSED_TOUGH
920            || mc == MONS_SENSED_NASTY;
921 }
922 
mons_allows_beogh(const monster & mon)923 bool mons_allows_beogh(const monster& mon)
924 {
925     if (!species::is_orcish(you.species) || you_worship(GOD_BEOGH))
926         return false; // no one else gives a damn
927 
928     return mons_genus(mon.type) == MONS_ORC
929            && mon.is_priest() && mon.god == GOD_BEOGH;
930 }
931 
mons_allows_beogh_now(const monster & mon)932 bool mons_allows_beogh_now(const monster& mon)
933 {
934     // Do the expensive LOS check last.
935     return mons_allows_beogh(mon)
936                && !mon.is_summoned() && !mon.friendly()
937                && !silenced(mon.pos()) && !mon.has_ench(ENCH_MUTE)
938                && !mons_is_confused(mon) && mons_is_seeking(mon)
939                && mon.foe == MHITYOU && !mons_is_immotile(mon)
940                && you.visible_to(&mon) && you.can_see(mon);
941 }
942 
943 // Returns true for monsters that obviously (to the player) feel
944 // "thematically at home" in a branch. Currently used for native
945 // monsters recognising traps and patrolling branch entrances.
mons_is_native_in_branch(const monster & mons,const branch_type branch)946 bool mons_is_native_in_branch(const monster& mons,
947                               const branch_type branch)
948 {
949     switch (branch)
950     {
951     case BRANCH_ELF:
952         return mons_genus(mons.type) == MONS_ELF;
953 
954     case BRANCH_ORC:
955         return mons_genus(mons.type) == MONS_ORC;
956 
957     case BRANCH_SHOALS:
958         return mons_species(mons.type) == MONS_CYCLOPS
959                || mons_species(mons.type) == MONS_MERFOLK
960                || mons.type == MONS_HARPY;
961 
962     case BRANCH_SLIME:
963         return mons_is_slime(mons);
964 
965     case BRANCH_SNAKE:
966         return mons_genus(mons.type) == MONS_NAGA
967                || mons_genus(mons.type) == MONS_SALAMANDER
968                || mons_genus(mons.type) == MONS_SNAKE;
969 
970     case BRANCH_ZOT:
971         return mons_genus(mons.type) == MONS_DRACONIAN
972                || mons.type == MONS_ORB_GUARDIAN
973                || mons.type == MONS_ORB_OF_FIRE
974                || mons.type == MONS_DEATH_COB
975                || mons.type == MONS_KILLER_KLOWN;
976 
977     case BRANCH_VAULTS:
978         return mons_genus(mons.type) == MONS_HUMAN;
979 
980     case BRANCH_CRYPT:
981         return mons.holiness() == MH_UNDEAD;
982 
983     case BRANCH_TOMB:
984         return mons_genus(mons.type) == MONS_MUMMY
985               || mons.type == MONS_USHABTI
986               || mons.type == MONS_DEATH_SCARAB;
987 
988     case BRANCH_SPIDER:
989         return mons_genus(mons.type) == MONS_SPIDER;
990 
991     case BRANCH_ABYSS:
992         return mons_is_abyssal_only(mons.type)
993                || mons.type == MONS_ABOMINATION_LARGE
994                || mons.type == MONS_ABOMINATION_SMALL
995                || mons.type == MONS_TENTACLED_MONSTROSITY
996                || mons.type == MONS_TENTACLED_STARSPAWN
997                || mons.type == MONS_THRASHING_HORROR
998                || mons.type == MONS_UNSEEN_HORROR
999                || mons.type == MONS_WORLDBINDER;
1000 
1001     default:
1002         return false;
1003     }
1004 }
1005 
mons_is_abyssal_only(monster_type mc)1006 bool mons_is_abyssal_only(monster_type mc)
1007 {
1008     switch (mc)
1009     {
1010     case MONS_ANCIENT_ZYME:
1011     case MONS_ELDRITCH_TENTACLE:
1012     case MONS_ELDRITCH_TENTACLE_SEGMENT:
1013     case MONS_LURKING_HORROR:
1014     case MONS_WRETCHED_STAR:
1015         return true;
1016     default:
1017         return false;
1018     }
1019 }
1020 
1021 // Monsters considered as "slime" for Jiyva.
mons_class_is_slime(monster_type mc)1022 bool mons_class_is_slime(monster_type mc)
1023 {
1024     return mons_genus(mc) == MONS_JELLY
1025            || mons_genus(mc) == MONS_FLOATING_EYE
1026            || mons_genus(mc) == MONS_GLOWING_ORANGE_BRAIN;
1027 }
1028 
mons_is_slime(const monster & mon)1029 bool mons_is_slime(const monster& mon)
1030 {
1031     return mons_class_is_slime(mon.type);
1032 }
1033 
herd_monster(const monster & mon)1034 bool herd_monster(const monster& mon)
1035 {
1036     return mons_class_flag(mon.type, M_HERD);
1037 }
1038 
mons_class_requires_band(monster_type mc)1039 bool mons_class_requires_band(monster_type mc)
1040 {
1041     return mons_class_flag(mc, M_REQUIRE_BAND);
1042 }
1043 
1044 // Plant or fungus or really anything with
1045 // permanent plant holiness
mons_class_is_plant(monster_type mc)1046 bool mons_class_is_plant(monster_type mc)
1047 {
1048     return bool(mons_class_holiness(mc) & MH_PLANT);
1049 }
1050 
mons_is_plant(const monster & mon)1051 bool mons_is_plant(const monster& mon)
1052 {
1053     return mons_class_is_plant(mon.type);
1054 }
1055 
mons_eats_items(const monster & mon)1056 bool mons_eats_items(const monster& mon)
1057 {
1058     return mons_is_slime(mon) && have_passive(passive_t::jelly_eating);
1059 }
1060 
1061 /* Is the actor susceptible to vampirism?
1062  *
1063  * Undead actors and summoned, temporary, or ghostified monsters are all not
1064  * susceptible.
1065  * @param act The actor.
1066  * @returns True if the actor is susceptible to vampirism, false otherwise.
1067  */
actor_is_susceptible_to_vampirism(const actor & act)1068 bool actor_is_susceptible_to_vampirism(const actor& act)
1069 {
1070     if (!(act.holiness() & MH_NATURAL) || act.is_summoned())
1071         return false;
1072 
1073     if (act.is_player())
1074         return true;
1075 
1076     const monster *mon = act.as_monster();
1077     // Don't allow HP draining from temporary monsters such as those created by
1078     // Sticks to Snakes.
1079     return !mon->has_ench(ENCH_FAKE_ABJURATION)
1080            // Nor from now-ghostly monsters.
1081            && !testbits(mon->flags, MF_SPECTRALISED);
1082 }
1083 
invalid_monster(const monster * mon)1084 bool invalid_monster(const monster* mon)
1085 {
1086     return !mon || invalid_monster_type(mon->type);
1087 }
1088 
invalid_monster_type(monster_type mt)1089 bool invalid_monster_type(monster_type mt)
1090 {
1091     return mt < 0 || mt >= NUM_MONSTERS
1092            || mon_entry[mt] == mon_entry[MONS_PROGRAM_BUG];
1093 }
1094 
invalid_monster_index(int i)1095 bool invalid_monster_index(int i)
1096 {
1097     return i < 0 || i >= MAX_MONSTERS;
1098 }
1099 
mons_is_statue(monster_type mc)1100 bool mons_is_statue(monster_type mc)
1101 {
1102     return mc == MONS_ORANGE_STATUE
1103            || mc == MONS_OBSIDIAN_STATUE
1104            || mc == MONS_ICE_STATUE
1105            || mc == MONS_ROXANNE;
1106 }
1107 
1108 /**
1109  * The mimic [(cackles|chortles...) and ]vanishes in[ a puff of smoke]!
1110  *
1111  * @param pos       The mimic's location.
1112  * @param name      The mimic's name.
1113  */
_mimic_vanish(const coord_def & pos,const string & name)1114 static void _mimic_vanish(const coord_def& pos, const string& name)
1115 {
1116     const bool can_place_smoke = !cloud_at(pos);
1117     if (can_place_smoke)
1118         place_cloud(CLOUD_BLACK_SMOKE, pos, 2 + random2(2), nullptr);
1119     if (!you.see_cell(pos))
1120         return;
1121 
1122     const char* const smoke_str = can_place_smoke ? " in a puff of smoke" : "";
1123 
1124     const bool can_cackle = !silenced(pos) && !silenced(you.pos());
1125     const string cackle = can_cackle ? getSpeakString("_laughs_") + " and " : "";
1126 
1127     mprf("The %s mimic %svanishes%s!",
1128          name.c_str(), cackle.c_str(), smoke_str);
1129     interrupt_activity(activity_interrupt::mimic);
1130 }
1131 
1132 /**
1133  * Clean up a "feature" that a mimic was pretending to be.
1134  *
1135  * @param pos   The location of the 'feature'.
1136  */
_destroy_mimic_feature(const coord_def & pos)1137 static void _destroy_mimic_feature(const coord_def &pos)
1138 {
1139 #if TAG_MAJOR_VERSION == 34
1140     const dungeon_feature_type feat = env.grid(pos);
1141 #endif
1142 
1143     unnotice_feature(level_pos(level_id::current(), pos));
1144     env.grid(pos) = DNGN_FLOOR;
1145     env.level_map_mask(pos) &= ~MMT_MIMIC;
1146     set_terrain_changed(pos);
1147     remove_markers_and_listeners_at(pos);
1148 
1149 #if TAG_MAJOR_VERSION == 34
1150     if (feat_is_door(feat))
1151         env.level_map_mask(pos) |= MMT_WAS_DOOR_MIMIC;
1152 #endif
1153 }
1154 
discover_mimic(const coord_def & pos)1155 void discover_mimic(const coord_def& pos)
1156 {
1157     item_def* item = item_mimic_at(pos);
1158     const bool feature_mimic = !item && feature_mimic_at(pos);
1159     // Is there really a mimic here?
1160     if (!item && !feature_mimic)
1161         return;
1162 
1163     const dungeon_feature_type feat = env.grid(pos);
1164 
1165     // If the feature has been destroyed, don't create a floor mimic.
1166     if (feature_mimic && !feat_is_mimicable(feat, false))
1167     {
1168         env.level_map_mask(pos) &= ~MMT_MIMIC;
1169         return;
1170     }
1171 
1172     const string name = feature_mimic ? "the " + string(feat_type_name(feat))
1173                                       : item->name(DESC_THE, false, false,
1174                                                              false, true);
1175     const bool plural = feature_mimic ? false : item->quantity > 1;
1176 
1177 #ifdef USE_TILE
1178     tileidx_t tile = tileidx_feature(pos);
1179     apply_variations(tile_env.flv(pos), &tile, pos);
1180 #endif
1181 
1182     if (you.see_cell(pos))
1183         mprf("%s %s a mimic!", name.c_str(), plural ? "are" : "is");
1184 
1185     const string shortname = feature_mimic ? feat_type_name(feat)
1186                                            : item->name(DESC_BASENAME);
1187     if (item)
1188         destroy_item(item->index(), true);
1189     else
1190         _destroy_mimic_feature(pos);
1191     _mimic_vanish(pos, shortname);
1192 
1193     // Just in case there's another one.
1194     if (mimic_at(pos))
1195         discover_mimic(pos);
1196 }
1197 
discover_shifter(monster & shifter)1198 void discover_shifter(monster& shifter)
1199 {
1200     shifter.flags |= MF_KNOWN_SHIFTER;
1201 }
1202 
mons_is_demon(monster_type mc)1203 bool mons_is_demon(monster_type mc)
1204 {
1205     return mons_class_holiness(mc) & MH_DEMONIC
1206              && (mons_demon_tier(mc) != 0 && mc != MONS_ANTAEUS
1207                  || mons_species(mc) == MONS_RAKSHASA);
1208 }
1209 
mons_demon_tier(monster_type mc)1210 int mons_demon_tier(monster_type mc)
1211 {
1212     switch (mons_base_char(mc))
1213     {
1214     case 'C':
1215         if (mc != MONS_ANTAEUS)
1216             return 0;
1217         // intentional fall-through for Antaeus
1218     case '&':
1219         return -1;
1220     case '1':
1221         return 1;
1222     case '2':
1223         return 2;
1224     case '3':
1225         return 3;
1226     case '4':
1227         return 4;
1228     case '5':
1229         return 5;
1230     default:
1231         return 0;
1232     }
1233 }
1234 
1235 // Beware; returns false for Tiamat!
mons_is_draconian(monster_type mc)1236 bool mons_is_draconian(monster_type mc)
1237 {
1238     return mc >= MONS_FIRST_DRACONIAN && mc <= MONS_LAST_DRACONIAN;
1239 }
1240 
mons_is_base_draconian(monster_type mc)1241 bool mons_is_base_draconian(monster_type mc)
1242 {
1243     return mc >= MONS_FIRST_DRACONIAN && mc <= MONS_LAST_BASE_DRACONIAN;
1244 }
1245 
mons_is_demonspawn(monster_type mc)1246 bool mons_is_demonspawn(monster_type mc)
1247 {
1248     return
1249 #if TAG_MAJOR_VERSION == 34
1250         mc == MONS_DEMONSPAWN ||
1251 #endif
1252         mc >= MONS_FIRST_DEMONSPAWN && mc <= MONS_LAST_DEMONSPAWN;
1253 }
1254 
1255 // Conjured (as opposed to summoned) monsters are actually here, even
1256 // though they're typically volatile (like, made of real fire). As such,
1257 // they should be immune to Abjuration or Recall. Also, they count as
1258 // things rather than beings.
mons_is_conjured(monster_type mc)1259 bool mons_is_conjured(monster_type mc)
1260 {
1261     return mons_is_projectile(mc)
1262            || mons_is_avatar(mc)
1263            || mons_class_flag(mc, M_CONJURED);
1264 }
1265 
mons_class_body_size(monster_type mc)1266 size_type mons_class_body_size(monster_type mc)
1267 {
1268     // Should pass base_type to get the right size for zombies, skeletons &c.
1269     // For normal monsters, base_type is set to type in the constructor.
1270     const monsterentry *e = get_monster_data(mc);
1271     return e ? e->size : SIZE_MEDIUM;
1272 }
1273 
max_corpse_chunks(monster_type mc)1274 int max_corpse_chunks(monster_type mc)
1275 {
1276     switch (mons_class_body_size(mc))
1277     {
1278     case SIZE_TINY:
1279         return 1;
1280     case SIZE_LITTLE:
1281         return 2;
1282     case SIZE_SMALL:
1283         return 3;
1284     case SIZE_MEDIUM:
1285         return 4;
1286     case SIZE_LARGE:
1287         return 9;
1288     case SIZE_BIG:
1289         return 10;
1290     case SIZE_GIANT:
1291         return 12;
1292     default:
1293         return 0;
1294     }
1295 }
1296 
derived_undead_avg_hp(monster_type mtype,int hd,int scale)1297 int derived_undead_avg_hp(monster_type mtype, int hd, int scale)
1298 {
1299     static const map<monster_type, int> hp_per_hd_by_type = {
1300         { MONS_ZOMBIE,          85 },
1301         { MONS_SKELETON,        70 },
1302         { MONS_SPECTRAL_THING,  60 },
1303         // Simulacra aren't tough, but you can create piles of them. - bwr
1304         { MONS_SIMULACRUM,      30 },
1305     };
1306 
1307     const int* hp_per_hd = map_find(hp_per_hd_by_type, mtype);
1308     ASSERT(hp_per_hd);
1309     return *hp_per_hd * hd * scale / 10;
1310 }
1311 
mons_genus(monster_type mc)1312 monster_type mons_genus(monster_type mc)
1313 {
1314     if (mc == RANDOM_DRACONIAN || mc == RANDOM_BASE_DRACONIAN
1315         || mc == RANDOM_NONBASE_DRACONIAN
1316         || (mc == MONS_PLAYER_ILLUSION && species::is_draconian(you.species)))
1317     {
1318         return MONS_DRACONIAN;
1319     }
1320 
1321     if (mc == RANDOM_DEMONSPAWN || mc == RANDOM_BASE_DEMONSPAWN
1322         || mc == RANDOM_NONBASE_DEMONSPAWN)
1323     {
1324         return MONS_DEMONSPAWN;
1325     }
1326 
1327     if (mc == MONS_NO_MONSTER)
1328         return MONS_NO_MONSTER;
1329 
1330     ASSERT_smc();
1331     return smc->genus;
1332 }
1333 
mons_species(monster_type mc)1334 monster_type mons_species(monster_type mc)
1335 {
1336     const monsterentry *me = get_monster_data(mc);
1337     return me ? me->species : MONS_PROGRAM_BUG;
1338 }
1339 
draco_or_demonspawn_subspecies(monster_type type,monster_type base)1340 monster_type draco_or_demonspawn_subspecies(monster_type type,
1341                                             monster_type base)
1342 {
1343     const monster_type species = mons_species(type);
1344 
1345     if ((species == MONS_DRACONIAN || species == MONS_DEMONSPAWN)
1346         && type != species)
1347     {
1348         return base;
1349     }
1350 
1351     return species;
1352 }
1353 
draco_or_demonspawn_subspecies(const monster & mon)1354 monster_type draco_or_demonspawn_subspecies(const monster& mon)
1355 {
1356     ASSERT(mons_genus(mon.type) == MONS_DRACONIAN
1357            || mons_genus(mon.type) == MONS_DEMONSPAWN);
1358 
1359     if (mon.type == MONS_PLAYER_ILLUSION
1360         && mons_genus(mon.type) == MONS_DRACONIAN)
1361     {
1362         return species::to_mons_species(mon.ghost->species);
1363     }
1364 
1365     return draco_or_demonspawn_subspecies(mon.type, mon.base_monster);
1366 }
1367 
mons_detected_base(monster_type mc)1368 monster_type mons_detected_base(monster_type mc)
1369 {
1370     return mons_genus(mc);
1371 }
1372 
1373 /** Does a monster of this type behold opponents like a siren?
1374  */
mons_is_siren_beholder(monster_type mc)1375 bool mons_is_siren_beholder(monster_type mc)
1376 {
1377     return mc == MONS_MERFOLK_SIREN || mc == MONS_MERFOLK_AVATAR;
1378 }
1379 
1380 /** Does this monster behold opponents like a siren?
1381  *  Ignores any disabilities, checking only the monster's type.
1382  */
mons_is_siren_beholder(const monster & mons)1383 bool mons_is_siren_beholder(const monster& mons)
1384 {
1385     return mons_is_siren_beholder(mons.type);
1386 }
1387 
get_shout_noise_level(const shout_type shout)1388 int get_shout_noise_level(const shout_type shout)
1389 {
1390     switch (shout)
1391     {
1392     case S_SILENT:
1393         return 0;
1394     case S_HISS:
1395     case S_VERY_SOFT:
1396         return 4;
1397     case S_SOFT:
1398         return 6;
1399     case S_LOUD:
1400         return 10;
1401     case S_LOUD_ROAR:
1402     case S_VERY_LOUD:
1403         return 12;
1404 
1405     default:
1406         return 8;
1407     }
1408 }
1409 
1410 // Only beasts and chaos spawns use S_RANDOM for noise type.
1411 // Pandemonium lords can also get here, but this is mostly used for the
1412 // "says" verb used for insults.
_shout_fits_monster(monster_type mc,int shout)1413 static bool _shout_fits_monster(monster_type mc, int shout)
1414 {
1415     if (shout == NUM_SHOUTS || shout >= NUM_LOUDNESS || shout == S_SILENT)
1416         return false;
1417 
1418     // Chaos spawns can do anything but demon taunts, since they're
1419     // not coherent enough to actually say words.
1420     if (mc == MONS_CHAOS_SPAWN)
1421         return shout != S_DEMON_TAUNT;
1422 
1423     // For Pandemonium lords, almost everything is fair game. It's only
1424     // used for the shouting verb ("say", "bellow", "roar", etc.) anyway.
1425     if (mc != MONS_HELL_BEAST && mc != MONS_MUTANT_BEAST)
1426         return true;
1427 
1428     switch (shout)
1429     {
1430     // Bees, cherubs and two-headed ogres never fit.
1431     case S_SHOUT2:
1432     case S_BUZZ:
1433     case S_CHERUB:
1434     // The beast cannot speak.
1435     case S_DEMON_TAUNT:
1436         return false;
1437     default:
1438         return true;
1439     }
1440 }
1441 
1442 // If demon_shout is true, we're trying to find a random verb and
1443 // loudness for a Pandemonium lord trying to shout.
mons_shouts(monster_type mc,bool demon_shout)1444 shout_type mons_shouts(monster_type mc, bool demon_shout)
1445 {
1446     ASSERT_smc();
1447     shout_type u = smc->shouts;
1448 
1449     // Pandemonium lords use this to get the noises.
1450     if (u == S_RANDOM || demon_shout && u == S_DEMON_TAUNT)
1451     {
1452         const int max_shout = (u == S_RANDOM ? NUM_SHOUTS : NUM_LOUDNESS);
1453         do
1454         {
1455             u = static_cast<shout_type>(random2(max_shout));
1456         }
1457         while (!_shout_fits_monster(mc, u));
1458     }
1459 
1460     return u;
1461 }
1462 
1463 /// Is the given monster type ever capable of shouting?
mons_can_shout(monster_type mc)1464 bool mons_can_shout(monster_type mc)
1465 {
1466     // don't use mons_shouts() to avoid S_RANDOM randomization.
1467     ASSERT_smc();
1468     return smc->shouts != S_SILENT;
1469 }
1470 
mons_is_ghost_demon(monster_type mc)1471 bool mons_is_ghost_demon(monster_type mc)
1472 {
1473     return mons_class_flag(mc, M_GHOST_DEMON);
1474 }
1475 
mons_is_pghost(monster_type mc)1476 bool mons_is_pghost(monster_type mc)
1477 {
1478     return mc == MONS_PLAYER_GHOST || mc == MONS_PLAYER_ILLUSION;
1479 }
1480 
1481 /**
1482  * What mutant beast tier does the given XL (base HD) correspond to?
1483  *
1484  * If the given value is between tiers, choose the higher possibility.
1485  *
1486  * @param xl    The XL (HD) of the mutant beast in question.
1487  * @return      The corresponding mutant beast tier (e.g. BT_MATURE).
1488  */
mutant_beast_tier(int xl)1489 int mutant_beast_tier(int xl)
1490 {
1491     for (int bt = BT_FIRST; bt < NUM_BEAST_TIERS; ++bt)
1492         if (xl <= beast_tiers[bt])
1493             return bt;
1494     return BT_NONE; // buggy
1495 }
1496 
1497 /**
1498  * Is the provided monster_type a demonspawn job type? (Not just any
1499  * demonspawn, but specifically one with a job! Or the job itself, depending
1500  * how you think about it.)
1501  *
1502  * @param mc    The monster type in question.
1503  * @return      Whether that monster type is a demonspawn job.
1504  **/
mons_is_demonspawn_job(monster_type mc)1505 bool mons_is_demonspawn_job(monster_type mc)
1506 {
1507     return mc >= MONS_FIRST_NONBASE_DEMONSPAWN
1508            && mc <= MONS_LAST_NONBASE_DEMONSPAWN;
1509 }
1510 
1511 /**
1512  * Is the provided monster_type a draconian job type? (Not just any draconian,
1513  * but specifically one with a job! Or the job itself, depending how you think
1514  * about it.)
1515  *
1516  * @param mc    The monster type in question.
1517  * @return      Whether that monster type is a draconian job.
1518  **/
mons_is_draconian_job(monster_type mc)1519 bool mons_is_draconian_job(monster_type mc)
1520 {
1521     return mc >= MONS_FIRST_NONBASE_DRACONIAN
1522            && mc <= MONS_LAST_NONBASE_DRACONIAN;
1523 }
1524 
1525 /**
1526  * Is the provided monster_type a job? (E.g. black sun, draconian knight)
1527  *
1528  * @param mc    The monster type in question.
1529  * @return      Whether that monster type is a job.
1530  **/
mons_is_job(monster_type mc)1531 bool mons_is_job(monster_type mc)
1532 {
1533     return mons_is_draconian_job(mc) || mons_is_demonspawn_job(mc);
1534 }
1535 
mons_is_unique(monster_type mc)1536 bool mons_is_unique(monster_type mc)
1537 {
1538     return mons_class_flag(mc, M_UNIQUE);
1539 }
1540 
mons_is_or_was_unique(const monster & mon)1541 bool mons_is_or_was_unique(const monster& mon)
1542 {
1543     return mons_is_unique(mon.type)
1544            || mon.props.exists(ORIGINAL_TYPE_KEY)
1545               && mons_is_unique((monster_type) mon.props[ORIGINAL_TYPE_KEY].get_int());
1546 }
1547 
1548 /**
1549  * Is the given type one of Hepliaklqana's granted ancestors?
1550  *
1551  * @param mc    The type of monster in question.
1552  * @return      Whether that monster is a player ancestor.
1553  */
mons_is_hepliaklqana_ancestor(monster_type mc)1554 bool mons_is_hepliaklqana_ancestor(monster_type mc)
1555 {
1556     return mons_class_flag(mc, M_ANCESTOR);
1557 }
1558 
1559 /**
1560  * Can this type of monster be blinded?
1561  *
1562  * Certain monsters, e.g. those with a powerful sense of smell, echolocation,
1563  * or no eyes, are completely immune to blinding.
1564  *
1565  * Note that 'dazzling' (from dazzling spray) has additional restrictions above
1566  * this.
1567  *
1568  * @param mc    The class of monster in question.
1569  * @return      Whether monsters of this type can get ENCH_BLIND.
1570  */
mons_can_be_blinded(monster_type mc)1571 bool mons_can_be_blinded(monster_type mc)
1572 {
1573     return !mons_class_flag(mc, M_UNBLINDABLE);
1574 }
1575 
1576 
1577 /**
1578  * Can this kind of monster be dazzled?
1579  *
1580  * The undead, nonliving, vegetative, or unblindable cannot be dazzled.
1581  *
1582  * @param mc    The class of monster in question.
1583  * @return      Whether monsters of this type can get ENCH_BLIND from Dazzling
1584  *              Spray.
1585  */
mons_can_be_dazzled(monster_type mc)1586 bool mons_can_be_dazzled(monster_type mc)
1587 {
1588     // This was implemented by checking type so that we could use it in
1589     // monster descriptions (which only have mon_info structs); not sure if
1590     // that's useful
1591 
1592     const mon_holy_type holiness = mons_class_holiness(mc);
1593     return !(holiness & (MH_UNDEAD | MH_NONLIVING | MH_PLANT))
1594         && mons_can_be_blinded(mc);
1595 }
1596 
mons_char(monster_type mc)1597 char32_t mons_char(monster_type mc)
1598 {
1599     if (Options.mon_glyph_overrides.count(mc)
1600         && Options.mon_glyph_overrides[mc].ch)
1601     {
1602         return Options.mon_glyph_overrides[mc].ch;
1603     }
1604     else
1605         return monster_symbols[mc].glyph;
1606 }
1607 
mons_base_char(monster_type mc)1608 char mons_base_char(monster_type mc)
1609 {
1610     const monsterentry *me = get_monster_data(mc);
1611     return me ? me->basechar : 0;
1612 }
1613 
mons_class_itemuse(monster_type mc)1614 mon_itemuse_type mons_class_itemuse(monster_type mc)
1615 {
1616     ASSERT_smc();
1617     return smc->gmon_use;
1618 }
1619 
mons_itemuse(const monster & mon)1620 mon_itemuse_type mons_itemuse(const monster& mon)
1621 {
1622     if (mons_enslaved_soul(mon))
1623         return mons_class_itemuse(mons_zombie_base(mon));
1624 
1625     return mons_class_itemuse(mon.type);
1626 }
1627 
mons_class_colour(monster_type mc)1628 int mons_class_colour(monster_type mc)
1629 {
1630     ASSERT_smc();
1631     // Player monster is a dummy monster used only for display purposes, so
1632     // it's ok to override it here.
1633     if (mc == MONS_PLAYER
1634         && Options.mon_glyph_overrides.count(MONS_PLAYER)
1635         && Options.mon_glyph_overrides[MONS_PLAYER].col)
1636     {
1637         return Options.mon_glyph_overrides[MONS_PLAYER].col;
1638     }
1639     else
1640         return monster_symbols[mc].colour;
1641 }
1642 
mons_class_can_regenerate(monster_type mc)1643 bool mons_class_can_regenerate(monster_type mc)
1644 {
1645     return !mons_class_flag(mc, M_NO_REGEN);
1646 }
1647 
mons_can_regenerate(const monster & m)1648 bool mons_can_regenerate(const monster& m)
1649 {
1650     const monster& mon = get_tentacle_head(m);
1651 
1652     if (testbits(mon.flags, MF_NO_REGEN))
1653         return false;
1654 
1655     return mons_class_can_regenerate(mon.type);
1656 }
1657 
mons_class_fast_regen(monster_type mc)1658 bool mons_class_fast_regen(monster_type mc)
1659 {
1660     return mons_class_flag(mc, M_FAST_REGEN);
1661 }
1662 
1663 /**
1664  * Do monsters of the given type ever leave a hide?
1665  *
1666  * @param mc      The class of monster in question.
1667  * @return        Whether the monster has a chance of dropping a hide when
1668  *                butchered.
1669  */
mons_class_leaves_hide(monster_type mc)1670 bool mons_class_leaves_hide(monster_type mc)
1671 {
1672     return hide_for_monster(mc) != NUM_ARMOURS;
1673 }
1674 
mons_zombie_size(monster_type mc)1675 int mons_zombie_size(monster_type mc)
1676 {
1677     mc = mons_species(mc);
1678     if (!mons_class_can_be_zombified(mc))
1679         return Z_NOZOMBIE;
1680 
1681     ASSERT_smc();
1682     return smc->size > SIZE_MEDIUM ? Z_BIG : Z_SMALL;
1683 }
1684 
mons_zombie_base(const monster & mon)1685 monster_type mons_zombie_base(const monster& mon)
1686 {
1687     return mons_species(mon.base_monster);
1688 }
1689 
mons_class_is_zombified(monster_type mc)1690 bool mons_class_is_zombified(monster_type mc)
1691 {
1692 #if TAG_MAJOR_VERSION == 34
1693     switch (mc)
1694     {
1695         case MONS_ZOMBIE_SMALL:     case MONS_ZOMBIE_LARGE:
1696         case MONS_SKELETON_SMALL:   case MONS_SKELETON_LARGE:
1697         case MONS_SIMULACRUM_SMALL: case MONS_SIMULACRUM_LARGE:
1698             return true;
1699         default:
1700             break;
1701     }
1702 #endif
1703 
1704     return mc == MONS_ZOMBIE
1705         || mc == MONS_SKELETON
1706         || mc == MONS_SIMULACRUM
1707         || mc == MONS_SPECTRAL_THING;
1708 }
1709 
mons_class_is_animated_weapon(monster_type type)1710 bool mons_class_is_animated_weapon(monster_type type)
1711 {
1712     return type == MONS_DANCING_WEAPON || type == MONS_SPECTRAL_WEAPON;
1713 }
1714 
mons_class_is_animated_object(monster_type type)1715 bool mons_class_is_animated_object(monster_type type)
1716 {
1717     return mons_class_is_animated_weapon(type)
1718         || type == MONS_ANIMATED_ARMOUR;
1719 }
1720 
mons_is_zombified(const monster & mon)1721 bool mons_is_zombified(const monster& mon)
1722 {
1723     return mons_class_is_zombified(mon.type);
1724 }
1725 
mons_base_type(const monster & mon)1726 monster_type mons_base_type(const monster& mon)
1727 {
1728     if (mons_class_is_zombified(mon.type))
1729         return mons_species(mon.base_monster);
1730     return mon.type;
1731 }
1732 
mons_class_can_leave_corpse(monster_type mc)1733 bool mons_class_can_leave_corpse(monster_type mc)
1734 {
1735     ASSERT_smc();
1736     return smc->leaves_corpse;
1737 }
1738 
mons_class_can_be_zombified(monster_type mc)1739 bool mons_class_can_be_zombified(monster_type mc)
1740 {
1741     monster_type ms = mons_species(mc);
1742     return !invalid_monster_type(ms)
1743            && mons_class_can_leave_corpse(ms);
1744 }
1745 
mons_can_be_zombified(const monster & mon)1746 bool mons_can_be_zombified(const monster& mon)
1747 {
1748     return mons_class_can_be_zombified(mon.type)
1749            && !mon.is_summoned()
1750            && !mons_enslaved_body_and_soul(mon);
1751 }
1752 
mons_class_can_use_stairs(monster_type mc)1753 bool mons_class_can_use_stairs(monster_type mc)
1754 {
1755     return (!mons_class_is_zombified(mc) || mc == MONS_SPECTRAL_THING)
1756            && !mons_is_tentacle_or_tentacle_segment(mc)
1757            && mc != MONS_SILENT_SPECTRE
1758            && mc != MONS_GERYON
1759            && mc != MONS_ROYAL_JELLY
1760            && mc != MONS_BALL_LIGHTNING
1761            && mc != MONS_FOXFIRE;
1762 }
1763 
mons_class_can_use_transporter(monster_type mc)1764 bool mons_class_can_use_transporter(monster_type mc)
1765 {
1766     return !mons_is_tentacle_or_tentacle_segment(mc);
1767 }
1768 
mons_can_use_stairs(const monster & mon,dungeon_feature_type stair)1769 bool mons_can_use_stairs(const monster& mon, dungeon_feature_type stair)
1770 {
1771     if (!mons_class_can_use_stairs(mon.type))
1772         return false;
1773 
1774     // Summons can't use stairs.
1775     if (mon.has_ench(ENCH_ABJ) || mon.has_ench(ENCH_FAKE_ABJURATION))
1776         return false;
1777 
1778     if (mon.has_ench(ENCH_FRIENDLY_BRIBED)
1779         && (feat_is_branch_entrance(stair) || feat_is_branch_exit(stair)
1780             || stair == DNGN_ENTER_HELL || stair == DNGN_EXIT_HELL))
1781     {
1782         return false;
1783     }
1784 
1785     // Everything else is fine
1786     return true;
1787 }
1788 
mons_enslaved_body_and_soul(const monster & mon)1789 bool mons_enslaved_body_and_soul(const monster& mon)
1790 {
1791     return mon.has_ench(ENCH_SOUL_RIPE);
1792 }
1793 
mons_enslaved_soul(const monster & mon)1794 bool mons_enslaved_soul(const monster& mon)
1795 {
1796     return testbits(mon.flags, MF_ENSLAVED_SOUL);
1797 }
1798 
name_zombie(monster & mon,monster_type mc,const string & mon_name)1799 void name_zombie(monster& mon, monster_type mc, const string &mon_name)
1800 {
1801     mon.mname = mon_name;
1802 
1803     // Special case for Blork the orc: shorten his name to "Blork" to
1804     // avoid mentions of "Blork the orc the orc zombie".
1805     if (mc == MONS_BLORK_THE_ORC)
1806         mon.mname = "Blork";
1807     // Also for the Lernaean hydra: treat Lernaean as an adjective to
1808     // avoid mentions of "the Lernaean hydra the X-headed hydra zombie".
1809     else if (mc == MONS_LERNAEAN_HYDRA)
1810     {
1811         mon.mname = "Lernaean";
1812         mon.flags |= MF_NAME_ADJECTIVE;
1813     }
1814     // Also for the Enchantress: treat Enchantress as an adjective to
1815     // avoid mentions of "the Enchantress the spriggan zombie".
1816     else if (mc == MONS_THE_ENCHANTRESS)
1817     {
1818         mon.mname = "Enchantress";
1819         mon.flags |= MF_NAME_ADJECTIVE;
1820     }
1821     else if (mons_species(mc) == MONS_SERPENT_OF_HELL)
1822         mon.mname = "";
1823 
1824     if (starts_with(mon.mname, "shaped "))
1825         mon.flags |= MF_NAME_SUFFIX;
1826 
1827     // It's unlikely there's a desc for "Duvessa the elf skeleton", but
1828     // we still want to allow it if overridden.
1829     if (!mon.props.exists("dbname"))
1830         mon.props["dbname"] = mons_class_name(mon.type);
1831 }
1832 
name_zombie(monster & mon,const monster & orig)1833 void name_zombie(monster& mon, const monster& orig)
1834 {
1835     if (!mons_is_unique(orig.type) && orig.mname.empty())
1836         return;
1837 
1838     string name;
1839 
1840     if (!orig.mname.empty())
1841         name = orig.mname;
1842     else
1843         name = mons_type_name(orig.type, DESC_PLAIN);
1844 
1845     name_zombie(mon, orig.type, name);
1846     mon.flags |= orig.flags & (MF_NAME_SUFFIX
1847                                  | MF_NAME_ADJECTIVE
1848                                  | MF_NAME_DESCRIPTOR);
1849 }
1850 
1851 // Derived undead deal 80% of the damage of the base form.
_downscale_zombie_damage(int damage)1852 static int _downscale_zombie_damage(int damage)
1853 {
1854     return max(1, 4 * damage / 5);
1855 }
1856 
_downscale_zombie_attack(const monster & mons,mon_attack_def attk,bool random)1857 static mon_attack_def _downscale_zombie_attack(const monster& mons,
1858                                                mon_attack_def attk,
1859                                                bool random)
1860 {
1861     switch (attk.type)
1862     {
1863     case AT_STING: case AT_SPORE:
1864     case AT_TOUCH: case AT_ENGULF:
1865         attk.type = AT_HIT;
1866         break;
1867     default:
1868         break;
1869     }
1870 
1871     if (mons.type == MONS_SIMULACRUM)
1872         attk.flavour = AF_COLD;
1873     else if (mons.type == MONS_SPECTRAL_THING && (!random || coinflip()))
1874         attk.flavour = AF_DRAIN;
1875     else if (attk.flavour != AF_REACH && attk.flavour != AF_CRUSH)
1876         attk.flavour = AF_PLAIN;
1877 
1878     attk.damage = _downscale_zombie_damage(attk.damage);
1879 
1880     return attk;
1881 }
1882 
1883 /**
1884  * What attack does the given mutant beast facet provide?
1885  *
1886  * @param facet     The facet in question; e.g. BF_STING.
1887  * @param tier      The tier of the mutant beast; e.g.
1888  * @return          The attack corresponding to the given facet; e.g. BT_LARVAL
1889  *                  { AT_STING, AF_REACH_STING, 10 }. Scales with HD.
1890  *                  For facets that don't provide an attack, is { }.
1891  */
_mutant_beast_facet_attack(int facet,int tier)1892 static mon_attack_def _mutant_beast_facet_attack(int facet, int tier)
1893 {
1894     const int dam = tier * 5;
1895     switch (facet)
1896     {
1897         case BF_STING:
1898             return { AT_STING, AF_REACH_STING, dam };
1899         case BF_OX:
1900             return { AT_TRAMPLE, AF_TRAMPLE, dam };
1901         case BF_WEIRD:
1902             return { AT_CONSTRICT, AF_CRUSH, dam };
1903         default:
1904             return { };
1905     }
1906 }
1907 
1908 /**
1909  * Get the attack type, attack flavour and damage for the given attack of a
1910  * mutant beast.
1911  *
1912  * @param mon           The monster in question.
1913  * @param attk_number   Which attack number to get.
1914  * @return              A mon_attack_def for the specified attack.
1915  */
_mutant_beast_attack(const monster & mon,int attk_number)1916 static mon_attack_def _mutant_beast_attack(const monster &mon, int attk_number)
1917 {
1918     const int tier = mutant_beast_tier(mon.get_experience_level());
1919     if (attk_number == 0)
1920         return { AT_HIT, AF_PLAIN, tier * 7 + 5 };
1921 
1922     if (!mon.props.exists(MUTANT_BEAST_FACETS))
1923         return { };
1924 
1925     int cur_attk = 1;
1926     for (auto facet : mon.props[MUTANT_BEAST_FACETS].get_vector())
1927     {
1928         const mon_attack_def atk = _mutant_beast_facet_attack(facet.get_int(),
1929                                                               tier);
1930         if (atk.type == AT_NONE)
1931             continue;
1932 
1933         if (cur_attk == attk_number)
1934             return atk;
1935 
1936         ++cur_attk;
1937     }
1938 
1939     return { };
1940 }
1941 
1942 /**
1943  * Get the attack type, attack flavour and damage for the given attack of an
1944  * ancestor granted by Hepliaklqana_ancestor_attack.
1945  *
1946  * @param mon           The monster in question.
1947  * @param attk_number   Which attack number to get.
1948  * @return              A mon_attack_def for the specified attack.
1949  */
_hepliaklqana_ancestor_attack(const monster & mon,int attk_number)1950 static mon_attack_def _hepliaklqana_ancestor_attack(const monster &mon,
1951                                                      int attk_number)
1952 {
1953     if (attk_number != 0)
1954         return { };
1955 
1956     const int HD = mon.get_experience_level();
1957     const int dam = HD + 3; // 4 at 1 HD, 21 at 18 HD (max)
1958     // battlemages do double base melee damage (+25-50% including their weapon)
1959     const int dam_mult = mon.type == MONS_ANCESTOR_BATTLEMAGE ? 2 : 1;
1960 
1961     return { AT_HIT, AF_PLAIN, dam * dam_mult };
1962 }
1963 
1964 /** Get the attack type, attack flavour and damage for a monster attack.
1965  *
1966  * @param mon The monster to look at.
1967  * @param attk_number Which attack number to get.
1968  * @param base_flavour If true, attack flavours that are randomised on every attack
1969  *                     will have their base flavour returned instead of one of the
1970  *                     random flavours.
1971  * @return  A mon_attack_def for the specified attack.
1972  */
mons_attack_spec(const monster & m,int attk_number,bool base_flavour)1973 mon_attack_def mons_attack_spec(const monster& m, int attk_number,
1974                                 bool base_flavour)
1975 {
1976     monster_type mc = m.type;
1977 
1978     const monster& mon = get_tentacle_head(m);
1979 
1980     const bool zombified = mons_is_zombified(mon);
1981 
1982     if (mon.has_hydra_multi_attack())
1983         attk_number -= mon.heads() - 1;
1984 
1985     if (attk_number < 0 || attk_number >= MAX_NUM_ATTACKS)
1986         attk_number = 0;
1987 
1988     if (mons_is_ghost_demon(mc))
1989     {
1990         if (attk_number == 0 && mon.ghost)
1991         {
1992             return { mon.ghost->att_type, mon.ghost->att_flav,
1993                      mon.ghost->damage };
1994         }
1995 
1996         return { AT_NONE, AF_PLAIN, 0 };
1997     }
1998     else if (mc == MONS_MUTANT_BEAST)
1999         return _mutant_beast_attack(mon, attk_number);
2000     else if (mons_is_hepliaklqana_ancestor(mc))
2001         return _hepliaklqana_ancestor_attack(mon, attk_number);
2002     else if (mons_is_demonspawn(mc) && attk_number != 0)
2003         mc = draco_or_demonspawn_subspecies(mon);
2004 
2005     if (zombified && mc != MONS_KRAKEN_TENTACLE)
2006         mc = mons_zombie_base(mon);
2007 
2008     ASSERT_smc();
2009     mon_attack_def attk = smc->attack[attk_number];
2010 
2011     if (attk_number == 0)
2012     {
2013         if (mons_is_demonspawn(mon.type))
2014         {
2015             const monsterentry* mbase =
2016                 get_monster_data (draco_or_demonspawn_subspecies(mon));
2017             ASSERT(mbase);
2018             attk.flavour = mbase->attack[0].flavour;
2019             return attk;
2020         }
2021 
2022         if (mons_is_player_shadow(mon))
2023         {
2024             if (!you.weapon())
2025                 attk.damage = max(1, you.skill_rdiv(SK_UNARMED_COMBAT, 10, 20));
2026         }
2027 
2028         // summoning miscast monster; hd scaled with miscast severity
2029         if (mon.type == MONS_NAMELESS)
2030             attk.damage = mon.get_hit_dice() * 2;
2031 
2032         // Boulder beetles get double attack damage and a normal 'hit' attack.
2033         if (mon.has_ench(ENCH_ROLLING))
2034         {
2035             attk.type = AT_HIT;
2036             attk.damage *= 2;
2037         }
2038     }
2039     else if (mons_species(mon.type) == MONS_DRACONIAN
2040              && mon.type != MONS_DRACONIAN
2041              && attk.type == AT_NONE
2042              && smc->attack[attk_number - 1].type != AT_NONE)
2043     {
2044         // Nonbase draconians inherit aux attacks from their base type.
2045         // Implicit assumption: base draconian types only get one aux
2046         // attack, and it's in their second attack slot.
2047         // If that changes this code will need to be changed.
2048         const monsterentry* mbase =
2049             get_monster_data (draco_or_demonspawn_subspecies(mon));
2050         ASSERT(mbase);
2051         return mbase->attack[1];
2052     }
2053 
2054     if (mon.type == MONS_ANIMATED_ARMOUR)
2055     {
2056         item_def *def = mon.get_defining_object();
2057         if (def)
2058         {
2059             ASSERT(def->base_type == OBJ_ARMOUR);
2060             const int typ = def->sub_type;
2061             const int ac = armour_prop(typ, PARM_AC);
2062             attk.damage = ac + ac * ac / 2;
2063         }
2064     }
2065 
2066     if (!base_flavour)
2067     {
2068         // TODO: randomization here is not the greatest way of doing any of
2069         // these...
2070         if (attk.type == AT_RANDOM)
2071             attk.type = random_choose(AT_HIT, AT_GORE);
2072 
2073         if (attk.type == AT_CHERUB)
2074             attk.type = random_choose(AT_HIT, AT_BITE, AT_PECK, AT_GORE);
2075 
2076         if (attk.flavour == AF_DRAIN_STAT)
2077             attk.flavour = random_choose(AF_DRAIN_STR, AF_DRAIN_INT,AF_DRAIN_DEX);
2078     }
2079 
2080     // Slime creature attacks are multiplied by the number merged.
2081     if (mon.type == MONS_SLIME_CREATURE && mon.blob_size > 1)
2082         attk.damage *= mon.blob_size;
2083 
2084     return zombified ? _downscale_zombie_attack(mon, attk, !base_flavour) : attk;
2085 }
2086 
_mons_damage(monster_type mc,int rt)2087 static int _mons_damage(monster_type mc, int rt)
2088 {
2089     if (rt < 0 || rt > 3)
2090         rt = 0;
2091     ASSERT_smc();
2092     return smc->attack[rt].damage;
2093 }
2094 
2095 /**
2096  * A short description of the given monster attack type.
2097  *
2098  * @param attack      The attack to be described; e.g. AT_HIT, AT_SPORE.
2099  * @param with_object Is the description being used with an object/target?
2100  *                    True results in e.g. "pounce on"; false, just "pounce".
2101  *                    Optional parameter, default true.
2102  * @return            A short description; e.g. "hit", "release spores at".
2103  */
mon_attack_name(attack_type attack,bool with_object)2104 string mon_attack_name(attack_type attack, bool with_object)
2105 {
2106     static const char *attack_types[] =
2107     {
2108         "hit",         // including weapon attacks
2109         "bite",
2110         "sting",
2111 
2112         // spore
2113         "release spores at",
2114 
2115         "touch",
2116         "engulf",
2117         "claw",
2118         "peck",
2119         "headbutt",
2120         "punch",
2121         "kick",
2122         "tentacle-slap",
2123         "tail-slap",
2124         "gore",
2125         "constrict",
2126         "trample",
2127         "trunk-slap",
2128 #if TAG_MAJOR_VERSION == 34
2129         "snap closed at",
2130         "splash",
2131 #endif
2132         "pounce on",
2133 #if TAG_MAJOR_VERSION == 34
2134         "sting",
2135 #endif
2136         "hit, bite, peck, or gore", // AT_CHERUB
2137 #if TAG_MAJOR_VERSION == 34
2138         "hit", // AT_SHOOT
2139 #endif
2140         "hit", // AT_WEAP_ONLY,
2141         "hit or gore", // AT_RANDOM
2142     };
2143     COMPILE_CHECK(ARRAYSZ(attack_types) == NUM_ATTACK_TYPES - AT_FIRST_ATTACK);
2144 
2145     const int verb_index = attack - AT_FIRST_ATTACK;
2146     ASSERT(verb_index < (int)ARRAYSZ(attack_types));
2147 
2148     if (with_object)
2149         return attack_types[verb_index];
2150     else
2151     {
2152         return replace_all(replace_all(attack_types[verb_index], " at", ""),
2153                                                                  " on", "");
2154     }
2155 }
2156 
2157 /**
2158  * Is this attack flavour 'plain'? A plain attack flavour doesn't imply the
2159  * target will be affected in any particular way beyond damage.
2160  * Certain attack flavours like AT_TRAMPLE, AT_SPORE, and AT_ENGULF do imply
2161  * additional effects, and are not considered 'plain'.
2162  */
is_plain_attack_type(attack_type attack)2163 bool is_plain_attack_type(attack_type attack)
2164 {
2165     switch (attack) {
2166         case AT_CONSTRICT:  // constriction
2167         case AT_ENGULF:     // water hold
2168         case AT_POUNCE:     // webbing
2169         case AT_SPORE:      // confusing spores
2170         case AT_STING:      // poison
2171         case AT_TRAMPLE:    // trampling
2172             return false;
2173         default:
2174             return true;
2175     }
2176 }
2177 
2178 /**
2179  * Does this monster attack flavour trigger even if the base attack does no
2180  * damage?
2181  *
2182  * @param flavour   The attack flavour in question; e.g. AF_COLD.
2183  * @return          Whether the flavour attack triggers on a successful hit
2184  *                  regardless of damage done.
2185  */
flavour_triggers_damageless(attack_flavour flavour)2186 bool flavour_triggers_damageless(attack_flavour flavour)
2187 {
2188     return flavour == AF_CRUSH
2189         || flavour == AF_ENGULF
2190         || flavour == AF_PURE_FIRE
2191         || flavour == AF_SHADOWSTAB
2192         || flavour == AF_DROWN
2193         || flavour == AF_CORRODE;
2194 }
2195 
2196 /**
2197  * How much special damage does the given attack flavour do for an attack from
2198  * a monster of the given hit dice?
2199  *
2200  * Various effects (e.g. acid) currently go through more complex codepaths. :(
2201  *
2202  * @param flavour       The attack flavour in question; e.g. AF_FIRE.
2203  * @param HD            The HD to calculate damage for.
2204  * @param random        Whether to roll damage, or (if false) just return
2205  *                      the top of the range.
2206  * @return              The damage that the given attack flavour does, before
2207  *                      resists and other effects are applied.
2208  */
flavour_damage(attack_flavour flavour,int HD,bool random)2209 int flavour_damage(attack_flavour flavour, int HD, bool random)
2210 {
2211     switch (flavour)
2212     {
2213         case AF_FIRE:
2214             if (random)
2215                 return HD + random2(HD);
2216             return HD * 2;
2217         case AF_COLD:
2218             if (random)
2219                 return HD + random2(HD*2);
2220             return HD * 3;
2221         case AF_ELEC:
2222             if (random)
2223                 return HD + random2(HD/2);
2224             return HD * 3 / 2;
2225         case AF_PURE_FIRE:
2226             if (random)
2227                 return HD * 3 / 2 + random2(HD);
2228             return HD * 5 / 2;
2229         default:
2230             return 0;
2231     }
2232 }
2233 
2234 /**
2235  * Does a monster attacking with this flavour reach as if using a polearm?
2236  *
2237  * @param flavour   The attack flavour in question; e.g. AF_COLD.
2238  * @return          Whether the flavour grants inherent reach.
2239  */
flavour_has_reach(attack_flavour flavour)2240 bool flavour_has_reach(attack_flavour flavour)
2241 {
2242     switch (flavour)
2243     {
2244         case AF_REACH:
2245         case AF_REACH_STING:
2246         case AF_REACH_TONGUE:
2247             return true;
2248         default:
2249             return false;
2250     }
2251 }
2252 
mons_invuln_will(const monster & mon)2253 bool mons_invuln_will(const monster& mon)
2254 {
2255     return get_monster_data(mon.type)->willpower == WILL_INVULN;
2256 }
2257 
mons_skeleton(monster_type mc)2258 bool mons_skeleton(monster_type mc)
2259 {
2260     return !mons_class_flag(mc, M_NO_SKELETON);
2261 }
2262 
mons_zombifiable(monster_type mc)2263 bool mons_zombifiable(monster_type mc)
2264 {
2265     return !mons_class_flag(mc, M_NO_ZOMBIE) && mons_zombie_size(mc);
2266 }
2267 
mons_flattens_trees(const monster & mon)2268 bool mons_flattens_trees(const monster& mon)
2269 {
2270     return mons_base_type(mon) == MONS_LERNAEAN_HYDRA;
2271 }
2272 
mons_class_res_polar_vortex(monster_type mc)2273 bool mons_class_res_polar_vortex(monster_type mc)
2274 {
2275     return get_resist(get_mons_class_resists(mc), MR_RES_VORTEX);
2276 }
2277 
2278 /**
2279  * Given an average max HP value for a given monster type, what should a given
2280  * monster have?
2281  *
2282  * @param avg_hp    The mean hp.
2283  * @param scale     A scale that the input avg_hp are multiplied by.
2284  * @return          A max HP value; no more than +-33% from the given average,
2285  *                  and within about +-10% of the average 95% of the time.
2286  *                  This value is not multiplied by the scale - it's an actual
2287  *                  hp value, regardless of the scale on the input.
2288  *                  If avg_hp is nonzero, always returns at least 1.
2289  */
hit_points(int avg_hp,int scale)2290 int hit_points(int avg_hp, int scale)
2291 {
2292     if (!avg_hp)
2293         return 0;
2294 
2295     const int min_perc = 33;
2296     const int hp_variance = div_rand_round(avg_hp * min_perc, 100);
2297     const int min_hp = avg_hp - hp_variance;
2298     const int hp = min_hp + random2avg(hp_variance * 2, 8);
2299     return max(1, div_rand_round(hp, scale));
2300 }
2301 
2302 // This function returns the standard number of hit dice for a type of
2303 // monster, not a particular monster's current hit dice. - bwr
mons_class_hit_dice(monster_type mc)2304 int mons_class_hit_dice(monster_type mc)
2305 {
2306     const monsterentry *me = get_monster_data(mc);
2307     return me ? me->HD : 0;
2308 }
2309 
2310 /**
2311  * What base WL does a monster of the given type have?
2312  *
2313  * @param type    The monster type in question.
2314  * @param base    The base type of the monster. (For e.g. draconians.)
2315  * @return        The WL of a normal monster of that type.
2316  */
mons_class_willpower(monster_type type,monster_type base)2317 int mons_class_willpower(monster_type type, monster_type base)
2318 {
2319     const monster_type base_type =
2320         base != MONS_NO_MONSTER &&
2321         (mons_is_draconian(type) || mons_is_demonspawn(type))
2322             ? draco_or_demonspawn_subspecies(type, base)
2323             : type;
2324 
2325     const int type_wl = (get_monster_data(base_type))->willpower;
2326 
2327     // Negative values get multiplied with monster hit dice.
2328     if (type_wl >= 0)
2329         return type_wl;
2330     return mons_class_hit_dice(base_type) * -type_wl * 4 / 3;
2331 }
2332 
2333 /**
2334  * Can a monster of the given type see invisible creatures?
2335  *
2336  * @param type    The monster type in question.
2337  * @param base    The base type of the monster. (For e.g. draconians.)
2338  * @return        Whether a normal monster of that type can see invisible
2339  *                things.
2340  */
mons_class_sees_invis(monster_type type,monster_type base)2341 bool mons_class_sees_invis(monster_type type, monster_type base)
2342 {
2343     if (mons_class_flag(type, M_SEE_INVIS))
2344         return true;
2345 
2346     if (base != MONS_NO_MONSTER && mons_is_demonspawn(type) // XXX: add dracs here? latent bug otherwise
2347         && mons_class_flag(draco_or_demonspawn_subspecies(type, base),
2348                            M_SEE_INVIS))
2349     {
2350         return true;
2351     }
2352 
2353     return false;
2354 }
2355 
2356 /**
2357  * What's the average hp for a given type of monster?
2358  *
2359  * @param mc        The type of monster in question.
2360  * @return          The average hp for that monster; rounds down.
2361  */
mons_avg_hp(monster_type mc)2362 int mons_avg_hp(monster_type mc)
2363 {
2364     const monsterentry* me = get_monster_data(mc);
2365 
2366     if (!me)
2367         return 0;
2368 
2369     // Hack for nonbase demonspawn: pretend it's a basic demonspawn with
2370     // a job.
2371     if (mons_is_demonspawn(mc)
2372         && mc != MONS_DEMONSPAWN
2373         && mons_species(mc) == MONS_DEMONSPAWN)
2374     {
2375         const monsterentry* mbase = get_monster_data(MONS_DEMONSPAWN);
2376         return (mbase->avg_hp_10x + me->avg_hp_10x) / 10;
2377     }
2378 
2379     return me->avg_hp_10x / 10;
2380 }
2381 
2382 /**
2383  * What's the maximum hp for a given type of monster?
2384  *
2385  * @param mc        The type of monster in question.
2386  * @param mbase     The type of the base monster, if applicable (for classed
2387  *                  monsters).
2388  * @return          The maximum hp for that monster; rounds down.
2389  */
mons_max_hp(monster_type mc,monster_type mbase_type)2390 int mons_max_hp(monster_type mc, monster_type mbase_type)
2391 {
2392     const monsterentry* me = get_monster_data(mc);
2393 
2394     if (!me)
2395         return 0;
2396 
2397     // TODO: merge the 133% with their use in hit_points()
2398 
2399     // Hack for nonbase demonspawn: pretend it's a basic demonspawn with
2400     // a job.
2401     if (mons_is_demonspawn(mc)
2402         && mc != MONS_DEMONSPAWN
2403         && mons_species(mc) == MONS_DEMONSPAWN)
2404     {
2405         const monsterentry* mbase =
2406             get_monster_data(mbase_type != MONS_NO_MONSTER ? mbase_type
2407                                                            : MONS_DEMONSPAWN);
2408         return (mbase->avg_hp_10x + me->avg_hp_10x) * 133 / 1000;
2409     }
2410 
2411     return me->avg_hp_10x * 133 / 1000;
2412 }
2413 
exper_value(const monster & mon,bool real)2414 int exper_value(const monster& mon, bool real)
2415 {
2416     int x_val = 0;
2417 
2418     // These four are the original arguments.
2419     const monster_type mc = mon.type;
2420     int hd                = mon.get_experience_level();
2421     int maxhp             = mon.max_hit_points;
2422 
2423     // pghosts and pillusions have no reasonable base values, and you can look
2424     // up the exact value anyway. Especially for pillusions.
2425     if (real || mon.type == MONS_PLAYER_GHOST || mon.type == MONS_PLAYER_ILLUSION)
2426     {
2427         // A berserking monster is much harder, but the xp value shouldn't
2428         // depend on whether it was berserk at the moment of death.
2429         if (mon.has_ench(ENCH_BERSERK))
2430             maxhp = (maxhp * 2 + 1) / 3;
2431     }
2432     else
2433     {
2434         const monsterentry *m = get_monster_data(mons_base_type(mon));
2435         ASSERT(m);
2436 
2437         // Use real hd, zombies would use the basic species and lose
2438         // information known to the player ("orc warrior zombie"). Monsters
2439         // levelling up is visible (although it may happen off-screen), so
2440         // this is hardly ever a leak. Only Pan lords are unknown in the
2441         // general.
2442         if (m->mc == MONS_PANDEMONIUM_LORD)
2443             hd = m->HD;
2444         maxhp = mons_max_hp(mc);
2445     }
2446 
2447     // Hacks to make merged slime creatures not worth so much exp. We
2448     // will calculate the experience we would get for 1 blob, and then
2449     // just multiply it so that exp is linear with blobs merged. -cao
2450     if (mon.type == MONS_SLIME_CREATURE && mon.blob_size > 1)
2451         maxhp /= mon.blob_size;
2452 
2453     // These are some values we care about.
2454     const int speed       = mons_base_speed(mon);
2455     const int modifier    = _mons_exp_mod(mc);
2456     const int item_usage  = mons_itemuse(mon);
2457 
2458     // XXX: Shapeshifters can qualify here, even though they can't cast.
2459     const bool spellcaster = mon.has_spells();
2460 
2461     // Early out for no XP monsters.
2462     if (!mons_class_gives_xp(mc))
2463         return 0;
2464 
2465     x_val = (16 + maxhp) * hd * hd / 10;
2466 
2467     // Let's calculate a simple difficulty modifier. - bwr
2468     int diff = 0;
2469 
2470     // Let's look for big spells.
2471     if (spellcaster)
2472     {
2473         for (const mon_spell_slot &slot : mon.spells)
2474         {
2475             switch (slot.spell)
2476             {
2477             case SPELL_PARALYSE:
2478             case SPELL_SMITING:
2479             case SPELL_SUMMON_EYEBALLS:
2480             case SPELL_CALL_DOWN_DAMNATION:
2481             case SPELL_HURL_DAMNATION:
2482             case SPELL_SYMBOL_OF_TORMENT:
2483             case SPELL_GLACIATE:
2484             case SPELL_FIRE_STORM:
2485             case SPELL_SHATTER:
2486             case SPELL_CHAIN_LIGHTNING:
2487             case SPELL_POLAR_VORTEX:
2488             case SPELL_LEGENDARY_DESTRUCTION:
2489             case SPELL_SUMMON_ILLUSION:
2490             case SPELL_SPELLFORGED_SERVITOR:
2491                 diff += 25;
2492                 break;
2493 
2494             case SPELL_SUMMON_GREATER_DEMON:
2495             case SPELL_HASTE:
2496             case SPELL_BLINK_RANGE:
2497             case SPELL_PETRIFY:
2498                 diff += 20;
2499                 break;
2500 
2501             case SPELL_LIGHTNING_BOLT:
2502             case SPELL_STICKY_FLAME_RANGE:
2503             case SPELL_MINDBURST:
2504             case SPELL_BANISHMENT:
2505             case SPELL_LEHUDIBS_CRYSTAL_SPEAR:
2506             case SPELL_IRON_SHOT:
2507             case SPELL_IOOD:
2508             case SPELL_FIREBALL:
2509             case SPELL_AGONY_RANGE:
2510             case SPELL_LRD:
2511             case SPELL_DIG:
2512             case SPELL_FAKE_MARA_SUMMON:
2513                 diff += 10;
2514                 break;
2515 
2516             case SPELL_HAUNT:
2517             case SPELL_SUMMON_DRAGON:
2518             case SPELL_SUMMON_HORRIBLE_THINGS:
2519             case SPELL_PLANEREND:
2520             case SPELL_SUMMON_EMPEROR_SCORPIONS:
2521                 diff += 7;
2522                 break;
2523 
2524             default:
2525                 break;
2526             }
2527         }
2528     }
2529 
2530     // Let's look at regeneration.
2531     if (mons_class_fast_regen(mc))
2532         diff += 15;
2533 
2534     // Monsters at normal or fast speed with big melee damage.
2535     if (speed >= 10)
2536     {
2537         int max_melee = 0;
2538         for (int i = 0; i < 4; ++i)
2539             max_melee += _mons_damage(mc, i);
2540 
2541         if (max_melee > 30)
2542             diff += (max_melee / ((speed == 10) ? 2 : 1));
2543     }
2544 
2545     // Monsters who can use equipment (even if only the equipment
2546     // they are given) can be considerably enhanced because of
2547     // the way weapons work for monsters. - bwr
2548     if (item_usage >= MONUSE_STARTING_EQUIPMENT)
2549         diff += 30;
2550 
2551     // Set a reasonable range on the difficulty modifier...
2552     // Currently 70% - 200%. - bwr
2553     if (diff > 100)
2554         diff = 100;
2555     else if (diff < -30)
2556         diff = -30;
2557 
2558     // Apply difficulty.
2559     x_val *= (100 + diff);
2560     x_val /= 100;
2561 
2562     // Basic speed modification.
2563     if (speed > 0)
2564     {
2565         x_val *= speed;
2566         x_val /= 10;
2567     }
2568 
2569     // Slow monsters without spells and items often have big HD which
2570     // cause the experience value to be overly large... this tries
2571     // to reduce the inappropriate amount of XP that results. - bwr
2572     if (speed < 10 && !spellcaster && item_usage < MONUSE_STARTING_EQUIPMENT)
2573         x_val /= 2;
2574 
2575     // Apply the modifier in the monster's definition.
2576     if (modifier > 0)
2577     {
2578         x_val *= modifier;
2579         x_val /= 10;
2580     }
2581 
2582     // Scale starcursed mass exp by what percentage of the whole it represents
2583     if (mon.type == MONS_STARCURSED_MASS)
2584         x_val = (x_val * mon.blob_size) / 12;
2585 
2586     // Further reduce xp from zombies
2587     if (mons_is_zombified(mon))
2588         x_val /= 2;
2589 
2590     // Reductions for big values. - bwr
2591     if (x_val > 100)
2592         x_val = 100 + ((x_val - 100) * 3) / 4;
2593     if (x_val > 750)
2594         x_val = 750 + (x_val - 750) / 3;
2595 
2596     // Slime creature exp hack part 2: Scale exp back up by the number
2597     // of blobs merged. -cao
2598     // Has to be after the stepdown to prevent issues with 4-5 merged slime
2599     // creatures. -pf
2600     if (mon.type == MONS_SLIME_CREATURE && mon.blob_size > 1)
2601         x_val *= mon.blob_size;
2602 
2603     // Guarantee the value is within limits.
2604     if (x_val <= 0)
2605         x_val = 1;
2606 
2607     return x_val;
2608 }
2609 
_random_mons_between(monster_type min,monster_type max)2610 static monster_type _random_mons_between(monster_type min, monster_type max)
2611 {
2612     monster_type mc = MONS_PROGRAM_BUG;
2613 
2614     do // skip removed monsters
2615     {
2616         mc = static_cast<monster_type>(random_range(min, max));
2617     }
2618     while (mons_is_removed(mc));
2619 
2620     return mc;
2621 }
2622 
random_draconian_monster_species()2623 monster_type random_draconian_monster_species()
2624 {
2625     return _random_mons_between(MONS_FIRST_BASE_DRACONIAN,
2626                                 MONS_LAST_SPAWNED_DRACONIAN);
2627 }
2628 
random_draconian_job()2629 monster_type random_draconian_job()
2630 {
2631     return _random_mons_between(MONS_FIRST_NONBASE_DRACONIAN,
2632                                 MONS_LAST_NONBASE_DRACONIAN);
2633 }
2634 
random_demonspawn_monster_species()2635 monster_type random_demonspawn_monster_species()
2636 {
2637     return _random_mons_between(MONS_FIRST_BASE_DEMONSPAWN,
2638                                 MONS_LAST_BASE_DEMONSPAWN);
2639 }
2640 
random_demonspawn_job()2641 monster_type random_demonspawn_job()
2642 {
2643     return _random_mons_between(MONS_FIRST_NONBASE_DEMONSPAWN,
2644                                 MONS_LAST_NONBASE_DEMONSPAWN);
2645 }
2646 
_get_mc_spellbook(const monster_type mon_type)2647 static mon_spellbook_type _get_mc_spellbook(const monster_type mon_type)
2648 {
2649     return static_cast<mon_spellbook_type>(get_monster_data(mon_type)->sec);
2650 }
2651 
get_spellbook(const monster_info & mon)2652 mon_spellbook_type get_spellbook(const monster_info &mon)
2653 {
2654     // special case for vault monsters: if they have a custom book,
2655     // treat it as MST_GHOST
2656     if (mon.props.exists(CUSTOM_SPELLS_KEY))
2657         return MST_GHOST;
2658 
2659     return _get_mc_spellbook(mon.type);
2660 }
2661 
2662 // Get a list of unique spells from a monster's preset spellbooks
2663 // or in the case of ghosts their actual spells.
2664 // If flags is non-zero, it returns only spells that match those flags.
get_unique_spells(const monster_info & mi,mon_spell_slot_flags flags)2665 vector<mon_spell_slot> get_unique_spells(const monster_info &mi,
2666                                mon_spell_slot_flags flags)
2667 {
2668     // No entry for MST_GHOST
2669     COMPILE_CHECK(ARRAYSZ(mspell_list) == NUM_MSTYPES - 1);
2670 
2671     const mon_spellbook_type book = get_spellbook(mi);
2672 
2673     // TODO: should we build an index to speed this reverse lookup?
2674     unsigned int msidx;
2675     for (msidx = 0; msidx < ARRAYSZ(mspell_list); ++msidx)
2676         if (mspell_list[msidx].type == book)
2677             break;
2678 
2679     vector<mon_spell_slot> slots;
2680 
2681     if (mons_genus(mi.type) == MONS_DRACONIAN)
2682     {
2683         const mon_spell_slot breath =
2684             drac_breath(mi.draco_or_demonspawn_subspecies());
2685         if (breath.flags & flags && breath.spell != SPELL_NO_SPELL)
2686             slots.push_back(breath);
2687         // No other spells; quit right away.
2688         if (book == MST_NO_SPELLS)
2689             return slots;
2690     }
2691 
2692     if (book != MST_GHOST)
2693         ASSERT(msidx < ARRAYSZ(mspell_list));
2694     for (const mon_spell_slot &slot : (book == MST_GHOST
2695                                        ? mi.spells
2696                                        : mspell_list[msidx].spells))
2697     {
2698         if (flags != MON_SPELL_NO_FLAGS && !(slot.flags & flags))
2699             continue;
2700 
2701         if (none_of(slots.begin(), slots.end(),
2702             [&](const mon_spell_slot& oldslot)
2703             {
2704                 return oldslot.spell == slot.spell;
2705             }))
2706         {
2707             slots.push_back(slot);
2708         }
2709     }
2710 
2711     return slots;
2712 }
2713 
drac_breath(monster_type drac_type)2714 mon_spell_slot drac_breath(monster_type drac_type)
2715 {
2716     spell_type sp;
2717     switch (drac_type)
2718     {
2719     case MONS_BLACK_DRACONIAN:   sp = SPELL_LIGHTNING_BOLT; break;
2720     case MONS_YELLOW_DRACONIAN:  sp = SPELL_ACID_SPLASH; break;
2721     case MONS_GREEN_DRACONIAN:   sp = SPELL_POISONOUS_CLOUD; break;
2722     case MONS_PURPLE_DRACONIAN:  sp = SPELL_QUICKSILVER_BOLT; break;
2723     case MONS_RED_DRACONIAN:     sp = SPELL_SEARING_BREATH; break;
2724     case MONS_WHITE_DRACONIAN:   sp = SPELL_COLD_BREATH; break;
2725     case MONS_DRACONIAN:
2726     case MONS_GREY_DRACONIAN:    sp = SPELL_NO_SPELL; break;
2727     case MONS_PALE_DRACONIAN:    sp = SPELL_STEAM_BALL; break;
2728 
2729     default:
2730         die("Invalid draconian subrace: %d", drac_type);
2731     }
2732 
2733     mon_spell_slot slot;
2734     slot.spell = sp;
2735     slot.freq = 62;
2736     slot.flags = MON_SPELL_NATURAL | MON_SPELL_BREATH;
2737     return slot;
2738 }
2739 
mons_load_spells(monster & mon)2740 void mons_load_spells(monster& mon)
2741 {
2742     const mon_spellbook_type book = _get_mc_spellbook(mon.type);
2743 
2744     if (book == MST_GHOST)
2745         return mon.load_ghost_spells();
2746 
2747     mon.spells.clear();
2748     if (mons_genus(mon.type) == MONS_DRACONIAN)
2749     {
2750         mon_spell_slot breath = drac_breath(draco_or_demonspawn_subspecies(mon));
2751         if (breath.spell != SPELL_NO_SPELL)
2752             mon.spells.push_back(breath);
2753     }
2754 
2755     if (book == MST_NO_SPELLS)
2756         return;
2757 
2758     dprf(DIAG_MONPLACE, "%s: loading spellbook #%d",
2759          mon.name(DESC_PLAIN, true).c_str(), static_cast<int>(book));
2760 
2761     for (const mon_spellbook &spbook : mspell_list)
2762         if (spbook.type == book)
2763         {
2764             mon.spells.insert(mon.spells.end(),
2765                                 spbook.spells.begin(), spbook.spells.end());
2766             break;
2767         }
2768 }
2769 
2770 // Never hand out DARKGREY as a monster colour, even if it is randomly
2771 // chosen.
random_monster_colour()2772 colour_t random_monster_colour()
2773 {
2774     colour_t col = DARKGREY;
2775     while (col == DARKGREY)
2776         col = random_colour();
2777 
2778     return col;
2779 }
2780 
init_abomination(monster & mon,int hd)2781 bool init_abomination(monster& mon, int hd)
2782 {
2783     if (mon.type != MONS_ABOMINATION_LARGE
2784         && mon.type != MONS_ABOMINATION_SMALL)
2785     {
2786         return false;
2787     }
2788 
2789     const int max_hd = mon.type == MONS_ABOMINATION_LARGE ? 30 : 15;
2790 
2791     mon.set_hit_dice(min(max_hd, hd));
2792 
2793     const monsterentry *m = get_monster_data(mon.type);
2794     const int hp = hit_points(div_rand_round(hd * m->avg_hp_10x, m->HD));
2795 
2796     mon.max_hit_points = hp;
2797     mon.hit_points     = hp;
2798 
2799     return true;
2800 }
2801 
2802 // Generate a shiny, new and unscarred monster.
define_monster(monster & mons)2803 void define_monster(monster& mons)
2804 {
2805     monster_type mcls         = mons.type;
2806     ASSERT(!mons_class_is_zombified(mcls)); // should have called define_zombie
2807 
2808     monster_type monbase      = mons.base_monster;
2809     const monsterentry *m     = get_monster_data(mcls);
2810     int col                   = mons_class_colour(mcls);
2811     int hd                    = mons_class_hit_dice(mcls);
2812     int hp = 0;
2813 
2814     mons.mname.clear();
2815 
2816     // misc
2817     mons.god = GOD_NO_GOD;
2818 
2819     switch (mcls)
2820     {
2821     case MONS_ABOMINATION_SMALL:
2822         hd = 4 + random2(4);
2823         mons.props[MON_SPEED_KEY] = 7 + random2avg(9, 2);
2824         break;
2825 
2826     case MONS_ABOMINATION_LARGE:
2827         hd = 8 + random2(4);
2828         mons.props[MON_SPEED_KEY] = 6 + random2avg(7, 2);
2829         break;
2830 
2831     case MONS_SLIME_CREATURE:
2832         // Slime creatures start off as only single un-merged blobs.
2833         mons.blob_size = 1;
2834         break;
2835 
2836     case MONS_HYDRA:
2837         // Hydras start off with 4 to 8 heads.
2838         mons.num_heads = random_range(4, 8);
2839         break;
2840 
2841     case MONS_LERNAEAN_HYDRA:
2842         // The Lernaean hydra starts off with 27 heads.
2843         mons.num_heads = 27;
2844         break;
2845 
2846     case MONS_TIAMAT:
2847         // Initialise to a random draconian type.
2848         draconian_change_colour(&mons);
2849         monbase = mons.base_monster;
2850         col = mons.colour;
2851         break;
2852 
2853     case MONS_STARCURSED_MASS:
2854         mons.blob_size = 12;
2855         break;
2856 
2857     // Randomize starting speed burst clock
2858     case MONS_SIXFIRHY:
2859     case MONS_JIANGSHI:
2860         mons.move_spurt = random2(360);
2861         break;
2862 
2863     case MONS_SHAMBLING_MANGROVE:
2864         mons.mangrove_pests = x_chance_in_y(3, 5) ? random_range(2, 3) : 0;
2865         break;
2866 
2867     case MONS_SERPENT_OF_HELL:
2868     case MONS_SERPENT_OF_HELL_COCYTUS:
2869     case MONS_SERPENT_OF_HELL_DIS:
2870     case MONS_SERPENT_OF_HELL_TARTARUS:
2871         mons.num_heads = 3;
2872         break;
2873 
2874     default:
2875         break;
2876     }
2877 
2878     if (mons_is_draconian_job(mcls))
2879     {
2880         // Professional draconians still have a base draconian type.
2881         // White draconians will never be draconian scorchers, but
2882         // apart from that, anything goes.
2883         do
2884         {
2885             monbase = random_draconian_monster_species();
2886         }
2887         while (drac_colour_incompatible(mcls, monbase));
2888     }
2889 
2890     if (mons_is_demonspawn_job(mcls))
2891     {
2892         // Some base demonspawn have more or less HP, AC, EV than their
2893         // brethren; those should be based on the base monster,
2894         // with modifiers taken from the job.
2895 
2896         monbase = (mons.base_monster == MONS_NO_MONSTER
2897                    || mons.base_monster == MONS_PROGRAM_BUG) // zombie gen
2898                   ? random_demonspawn_monster_species()
2899                   : mons.base_monster;
2900 
2901         const monsterentry* mbase = get_monster_data(monbase);
2902         hp = hit_points(mbase->avg_hp_10x + m->avg_hp_10x);
2903     }
2904 
2905     if (col == COLOUR_UNDEF) // but never give out darkgrey to monsters
2906         col = random_monster_colour();
2907 
2908     // Some calculations.
2909     if (hp == 0)
2910         hp = hit_points(m->avg_hp_10x);
2911     const int hp_max = hp;
2912 
2913     // So let it be written, so let it be done.
2914     mons.set_hit_dice(hd);
2915     mons.hit_points      = hp;
2916     mons.max_hit_points  = hp_max;
2917     mons.speed_increment = 70;
2918 
2919     if (mons.base_monster == MONS_NO_MONSTER
2920         || mons.base_monster == MONS_PROGRAM_BUG) // latter is zombie gen
2921     {
2922         mons.base_monster = monbase;
2923     }
2924 
2925     mons.flags      = MF_NO_FLAGS;
2926     mons.experience = 0;
2927     mons.colour     = col;
2928 
2929     mons.bind_melee_flags();
2930 
2931     mons_load_spells(mons);
2932     mons.bind_spell_flags();
2933 
2934     // Reset monster enchantments.
2935     mons.enchantments.clear();
2936     mons.ench_cache.reset();
2937     mons.ench_countdown = 0;
2938 
2939     switch (mcls)
2940     {
2941     case MONS_PANDEMONIUM_LORD:
2942     {
2943         ghost_demon ghost;
2944         ghost.init_pandemonium_lord();
2945         mons.set_ghost(ghost);
2946         mons.ghost_demon_init();
2947         mons.bind_melee_flags();
2948         mons.bind_spell_flags();
2949         break;
2950     }
2951 
2952     case MONS_PLAYER_GHOST:
2953     {
2954         if (define_ghost_from_bones(mons))
2955             break;
2956         dprf("No ghosts found in bones, falling back on mirrored player");
2957         // intentional fallthrough -- fall back on mirroring the player
2958     }
2959     case MONS_PLAYER_ILLUSION:
2960     {
2961         ghost_demon ghost;
2962         ghost.init_player_ghost();
2963         if (mcls == MONS_PLAYER_GHOST)
2964         {
2965             // still don't allow undead ghosts, even mirrored
2966             if (you.undead_state(false) != US_ALIVE)
2967                 ghost.species = SP_HUMAN;
2968             mons.props[MIRRORED_GHOST_KEY] = true;
2969         }
2970         mons.set_ghost(ghost);
2971         mons.ghost_init(!mons.props.exists("fake"));
2972         break;
2973     }
2974 
2975     case MONS_UGLY_THING:
2976     case MONS_VERY_UGLY_THING:
2977     {
2978         ghost_demon ghost;
2979         ghost.init_ugly_thing(mcls == MONS_VERY_UGLY_THING);
2980         mons.set_ghost(ghost);
2981         mons.uglything_init();
2982         break;
2983     }
2984 
2985     // Load with dummy values so certain monster properties can be queried
2986     // before placement without crashing (proper setup is done later here)
2987     case MONS_DANCING_WEAPON:
2988     case MONS_SPECTRAL_WEAPON:
2989     {
2990         ghost_demon ghost;
2991         mons.set_ghost(ghost);
2992         break;
2993     }
2994 
2995     default:
2996         break;
2997     }
2998 
2999     mons.calc_speed();
3000 
3001     // When all is said and done, this monster had better have some hit
3002     // points, or it will be dead on arrival
3003     ASSERT(mons.hit_points > 0);
3004     ASSERT(mons.max_hit_points > 0);
3005 }
3006 
3007 static const char *ugly_colour_names[] =
3008 {
3009     "red", "brown", "green", "cyan", "purple", "white"
3010 };
3011 
ugly_thing_colour_name(colour_t colour)3012 string ugly_thing_colour_name(colour_t colour)
3013 {
3014     int colour_offset = ugly_thing_colour_offset(colour);
3015 
3016     if (colour_offset == -1)
3017         return "buggy";
3018 
3019     return ugly_colour_names[colour_offset];
3020 }
3021 
3022 static const colour_t ugly_colour_values[] =
3023 {
3024     RED, BROWN, GREEN, CYAN, MAGENTA, LIGHTGREY
3025 };
3026 
ugly_thing_random_colour()3027 colour_t ugly_thing_random_colour()
3028 {
3029     return RANDOM_ELEMENT(ugly_colour_values);
3030 }
3031 
str_to_ugly_thing_colour(const string & s)3032 int str_to_ugly_thing_colour(const string &s)
3033 {
3034     COMPILE_CHECK(ARRAYSZ(ugly_colour_values) == ARRAYSZ(ugly_colour_names));
3035     for (int i = 0, size = ARRAYSZ(ugly_colour_values); i < size; ++i)
3036         if (s == ugly_colour_names[i])
3037             return ugly_colour_values[i];
3038     return BLACK;
3039 }
3040 
ugly_thing_colour_offset(colour_t colour)3041 int ugly_thing_colour_offset(colour_t colour)
3042 {
3043     for (unsigned i = 0; i < ARRAYSZ(ugly_colour_values); ++i)
3044     {
3045         if (make_low_colour(colour) == ugly_colour_values[i])
3046             return i;
3047     }
3048 
3049     return -1;
3050 }
3051 
ugly_thing_apply_uniform_band_colour(mgen_data & mg,const monster_type * band_monsters,size_t num_monsters_in_band)3052 void ugly_thing_apply_uniform_band_colour(mgen_data &mg,
3053     const monster_type *band_monsters, size_t num_monsters_in_band)
3054 {
3055     // Verify that the whole band is ugly.
3056     for (size_t i = 0; i < num_monsters_in_band; i++)
3057     {
3058         if (!(band_monsters[i] == MONS_UGLY_THING
3059             || band_monsters[i] == MONS_VERY_UGLY_THING))
3060         {
3061             return;
3062         }
3063     }
3064 
3065     // Apply a uniform colour to a fully-ugly band.
3066     if (ugly_thing_colour_offset(mg.colour) == -1)
3067         mg.colour = ugly_thing_random_colour();
3068 }
3069 
3070 static const char *drac_colour_names[] =
3071 {
3072     "black",
3073 #if TAG_MAJOR_VERSION == 34
3074     "",
3075 #endif
3076     "yellow", "green", "purple", "red", "white", "grey", "pale"
3077 };
3078 
draconian_colour_name(monster_type mon_type)3079 string draconian_colour_name(monster_type mon_type)
3080 {
3081     COMPILE_CHECK(ARRAYSZ(drac_colour_names) ==
3082                   MONS_LAST_BASE_DRACONIAN - MONS_DRACONIAN);
3083 
3084     if (!mons_is_base_draconian(mon_type) || mon_type == MONS_DRACONIAN)
3085         return "buggy";
3086 
3087     return drac_colour_names[mon_type - MONS_FIRST_BASE_DRACONIAN];
3088 }
3089 
draconian_colour_by_name(const string & name)3090 monster_type draconian_colour_by_name(const string &name)
3091 {
3092     COMPILE_CHECK(ARRAYSZ(drac_colour_names)
3093                   == (MONS_LAST_BASE_DRACONIAN - MONS_DRACONIAN));
3094 
3095     for (unsigned i = 0; i < ARRAYSZ(drac_colour_names); ++i)
3096     {
3097         if (name == drac_colour_names[i])
3098             return static_cast<monster_type>(i + MONS_FIRST_BASE_DRACONIAN);
3099     }
3100 
3101     return MONS_PROGRAM_BUG;
3102 }
3103 
3104 // TODO: Remove "putrid" when TAG_MAJOR_VERSION > 34
3105 static const char *demonspawn_base_names[] =
3106 {
3107     "monstrous", "gelid", "infernal",
3108 #if TAG_MAJOR_VERSION == 34
3109     "putrid",
3110 #endif
3111     "torturous",
3112 };
3113 
demonspawn_base_name(monster_type mon_type)3114 string demonspawn_base_name(monster_type mon_type)
3115 {
3116     COMPILE_CHECK(ARRAYSZ(demonspawn_base_names) ==
3117                   MONS_LAST_BASE_DEMONSPAWN - MONS_FIRST_BASE_DEMONSPAWN + 1);
3118 
3119     if (mon_type < MONS_FIRST_BASE_DEMONSPAWN
3120         || mon_type > MONS_LAST_BASE_DEMONSPAWN)
3121     {
3122         return "buggy";
3123     }
3124 
3125     return demonspawn_base_names[mon_type - MONS_FIRST_BASE_DEMONSPAWN];
3126 }
3127 
demonspawn_base_by_name(const string & name)3128 monster_type demonspawn_base_by_name(const string &name)
3129 {
3130     COMPILE_CHECK(ARRAYSZ(demonspawn_base_names) ==
3131                   MONS_LAST_BASE_DEMONSPAWN - MONS_FIRST_BASE_DEMONSPAWN + 1);
3132 
3133     for (unsigned i = 0; i < ARRAYSZ(demonspawn_base_names); ++i)
3134     {
3135         if (name == demonspawn_base_names[i])
3136             return static_cast<monster_type>(i + MONS_FIRST_BASE_DEMONSPAWN);
3137     }
3138 
3139     return MONS_PROGRAM_BUG;
3140 }
3141 
mons_type_name(monster_type mc,description_level_type desc)3142 string mons_type_name(monster_type mc, description_level_type desc)
3143 {
3144     string result;
3145 
3146     if (!mons_is_unique(mc))
3147     {
3148         switch (desc)
3149         {
3150         case DESC_THE:       result = "the "; break;
3151         case DESC_A:         result = "a ";   break;
3152         case DESC_PLAIN: default:             break;
3153         }
3154     }
3155 
3156     switch (mc)
3157     {
3158     case RANDOM_MONSTER:
3159         result += "random monster";
3160         return result;
3161     case RANDOM_DRACONIAN:
3162         result += "random draconian";
3163         return result;
3164     case RANDOM_BASE_DRACONIAN:
3165         result += "random base draconian";
3166         return result;
3167     case RANDOM_NONBASE_DRACONIAN:
3168         result += "random nonbase draconian";
3169         return result;
3170     case RANDOM_DEMONSPAWN:
3171         result += "random demonspawn";
3172         return result;
3173     case RANDOM_BASE_DEMONSPAWN:
3174         result += "random base demonspawn";
3175         return result;
3176     case RANDOM_NONBASE_DEMONSPAWN:
3177         result += "random nonbase demonspawn";
3178         return result;
3179     case WANDERING_MONSTER:
3180         result += "wandering monster";
3181         return result;
3182     default: ;
3183     }
3184 
3185     const monsterentry *me = get_monster_data(mc);
3186     if (me == nullptr)
3187     {
3188         result += make_stringf("invalid monster_type %d", mc);
3189         return result;
3190     }
3191 
3192     result += me->name;
3193 
3194     // Vowel fix: Change 'a orc' to 'an orc'..
3195     if (result.length() >= 3
3196         && (result[0] == 'a' || result[0] == 'A')
3197         && result[1] == ' '
3198         && is_vowel(result[2])
3199         // XXX: Hack
3200         && !starts_with(&result[2], "one-"))
3201     {
3202         result.insert(1, "n");
3203     }
3204 
3205     return result;
3206 }
3207 
_get_proper_monster_name(const monster & mon)3208 static string _get_proper_monster_name(const monster& mon)
3209 {
3210     const monsterentry *me = mon.find_monsterentry();
3211     if (!me)
3212         return "";
3213 
3214     string name = getRandNameString(me->name, " name");
3215     if (!name.empty())
3216         return name;
3217 
3218     return getRandNameString(get_monster_data(mons_genus(mon.type))->name,
3219                              " name");
3220 }
3221 
3222 // Names a previously unnamed monster.
give_monster_proper_name(monster & mon,bool orcs_only)3223 bool give_monster_proper_name(monster& mon, bool orcs_only)
3224 {
3225     // Already has a unique name.
3226     if (mon.is_named())
3227         return false;
3228 
3229     // Since this is called from the various divine blessing routines,
3230     // don't bless non-orcs, and normally don't bless plain orcs, either.
3231     if (orcs_only)
3232     {
3233         if (mons_genus(mon.type) != MONS_ORC
3234             || mon.type == MONS_ORC && !one_chance_in(8))
3235         {
3236             return false;
3237         }
3238     }
3239 
3240     mon.mname = _get_proper_monster_name(mon);
3241     if (!mon.props.exists("dbname"))
3242         mon.props["dbname"] = mons_class_name(mon.type);
3243 
3244     if (mon.friendly())
3245         take_note(Note(NOTE_NAMED_ALLY, 0, 0, mon.mname));
3246 
3247     return mon.is_named();
3248 }
3249 
3250 // See mons_init for initialization of mon_entry array.
get_monster_data(monster_type mc)3251 monsterentry *get_monster_data(monster_type mc)
3252 {
3253     if (mc >= 0 && mc < NUM_MONSTERS)
3254         return &mondata[mon_entry[mc]];
3255     else
3256         return nullptr;
3257 }
3258 
_mons_exp_mod(monster_type mc)3259 static int _mons_exp_mod(monster_type mc)
3260 {
3261     ASSERT_smc();
3262     return smc->exp_mod;
3263 }
3264 
mons_class_base_speed(monster_type mc)3265 int mons_class_base_speed(monster_type mc)
3266 {
3267     ASSERT_smc();
3268     return smc->speed;
3269 }
3270 
mons_class_energy(monster_type mc)3271 mon_energy_usage mons_class_energy(monster_type mc)
3272 {
3273     ASSERT_smc();
3274     return smc->energy_usage;
3275 }
3276 
mons_energy(const monster & mon)3277 mon_energy_usage mons_energy(const monster& mon)
3278 {
3279     mon_energy_usage meu = mons_class_energy(mons_base_type(mon));
3280     if (mon.ghost)
3281         meu.move = meu.swim = mon.ghost->move_energy;
3282     return meu;
3283 }
3284 
mons_class_zombie_base_speed(monster_type zombie_base_mc)3285 int mons_class_zombie_base_speed(monster_type zombie_base_mc)
3286 {
3287     return max(3, mons_class_base_speed(zombie_base_mc) - 2);
3288 }
3289 
3290 /**
3291  * What's this monster's base speed, before temporary effects are applied?
3292  *
3293  * @param mon       The monster in question.
3294  * @param known     Whether to include only information the player knows about,
3295  *                  i.e. not the speed of certain monsters with varying speeds
3296  *                  (abominations, hell beasts)
3297  * @return          The speed of the monster.
3298  */
mons_base_speed(const monster & mon,bool known)3299 int mons_base_speed(const monster& mon, bool known)
3300 {
3301     if (mon.ghost)
3302         return mon.ghost->speed;
3303 
3304     if (mon.props.exists(MON_SPEED_KEY)
3305         && (!known || mon.type == MONS_MUTANT_BEAST))
3306     {
3307         return mon.props[MON_SPEED_KEY];
3308     }
3309 
3310     if (mon.type == MONS_SPECTRAL_THING)
3311         return mons_class_base_speed(mons_zombie_base(mon));
3312 
3313     return mons_is_zombified(mon) ? mons_class_zombie_base_speed(mons_zombie_base(mon))
3314                                   : mons_class_base_speed(mon.type);
3315 }
3316 
mons_class_intel(monster_type mc)3317 mon_intel_type mons_class_intel(monster_type mc)
3318 {
3319     ASSERT_smc();
3320     return smc->intel;
3321 }
3322 
mons_intel(const monster & m)3323 mon_intel_type mons_intel(const monster& m)
3324 {
3325     const monster& mon = get_tentacle_head(m);
3326 
3327     if (mons_enslaved_soul(mon))
3328         return mons_class_intel(mons_zombie_base(mon));
3329 
3330     return mons_class_intel(mon.type);
3331 }
3332 
_mons_class_habitat(monster_type mc,bool real_amphibious=false)3333 static habitat_type _mons_class_habitat(monster_type mc,
3334                                         bool real_amphibious = false)
3335 {
3336     const monsterentry *me = get_monster_data(mc);
3337     habitat_type ht = (me ? me->habitat
3338                           : get_monster_data(MONS_PROGRAM_BUG)->habitat);
3339     if (!real_amphibious)
3340     {
3341         // XXX: No class equivalent of monster::body_size(PSIZE_BODY)!
3342         size_type st = (me ? me->size
3343                            : get_monster_data(MONS_PROGRAM_BUG)->size);
3344         if (ht == HT_LAND && st >= SIZE_GIANT)
3345             ht = HT_AMPHIBIOUS;
3346     }
3347     return ht;
3348 }
3349 
mons_habitat_type(monster_type t,monster_type base_t,bool real_amphibious)3350 habitat_type mons_habitat_type(monster_type t, monster_type base_t,
3351                                bool real_amphibious)
3352 {
3353     return _mons_class_habitat(fixup_zombie_type(t, base_t),
3354                                real_amphibious);
3355 }
3356 
mons_habitat(const monster & mon,bool real_amphibious)3357 habitat_type mons_habitat(const monster& mon, bool real_amphibious)
3358 {
3359     const monster_type type = mons_is_job(mon.type)
3360         ? draco_or_demonspawn_subspecies(mon) : mon.type;
3361 
3362     return mons_habitat_type(type, mons_base_type(mon), real_amphibious);
3363 }
3364 
mons_class_primary_habitat(monster_type mc)3365 habitat_type mons_class_primary_habitat(monster_type mc)
3366 {
3367     habitat_type ht = _mons_class_habitat(mc);
3368     if (ht == HT_AMPHIBIOUS || ht == HT_AMPHIBIOUS_LAVA)
3369         ht = HT_LAND;
3370     return ht;
3371 }
3372 
mons_primary_habitat(const monster & mon)3373 habitat_type mons_primary_habitat(const monster& mon)
3374 {
3375     const monster_type type = mons_is_job(mon.type)
3376         ? draco_or_demonspawn_subspecies(mon) : mons_base_type(mon);
3377 
3378     return mons_class_primary_habitat(type);
3379 }
3380 
mons_class_secondary_habitat(monster_type mc)3381 habitat_type mons_class_secondary_habitat(monster_type mc)
3382 {
3383     habitat_type ht = _mons_class_habitat(mc);
3384     if (ht == HT_AMPHIBIOUS)
3385         ht = HT_WATER;
3386     if (ht == HT_AMPHIBIOUS_LAVA)
3387         ht = HT_LAVA;
3388     return ht;
3389 }
3390 
mons_secondary_habitat(const monster & mon)3391 habitat_type mons_secondary_habitat(const monster& mon)
3392 {
3393     return mons_class_secondary_habitat(mons_base_type(mon));
3394 }
3395 
intelligent_ally(const monster & mon)3396 bool intelligent_ally(const monster& mon)
3397 {
3398     return mon.attitude == ATT_FRIENDLY && mons_intel(mon) >= I_HUMAN;
3399 }
3400 
mons_power(monster_type mc)3401 int mons_power(monster_type mc)
3402 {
3403     // For now, just return monster hit dice.
3404     ASSERT_smc();
3405     return mons_class_hit_dice(mc);
3406 }
3407 
3408 /// Are two actors 'aligned'? (Will they refuse to attack each-other?)
mons_aligned(const actor * m1,const actor * m2)3409 bool mons_aligned(const actor *m1, const actor *m2)
3410 {
3411     if (!m1 || !m2)
3412         return true;
3413 
3414     if (mons_is_projectile(m1->type) || mons_is_projectile(m2->type))
3415         return true; // they won't directly attack each-other, anyway
3416 
3417     return mons_atts_aligned(m1->temp_attitude(), m2->temp_attitude());
3418 }
3419 
mons_atts_aligned(mon_attitude_type fr1,mon_attitude_type fr2)3420 bool mons_atts_aligned(mon_attitude_type fr1, mon_attitude_type fr2)
3421 {
3422     if (mons_att_wont_attack(fr1) && mons_att_wont_attack(fr2))
3423         return true;
3424 
3425     return fr1 == fr2;
3426 }
3427 
mons_class_wields_two_weapons(monster_type mc)3428 bool mons_class_wields_two_weapons(monster_type mc)
3429 {
3430     return mons_class_flag(mc, M_TWO_WEAPONS);
3431 }
3432 
mons_wields_two_weapons(const monster & mon)3433 bool mons_wields_two_weapons(const monster& mon)
3434 {
3435     if (testbits(mon.flags, MF_TWO_WEAPONS))
3436         return true;
3437 
3438     return mons_class_wields_two_weapons(mons_base_type(mon));
3439 }
3440 
3441 // When this monster reaches its target, does it do impact damage
3442 // and then cease to exist?
mons_destroyed_on_impact(const monster & m)3443 bool mons_destroyed_on_impact(const monster& m)
3444 {
3445     return mons_is_projectile(m) || m.type == MONS_FOXFIRE;
3446 }
3447 
3448 // When this monster reaches its target, does it explode and then
3449 // cease to exist?
mons_blows_up(const monster & m)3450 bool mons_blows_up(const monster& m)
3451 {
3452     return mon_explodes_on_death(m.type) && m.type != MONS_BENNU;
3453 }
3454 
3455 // When this monster reaches its target, does it cease to exist?
mons_self_destructs(const monster & m)3456 bool mons_self_destructs(const monster& m)
3457 {
3458     return mons_blows_up(m) || mons_destroyed_on_impact(m);
3459 }
3460 
mons_att_wont_attack(mon_attitude_type fr)3461 bool mons_att_wont_attack(mon_attitude_type fr)
3462 {
3463     return fr == ATT_FRIENDLY || fr == ATT_GOOD_NEUTRAL
3464            || fr == ATT_STRICT_NEUTRAL;
3465 }
3466 
mons_attitude(const monster & m)3467 mon_attitude_type mons_attitude(const monster& m)
3468 {
3469     return m.temp_attitude();
3470 }
3471 
mons_is_confused(const monster & m,bool class_too)3472 bool mons_is_confused(const monster& m, bool class_too)
3473 {
3474     return (m.has_ench(ENCH_CONFUSION) || m.has_ench(ENCH_MAD))
3475            && (class_too || !mons_class_flag(m.type, M_CONFUSED));
3476 }
3477 
mons_is_wandering(const monster & m)3478 bool mons_is_wandering(const monster& m)
3479 {
3480     return m.behaviour == BEH_WANDER;
3481 }
3482 
mons_is_seeking(const monster & m)3483 bool mons_is_seeking(const monster& m)
3484 {
3485     return m.behaviour == BEH_SEEK;
3486 }
3487 
mons_is_unbreathing(monster_type mc)3488 bool mons_is_unbreathing(monster_type mc)
3489 {
3490     return bool(mons_class_holiness(mc) & (MH_UNDEAD | MH_NONLIVING
3491                                            | MH_PLANT));
3492 }
3493 
3494 // Either running in fear, or trapped and unable to do so (but still wishing to)
mons_is_fleeing(const monster & m)3495 bool mons_is_fleeing(const monster& m)
3496 {
3497     return m.behaviour == BEH_FLEE || mons_is_cornered(m);
3498 }
3499 
3500 // Can be either an orderly withdrawal (from which the monster can stop at will)
3501 // or running in fear (from which they cannot)
mons_is_retreating(const monster & m)3502 bool mons_is_retreating(const monster& m)
3503 {
3504     return m.behaviour == BEH_RETREAT || mons_is_fleeing(m);
3505 }
3506 
mons_is_cornered(const monster & m)3507 bool mons_is_cornered(const monster& m)
3508 {
3509     return m.behaviour == BEH_CORNERED;
3510 }
3511 
mons_is_influenced_by_sanctuary(const monster & m)3512 bool mons_is_influenced_by_sanctuary(const monster& m)
3513 {
3514     return !m.wont_attack() && !m.is_stationary();
3515 }
3516 
mons_is_fleeing_sanctuary(const monster & m)3517 bool mons_is_fleeing_sanctuary(const monster& m)
3518 {
3519     return sanctuary_exists()
3520            && (m.flags & MF_FLEEING_FROM_SANCTUARY)
3521            && mons_is_influenced_by_sanctuary(m);
3522 }
3523 
mons_just_slept(const monster & m)3524 bool mons_just_slept(const monster& m)
3525 {
3526     return bool(m.flags & MF_JUST_SLEPT);
3527 }
3528 
3529 // Moving body parts, turning oklob flowers and so on counts as motile here.
3530 // So does preparing resurrect, struggling against a net, etc.
mons_is_immotile(const monster & mons)3531 bool mons_is_immotile(const monster& mons)
3532 {
3533     return mons_is_firewood(mons)
3534         || mons.petrified()
3535         || mons.asleep()
3536         || mons.paralysed();
3537 }
3538 
mons_is_batty(const monster & m)3539 bool mons_is_batty(const monster& m)
3540 {
3541     return mons_class_flag(m.type, M_BATTY) || m.has_facet(BF_BAT);
3542 }
3543 
mons_is_removed(monster_type mc)3544 bool mons_is_removed(monster_type mc)
3545 {
3546     return mc != MONS_PROGRAM_BUG && mons_species(mc) == MONS_PROGRAM_BUG;
3547 }
3548 
mons_looks_stabbable(const monster & m)3549 bool mons_looks_stabbable(const monster& m)
3550 {
3551     const stab_type st = find_stab_type(&you, m, false);
3552     return stab_bonus_denom(st) == 1; // top-tier stab
3553 }
3554 
mons_looks_distracted(const monster & m)3555 bool mons_looks_distracted(const monster& m)
3556 {
3557     const stab_type st = find_stab_type(&you, m, false);
3558     return !m.friendly()
3559            && st != STAB_NO_STAB
3560            && !mons_looks_stabbable(m);
3561 }
3562 
mons_start_fleeing_from_sanctuary(monster & mons)3563 void mons_start_fleeing_from_sanctuary(monster& mons)
3564 {
3565     mons.flags |= MF_FLEEING_FROM_SANCTUARY;
3566     mons.target = env.sanctuary_pos;
3567     behaviour_event(&mons, ME_SCARE, 0, env.sanctuary_pos);
3568 }
3569 
mons_stop_fleeing_from_sanctuary(monster & mons)3570 void mons_stop_fleeing_from_sanctuary(monster& mons)
3571 {
3572     const bool had_flag {mons.flags & MF_FLEEING_FROM_SANCTUARY};
3573     mons.flags &= (~MF_FLEEING_FROM_SANCTUARY);
3574     if (had_flag)
3575         behaviour_event(&mons, ME_EVAL, &you);
3576 }
3577 
mons_pacify(monster & mon,mon_attitude_type att,bool no_xp)3578 void mons_pacify(monster& mon, mon_attitude_type att, bool no_xp)
3579 {
3580     // If the _real_ (non-charmed) attitude is already that or better,
3581     // don't degrade it.
3582     if (mon.attitude >= att)
3583         return;
3584 
3585     // Must be done before attitude change, so that proper targets are affected
3586     if (mon.type == MONS_FLAYED_GHOST)
3587         end_flayed_effect(&mon);
3588 
3589     // Make the monster permanently neutral.
3590     mon.attitude = att;
3591     mon.flags |= MF_WAS_NEUTRAL;
3592 
3593     if (!testbits(mon.flags, MF_PACIFIED) // Don't allow repeatedly pacifying.
3594         && !no_xp
3595         && !mon.is_summoned()
3596         && !testbits(mon.flags, MF_NO_REWARD))
3597     {
3598         // Give the player half of the monster's XP.
3599         gain_exp((exper_value(mon) + 1) / 2);
3600     }
3601     mon.flags |= MF_PACIFIED;
3602 
3603     if (mon.type == MONS_GERYON)
3604     {
3605         simple_monster_message(mon,
3606             make_stringf(" discards %s horn.",
3607                          mon.pronoun(PRONOUN_POSSESSIVE).c_str()).c_str());
3608         monster_drop_things(&mon, false, item_is_horn_of_geryon);
3609     }
3610 
3611     // End constriction.
3612     mon.stop_constricting_all();
3613     mon.stop_being_constricted();
3614 
3615     // Cancel fleeing and such.
3616     mon.behaviour = BEH_WANDER;
3617 
3618     // Remove haunting, which would otherwise cause monster to continue attacking
3619     mon.del_ench(ENCH_HAUNTING, true, true);
3620 
3621     // Remove level annotation.
3622     mon.props["no_annotate"] = true;
3623     remove_unique_annotation(&mon);
3624 
3625     // Make the monster begin leaving the level.
3626     behaviour_event(&mon, ME_EVAL);
3627 
3628     if (mons_is_mons_class(&mon, MONS_PIKEL))
3629         pikel_band_neutralise();
3630     if (mons_is_elven_twin(&mon))
3631         elven_twins_pacify(&mon);
3632     if (mons_is_mons_class(&mon, MONS_KIRKE))
3633         hogs_to_humans();
3634     if (mon.type == MONS_VAULT_WARDEN)
3635         timeout_terrain_changes(0, true);
3636 
3637     mons_att_changed(&mon);
3638 }
3639 
_mons_should_fire_beneficial(bolt & beam)3640 static bool _mons_should_fire_beneficial(bolt &beam)
3641 {
3642     // Should monster heal other, haste other or might other be able to
3643     // target the player? Saying no for now. -cao
3644     if (beam.target == you.pos())
3645         return false;
3646 
3647     // Assuming all beneficial beams are enchantments if a foe is in
3648     // the path the beam will definitely hit them so we shouldn't fire
3649     // in that case.
3650     if (beam.friend_info.count == 0
3651         || beam.foe_info.count != 0)
3652     {
3653         return false;
3654     }
3655 
3656     // Should beneficial monster enchantment beams be allowed in a
3657     // sanctuary? -cao
3658     if (is_sanctuary(you.pos()) || is_sanctuary(beam.source))
3659         return false;
3660 
3661     return true;
3662 }
3663 
_beneficial_beam_flavour(beam_type flavour)3664 static bool _beneficial_beam_flavour(beam_type flavour)
3665 {
3666     switch (flavour)
3667     {
3668     case BEAM_HASTE:
3669     case BEAM_HEALING:
3670     case BEAM_INVISIBILITY:
3671     case BEAM_MIGHT:
3672     case BEAM_AGILITY:
3673     case BEAM_RESISTANCE:
3674     case BEAM_CONCENTRATE_VENOM:
3675         return true;
3676 
3677     default:
3678         return false;
3679     }
3680 }
3681 
mons_should_fire(bolt & beam,bool ignore_good_idea)3682 bool mons_should_fire(bolt &beam, bool ignore_good_idea)
3683 {
3684     dprf("tracer: foes %d (pow: %d), friends %d (pow: %d), "
3685          "foe_ratio: %d",
3686          beam.foe_info.count, beam.foe_info.power,
3687          beam.friend_info.count, beam.friend_info.power,
3688          beam.foe_ratio);
3689 
3690     // Use different evaluation criteria if the beam is a beneficial
3691     // enchantment (haste other).
3692     if (_beneficial_beam_flavour(beam.flavour))
3693         return _mons_should_fire_beneficial(beam);
3694 
3695     if (is_sanctuary(you.pos()) || is_sanctuary(beam.source))
3696         return false;
3697 
3698     if (ignore_good_idea)
3699         return true;
3700     // After this point, safety/self-interest checks only.
3701 
3702 
3703     // Friendly monsters shouldn't be targeting you: this will happen
3704     // often because the default behaviour for charmed monsters is to
3705     // have you as a target. While foe_ratio will handle this, we
3706     // don't want a situation where a friendly dragon breathes through
3707     // you to hit other creatures... it should target the other
3708     // creatures, and coincidentally hit you.
3709     //
3710     // FIXME: this can cause problems with reflection, bounces, etc.
3711     // It would be better to have the monster fire logic never reach
3712     // this point for friendlies.
3713     if (monster_by_mid(beam.source_id))
3714     {
3715         monster* m = monster_by_mid(beam.source_id);
3716         if (m->alive() && m->friendly() && beam.target == you.pos())
3717             return false;
3718     }
3719 
3720     // Use of foeRatio:
3721     // The higher this number, the more monsters will _avoid_ collateral
3722     // damage to their friends.
3723     // Setting this to zero will in fact have all monsters ignore their
3724     // friends when considering collateral damage.
3725 
3726     // Quick check - did we in fact get any foes?
3727     if (beam.foe_info.count == 0)
3728         return false;
3729 
3730     // If we hit no friends, fire away.
3731     if (beam.friend_info.count == 0)
3732         return true;
3733 
3734     // Only fire if they do acceptably low collateral damage.
3735     return beam.foe_info.power >=
3736            div_round_up(beam.foe_ratio *
3737                         (beam.foe_info.power + beam.friend_info.power),
3738                         100);
3739 }
3740 
3741 /**
3742  * Can monsters use the given spell effectively from range? (If a monster has
3743  * the given spell, should it try to keep its distance from its enemies?)
3744  *
3745  * @param monspell      The spell in question.
3746  * @param attack_only   Whether to only count spells which directly harm
3747  *                      enemies (damage or stat drain). Overrides ench_too.
3748  * @param ench_too      Whether to count temporary debilitating effects (Slow,
3749  *                      etc).
3750  * @return              Whether the given spell should be considered 'ranged'.
3751  */
_ms_ranged_spell(spell_type monspell,bool attack_only=false,bool ench_too=true)3752 static bool _ms_ranged_spell(spell_type monspell, bool attack_only = false,
3753                              bool ench_too = true)
3754 {
3755     // summoning spells are usable from ranged, but not direct attacks.
3756     if (spell_typematch(monspell, spschool::summoning)
3757         || monspell == SPELL_CONJURE_BALL_LIGHTNING)
3758     {
3759         return !attack_only;
3760     }
3761 
3762     const spell_flags flags = get_spell_flags(monspell);
3763 
3764     // buffs & escape spells aren't considered 'ranged'.
3765     if (testbits(flags, spflag::selfench)
3766         || testbits(flags, spflag::escape)
3767         || monspell == SPELL_BLINK_OTHER_CLOSE)
3768     {
3769         return false;
3770     }
3771 
3772     // conjurations are attacks.
3773     if (spell_typematch(monspell, spschool::conjuration))
3774         return true;
3775 
3776     // hexes that aren't conjurations or summons are enchantments.
3777     if (spell_typematch(monspell, spschool::hexes))
3778         return !attack_only && ench_too;
3779 
3780     switch (monspell)
3781     {
3782     case SPELL_NO_SPELL:
3783     case SPELL_CANTRIP:
3784     case SPELL_BLINK_CLOSE:
3785     case SPELL_VAMPIRIC_DRAINING:
3786         return false;
3787 
3788     default:
3789         // Everything else is probably some kind of attack, hopefully.
3790         return true;
3791     }
3792 }
3793 
3794 // Returns true if the monster has an ability that can affect the target
3795 // anywhere in LOS_DEFAULT; i.e., even through glass.
mons_has_los_ability(monster_type mon_type)3796 bool mons_has_los_ability(monster_type mon_type)
3797 {
3798     return mons_is_siren_beholder(mon_type)
3799            || mon_type == MONS_STARCURSED_MASS;
3800 }
3801 
mons_has_ranged_spell(const monster & mon,bool attack_only,bool ench_too)3802 bool mons_has_ranged_spell(const monster& mon, bool attack_only,
3803                            bool ench_too)
3804 {
3805     // Monsters may have spell-like abilities.
3806     if (mons_has_los_ability(mon.type))
3807         return true;
3808 
3809     for (const mon_spell_slot &slot : mon.spells)
3810         if (_ms_ranged_spell(slot.spell, attack_only, ench_too))
3811             return true;
3812 
3813     return false;
3814 }
3815 
3816 // Returns whether the monster has a spell which is theoretically capable of
3817 // causing an incapacitation state in the target foe (ie: it can cast sleep
3818 // and the foe is not immune to being put to sleep)
3819 //
3820 // Note that this only current checks for inherent obvious immunity (ie: sleep
3821 // immunity from being undead) and not immunity that might be granted by gear
3822 // (such as clarity or stasis)
mons_has_incapacitating_spell(const monster & mon,const actor & foe)3823 bool mons_has_incapacitating_spell(const monster& mon, const actor& foe)
3824 {
3825     for (const mon_spell_slot &slot : mon.spells)
3826     {
3827         switch (slot.spell)
3828         {
3829         case SPELL_SLEEP:
3830             if (foe.can_sleep())
3831                 return true;
3832             break;
3833 
3834         case SPELL_HIBERNATION:
3835             if (foe.can_hibernate(false, true))
3836                 return true;
3837             break;
3838 
3839         case SPELL_CONFUSE:
3840         case SPELL_MASS_CONFUSION:
3841         case SPELL_PARALYSE:
3842             return true;
3843 
3844         case SPELL_PETRIFY:
3845             if (foe.res_petrify())
3846                 return true;
3847             break;
3848 
3849         default:
3850             break;
3851         }
3852     }
3853 
3854     return false;
3855 }
3856 
_mons_has_usable_ranged_weapon(const monster * mon)3857 static bool _mons_has_usable_ranged_weapon(const monster* mon)
3858 {
3859     // Ugh.
3860     const item_def *weapon  = mon->launcher();
3861     const item_def *primary = mon->mslot_item(MSLOT_WEAPON);
3862     const item_def *missile = mon->missiles();
3863 
3864     // We don't have a usable ranged weapon if a different cursed weapon
3865     // is presently equipped.
3866     if (weapon != primary && primary && primary->cursed())
3867         return false;
3868 
3869     if (!missile)
3870         return false;
3871 
3872     return is_launched(mon, weapon, *missile) != launch_retval::FUMBLED;
3873 }
3874 
_mons_has_attack_wand(const monster & mon)3875 static bool _mons_has_attack_wand(const monster& mon)
3876 {
3877     const item_def *wand = mon.mslot_item(MSLOT_WAND);
3878 
3879     return wand && is_offensive_wand(*wand);
3880 }
3881 
mons_has_ranged_attack(const monster & mon)3882 bool mons_has_ranged_attack(const monster& mon)
3883 {
3884     return mons_has_ranged_spell(mon, true)
3885            || _mons_has_usable_ranged_weapon(&mon)
3886            || mon.reach_range() != REACH_NONE
3887            || _mons_has_attack_wand(mon);
3888 }
3889 
mons_has_incapacitating_ranged_attack(const monster & mon,const actor & foe)3890 bool mons_has_incapacitating_ranged_attack(const monster& mon, const actor& foe)
3891 {
3892     if (!_mons_has_usable_ranged_weapon(&mon))
3893         return false;
3894 
3895     const item_def *missile = mon.missiles();
3896 
3897     if (missile && missile->sub_type == MI_THROWING_NET)
3898         return true;
3899     else if (missile && missile->sub_type == MI_DART)
3900     {
3901         switch (get_ammo_brand(*missile))
3902         {
3903         // Not actually incapacitating, but marked as such so that
3904         // assassins will prefer using it while ammo remains
3905         case SPMSL_CURARE:
3906             if (foe.res_poison() <= 0)
3907                 return true;
3908             break;
3909 
3910         case SPMSL_BLINDING:
3911 #if TAG_MAJOR_VERSION == 34
3912         case SPMSL_CONFUSION:
3913         case SPMSL_PARALYSIS:
3914 #endif
3915             return true;
3916 
3917         default:
3918             break;
3919         }
3920     }
3921 
3922     return false;
3923 }
3924 
mons_can_attack(const monster & mon)3925 bool mons_can_attack(const monster& mon)
3926 {
3927     const actor* foe = mon.get_foe();
3928     if (!foe || !mon.can_see(*foe))
3929         return false;
3930 
3931     if (mons_has_ranged_attack(mon) && mon.see_cell_no_trans(foe->pos()))
3932         return true;
3933 
3934     return adjacent(mon.pos(), foe->pos());
3935 }
3936 
3937 /**
3938  * What gender are monsters of the given class?
3939  *
3940  * Used for pronoun selection.
3941  *
3942  * @param mc        The type of monster in question
3943  * @return          GENDER_NEUTER, _NEUTRAL, _FEMALE, or _MALE.
3944  */
mons_class_gender(monster_type mc)3945 gender_type mons_class_gender(monster_type mc)
3946 {
3947     const bool female = mons_class_flag(mc, M_FEMALE);
3948     const bool male = mons_class_flag(mc, M_MALE);
3949     const bool neutral = mons_class_flag(mc, M_GENDER_NEUTRAL);
3950     ASSERT(male + female + neutral <= 1);
3951     return male ? GENDER_MALE :
3952          female ? GENDER_FEMALE :
3953         neutral ? GENDER_NEUTRAL :
3954                   GENDER_NEUTER;
3955 }
3956 
3957 // Use of variant (upper-/lowercase is irrelevant here):
3958 // PRONOUN_SUBJECTIVE : _She_ is tap dancing.
3959 // PRONOUN_POSSESSIVE : _Its_ sword explodes!
3960 // PRONOUN_REFLEXIVE  : The wizard mumbles to _herself_.
3961 // PRONOUN_OBJECTIVE  : You miss _him_.
mons_pronoun(monster_type mon_type,pronoun_type variant,bool visible)3962 const char *mons_pronoun(monster_type mon_type, pronoun_type variant,
3963                          bool visible)
3964 {
3965     const gender_type gender = !visible ? GENDER_NEUTER
3966                                         : mons_class_gender(mon_type);
3967     return decline_pronoun(gender, variant);
3968 }
3969 
3970 // XXX: this is awful and should not exist
3971 static const spell_type smitey_spells[] = {
3972     SPELL_SMITING,
3973     SPELL_AIRSTRIKE,
3974     SPELL_SYMBOL_OF_TORMENT,
3975     SPELL_CALL_DOWN_DAMNATION,
3976     SPELL_FIRE_STORM,
3977     SPELL_SHATTER,
3978     SPELL_POLAR_VORTEX,          // dubious
3979     SPELL_GLACIATE,         // dubious
3980     SPELL_OZOCUBUS_REFRIGERATION,
3981     SPELL_MASS_CONFUSION,
3982     SPELL_ENTROPIC_WEAVE,
3983 };
3984 
3985 /**
3986  * Does the given monster have spells that can damage without requiring LOF?
3987  *
3988  * Smitey (smite, airstrike), full-LOS (torment, refrigeration)...
3989  *
3990  * @param mon   The monster in question.
3991  * @return      Whether the given monster has 'smitey' effects.
3992  */
_mons_has_smite_attack(const monster * mons)3993 static bool _mons_has_smite_attack(const monster* mons)
3994 {
3995     return any_of(begin(smitey_spells), end(smitey_spells),
3996                   [=] (spell_type sp) { return mons->has_spell(sp); });
3997 }
3998 
3999 /**
4000  * Is the given monster smart and pushy enough to displace other
4001  * monsters?
4002  *
4003  * A shover should not cause damage to the shovee by
4004  * displacing it, so monsters that trail clouds of badness are
4005  * ineligible. The shover should also benefit from shoving, so monsters
4006  * that can smite/torment are ineligible.
4007  *
4008  * @param m     The monster in question.
4009  * @return      Whether that monster is ever allowed to 'push' other monsters.
4010  */
monster_shover(const monster & m)4011 bool monster_shover(const monster& m)
4012 {
4013     // Efreet and fire elementals are disqualified because they leave behind
4014     // clouds of flame. Curse toes are disqualified because they trail
4015     // clouds of miasma.
4016     if (mons_genus(m.type) == MONS_EFREET || m.type == MONS_FIRE_ELEMENTAL
4017         || m.type == MONS_CURSE_TOE)
4018     {
4019         return false;
4020     }
4021 
4022     // Monsters too stupid to use stairs (e.g. non-spectral zombified undead)
4023     // are also disqualified.
4024     // However, summons *can* push past pals & cause trouble.
4025     // XXX: redundant with intelligence check?
4026     if (!mons_can_use_stairs(m) && !m.is_summoned())
4027         return false;
4028 
4029     // Geryon really profits from *not* pushing past hell beasts.
4030     if (m.type == MONS_GERYON)
4031         return false;
4032     // Likewise, Robin and her mob.
4033     if (m.type == MONS_ROBIN)
4034         return false;
4035 
4036     // no mindless creatures pushing, aside from jellies, which just kind of ooze.
4037     return mons_intel(m) > I_BRAINLESS || mons_genus(m.type) == MONS_JELLY;
4038 }
4039 
4040 /**
4041  * Is the first monster considered 'senior' to the second; that is, can it
4042  * 'push' (swap with) the latter?
4043  *
4044  * Generally, this is true if m1 and m2 are related, and m1 is higher up the
4045  * totem pole than m2.
4046  *
4047  * Not guaranteed to be transitive or symmetric, though it probably should be.
4048  *
4049  * @param m1        The potentially senior monster.
4050  * @param m2        The potentially junior monster.
4051  * @param fleeing   Whether the first monster is running away; relevant for
4052  *                  smiters pushing melee monsters out of the way.
4053  * @return          Whether m1 can push m2.
4054  */
monster_senior(const monster & m1,const monster & m2,bool fleeing)4055 bool monster_senior(const monster& m1, const monster& m2, bool fleeing)
4056 {
4057     // non-fleeing smiters won't push past anything.
4058     if (_mons_has_smite_attack(&m1) && !fleeing)
4059         return false;
4060 
4061     // Fannar's ice beasts can push past Fannar, who benefits from this.
4062     if (m1.type == MONS_ICE_BEAST && m2.type == MONS_FANNAR)
4063         return true;
4064 
4065     // Special-case spectral things to push past things that summon them
4066     // (revenants, ghost crabs).
4067     // XXX: unify this logic with Fannar's & Geryon's? (summon-buddies?)
4068     if (m1.type == MONS_SPECTRAL_THING
4069         && (m2.type == MONS_REVENANT || m2.type == MONS_GHOST_CRAB))
4070     {
4071         return true;
4072     }
4073 
4074     // Band leaders can displace followers regardless of type considerations.
4075     // -cao
4076     if (m2.props.exists("band_leader"))
4077     {
4078         unsigned leader_mid = m2.props["band_leader"].get_int();
4079         if (leader_mid == m1.mid)
4080             return true;
4081     }
4082     // And prevent followers to displace the leader to avoid constant swapping.
4083     else if (m1.props.exists("band_leader"))
4084     {
4085         unsigned leader_mid = m1.props["band_leader"].get_int();
4086         if (leader_mid == m2.mid)
4087             return false;
4088     }
4089 
4090     // Monsters smart enough to use stairs can push past monsters too stupid
4091     // to use stairs (so that e.g. non-zombified or spectral zombified undead
4092     // can push past non-spectral zombified undead).
4093     if (mons_class_can_use_stairs(m1.type)
4094         && !mons_class_can_use_stairs(m2.type))
4095     {
4096         return true;
4097     }
4098     // This check assumes that demonicness is always carried at the monster type
4099     // level; this is because a full holiness check in such an often-called
4100     // function is costly.
4101     const bool related = mons_genus(m1.type) == mons_genus(m2.type)
4102                             || (   mons_class_holiness(m1.type) & MH_DEMONIC
4103                                 && mons_class_holiness(m2.type) & MH_DEMONIC);
4104 
4105     // Let all related monsters (all demons are 'related') push past ones that
4106     // are weaker at all. Unrelated ones have to be quite a bit stronger, to
4107     // reduce excessive swapping and because HD correlates only weakly with
4108     // monster strength.
4109     return related && fleeing
4110            || related && m1.get_hit_dice() > m2.get_hit_dice()
4111            || m1.get_hit_dice() > m2.get_hit_dice() + 5;
4112 }
4113 
mons_class_can_pass(monster_type mc,const dungeon_feature_type grid)4114 bool mons_class_can_pass(monster_type mc, const dungeon_feature_type grid)
4115 {
4116     if (grid == DNGN_MALIGN_GATEWAY)
4117     {
4118         return mc == MONS_ELDRITCH_TENTACLE
4119                || mc == MONS_ELDRITCH_TENTACLE_SEGMENT;
4120     }
4121 
4122     return !feat_is_solid(grid);
4123 }
4124 
_mons_can_open_doors(const monster * mon)4125 static bool _mons_can_open_doors(const monster* mon)
4126 {
4127     return mons_itemuse(*mon) >= MONUSE_OPEN_DOORS;
4128 }
4129 
4130 // Some functions that check whether a monster can open/eat/pass a
4131 // given door. These all return false if there's no closed door there.
mons_can_open_door(const monster & mon,const coord_def & pos)4132 bool mons_can_open_door(const monster& mon, const coord_def& pos)
4133 {
4134     if (!_mons_can_open_doors(&mon))
4135         return false;
4136 
4137     // Creatures allied with the player can't open doors.
4138     // (to prevent sabotaging the player accidentally.)
4139     if (mon.friendly())
4140         return false;
4141 
4142     if (env.markers.property_at(pos, MAT_ANY, "door_restrict") == "veto")
4143         return false;
4144 
4145     return true;
4146 }
4147 
mons_can_eat_door(const monster & mon,const coord_def & pos)4148 bool mons_can_eat_door(const monster& mon, const coord_def& pos)
4149 {
4150     if (env.markers.property_at(pos, MAT_ANY, "door_restrict") == "veto")
4151         return false;
4152 
4153     return mons_eats_items(mon)
4154            || mons_class_flag(mons_base_type(mon), M_EAT_DOORS);
4155 }
4156 
mons_can_destroy_door(const monster & mon,const coord_def & pos)4157 bool mons_can_destroy_door(const monster& mon, const coord_def& pos)
4158 {
4159     if (!mons_class_flag(mons_base_type(mon), M_CRASH_DOORS))
4160         return false;
4161 
4162     if (env.markers.property_at(pos, MAT_ANY, "door_restrict") == "veto")
4163         return false;
4164 
4165     return true;
4166 }
4167 
_mons_can_pass_door(const monster * mon,const coord_def & pos)4168 static bool _mons_can_pass_door(const monster* mon, const coord_def& pos)
4169 {
4170     return mon->can_pass_through_feat(DNGN_FLOOR)
4171            && (mons_can_open_door(*mon, pos)
4172                || mons_can_eat_door(*mon, pos)
4173                || mons_can_destroy_door(*mon, pos));
4174 }
4175 
mons_can_traverse(const monster & mon,const coord_def & p,bool only_in_sight,bool checktraps)4176 bool mons_can_traverse(const monster& mon, const coord_def& p,
4177                        bool only_in_sight, bool checktraps)
4178 {
4179     // Friendly summons should avoid pathing out of the player's sight
4180     // (especially as attempting this may make them ignore valid, but longer
4181     // paths).
4182     if (only_in_sight && !you.see_cell_no_trans(p))
4183         return false;
4184 
4185     if (cell_is_runed(p))
4186         return false;
4187 
4188     // Includes sealed doors.
4189     if (feat_is_closed_door(env.grid(p)) && _mons_can_pass_door(&mon, p))
4190         return true;
4191 
4192     if (!mon.is_habitable(p))
4193         return false;
4194 
4195     return !checktraps || mon.is_trap_safe(p);
4196 }
4197 
mons_remove_from_grid(const monster & mon)4198 void mons_remove_from_grid(const monster& mon)
4199 {
4200     const coord_def pos = mon.pos();
4201     if (map_bounds(pos) && env.mgrid(pos) == mon.mindex())
4202         env.mgrid(pos) = NON_MONSTER;
4203 }
4204 
equip_slot_to_mslot(equipment_type eq)4205 mon_inv_type equip_slot_to_mslot(equipment_type eq)
4206 {
4207     switch (eq)
4208     {
4209     case EQ_WEAPON:      return MSLOT_WEAPON;
4210     case EQ_BODY_ARMOUR: return MSLOT_ARMOUR;
4211     case EQ_SHIELD:      return MSLOT_SHIELD;
4212     case EQ_RINGS:
4213     case EQ_AMULET:      return MSLOT_JEWELLERY;
4214     default: return NUM_MONSTER_SLOTS;
4215     }
4216 }
4217 
item_to_mslot(const item_def & item)4218 mon_inv_type item_to_mslot(const item_def &item)
4219 {
4220     switch (item.base_type)
4221     {
4222     case OBJ_WEAPONS:
4223     case OBJ_STAVES:
4224 #if TAG_MAJOR_VERSION == 34
4225     case OBJ_RODS:
4226 #endif
4227         return MSLOT_WEAPON;
4228     case OBJ_MISSILES:
4229         return MSLOT_MISSILE;
4230     case OBJ_ARMOUR:
4231         return equip_slot_to_mslot(get_armour_slot(item));
4232     case OBJ_JEWELLERY:
4233         return MSLOT_JEWELLERY;
4234     case OBJ_WANDS:
4235         return MSLOT_WAND;
4236     case OBJ_BOOKS:
4237     case OBJ_SCROLLS:
4238         return MSLOT_SCROLL;
4239     case OBJ_POTIONS:
4240         return MSLOT_POTION;
4241     case OBJ_MISCELLANY:
4242         return MSLOT_MISCELLANY;
4243     case OBJ_GOLD:
4244         return MSLOT_GOLD;
4245     default:
4246         return NUM_MONSTER_SLOTS;
4247     }
4248 }
4249 
royal_jelly_ejectable_monster()4250 monster_type royal_jelly_ejectable_monster()
4251 {
4252     return random_choose(MONS_ACID_BLOB,
4253                          MONS_AZURE_JELLY,
4254                          MONS_ROCKSLIME,
4255                          MONS_QUICKSILVER_OOZE);
4256 }
4257 
4258 // Replaces @foe_god@ and @god_is@ with foe's god name.
4259 //
4260 // Atheists get "You"/"you", and worshippers of nameless gods get "Your
4261 // god"/"your god".
_replace_god_name(god_type god,bool need_verb=false,bool capital=false)4262 static string _replace_god_name(god_type god, bool need_verb = false,
4263                                 bool capital = false)
4264 {
4265     string result;
4266 
4267     if (god == GOD_NO_GOD)
4268         result = capital ? "You" : "you";
4269     else if (god == GOD_NAMELESS)
4270         result = capital ? "Your god" : "your god";
4271     else
4272     {
4273         const string godname = god_name(god, false);
4274         result = capital ? uppercase_first(godname) : godname;
4275     }
4276 
4277     if (need_verb)
4278     {
4279         result += ' ';
4280         result += conjugate_verb("be", god == GOD_NO_GOD);
4281     }
4282 
4283     return result;
4284 }
4285 
_get_species_insult(const string & species,const string & type)4286 static string _get_species_insult(const string &species, const string &type)
4287 {
4288     string insult;
4289     string lookup;
4290 
4291     // Get species genus.
4292     if (!species.empty())
4293     {
4294         lookup  = "insult ";
4295         lookup += species;
4296         lookup += " ";
4297         lookup += type;
4298 
4299         insult  = getSpeakString(lowercase(lookup));
4300     }
4301 
4302     if (insult.empty()) // Species too specific?
4303     {
4304         lookup  = "insult general ";
4305         lookup += type;
4306 
4307         insult  = getSpeakString(lookup);
4308     }
4309 
4310     return insult;
4311 }
4312 
4313 // From should be of the form "prefix @tag@". Replaces all substrings
4314 // of the form "prefix @tag@" with to, and all strings of the form
4315 // "prefix @tag/alt@" with either to (if nonempty) or "prefix alt".
_replace_speech_tag(string msg,string from,const string & to)4316 static string _replace_speech_tag(string msg, string from, const string &to)
4317 {
4318     if (from.empty())
4319         return msg;
4320     msg = replace_all(msg, from, to);
4321 
4322     // Change the @ to a / for the next search
4323     from[from.size() - 1] = '/';
4324 
4325     // @tag/alternative@
4326     size_t pos = 0;
4327     while ((pos = msg.find(from, pos)) != string::npos)
4328     {
4329         // beginning of tag
4330         const size_t at_pos = msg.find('@', pos);
4331         // beginning of alternative
4332         const size_t alt_pos = pos + from.size();
4333         // end of tag (one-past-the-end of alternative)
4334         const size_t alt_end = msg.find('@', alt_pos);
4335 
4336         // unclosed @tag/alt, or "from" has no @: leave it alone.
4337         if (alt_end == string::npos || at_pos == string::npos)
4338             break;
4339 
4340         if (to.empty())
4341         {
4342             // Replace only the @...@ part.
4343             msg.replace(at_pos, alt_end - at_pos + 1,
4344                         msg.substr(alt_pos, alt_end - alt_pos));
4345             pos = at_pos + (alt_end - alt_pos);
4346         }
4347         else
4348         {
4349             // Replace the whole from string, up to the second @
4350             msg.replace(pos, alt_end - pos + 1, to);
4351             pos += to.size();
4352         }
4353     }
4354     return msg;
4355 }
4356 
4357 // Replaces the "@foo@" strings in monster shout and monster speak
4358 // definitions.
do_mon_str_replacements(const string & in_msg,const monster & mons,int s_type)4359 string do_mon_str_replacements(const string &in_msg, const monster& mons,
4360                                int s_type)
4361 {
4362     string msg = in_msg;
4363 
4364     const actor* foe = (mons.wont_attack()
4365                           && invalid_monster_index(mons.foe)) ?
4366                              &you : mons.get_foe();
4367 
4368     if (s_type < 0 || s_type >= NUM_LOUDNESS || s_type == NUM_SHOUTS)
4369         s_type = mons_shouts(mons.type);
4370 
4371     msg = maybe_pick_random_substring(msg);
4372 
4373     // FIXME: Handle player_genus in case it was not generalised to foe_genus.
4374     msg = replace_all(msg, "@a_player_genus@",
4375                 article_a(species::name(you.species, species::SPNAME_GENUS)));
4376     msg = replace_all(msg, "@player_genus@",
4377                 species::name(you.species, species::SPNAME_GENUS));
4378     msg = replace_all(msg, "@player_genus_plural@",
4379                 pluralise(species::name(you.species, species::SPNAME_GENUS)));
4380 
4381     string foe_genus;
4382 
4383     if (foe == nullptr)
4384         ;
4385     else if (foe->is_player())
4386     {
4387         foe_genus = species::name(you.species, species::SPNAME_GENUS);
4388 
4389         msg = _replace_speech_tag(msg, " @to_foe@", "");
4390         msg = _replace_speech_tag(msg, " @at_foe@", "");
4391 
4392         msg = _replace_speech_tag(msg, " @to_foe@", "");
4393         msg = _replace_speech_tag(msg, " @at_foe@", "");
4394 
4395         msg = replace_all(msg, "@player_only@", "");
4396         msg = replace_all(msg, " @foe,@", ",");
4397 
4398         msg = replace_all(msg, "@player", "@foe");
4399         msg = replace_all(msg, "@Player", "@Foe");
4400 
4401         msg = replace_all(msg, "@foe_possessive@", "your");
4402         msg = replace_all(msg, "@foe@", "you");
4403         msg = replace_all(msg, "@Foe@", "You");
4404 
4405         msg = replace_all(msg, "@foe_name@", you.your_name);
4406         msg = replace_all(msg, "@foe_species@", species::name(you.species));
4407         msg = replace_all(msg, "@foe_genus@", foe_genus);
4408         msg = replace_all(msg, "@Foe_genus@", uppercase_first(foe_genus));
4409         msg = replace_all(msg, "@foe_genus_plural@",
4410                           pluralise(foe_genus));
4411     }
4412     else
4413     {
4414         string foe_name;
4415         const monster* m_foe = foe->as_monster();
4416         if (m_foe->attitude == ATT_FRIENDLY
4417             && !mons_is_unique(m_foe->type)
4418             && !crawl_state.game_is_arena())
4419         {
4420             foe_name = foe->name(DESC_YOUR);
4421             const string::size_type pos = foe_name.find("'");
4422             if (pos != string::npos)
4423                 foe_name = foe_name.substr(0, pos);
4424         }
4425         else
4426             foe_name = foe->name(DESC_THE);
4427 
4428         string prep = "at";
4429         if (s_type == S_SILENT || s_type == S_SHOUT || s_type == S_NORMAL)
4430             prep = "to";
4431         msg = replace_all(msg, "@says@ @to_foe@", "@says@ " + prep + " @foe@");
4432 
4433         msg = _replace_speech_tag(msg, " @to_foe@", " to @foe@");
4434         msg = _replace_speech_tag(msg, " @at_foe@", " at @foe@");
4435 
4436         msg = replace_all(msg, "@foe,@", "@foe@,");
4437         msg = replace_all(msg, "@foe_possessive@", "@foe@'s");
4438         msg = replace_all(msg, "@foe@", foe_name);
4439         msg = replace_all(msg, "@Foe@", uppercase_first(foe_name));
4440 
4441         if (m_foe->is_named())
4442             msg = replace_all(msg, "@foe_name@", foe->name(DESC_PLAIN, true));
4443 
4444         string species = mons_type_name(mons_species(m_foe->type), DESC_PLAIN);
4445 
4446         msg = replace_all(msg, "@foe_species@", species);
4447 
4448         foe_genus = mons_type_name(mons_genus(m_foe->type), DESC_PLAIN);
4449 
4450         msg = replace_all(msg, "@foe_genus@", foe_genus);
4451         msg = replace_all(msg, "@Foe_genus@", uppercase_first(foe_genus));
4452         msg = replace_all(msg, "@foe_genus_plural@", pluralise(foe_genus));
4453     }
4454 
4455     description_level_type nocap = DESC_THE, cap = DESC_THE;
4456 
4457     if (mons.is_named() && you.can_see(mons))
4458     {
4459         const string name = mons.name(DESC_THE);
4460 
4461         msg = replace_all(msg, "@the_something@", name);
4462         msg = replace_all(msg, "@The_something@", name);
4463         msg = replace_all(msg, "@the_monster@",   name);
4464         msg = replace_all(msg, "@The_monster@",   name);
4465     }
4466     else if (mons.attitude == ATT_FRIENDLY
4467              && !mons_is_unique(mons.type)
4468              && !crawl_state.game_is_arena()
4469              && you.can_see(mons))
4470     {
4471         nocap = DESC_PLAIN;
4472         cap   = DESC_PLAIN;
4473 
4474         msg = replace_all(msg, "@the_something@", "your @the_something@");
4475         msg = replace_all(msg, "@The_something@", "Your @The_something@");
4476         msg = replace_all(msg, "@the_monster@",   "your @the_monster@");
4477         msg = replace_all(msg, "@The_monster@",   "Your @the_monster@");
4478     }
4479 
4480     if (you.see_cell(mons.pos()))
4481     {
4482         dungeon_feature_type feat = env.grid(mons.pos());
4483         if (feat_is_solid(feat) || feat >= NUM_FEATURES)
4484             msg = replace_all(msg, "@surface@", "buggy surface");
4485         else if (feat == DNGN_LAVA)
4486             msg = replace_all(msg, "@surface@", "lava");
4487         else if (feat_is_water(feat))
4488             msg = replace_all(msg, "@surface@", "water");
4489         else if (feat_is_altar(feat))
4490             msg = replace_all(msg, "@surface@", "altar");
4491         else
4492             msg = replace_all(msg, "@surface@", "ground");
4493 
4494         msg = replace_all(msg, "@feature@", raw_feature_description(mons.pos()));
4495     }
4496     else
4497     {
4498         msg = replace_all(msg, "@surface@", "buggy unseen surface");
4499         msg = replace_all(msg, "@feature@", "buggy unseen feature");
4500     }
4501 
4502     string something = mons.name(DESC_PLAIN);
4503     msg = replace_all(msg, "@something@",   something);
4504     msg = replace_all(msg, "@a_something@", mons.name(DESC_A));
4505     msg = replace_all(msg, "@the_something@", mons.name(nocap));
4506 
4507     something[0] = toupper_safe(something[0]);
4508     msg = replace_all(msg, "@Something@",   something);
4509     msg = replace_all(msg, "@A_something@", mons.name(DESC_A));
4510     msg = replace_all(msg, "@The_something@", mons.name(cap));
4511 
4512     // Player name.
4513     msg = replace_all(msg, "@player_name@", you.your_name);
4514 
4515     string plain = mons.name(DESC_PLAIN);
4516     msg = replace_all(msg, "@monster@",     plain);
4517     msg = replace_all(msg, "@a_monster@",   mons.name(DESC_A));
4518     msg = replace_all(msg, "@the_monster@", mons.name(nocap));
4519 
4520     plain[0] = toupper_safe(plain[0]);
4521     msg = replace_all(msg, "@Monster@",     plain);
4522     msg = replace_all(msg, "@A_monster@",   mons.name(DESC_A));
4523     msg = replace_all(msg, "@The_monster@", mons.name(cap));
4524 
4525     msg = replace_all(msg, "@Subjective@",
4526                       mons.pronoun(PRONOUN_SUBJECTIVE));
4527     msg = replace_all(msg, "@subjective@",
4528                       mons.pronoun(PRONOUN_SUBJECTIVE));
4529     msg = replace_all(msg, "@Possessive@",
4530                       mons.pronoun(PRONOUN_POSSESSIVE));
4531     msg = replace_all(msg, "@possessive@",
4532                       mons.pronoun(PRONOUN_POSSESSIVE));
4533     msg = replace_all(msg, "@reflexive@",
4534                       mons.pronoun(PRONOUN_REFLEXIVE));
4535     msg = replace_all(msg, "@objective@",
4536                       mons.pronoun(PRONOUN_OBJECTIVE));
4537 
4538     // Body parts.
4539     bool   can_plural = false;
4540     string part_str   = mons.hand_name(false, &can_plural);
4541 
4542     msg = replace_all(msg, "@hand@", part_str);
4543     msg = replace_all(msg, "@Hand@", uppercase_first(part_str));
4544 
4545     if (!can_plural)
4546         part_str = "NO PLURAL HANDS";
4547     else
4548         part_str = mons.hand_name(true);
4549 
4550     msg = replace_all(msg, "@hands@", part_str);
4551     msg = replace_all(msg, "@Hands@", uppercase_first(part_str));
4552 
4553     can_plural = false;
4554     part_str   = mons.arm_name(false, &can_plural);
4555 
4556     msg = replace_all(msg, "@arm@", part_str);
4557     msg = replace_all(msg, "@Arm@", uppercase_first(part_str));
4558 
4559     if (!can_plural)
4560         part_str = "NO PLURAL ARMS";
4561     else
4562         part_str = mons.arm_name(true);
4563 
4564     msg = replace_all(msg, "@arms@", part_str);
4565     msg = replace_all(msg, "@Arms@", uppercase_first(part_str));
4566 
4567     can_plural = false;
4568     part_str   = mons.foot_name(false, &can_plural);
4569 
4570     msg = replace_all(msg, "@foot@", part_str);
4571     msg = replace_all(msg, "@Foot@", uppercase_first(part_str));
4572 
4573     if (!can_plural)
4574         part_str = "NO PLURAL FEET";
4575     else
4576         part_str = mons.foot_name(true);
4577 
4578     msg = replace_all(msg, "@feet@", part_str);
4579     msg = replace_all(msg, "@Feet@", uppercase_first(part_str));
4580 
4581     if (foe != nullptr)
4582     {
4583         const god_type god = foe->deity();
4584 
4585         // Replace with "you are" for atheists.
4586         msg = replace_all(msg, "@god_is@",
4587                           _replace_god_name(god, true, false));
4588         msg = replace_all(msg, "@God_is@", _replace_god_name(god, true, true));
4589 
4590         // No verb needed.
4591         msg = replace_all(msg, "@foe_god@",
4592                           _replace_god_name(god, false, false));
4593         msg = replace_all(msg, "@Foe_god@",
4594                           _replace_god_name(god, false, true));
4595     }
4596 
4597     // The monster's god, not the player's. Atheists get
4598     // "NO GOD"/"NO GOD"/"NO_GOD"/"NO_GOD", and worshippers of nameless
4599     // gods get "a god"/"its god/my God/My God".
4600     //
4601     // XXX: Crawl currently has no first-person possessive pronoun;
4602     // if it gets one, it should be used for the last two entries.
4603     if (mons.god == GOD_NO_GOD)
4604     {
4605         msg = replace_all(msg, "@a_God@", "NO GOD");
4606         msg = replace_all(msg, "@A_God@", "NO GOD");
4607         msg = replace_all(msg, "@possessive_God@", "NO GOD");
4608         msg = replace_all(msg, "@Possessive_God@", "NO GOD");
4609 
4610         msg = replace_all(msg, "@my_God@", "NO GOD");
4611         msg = replace_all(msg, "@My_God@", "NO GOD");
4612     }
4613     else if (mons.god == GOD_NAMELESS)
4614     {
4615         msg = replace_all(msg, "@a_God@", "a god");
4616         msg = replace_all(msg, "@A_God@", "A god");
4617         const string possessive = mons.pronoun(PRONOUN_POSSESSIVE) + " god";
4618         msg = replace_all(msg, "@possessive_God@", possessive);
4619         msg = replace_all(msg, "@Possessive_God@", uppercase_first(possessive));
4620 
4621         msg = replace_all(msg, "@my_God@", "my God");
4622         msg = replace_all(msg, "@My_God@", "My God");
4623     }
4624     else
4625     {
4626         const string godname = god_name(mons.god);
4627         const string godcap = uppercase_first(godname);
4628         msg = replace_all(msg, "@a_God@", godname);
4629         msg = replace_all(msg, "@A_God@", godcap);
4630         msg = replace_all(msg, "@possessive_God@", godname);
4631         msg = replace_all(msg, "@Possessive_God@", godcap);
4632 
4633         msg = replace_all(msg, "@my_God@", godname);
4634         msg = replace_all(msg, "@My_God@", godcap);
4635     }
4636 
4637     // Replace with species specific insults.
4638     if (msg.find("@species_insult_") != string::npos)
4639     {
4640         msg = replace_all(msg, "@species_insult_adj1@",
4641                                _get_species_insult(foe_genus, "adj1"));
4642         msg = replace_all(msg, "@species_insult_adj2@",
4643                                _get_species_insult(foe_genus, "adj2"));
4644         msg = replace_all(msg, "@species_insult_noun@",
4645                                _get_species_insult(foe_genus, "noun"));
4646     }
4647 
4648     static const char * sound_list[] =
4649     {
4650         "says",         // actually S_SILENT
4651         "shouts",
4652         "barks",
4653         "howls",
4654         "shouts",
4655         "roars",
4656         "screams",
4657         "bellows",
4658         "bleats",
4659         "trumpets",
4660         "screeches",
4661         "buzzes",
4662         "moans",
4663         "gurgles",
4664         "croaks",
4665         "growls",
4666         "hisses",
4667         "sneers",       // S_DEMON_TAUNT
4668         "says",         // S_CHERUB -- they just speak normally.
4669         "squeals",
4670         "roars",
4671         "buggily says", // NUM_SHOUTS
4672         "breathes",     // S_VERY_SOFT
4673         "whispers",     // S_SOFT
4674         "says",         // S_NORMAL
4675         "shouts",       // S_LOUD
4676         "screams",      // S_VERY_LOUD
4677     };
4678     COMPILE_CHECK(ARRAYSZ(sound_list) == NUM_LOUDNESS);
4679 
4680     if (s_type < 0 || s_type >= NUM_LOUDNESS || s_type == NUM_SHOUTS)
4681     {
4682         mprf(MSGCH_DIAGNOSTICS, "Invalid @says@ type.");
4683         msg = replace_all(msg, "@says@", "buggily says");
4684     }
4685     else
4686         msg = replace_all(msg, "@says@", sound_list[s_type]);
4687 
4688     msg = maybe_capitalise_substring(msg);
4689 
4690     return msg;
4691 }
4692 
4693 /**
4694  * Get the monster body shape of the given monster.
4695  * @param mon  The monster in question.
4696  * @return     The mon_body_shape type of this monster.
4697  */
get_mon_shape(const monster & mon)4698 mon_body_shape get_mon_shape(const monster& mon)
4699 {
4700     monster_type base_type;
4701     if (mons_is_pghost(mon.type))
4702         base_type = species::to_mons_species(mon.ghost->species);
4703     else if (mons_is_zombified(mon))
4704         base_type = mon.base_monster;
4705     else
4706         base_type = mon.type;
4707     return get_mon_shape(base_type);
4708 }
4709 
4710 /**
4711  * Get the monster body shape of the given monster type.
4712  * @param mc  The monster type in question.
4713  * @return     The mon_body_shape type of this monster type.
4714  */
get_mon_shape(const monster_type mc)4715 mon_body_shape get_mon_shape(const monster_type mc)
4716 {
4717     if (mc == MONS_CHAOS_SPAWN)
4718     {
4719         return static_cast<mon_body_shape>(random_range(MON_SHAPE_HUMANOID,
4720                                                         MON_SHAPE_MISC));
4721     }
4722 
4723     ASSERT_smc();
4724     return smc->shape;
4725 }
4726 
4727 /**
4728  * What's the normal tile for a given monster type?
4729  *
4730  * @param mc    The monster type in question.
4731  * @return      The tile for that monster, or TILEP_MONS_PROGRAM_BUG for mons
4732  *              with variable tiles (e.g. merfolk, hydras, slime creatures).
4733  */
get_mon_base_tile(monster_type mc)4734 tileidx_t get_mon_base_tile(monster_type mc)
4735 {
4736     ASSERT_smc();
4737     return smc->tile.base;
4738 }
4739 
4740 /**
4741  * How should a given monster type's tile vary?
4742  *
4743  * @param mc    The monster type in question.
4744  * @return      An enum describing how display of the monster should vary
4745  *              (by individual monster instance, or whether they're in water,
4746  *              etc)
4747  */
get_mon_tile_variation(monster_type mc)4748 mon_type_tile_variation get_mon_tile_variation(monster_type mc)
4749 {
4750     ASSERT_smc();
4751     return smc->tile.variation;
4752 }
4753 
4754 /**
4755  * What's the normal tile for corpses of a given monster type?
4756  *
4757  * @param mc    The monster type in question.
4758  * @return      The tile for that monster's corpse; may be varied slightly
4759  *              further in some special cases (ugly things, klowns).
4760  */
get_mon_base_corpse_tile(monster_type mc)4761 tileidx_t get_mon_base_corpse_tile(monster_type mc)
4762 {
4763     ASSERT_smc();
4764     return smc->corpse_tile;
4765 }
4766 
4767 
4768 /**
4769  * Get a DB lookup string for the given monster body shape.
4770  * @param mon  The monster body shape type in question.
4771  * @return     A DB lookup string for the monster body shape.
4772  */
get_mon_shape_str(const mon_body_shape shape)4773 string get_mon_shape_str(const mon_body_shape shape)
4774 {
4775     ASSERT_RANGE(shape, MON_SHAPE_HUMANOID, MON_SHAPE_MISC + 1);
4776 
4777     static const char *shape_names[] =
4778     {
4779         "bug", "humanoid", "winged humanoid", "tailed humanoid",
4780         "winged tailed humanoid", "centaur", "naga",
4781         "quadruped", "tailless quadruped", "winged quadruped",
4782         "bat", "bird", "snake", "fish",  "insect", "winged insect",
4783         "arachnid", "centipede", "snail", "plant", "fungus", "orb",
4784         "blob", "misc"
4785     };
4786 
4787     COMPILE_CHECK(ARRAYSZ(shape_names) == MON_SHAPE_MISC + 1);
4788     return shape_names[shape];
4789 }
4790 
4791 /** Is this body shape partially humanoid (i.e. does it at least have a
4792  *  humanoid upper body)?
4793  *  @param shape  The body shape in question.
4794  *  @returns      Whether this body shape is partially humanoid.
4795  */
mon_shape_is_humanoid(mon_body_shape shape)4796 bool mon_shape_is_humanoid(mon_body_shape shape)
4797 {
4798     return shape >= MON_SHAPE_FIRST_HUMANOID
4799            && shape <= MON_SHAPE_LAST_HUMANOID;
4800 }
4801 
player_or_mon_in_sanct(const monster & mons)4802 bool player_or_mon_in_sanct(const monster& mons)
4803 {
4804     return is_sanctuary(you.pos()) || is_sanctuary(mons.pos());
4805 }
4806 
get_dist_to_nearest_monster()4807 int get_dist_to_nearest_monster()
4808 {
4809     int minRange = LOS_RADIUS + 1;
4810     for (radius_iterator ri(you.pos(), LOS_NO_TRANS, true); ri; ++ri)
4811     {
4812         const monster* mon = monster_at(*ri);
4813         if (mon == nullptr)
4814             continue;
4815 
4816         if (!mon->visible_to(&you))
4817             continue;
4818 
4819         // Plants/fungi don't count.
4820         if ((!mons_is_threatening(*mon) || mon->wont_attack())
4821             && !mons_class_is_test(mon->type))
4822         {
4823             continue;
4824         }
4825 
4826         int dist = grid_distance(you.pos(), *ri);
4827         if (dist < minRange)
4828             minRange = dist;
4829     }
4830     return minRange;
4831 }
4832 
monster_nearby()4833 bool monster_nearby()
4834 {
4835     for (radius_iterator ri(you.pos(), LOS_DEFAULT); ri; ++ri)
4836         if (monster_at(*ri))
4837             return true;
4838     return false;
4839 }
4840 
actor_by_mid(mid_t m,bool require_valid)4841 actor *actor_by_mid(mid_t m, bool require_valid)
4842 {
4843     if (m == MID_PLAYER)
4844         return &you;
4845     return monster_by_mid(m, require_valid);
4846 }
4847 
monster_by_mid(mid_t m,bool require_valid)4848 monster *monster_by_mid(mid_t m, bool require_valid)
4849 {
4850     if (!require_valid)
4851     {
4852         if (m == MID_ANON_FRIEND)
4853             return &env.mons[ANON_FRIENDLY_MONSTER];
4854         if (m == MID_YOU_FAULTLESS)
4855             return &env.mons[YOU_FAULTLESS];
4856     }
4857 
4858     if (unsigned short *mc = map_find(env.mid_cache, m))
4859         return &env.mons[*mc];
4860     return 0;
4861 }
4862 
init_anon()4863 void init_anon()
4864 {
4865     monster &mon = env.mons[ANON_FRIENDLY_MONSTER];
4866     mon.reset();
4867     mon.type = MONS_PROGRAM_BUG;
4868     mon.mid = MID_ANON_FRIEND;
4869     mon.attitude = ATT_FRIENDLY;
4870     mon.hit_points = mon.max_hit_points = 1000;
4871 
4872     monster &yf = env.mons[YOU_FAULTLESS];
4873     yf.reset();
4874     yf.type = MONS_PROGRAM_BUG;
4875     yf.mid = MID_YOU_FAULTLESS;
4876     yf.attitude = ATT_FRIENDLY; // higher than this, actually
4877     yf.hit_points = mon.max_hit_points = 1000;
4878 }
4879 
find_agent(mid_t m,kill_category kc)4880 actor *find_agent(mid_t m, kill_category kc)
4881 {
4882     actor *agent = actor_by_mid(m);
4883     if (agent)
4884         return agent;
4885     switch (kc)
4886     {
4887     case KC_YOU:
4888         // shouldn't happen, there ought to be a valid mid
4889         return &you;
4890     case KC_FRIENDLY:
4891         return &env.mons[ANON_FRIENDLY_MONSTER];
4892     case KC_OTHER:
4893         // currently hostile dead/gone monsters are no different from env
4894         return 0;
4895     case KC_NCATEGORIES:
4896     default:
4897         die("invalid kill category");
4898     }
4899 }
4900 
mons_class_name(monster_type mc)4901 const char* mons_class_name(monster_type mc)
4902 {
4903     // Usually, all invalids return "program bug", but since it has value of 0,
4904     // it's good to tell them apart in error messages.
4905     if (invalid_monster_type(mc) && mc != MONS_PROGRAM_BUG)
4906         return "INVALID";
4907 
4908     return get_monster_data(mc)->name;
4909 }
4910 
mons_threat_level(const monster & mon,bool real)4911 mon_threat_level_type mons_threat_level(const monster &mon, bool real)
4912 {
4913     const monster& threat = get_tentacle_head(mon);
4914     const double factor = sqrt(exp_needed(you.experience_level) / 30.0);
4915     const int tension = exper_value(threat, real) / (1 + factor);
4916 
4917     if (tension <= 0)
4918     {
4919         // Conjurators use melee to conserve mana, MDFis switch plates...
4920         return MTHRT_TRIVIAL;
4921     }
4922     else if (tension <= 5)
4923     {
4924         // An easy fight but not ignorable.
4925         return MTHRT_EASY;
4926     }
4927     else if (tension <= 32)
4928     {
4929         // Hard but reasonable.
4930         return MTHRT_TOUGH;
4931     }
4932     else
4933     {
4934         // Check all wands/jewels several times, wear brown pants...
4935         return MTHRT_NASTY;
4936     }
4937 }
4938 
mons_foe_is_marked(const monster & mon)4939 bool mons_foe_is_marked(const monster& mon)
4940 {
4941     if (mon.foe == MHITYOU)
4942         return you.duration[DUR_SENTINEL_MARK];
4943     else
4944         return false;
4945 }
4946 
debug_mondata()4947 void debug_mondata()
4948 {
4949     string fails;
4950 
4951     for (monster_type mc = MONS_0; mc < NUM_MONSTERS; ++mc)
4952     {
4953         if (invalid_monster_type(mc))
4954             continue;
4955         const char* name = mons_class_name(mc);
4956         if (!name)
4957         {
4958             fails += make_stringf("Monster %d has no name\n", mc);
4959             continue;
4960         }
4961 
4962         const monsterentry *md = get_monster_data(mc);
4963 
4964         int WL = md->willpower;
4965         if (WL < 0)
4966             WL = md->HD * -WL * 4 / 3;
4967         if (md->willpower > 200 && md->willpower != WILL_INVULN)
4968             fails += make_stringf("%s has WL %d > 200\n", name, WL);
4969         if (get_resist(md->resists, MR_RES_POISON) == 2)
4970             fails += make_stringf("%s has rPois++\n", name);
4971         if (get_resist(md->resists, MR_RES_ELEC) == 2)
4972             fails += make_stringf("%s has rElec++\n", name);
4973 
4974         // Tests below apply only to real monsters.
4975         if (md->bitfields & M_CANT_SPAWN)
4976             continue;
4977 
4978         if (!md->HD && md->basechar != 'Z') // derived undead...
4979             fails += make_stringf("%s has 0 HD: %d\n", name, md->HD);
4980         if (md->avg_hp_10x <= 0 && md->basechar != 'Z')
4981             fails += make_stringf("%s has <= 0 HP: %d", name, md->avg_hp_10x);
4982 
4983         if (md->basechar == ' ')
4984             fails += make_stringf("%s has an empty glyph\n", name);
4985 
4986         if (md->AC < 0 && !mons_is_job(mc))
4987             fails += make_stringf("%s has negative AC\n", name);
4988         if (md->ev < 0 && !mons_is_job(mc))
4989             fails += make_stringf("%s has negative EV\n", name);
4990         if (md->exp_mod < 0)
4991             fails += make_stringf("%s has negative xp mod\n", name);
4992 
4993         if (md->speed < 0)
4994             fails += make_stringf("%s has 0 speed\n", name);
4995         else if (md->speed == 0 && !mons_class_is_firewood(mc))
4996             fails += make_stringf("%s has 0 speed\n", name);
4997 
4998         const bool male = mons_class_flag(mc, M_MALE);
4999         const bool female = mons_class_flag(mc, M_FEMALE);
5000         const bool neutral = mons_class_flag(mc, M_GENDER_NEUTRAL);
5001         if (male + female + neutral > 1)
5002             fails += make_stringf("%s has too many genders\n", name);
5003 
5004         if (md->shape == MON_SHAPE_BUGGY)
5005             fails += make_stringf("%s has no defined shape\n", name);
5006 
5007         const bool has_corpse_tile = md->corpse_tile
5008                                      && md->corpse_tile != TILE_ERROR;
5009         if (md->species != mc)
5010         {
5011             if (has_corpse_tile)
5012             {
5013                 fails +=
5014                     make_stringf("%s isn't a species but has a corpse tile\n",
5015                                  name);
5016             }
5017         }
5018         else if (!md->leaves_corpse)
5019         {
5020             if (has_corpse_tile)
5021             {
5022                 fails += make_stringf("%s has a corpse tile & no corpse\n",
5023                                       name);
5024             }
5025         }
5026         else if (!has_corpse_tile)
5027             fails += make_stringf("%s has a corpse but no corpse tile\n", name);
5028     }
5029 
5030     dump_test_fails(fails, "mon-data");
5031 }
5032 
5033 /**
5034  * Iterate over mspell_list (mon-spell.h) and look for anything that seems
5035  * incorrect. Dump the output to a text file & print its location to the
5036  * console.
5037  */
debug_monspells()5038 void debug_monspells()
5039 {
5040     string fails;
5041 
5042     // first, build a map from spellbooks to the first monster that uses them
5043     // (zero-initialised, where 0 == MONS_PROGRAM_BUG).
5044     monster_type mon_book_map[NUM_MSTYPES] = { };
5045     for (monster_type mc = MONS_0; mc < NUM_MONSTERS; ++mc)
5046     {
5047         if (!invalid_monster_type(mc))
5048         {
5049             mon_spellbook_type mon_book = _get_mc_spellbook(mc);
5050             if (mon_book < ARRAYSZ(mon_book_map) && !mon_book_map[mon_book])
5051                 mon_book_map[mon_book] = mc;
5052         }
5053     }
5054 
5055     // then, check every spellbook for errors.
5056 
5057     for (const mon_spellbook &spbook : mspell_list)
5058     {
5059         string book_name;
5060         const monster_type sample_mons = mon_book_map[spbook.type];
5061         if (!sample_mons)
5062         {
5063             string spells;
5064             if (spbook.spells.empty())
5065                 spells = "no spells";
5066             else
5067                 for (const mon_spell_slot &spslot : spbook.spells)
5068                     if (is_valid_spell(spslot.spell))
5069                         spells += make_stringf(",%s", spell_title(spslot.spell));
5070 
5071             fails += make_stringf("Book #%d is unused (%s)\n", spbook.type,
5072                                   spells.c_str());
5073             book_name = make_stringf("#%d", spbook.type);
5074         }
5075         else
5076         {
5077             const char * const mons_name = get_monster_data(sample_mons)->name;
5078             book_name = mons_name;
5079         }
5080 
5081         const char * const bknm = book_name.c_str();
5082 
5083         if (!spbook.spells.size())
5084             fails += make_stringf("Empty book %s\n", bknm);
5085 
5086         for (const mon_spell_slot &spslot : spbook.spells)
5087         {
5088             string spell_name;
5089             if (!is_valid_spell(spslot.spell))
5090             {
5091                 fails += make_stringf("Book %s contains invalid spell %d\n",
5092                                       bknm, spslot.spell);
5093                 spell_name = to_string(spslot.spell);
5094             }
5095             else
5096                 spell_name = spell_title(spslot.spell);
5097 
5098             // TODO: export this value somewhere
5099             const int max_freq = 200;
5100             if (spslot.freq > max_freq)
5101             {
5102                 fails += make_stringf("Spellbook %s has spell %s at freq %d "
5103                                       "(greater than max freq %d)\n",
5104                                       bknm, spell_name.c_str(),
5105                                       spslot.freq, max_freq);
5106             }
5107 
5108             mon_spell_slot_flag category = MON_SPELL_NO_FLAGS;
5109             for (const auto flag : mon_spell_slot_flags::range())
5110             {
5111                 if (!(spslot.flags & flag))
5112                     continue;
5113 
5114                 if (flag >= MON_SPELL_FIRST_CATEGORY
5115                     && flag <= MON_SPELL_LAST_CATEGORY)
5116                 {
5117                     if (category == MON_SPELL_NO_FLAGS)
5118                         category = flag;
5119                     else
5120                     {
5121                         fails += make_stringf("Spellbook %s has spell %s in "
5122                                               "multiple categories (%d and %d)\n",
5123                                               bknm, spell_name.c_str(),
5124                                               category, flag);
5125                     }
5126                 }
5127 
5128                 COMPILE_CHECK(MON_SPELL_NOISY > MON_SPELL_LAST_CATEGORY);
5129                 if (flag == MON_SPELL_NOISY
5130                     && category && !(category & MON_SPELL_INNATE_MASK))
5131                 {
5132                     fails += make_stringf("Spellbook %s has spell %s marked "
5133                                           "MON_SPELL_NOISY redundantly\n",
5134                                           bknm, spell_name.c_str());
5135                 }
5136             }
5137 
5138             if (category == MON_SPELL_NO_FLAGS)
5139             {
5140                 fails += make_stringf("Spellbook %s has spell %s with no "
5141                                       "category\n", bknm, spell_name.c_str());
5142             }
5143         }
5144     }
5145 
5146     dump_test_fails(fails, "mon-spell");
5147 }
5148 
5149 // Used when clearing level data, to ensure any additional reset quirks
5150 // are handled properly.
reset_all_monsters()5151 void reset_all_monsters()
5152 {
5153     for (auto &mons : menv_real)
5154     {
5155         // The monsters here have already been saved or discarded, so this
5156         // is the only place when a constricting monster can legitimately
5157         // be reset. Thus, clear constriction manually.
5158         if (!invalid_monster(&mons))
5159         {
5160             delete mons.constricting;
5161             mons.constricting = nullptr;
5162         }
5163         mons.reset();
5164     }
5165 
5166     env.mid_cache.clear();
5167 }
5168 
mons_is_recallable(const actor * caller,const monster & targ)5169 bool mons_is_recallable(const actor* caller, const monster& targ)
5170 {
5171     // For player, only recall friendly monsters
5172     if (caller == &you)
5173     {
5174         if (!targ.friendly())
5175             return false;
5176     }
5177     // Monster recall requires same attitude and at least normal intelligence
5178     else if (mons_intel(targ) < I_HUMAN
5179              || (!caller && targ.friendly())
5180              || (caller && !mons_aligned(&targ, caller->as_monster()))
5181              || targ.type == MONS_PLAYER_GHOST)
5182     {
5183         return false;
5184     }
5185 
5186     return targ.alive()
5187            && !mons_class_is_stationary(targ.type)
5188            && !mons_is_conjured(targ.type);
5189 }
5190 
get_on_level_followers()5191 vector<monster* > get_on_level_followers()
5192 {
5193     vector<monster* > mon_list;
5194     for (monster_iterator mi; mi; ++mi)
5195         if (mons_is_recallable(&you, **mi) && mi->foe == MHITYOU)
5196             mon_list.push_back(*mi);
5197 
5198     return mon_list;
5199 }
5200 
5201 // Return the number of monsters of the specified type.
5202 // If friendly_only is true, only count friendly
5203 // monsters, otherwise all of them
count_monsters(monster_type mtyp,bool friendly_only)5204 int count_monsters(monster_type mtyp, bool friendly_only)
5205 {
5206     return count_if(begin(env.mons), end(env.mons),
5207                     [=] (const monster &mons) -> bool
5208                     {
5209                         return mons.alive() && mons.type == mtyp
5210                             && (!friendly_only || mons.friendly());
5211                     });
5212 }
5213 
count_allies()5214 int count_allies()
5215 {
5216     return count_if(begin(env.mons), end(env.mons),
5217                     [] (const monster &mons) -> bool
5218                     {
5219                         return mons.alive() && mons.friendly();
5220                     });
5221 }
5222 
mons_stores_tracking_data(const monster & mons)5223 bool mons_stores_tracking_data(const monster& mons)
5224 {
5225     return mons.type == MONS_THORN_HUNTER
5226            || mons.type == MONS_MERFOLK_AVATAR
5227            || mons.type == MONS_BOULDER_BEETLE;
5228 }
5229 
mons_is_beast(monster_type mc)5230 bool mons_is_beast(monster_type mc)
5231 {
5232     if (!(mons_class_holiness(mc) & MH_NATURAL)
5233         || mons_class_intel(mc) != I_ANIMAL)
5234     {
5235         return false;
5236     }
5237     else if ((mons_genus(mc) == MONS_DRAGON
5238               && mc != MONS_SWAMP_DRAGON)
5239              || mons_genus(mc) == MONS_UGLY_THING
5240              || mc == MONS_ICE_BEAST
5241              || mc == MONS_SKY_BEAST
5242              || mc == MONS_BUTTERFLY)
5243     {
5244         return false;
5245     }
5246     else
5247         return true;
5248 }
5249 
mons_is_avatar(monster_type mc)5250 bool mons_is_avatar(monster_type mc)
5251 {
5252     return mons_class_flag(mc, M_AVATAR);
5253 }
5254 
mons_is_wrath_avatar(const monster & mon)5255 bool mons_is_wrath_avatar(const monster &mon)
5256 {
5257     return mon.type == MONS_PLAYER_SHADOW // ugh
5258         && mon.attitude != ATT_FRIENDLY;
5259 }
5260 
mons_is_player_shadow(const monster & mon)5261 bool mons_is_player_shadow(const monster& mon)
5262 {
5263     return mon.type == MONS_PLAYER_SHADOW
5264         && mon.attitude == ATT_FRIENDLY; // hostile shadows are god wrath
5265 }
5266 
mons_has_attacks(const monster & mon)5267 bool mons_has_attacks(const monster& mon)
5268 {
5269     const mon_attack_def attk = mons_attack_spec(mon, 0);
5270     return attk.type != AT_NONE && attk.damage > 0;
5271 }
5272 
5273 // The default suitable() function for choose_random_nearby_monster().
choose_any_monster(const monster & mon)5274 bool choose_any_monster(const monster& mon)
5275 {
5276     return !mons_is_projectile(mon.type);
5277 }
5278 
5279 // Find a nearby monster and return its index, including you as a
5280 // possibility with probability weight. suitable() should return true
5281 // for the type of monster wanted.
5282 // If prefer_named is true, named monsters (including uniques) are twice
5283 // as likely to get chosen compared to non-named ones.
5284 // If prefer_priest is true, priestly monsters (including uniques) are
5285 // twice as likely to get chosen compared to non-priestly ones.
choose_random_nearby_monster(int weight,bool (* suitable)(const monster & mon),bool prefer_named_or_priest)5286 monster* choose_random_nearby_monster(int weight,
5287                                       bool (*suitable)(const monster& mon),
5288                                       bool prefer_named_or_priest)
5289 {
5290     monster* chosen = nullptr;
5291     for (radius_iterator ri(you.pos(), LOS_NO_TRANS); ri; ++ri)
5292     {
5293         monster* mon = monster_at(*ri);
5294         if (!mon || !suitable(*mon))
5295             continue;
5296 
5297         // FIXME: if the intent is to favour monsters
5298         // named by $DEITY, we should set a flag on the
5299         // monster (something like MF_DEITY_PREFERRED) and
5300         // use that instead of checking the name, given
5301         // that other monsters can also have names.
5302 
5303         // True, but it's currently only used for orcs, and
5304         // Blork and Urug also being preferred to non-named orcs
5305         // is fine, I think. Once more gods name followers (and
5306         // prefer them) that should be changed, of course. (jpeg)
5307         int mon_weight = 1;
5308 
5309         if (prefer_named_or_priest)
5310             mon_weight += mon->is_named() + mon->is_priest();
5311 
5312         if (x_chance_in_y(mon_weight, weight += mon_weight))
5313             chosen = mon;
5314     }
5315 
5316     return chosen;
5317 }
5318 
choose_random_monster_on_level(int weight,bool (* suitable)(const monster & mon),bool prefer_named_or_priest)5319 monster* choose_random_monster_on_level(int weight,
5320                                         bool (*suitable)(const monster& mon),
5321                                         bool prefer_named_or_priest)
5322 {
5323     monster* chosen = nullptr;
5324 
5325     for (rectangle_iterator ri(1); ri; ++ri)
5326     {
5327         monster* mon = monster_at(*ri);
5328         if (!mon || !suitable(*mon))
5329             continue;
5330 
5331         int mon_weight = 1;
5332 
5333         if (prefer_named_or_priest)
5334             mon_weight += mon->is_named() + mon->is_priest();
5335 
5336         if (x_chance_in_y(mon_weight, weight += mon_weight))
5337             chosen = mon;
5338     }
5339 
5340     return chosen;
5341 }
5342 
update_monster_symbol(monster_type mtype,cglyph_t md)5343 void update_monster_symbol(monster_type mtype, cglyph_t md)
5344 {
5345     ASSERT(mtype != MONS_0);
5346 
5347     if (md.ch)
5348         monster_symbols[mtype].glyph = get_glyph_override(md.ch);
5349     if (md.col)
5350         monster_symbols[mtype].colour = md.col;
5351 }
5352 
spell_freq_for_hd(int hd)5353 int spell_freq_for_hd(int hd)
5354 {
5355     return hd + 50;
5356 }
5357 
normalize_spell_freq(monster_spells & spells,int total_freq)5358 void normalize_spell_freq(monster_spells &spells, int total_freq)
5359 {
5360     // Not using std::accumulate because slot.freq is only a uint8_t.
5361     unsigned int total_given_freq = 0;
5362     for (const auto &slot : spells)
5363         total_given_freq += slot.freq;
5364 
5365     if (total_given_freq > 0)
5366     {
5367         for (auto &slot : spells)
5368             slot.freq = total_freq * slot.freq / total_given_freq;
5369     }
5370     else
5371     {
5372         const size_t numspells = spells.size();
5373         for (auto &slot : spells)
5374             slot.freq = total_freq / numspells;
5375     }
5376 }
5377 
5378 /// Rounded to player-visible approximations, how hurt is this monster?
mons_get_damage_level(const monster & mons)5379 mon_dam_level_type mons_get_damage_level(const monster& mons)
5380 {
5381     if (mons.hit_points <= mons.max_hit_points / 5)
5382         return MDAM_ALMOST_DEAD;
5383     else if (mons.hit_points <= mons.max_hit_points * 2 / 5)
5384         return MDAM_SEVERELY_DAMAGED;
5385     else if (mons.hit_points <= mons.max_hit_points * 3 / 5)
5386         return MDAM_HEAVILY_DAMAGED;
5387     else if (mons.hit_points <= mons.max_hit_points * 4 / 5)
5388         return MDAM_MODERATELY_DAMAGED;
5389     else if (mons.hit_points < mons.max_hit_points)
5390         return MDAM_LIGHTLY_DAMAGED;
5391     else
5392         return MDAM_OKAY;
5393 }
5394 
get_damage_level_string(mon_holy_type holi,mon_dam_level_type mdam)5395 string get_damage_level_string(mon_holy_type holi, mon_dam_level_type mdam)
5396 {
5397     ostringstream ss;
5398     switch (mdam)
5399     {
5400     case MDAM_ALMOST_DEAD:
5401         ss << "almost";
5402         ss << (wounded_damaged(holi) ? " destroyed" : " dead");
5403         return ss.str();
5404     case MDAM_SEVERELY_DAMAGED:
5405         ss << "severely";
5406         break;
5407     case MDAM_HEAVILY_DAMAGED:
5408         ss << "heavily";
5409         break;
5410     case MDAM_MODERATELY_DAMAGED:
5411         ss << "moderately";
5412         break;
5413     case MDAM_LIGHTLY_DAMAGED:
5414         ss << "lightly";
5415         break;
5416     case MDAM_OKAY:
5417     default:
5418         ss << "not";
5419         break;
5420     }
5421     ss << (wounded_damaged(holi) ? " damaged" : " wounded");
5422     return ss.str();
5423 }
5424 
print_wounds(const monster & mons)5425 void print_wounds(const monster& mons)
5426 {
5427     if (!mons.alive() || mons.hit_points == mons.max_hit_points)
5428         return;
5429 
5430     mon_dam_level_type dam_level = mons_get_damage_level(mons);
5431     string desc = get_damage_level_string(mons.holiness(), dam_level);
5432 
5433     desc.insert(0, " is ");
5434     desc += ".";
5435     simple_monster_message(mons, desc.c_str(), MSGCH_MONSTER_DAMAGE,
5436                            dam_level);
5437 }
5438 
5439 // (true == 'damaged') [constructs, undead, etc.]
5440 // and (false == 'wounded') [living creatures, etc.] {dlb}
wounded_damaged(mon_holy_type holi)5441 bool wounded_damaged(mon_holy_type holi)
5442 {
5443     // this schema needs to be abstracted into real categories {dlb}:
5444     return bool(holi & (MH_UNDEAD | MH_NONLIVING | MH_PLANT));
5445 }
5446 
5447 // Is this monster interesting enough to make notes about?
mons_is_notable(const monster & mons)5448 bool mons_is_notable(const monster& mons)
5449 {
5450     if (crawl_state.game_is_arena())
5451         return false;
5452 
5453     // (Ex-)Unique monsters are always interesting
5454     if (mons_is_or_was_unique(mons))
5455         return true;
5456     // If it's never going to attack us, then not interesting
5457     if (mons.friendly())
5458         return false;
5459     // tentacles aren't real monsters.
5460     if (mons_is_tentacle_or_tentacle_segment(mons.type))
5461         return false;
5462     // Hostile ghosts and illusions are always interesting.
5463     if (mons.type == MONS_PLAYER_GHOST
5464         || mons.type == MONS_PLAYER_ILLUSION
5465         || mons.type == MONS_PANDEMONIUM_LORD)
5466     {
5467         return true;
5468     }
5469     // Jellies are never interesting to Jiyva.
5470     if (mons.type == MONS_JELLY && have_passive(passive_t::jellies_army))
5471         return false;
5472     if (mons_threat_level(mons) == MTHRT_NASTY)
5473         return true;
5474     const auto &nm = Options.note_monsters;
5475     // Don't waste time on moname() if user isn't using this option
5476     if (!nm.empty())
5477     {
5478         const string iname = mons_type_name(mons.type, DESC_A);
5479         return any_of(begin(nm), end(nm), [&](const text_pattern &pat) -> bool
5480                                           { return pat.matches(iname); });
5481     }
5482 
5483     return false;
5484 }
5485 
5486 /**
5487  * Set up fields for mutant beasts that vary by tier & facets (that is, that
5488  * vary between individual beasts).
5489  *
5490  * @param mons          The beast to be initialized.
5491  * @param HD            The beast's HD. If 0, default to mon-data's version.
5492  * @param beast_facets  The beast's facets (e.g. fire, bat).
5493  *                      If empty, chooses two distinct facets at random.
5494  */
init_mutant_beast(monster & mons,short HD,vector<int> beast_facets)5495 void init_mutant_beast(monster &mons, short HD, vector<int> beast_facets)
5496 {
5497     if (!HD)
5498         HD = mons.get_experience_level();
5499 
5500     // MUTANT_BEAST_TIER is to make it visible for mon-info
5501     mons.props[MUTANT_BEAST_TIER] = HD;
5502 
5503     if (beast_facets.empty())
5504     {
5505         vector<int> available_facets;
5506         for (int f = BF_FIRST; f <= BF_LAST; ++f)
5507             available_facets.insert(available_facets.end(), f);
5508 
5509         ASSERT(available_facets.size() >= 2);
5510 
5511         shuffle_array(available_facets);
5512         beast_facets.push_back(available_facets[0]);
5513         beast_facets.push_back(available_facets[1]);
5514 
5515         ASSERT(beast_facets.size() == 2);
5516         ASSERT(beast_facets[0] != beast_facets[1]);
5517     }
5518 
5519     for (auto facet : beast_facets)
5520     {
5521         mons.props[MUTANT_BEAST_FACETS].get_vector().push_back(facet);
5522 
5523         switch (facet)
5524         {
5525             case BF_BAT:
5526                 mons.props[MON_SPEED_KEY] = mons.speed * 2;
5527                 mons.calc_speed();
5528                 break;
5529             case BF_FIRE:
5530                 mons.spells.emplace_back(SPELL_FIRE_BREATH, 60,
5531                                          MON_SPELL_NATURAL | MON_SPELL_BREATH);
5532                 mons.props[CUSTOM_SPELLS_KEY] = true;
5533                 break;
5534             case BF_SHOCK:
5535                 mons.spells.emplace_back(SPELL_BLINKBOLT, 60,
5536                                          MON_SPELL_NATURAL);
5537                 mons.props[CUSTOM_SPELLS_KEY] = true;
5538                 break;
5539             default:
5540                 break;
5541         }
5542     }
5543 }
5544 
5545 /**
5546  * If a monster needs to charge up to cast a spell (by 'casting' the spell
5547  * repeatedly), how many charges does it need until it can actually set the
5548  * spell off?
5549  *
5550  * @param m     The type of monster in question.
5551  * @return      The required number of charges.
5552  */
max_mons_charge(monster_type m)5553 int max_mons_charge(monster_type m)
5554 {
5555     switch (m)
5556     {
5557         case MONS_ORB_SPIDER:
5558         case MONS_FLOATING_EYE:
5559             return 1;
5560         default:
5561             return 0;
5562     }
5563 }
5564 
5565 // Deal out damage to nearby pain-bonded monsters based on the distance between them.
radiate_pain_bond(const monster & mon,int damage,const monster * original_target)5566 void radiate_pain_bond(const monster& mon, int damage, const monster* original_target)
5567 {
5568     for (actor_near_iterator ai(mon.pos(), LOS_NO_TRANS); ai; ++ai)
5569     {
5570         if (!ai->is_monster())
5571             continue;
5572 
5573         monster* target = ai->as_monster();
5574 
5575         if (&mon == target) // no self-sharing
5576             continue;
5577 
5578         // Only other pain-bonded monsters are affected.
5579         if (!target->has_ench(ENCH_PAIN_BOND))
5580             continue;
5581 
5582         int distance = target->pos().distance_from(mon.pos());
5583         if (distance > 3)
5584             continue;
5585 
5586         damage = max(0, div_rand_round(damage * (4 - distance), 5));
5587 
5588         if (damage > 0)
5589         {
5590             behaviour_event(target, ME_ANNOY, &you, you.pos());
5591 
5592             // save any potential cleanup of the original target for later
5593             // (in `monster::hurt`).
5594             if (target == original_target)
5595                 damage = target->hurt(&you, damage, BEAM_SHARED_PAIN, KILLED_BY_MONSTER, "", "", false);
5596             else
5597                 damage = target->hurt(&you, damage, BEAM_SHARED_PAIN);
5598 
5599             if (damage > 0)
5600                 radiate_pain_bond(*target, damage, original_target);
5601         }
5602     }
5603 }
5604 
5605 // When a monster explodes violently, add some spice
throw_monster_bits(const monster & mon)5606 void throw_monster_bits(const monster& mon)
5607 {
5608     for (actor_near_iterator ai(mon.pos(), LOS_NO_TRANS); ai; ++ai)
5609     {
5610         if (!ai->is_monster())
5611             continue;
5612 
5613         monster* target = ai->as_monster();
5614 
5615         if (&mon == target) // can't throw chunks of something at itself.
5616             continue;
5617 
5618         int distance = target->pos().distance_from(mon.pos());
5619         if (!one_chance_in(distance + 4)) // generally gonna miss
5620             continue;
5621 
5622         int damage = 1 + random2(mon.get_hit_dice());
5623 
5624         mprf("%s is hit by a flying piece of %s!",
5625                 target->name(DESC_THE, false).c_str(),
5626                 mon.name(DESC_THE, false).c_str());
5627 
5628         // Because someone will get a kick out of this some day.
5629         if (mons_class_flag(mons_base_type(mon), M_ACID_SPLASH))
5630             target->corrode_equipment("a flying bit", 1);
5631 
5632         behaviour_event(target, ME_ANNOY, &you, you.pos());
5633         target->hurt(&you, damage);
5634     }
5635 }
5636 
5637 /// Add an ancestor spell to the given list.
_add_ancestor_spell(monster_spells & spells,spell_type spell)5638 static void _add_ancestor_spell(monster_spells &spells, spell_type spell)
5639 {
5640     spells.emplace_back(spell, 25, MON_SPELL_WIZARD);
5641 }
5642 
5643 /**
5644  * Set the correct spells for a given ancestor, corresponding to their HD and
5645  * type.
5646  *
5647  * @param ancestor      The ancestor in question.
5648  * @param notify        Whether to print messages if anything changes.
5649  */
set_ancestor_spells(monster & ancestor,bool notify)5650 void set_ancestor_spells(monster &ancestor, bool notify)
5651 {
5652     ASSERT(mons_is_hepliaklqana_ancestor(ancestor.type));
5653 
5654     vector<spell_type> old_spells;
5655     for (auto spellslot : ancestor.spells)
5656         old_spells.emplace_back(spellslot.spell);
5657 
5658     ancestor.spells = {};
5659     const int HD = ancestor.get_experience_level();
5660     switch (ancestor.type)
5661     {
5662     case MONS_ANCESTOR_BATTLEMAGE:
5663         _add_ancestor_spell(ancestor.spells, HD >= 10 ?
5664                                              SPELL_BOLT_OF_MAGMA :
5665                                              SPELL_THROW_FROST);
5666         _add_ancestor_spell(ancestor.spells, HD >= 16 ?
5667                                              SPELL_LEHUDIBS_CRYSTAL_SPEAR :
5668                                              SPELL_STONE_ARROW);
5669         break;
5670     case MONS_ANCESTOR_HEXER:
5671         _add_ancestor_spell(ancestor.spells, HD >= 10 ? SPELL_PARALYSE
5672                                                       : SPELL_SLOW);
5673         _add_ancestor_spell(ancestor.spells, HD >= 13 ? SPELL_MASS_CONFUSION
5674                                                       : SPELL_CONFUSE);
5675         break;
5676     default:
5677         break;
5678     }
5679 
5680     if (HD >= 13)
5681         ancestor.spells.emplace_back(SPELL_HASTE, 25, MON_SPELL_WIZARD);
5682 
5683     if (ancestor.spells.size())
5684         ancestor.props[CUSTOM_SPELLS_KEY] = true;
5685 
5686     if (!notify)
5687         return;
5688 
5689     for (auto spellslot : ancestor.spells)
5690     {
5691         if (find(old_spells.begin(), old_spells.end(), spellslot.spell)
5692             == old_spells.end())
5693         {
5694             mprf("%s regains %s memory of %s.",
5695                  ancestor.name(DESC_YOUR, true).c_str(),
5696                  ancestor.pronoun(PRONOUN_POSSESSIVE, true).c_str(),
5697                  spell_title(spellslot.spell));
5698         }
5699     }
5700 }
5701 
_apply_to_monsters(monster_func f,radius_iterator && ri)5702 static bool _apply_to_monsters(monster_func f, radius_iterator&& ri)
5703 {
5704     bool affected_any = false;
5705     for (; ri; ri++)
5706     {
5707         monster* mons = monster_at(*ri);
5708         if (!invalid_monster(mons))
5709             affected_any = f(*mons) || affected_any;
5710     }
5711 
5712     return affected_any;
5713 }
5714 
apply_monsters_around_square(monster_func f,const coord_def & where,int radius)5715 bool apply_monsters_around_square(monster_func f, const coord_def& where,
5716                                   int radius)
5717 {
5718     return _apply_to_monsters(f, radius_iterator(where, radius, C_SQUARE,
5719                                                  LOS_NO_TRANS, true));
5720 }
5721 
apply_visible_monsters(monster_func f,const coord_def & where,los_type los)5722 bool apply_visible_monsters(monster_func f, const coord_def& where, los_type los)
5723 {
5724     return _apply_to_monsters(f, radius_iterator(where, los, true));
5725 }
5726