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