1 /**
2  * @file
3  * @brief Monsters doing stuff (monsters acting).
4 **/
5 
6 #include "AppHdr.h"
7 
8 #include "mon-act.h"
9 
10 #include "act-iter.h"
11 #include "areas.h"
12 #include "arena.h"
13 #include "attitude-change.h"
14 #include "bloodspatter.h"
15 #include "cloud.h"
16 #include "colour.h"
17 #include "coordit.h"
18 #include "corpse.h"
19 #include "dbg-scan.h"
20 #include "delay.h"
21 #include "directn.h" // feature_description_at
22 #include "dungeon.h"
23 #include "english.h" // apostrophise
24 #include "fight.h"
25 #include "fineff.h"
26 #include "ghost.h"
27 #include "god-abil.h" // GOZAG_GOLD_AURA_KEY
28 #include "god-passive.h"
29 #include "god-prayer.h"
30 #include "hints.h"
31 #include "item-name.h"
32 #include "item-prop.h"
33 #include "item-status-flag-type.h"
34 #include "items.h"
35 #include "level-state-type.h"
36 #include "libutil.h"
37 #include "losglobal.h"
38 #include "los.h"
39 #include "mapmark.h"
40 #include "message.h"
41 #include "mon-abil.h"
42 #include "mon-behv.h"
43 #include "mon-book.h"
44 #include "mon-cast.h"
45 #include "mon-death.h"
46 #include "mon-movetarget.h"
47 #include "mon-place.h"
48 #include "mon-poly.h"
49 #include "mon-project.h"
50 #include "mon-speak.h"
51 #include "mon-tentacle.h"
52 #include "nearby-danger.h"
53 #include "religion.h"
54 #include "shout.h"
55 #include "spl-book.h"
56 #include "spl-clouds.h"
57 #include "spl-damage.h"
58 #include "spl-summoning.h"
59 #include "spl-transloc.h"
60 #include "spl-util.h"
61 #include "spl-zap.h"
62 #include "state.h"
63 #include "stringutil.h"
64 #include "target.h"
65 #include "teleport.h"
66 #include "terrain.h"
67 #include "throw.h"
68 #include "timed-effects.h"
69 #include "traps.h"
70 #include "viewchar.h"
71 #include "view.h"
72 
73 static bool _handle_pickup(monster* mons);
74 static void _mons_in_cloud(monster& mons);
75 static bool _monster_move(monster* mons);
76 static bool _monster_swaps_places(monster* mon, const coord_def& delta);
77 
78 // [dshaligram] Doesn't need to be extern.
79 static coord_def mmov;
80 
81 /**
82  * Get the monster's "hit dice".
83  *
84  * @return          The monster's HD.
85  */
get_hit_dice() const86 int monster::get_hit_dice() const
87 {
88     const int base_hd = get_experience_level();
89 
90     const mon_enchant drain_ench = get_ench(ENCH_DRAINED);
91     const int drained_hd = base_hd - drain_ench.degree;
92 
93     // temp malmuts (-25% HD)
94     if (has_ench(ENCH_WRETCHED))
95         return max(drained_hd * 3 / 4, 1);
96     return max(drained_hd, 1);
97 }
98 
99 /**
100  * Get the monster's "experience level" - their hit dice, unmodified by
101  * temporary enchantments (draining).
102  *
103  * @return          The monster's XL.
104  */
get_experience_level() const105 int monster::get_experience_level() const
106 {
107     return hit_dice;
108 }
109 
110 static const coord_def mon_compass[8] =
111 {
112     { -1,-1 }, { 0,-1 }, {  1,-1 }, {  1,0 }, // bjnl
113     {  1, 1 }, { 0, 1 }, { -1, 1 }, { -1,0 }  // ukyh
114 };
115 
_compass_idx(const coord_def & mov)116 static int _compass_idx(const coord_def& mov)
117 {
118     for (int i = 0; i < 8; i++)
119         if (mon_compass[i] == mov)
120             return i;
121     return -1;
122 }
123 
_mons_natural_regen_roll(monster * mons)124 static inline bool _mons_natural_regen_roll(monster* mons)
125 {
126     const int regen_rate = mons->natural_regen_rate();
127     return x_chance_in_y(regen_rate, 25);
128 }
129 
130 // Do natural regeneration for monster.
_monster_regenerate(monster * mons)131 static void _monster_regenerate(monster* mons)
132 {
133     // Early bailout so that regen-based triggers don't get spammed
134     if (mons->hit_points == mons->max_hit_points)
135         return;
136 
137     if (crawl_state.disables[DIS_MON_REGEN])
138         return;
139 
140     if (mons->has_ench(ENCH_SICK)
141         || !mons_can_regenerate(*mons) && !(mons->has_ench(ENCH_REGENERATION)))
142     {
143         return;
144     }
145 
146     // Non-land creatures out of their element cannot regenerate.
147     if (mons_primary_habitat(*mons) != HT_LAND
148         && !monster_habitable_grid(mons, env.grid(mons->pos())))
149     {
150         return;
151     }
152 
153     if (mons_class_fast_regen(mons->type)
154         || mons->has_ench(ENCH_REGENERATION)
155         || _mons_natural_regen_roll(mons))
156     {
157         mons->heal(1);
158     }
159 
160     if (mons_is_hepliaklqana_ancestor(mons->type))
161     {
162         if (mons->hit_points == mons->max_hit_points && you.can_see(*mons))
163             interrupt_activity(activity_interrupt::ancestor_hp);
164     }
165 }
166 
_escape_water_hold(monster & mons)167 static void _escape_water_hold(monster& mons)
168 {
169     if (mons.has_ench(ENCH_WATER_HOLD))
170     {
171         simple_monster_message(mons, " is no longer engulfed.");
172         mons.del_ench(ENCH_WATER_HOLD);
173     }
174 }
175 
_handle_manticore_barbs(monster & mons)176 static void _handle_manticore_barbs(monster& mons)
177 {
178     if (mons.has_ench(ENCH_BARBS))
179     {
180         mon_enchant barbs = mons.get_ench(ENCH_BARBS);
181 
182         // Save these first because hurt() might kill the monster.
183         const coord_def pos = mons.pos();
184         const monster_type type = mons.type;
185         mons.hurt(monster_by_mid(barbs.source),
186                   roll_dice(2, barbs.degree * 2 + 2));
187         bleed_onto_floor(pos, type, 2, false);
188         if (coinflip())
189         {
190             barbs.duration--;
191             mons.update_ench(barbs);
192         }
193     }
194 }
195 
_swap_monsters(monster & mover,monster & moved)196 static bool _swap_monsters(monster& mover, monster& moved)
197 {
198     // Can't swap with a stationary monster.
199     // Although nominally stationary kraken tentacles can be swapped
200     // with the main body.
201     if (moved.is_stationary() && !moved.is_child_tentacle())
202         return false;
203 
204     // If the target monster is constricted it is stuck
205     // and not eligible to be swapped with
206     if (moved.is_constricted())
207     {
208         dprf("%s fails to swap with %s, constricted.",
209             mover.name(DESC_THE).c_str(),
210             moved.name(DESC_THE).c_str());
211             return false;
212     }
213 
214     // Swapping is a purposeful action.
215     if (mover.confused())
216         return false;
217 
218     // Right now just happens in sanctuary.
219     if (!is_sanctuary(mover.pos()) || !is_sanctuary(moved.pos()))
220         return false;
221 
222     // A friendly or good-neutral monster moving past a fleeing hostile
223     // or neutral monster, or vice versa.
224     if (mover.wont_attack() == moved.wont_attack()
225         || mons_is_retreating(mover) == mons_is_retreating(moved))
226     {
227         return false;
228     }
229 
230     // Don't swap places if the player explicitly ordered their pet to
231     // attack monsters.
232     if ((mover.friendly() || moved.friendly())
233         && you.pet_target != MHITYOU && you.pet_target != MHITNOT)
234     {
235         return false;
236     }
237 
238     // Okay, we can probably do the swap.
239     if (!mover.swap_with(&moved))
240         return false;
241 
242     if (you.can_see(mover) && you.can_see(moved))
243     {
244         mprf("%s and %s swap places.", mover.name(DESC_THE).c_str(),
245              moved.name(DESC_THE).c_str());
246     }
247 
248     _escape_water_hold(mover);
249 
250     _handle_manticore_barbs(mover);
251     _handle_manticore_barbs(moved);
252 
253     if (moved.type == MONS_FOXFIRE)
254     {
255         mprf(MSGCH_GOD, "By Zin's power the foxfire is contained!");
256         monster_die(moved, KILL_DISMISSED, NON_MONSTER, true);
257     }
258 
259     return true;
260 }
261 
_do_mon_spell(monster * mons)262 static bool _do_mon_spell(monster* mons)
263 {
264     if (handle_mon_spell(mons))
265     {
266         // If a Pan lord/pghost is known to be a spellcaster, it's safer
267         // to assume it has ranged spells too. For others, it'd just
268         // lead to unnecessary false positives.
269         if (mons_is_ghost_demon(mons->type))
270             mons->flags |= MF_SEEN_RANGED;
271 
272         mmov.reset();
273         return true;
274     }
275 
276     return false;
277 }
278 
_swim_or_move_energy(monster & mon)279 static void _swim_or_move_energy(monster& mon)
280 {
281     const dungeon_feature_type feat = env.grid(mon.pos());
282 
283     // FIXME: Replace check with mons_is_swimming()?
284     mon.lose_energy(((feat_is_lava(feat) || feat_is_water(feat))
285                      && mon.ground_level()) ? EUT_SWIM : EUT_MOVE);
286 }
287 
_unfriendly_or_impaired(const monster & mon)288 static bool _unfriendly_or_impaired(const monster& mon)
289 {
290     return !mon.wont_attack() || mon.has_ench(ENCH_INSANE) || mon.confused();
291 }
292 
293 // Check up to eight grids in the given direction for whether there's a
294 // monster of the same alignment as the given monster that happens to
295 // have a ranged attack. If this is true for the first monster encountered,
296 // returns true. Otherwise returns false.
_ranged_ally_in_dir(monster * mon,coord_def p)297 static bool _ranged_ally_in_dir(monster* mon, coord_def p)
298 {
299     coord_def pos = mon->pos();
300 
301     for (int i = 1; i <= LOS_RADIUS; i++)
302     {
303         pos += p;
304         if (!in_bounds(pos))
305             break;
306 
307         const actor* ally = actor_at(pos);
308         if (ally == nullptr)
309             continue;
310 
311         if (mons_aligned(mon, ally))
312         {
313             // Hostile monsters of normal intelligence only move aside for
314             // monsters of the same type.
315             if (_unfriendly_or_impaired(*mon)
316                 && mons_genus(mon->type) != mons_genus(ally->type))
317             {
318                 return false;
319             }
320 
321             // XXX: Sometimes the player wants llies in front of them to stay
322             // out of LOF. However use of allies for cover is extremely common,
323             // so it doesn't work well to always have allies move out of player
324             // LOF. Until a better interface or method can be found to handle
325             // both cases, have allies move out of the way only for other
326             // monsters.
327             if (ally->is_monster())
328                 return mons_has_ranged_attack(*(ally->as_monster()));
329         }
330         break;
331     }
332     return false;
333 }
334 
335 // Check whether there's a monster of the same type and alignment adjacent
336 // to the given monster in at least one of three given directions (relative to
337 // the monster position).
_allied_monster_at(monster * mon,coord_def a,coord_def b,coord_def c)338 static bool _allied_monster_at(monster* mon, coord_def a, coord_def b,
339                                coord_def c)
340 {
341     for (coord_def delta : { a, b, c })
342     {
343         coord_def pos = mon->pos() + delta;
344         if (!in_bounds(pos))
345             continue;
346 
347         const monster* ally = monster_at(pos);
348         if (ally == nullptr)
349             continue;
350 
351         if (ally->is_stationary() || ally->reach_range() > REACH_NONE)
352             continue;
353 
354         // Hostile monsters of normal intelligence only move aside for
355         // monsters of the same genus.
356         if (_unfriendly_or_impaired(*mon)
357             && mons_genus(mon->type) != mons_genus(ally->type))
358         {
359             continue;
360         }
361 
362         if (mons_aligned(mon, ally))
363             return true;
364     }
365 
366     return false;
367 }
368 
369 // Altars as well as branch entrances are considered interesting for
370 // some monster types.
_mon_on_interesting_grid(monster * mon)371 static bool _mon_on_interesting_grid(monster* mon)
372 {
373     const dungeon_feature_type feat = env.grid(mon->pos());
374 
375     switch (feat)
376     {
377     // Holy beings will tend to patrol around altars to the good gods.
378     case DNGN_ALTAR_ELYVILON:
379         if (!one_chance_in(3))
380             return false;
381         // else fall through
382     case DNGN_ALTAR_ZIN:
383     case DNGN_ALTAR_SHINING_ONE:
384         return mon->is_holy();
385 
386     // Orcs will tend to patrol around altars to Beogh, and guard the
387     // stairway from and to the Orcish Mines.
388     case DNGN_ALTAR_BEOGH:
389     case DNGN_ENTER_ORC:
390     case DNGN_EXIT_ORC:
391         return mons_is_native_in_branch(*mon, BRANCH_ORC);
392 
393     // Same for elves and the Elven Halls.
394     case DNGN_ENTER_ELF:
395     case DNGN_EXIT_ELF:
396         return mons_is_native_in_branch(*mon, BRANCH_ELF);
397 
398     // Spiders...
399     case DNGN_ENTER_SPIDER:
400         return mons_is_native_in_branch(*mon, BRANCH_SPIDER);
401 
402     default:
403         return false;
404     }
405 }
406 
407 // If a hostile monster finds itself on a grid of an "interesting" feature,
408 // while unoccupied, it will remain in that area, and try to return to it
409 // if it left it for fighting, seeking etc.
_maybe_set_patrol_route(monster * mons)410 static void _maybe_set_patrol_route(monster* mons)
411 {
412     if (_mon_on_interesting_grid(mons) // Patrolling shouldn't always happen
413         && one_chance_in(4)
414         && mons_is_wandering(*mons)
415         && !mons->is_patrolling()
416         && !mons->friendly())
417     {
418         mons->patrol_point = mons->pos();
419     }
420 }
421 
_mons_can_cast_dig(const monster * mons,bool random)422 static bool _mons_can_cast_dig(const monster* mons, bool random)
423 {
424     if (mons->foe == MHITNOT || !mons->has_spell(SPELL_DIG) || mons->confused()
425         || mons->berserk_or_insane())
426     {
427         return false;
428     }
429 
430     const bool antimagiced = mons->has_ench(ENCH_ANTIMAGIC)
431                       && (random
432                           && !x_chance_in_y(4 * BASELINE_DELAY,
433                                             4 * BASELINE_DELAY
434                                             + mons->get_ench(ENCH_ANTIMAGIC).duration)
435                       || (!random
436                           && mons->get_ench(ENCH_ANTIMAGIC).duration
437                              >= 4 * BASELINE_DELAY));
438     const auto flags = mons->spell_slot_flags(SPELL_DIG);
439     return !(antimagiced && flags & MON_SPELL_ANTIMAGIC_MASK)
440             && !(mons->is_silenced() && flags & MON_SPELL_SILENCE_MASK);
441 }
442 
_mons_can_zap_dig(const monster * mons)443 static bool _mons_can_zap_dig(const monster* mons)
444 {
445     return mons->foe != MHITNOT
446            && !mons->asleep()
447            && !mons->confused() // they don't get here anyway
448            && !mons->berserk_or_insane()
449            && !mons->submerged()
450            && mons_itemuse(*mons) >= MONUSE_STARTING_EQUIPMENT
451            && mons->inv[MSLOT_WAND] != NON_ITEM
452            && env.item[mons->inv[MSLOT_WAND]].is_type(OBJ_WANDS, WAND_DIGGING)
453            && env.item[mons->inv[MSLOT_WAND]].charges > 0;
454 }
455 
_set_mons_move_dir(const monster * mons,coord_def * dir,coord_def * delta)456 static void _set_mons_move_dir(const monster* mons,
457                                coord_def* dir, coord_def* delta)
458 {
459     ASSERT(dir);
460     ASSERT(delta);
461 
462     // Some calculations.
463     if ((mons->can_burrow()
464          || _mons_can_cast_dig(mons, false))
465         && mons->foe == MHITYOU)
466     {
467         // Digging monsters always move in a straight line in your direction.
468         *delta = you.pos() - mons->pos();
469     }
470     else
471     {
472         *delta = (mons->firing_pos.zero() ? mons->target : mons->firing_pos)
473                  - mons->pos();
474     }
475 
476     // Move the monster.
477     *dir = delta->sgn();
478 
479     if (mons_is_retreating(*mons)
480         && (!mons->friendly() || mons->target != you.pos()))
481     {
482         *dir *= -1;
483     }
484 }
485 
486 typedef FixedArray< bool, 3, 3 > move_array;
487 
_fill_good_move(const monster * mons,move_array * good_move)488 static void _fill_good_move(const monster* mons, move_array* good_move)
489 {
490     for (int count_x = 0; count_x < 3; count_x++)
491         for (int count_y = 0; count_y < 3; count_y++)
492         {
493             const int targ_x = mons->pos().x + count_x - 1;
494             const int targ_y = mons->pos().y + count_y - 1;
495 
496             // Bounds check: don't consider moving out of grid!
497             if (!in_bounds(targ_x, targ_y))
498             {
499                 (*good_move)[count_x][count_y] = false;
500                 continue;
501             }
502 
503             (*good_move)[count_x][count_y] =
504                 mon_can_move_to_pos(mons, coord_def(count_x-1, count_y-1));
505         }
506 }
507 
508 // This only tracks movement, not whether hitting an
509 // adjacent monster is a possible move.
mons_can_move_towards_target(const monster * mon)510 bool mons_can_move_towards_target(const monster* mon)
511 {
512     coord_def mov, delta;
513     _set_mons_move_dir(mon, &mov, &delta);
514 
515     move_array good_move;
516     _fill_good_move(mon, &good_move);
517 
518     int dir = _compass_idx(mov);
519     for (int i = -1; i <= 1; ++i)
520     {
521         const int altdir = (dir + i + 8) % 8;
522         const coord_def p = mon_compass[altdir] + coord_def(1, 1);
523         if (good_move(p))
524             return true;
525     }
526 
527     return false;
528 }
529 
530 static const string BATTY_TURNS_KEY = "BATTY_TURNS";
531 
_be_batty(monster & mons)532 static void _be_batty(monster &mons)
533 {
534     mons.behaviour = BEH_WANDER;
535     set_random_target(&mons);
536     mons.props[BATTY_TURNS_KEY] = 0;
537 }
538 
_fungal_move_check(monster & mon)539 static bool _fungal_move_check(monster &mon)
540 {
541     // These monsters have restrictions on moving while you are looking.
542     if ((mon.type == MONS_WANDERING_MUSHROOM || mon.type == MONS_DEATHCAP)
543             && mon.foe_distance() > 1 // can attack if adjacent
544         || (mon.type == MONS_LURKING_HORROR
545             // 1 in los chance at max los
546             && mon.foe_distance() > random2(you.current_vision + 1)))
547     {
548         if (!mon.wont_attack() && is_sanctuary(mon.pos()))
549             return true;
550 
551         if (mon_enemies_around(&mon))
552             return false;
553     }
554     return true;
555 }
556 
_handle_movement(monster * mons)557 static void _handle_movement(monster* mons)
558 {
559     if (!_fungal_move_check(*mons))
560     {
561         mmov.reset();
562         return;
563     }
564 
565     _maybe_set_patrol_route(mons);
566 
567     if (sanctuary_exists())
568     {
569         // Monsters will try to flee out of a sanctuary.
570         if (is_sanctuary(mons->pos())
571             && mons_is_influenced_by_sanctuary(*mons)
572             && !mons_is_fleeing_sanctuary(*mons))
573         {
574             mons_start_fleeing_from_sanctuary(*mons);
575         }
576         else if (mons_is_fleeing_sanctuary(*mons)
577                  && !is_sanctuary(mons->pos()))
578         {
579             // Once outside there's a chance they'll regain their courage.
580             // Nonliving and berserking monsters always stop immediately,
581             // since they're only being forced out rather than actually
582             // scared.
583             if (mons->is_nonliving()
584                 || mons->berserk()
585                 || mons->has_ench(ENCH_INSANE)
586                 || x_chance_in_y(2, 5))
587             {
588                 mons_stop_fleeing_from_sanctuary(*mons);
589             }
590         }
591     }
592 
593     coord_def delta;
594     _set_mons_move_dir(mons, &mmov, &delta);
595 
596     if (sanctuary_exists())
597     {
598         // Don't allow monsters to enter a sanctuary or attack you inside a
599         // sanctuary, even if you're right next to them.
600         if (is_sanctuary(mons->pos() + mmov)
601             && (!is_sanctuary(mons->pos())
602                 || mons->pos() + mmov == you.pos()))
603         {
604             mmov.reset();
605         }
606     }
607 
608     // Bounds check: don't let fleeing monsters try to run off the grid.
609     const coord_def s = mons->pos() + mmov;
610     if (!in_bounds_x(s.x))
611         mmov.x = 0;
612     if (!in_bounds_y(s.y))
613         mmov.y = 0;
614 
615     if (delta.rdist() > 3)
616     {
617         // Reproduced here is some semi-legacy code that makes monsters
618         // move somewhat randomly along oblique paths. It is an
619         // exceedingly good idea, given crawl's unique line of sight
620         // properties.
621         //
622         // Added a check so that oblique movement paths aren't used when
623         // close to the target square. -- bwr
624 
625         // Sometimes we'll just move parallel the x axis.
626         if (abs(delta.x) > abs(delta.y) && coinflip())
627             mmov.y = 0;
628 
629         // Sometimes we'll just move parallel the y axis.
630         if (abs(delta.y) > abs(delta.x) && coinflip())
631             mmov.x = 0;
632     }
633 
634     // Now quit if we can't move.
635     if (mmov.origin())
636         return;
637 
638     const coord_def newpos(mons->pos() + mmov);
639 
640     // Filling this is relatively costly and not always needed, so be a bit
641     // lazy about it.
642     move_array good_move;
643     bool good_move_filled = false;
644 
645     // If the monster is moving in your direction, whether to attack or
646     // protect you, or towards a monster it intends to attack, check
647     // whether we first need to take a step to the side to make sure the
648     // reinforcement can follow through. Only do this with 50% chance,
649     // though, so it's not completely predictable.
650 
651     // First, check whether the monster is smart enough to even consider
652     // this.
653     if ((newpos == you.pos()
654            || monster_at(newpos) && mons->foe == env.mgrid(newpos))
655         && mons_intel(*mons) > I_BRAINLESS
656         && coinflip()
657         && !mons_is_confused(*mons) && !mons->caught()
658         && !mons->berserk_or_insane())
659     {
660         _fill_good_move(mons, &good_move);
661         good_move_filled = true;
662         // If the monster is moving parallel to the x or y axis, check
663         // if there are other unblocked grids adjacent to the target and
664         // whether
665         //
666         // a) the neighbouring grids are blocked and an ally is behind us,
667         // or
668         // b) we're intelligent and blocking a ranged attack
669         if (mmov.y == 0)
670         {
671             if ((good_move[mmov.x+1][0] || good_move[mmov.x+1][2])
672                 && (_allied_monster_at(mons, coord_def(-mmov.x, -1),
673                                        coord_def(-mmov.x, 0),
674                                        coord_def(-mmov.x, 1))
675                        && !good_move[1][0] && !good_move[1][2]
676                     || mons_intel(*mons) >= I_HUMAN
677                        && _ranged_ally_in_dir(mons, coord_def(-mmov.x, 0))))
678             {
679                 if (good_move[mmov.x+1][0])
680                     mmov.y = -1;
681                 if (good_move[mmov.x+1][2] && (mmov.y == 0 || coinflip()))
682                     mmov.y = 1;
683             }
684         }
685         else if (mmov.x == 0)
686         {
687             if ((good_move[0][mmov.y+1] || good_move[2][mmov.y+1])
688                 && (_allied_monster_at(mons, coord_def(-1, -mmov.y),
689                                        coord_def(0, -mmov.y),
690                                        coord_def(1, -mmov.y))
691                        && !good_move[0][1] && !good_move[2][1]
692                     || mons_intel(*mons) >= I_HUMAN
693                        && _ranged_ally_in_dir(mons, coord_def(0, -mmov.y))))
694             {
695                 if (good_move[0][mmov.y+1])
696                     mmov.x = -1;
697                 if (good_move[2][mmov.y+1] && (mmov.x == 0 || coinflip()))
698                     mmov.x = 1;
699             }
700         }
701         else // We're moving diagonally.
702         {
703             if (good_move[mmov.x+1][1])
704             {
705                 if (!good_move[1][mmov.y+1]
706                        && _allied_monster_at(mons, coord_def(-mmov.x, -1),
707                                            coord_def(-mmov.x, 0),
708                                            coord_def(-mmov.x, 1))
709                     || mons_intel(*mons) >= I_HUMAN
710                        && _ranged_ally_in_dir(mons, coord_def(-mmov.x, -mmov.y)))
711                 {
712                     mmov.y = 0;
713                 }
714             }
715             else if (good_move[1][mmov.y+1]
716                      && _allied_monster_at(mons, coord_def(-1, -mmov.y),
717                                             coord_def(0, -mmov.y),
718                                             coord_def(1, -mmov.y))
719                          || mons_intel(*mons) >= I_HUMAN
720                             && _ranged_ally_in_dir(mons, coord_def(-mmov.x, -mmov.y)))
721             {
722                 mmov.x = 0;
723             }
724         }
725     }
726 
727     // Now quit if we can't move.
728     if (mmov.origin())
729         return;
730 
731     // everything below here is irrelevant if the player is not in bounds, for
732     // example if they have stepped from time.
733     if (!in_bounds(you.pos()))
734         return;
735 
736     // Try to stay in sight of the player if we're moving towards
737     // him/her, in order to avoid the monster coming into view,
738     // shouting, and then taking a step in a path to the player which
739     // temporarily takes it out of view, which can lead to the player
740     // getting "comes into view" and shout messages with no monster in
741     // view.
742 
743     // Doesn't matter for arena mode.
744     if (crawl_state.game_is_arena())
745         return;
746 
747     // Did we just come into view?
748     // TODO: This doesn't seem to work right. Fix, or remove?
749 
750     if (mons->seen_context != SC_JUST_SEEN)
751         return;
752     if (testbits(mons->flags, MF_WAS_IN_VIEW))
753         return;
754 
755     const coord_def old_pos  = mons->pos();
756     const int       old_dist = grid_distance(you.pos(), old_pos);
757 
758     // We're already staying in the player's LOS.
759     if (you.see_cell(old_pos + mmov))
760         return;
761 
762     // We're not moving towards the player.
763     if (grid_distance(you.pos(), old_pos + mmov) >= old_dist)
764     {
765         // Instead of moving out of view, we stay put.
766         if (you.see_cell(old_pos))
767             mmov.reset();
768         return;
769     }
770 
771     // Try to find a move that brings us closer to the player while
772     // keeping us in view.
773     int matches = 0;
774     for (int i = 0; i < 3; i++)
775         for (int j = 0; j < 3; j++)
776         {
777             if (i == 0 && j == 0)
778                 continue;
779 
780             coord_def d(i - 1, j - 1);
781             coord_def tmp = old_pos + d;
782             if (!you.see_cell(tmp))
783                 continue;
784 
785             if (!good_move_filled)
786             {
787                 _fill_good_move(mons, &good_move);
788                 good_move_filled = true;
789             }
790             if (!good_move[i][j])
791                 continue;
792 
793             if (grid_distance(you.pos(), tmp) < old_dist)
794             {
795                 if (one_chance_in(++matches))
796                     mmov = d;
797                 break;
798             }
799         }
800 
801     // We haven't been able to find a visible cell to move to. If previous
802     // position was visible, we stay put.
803     if (you.see_cell(old_pos) && !you.see_cell(old_pos + mmov))
804         mmov.reset();
805 }
806 
_handle_potion(monster & mons)807 static bool _handle_potion(monster& mons)
808 {
809     item_def* potion = mons.mslot_item(MSLOT_POTION);
810     if (mons.asleep()
811         || !potion
812         || !one_chance_in(3)
813         || mons_itemuse(mons) < MONUSE_STARTING_EQUIPMENT
814         || potion->base_type != OBJ_POTIONS)
815     {
816         return false;
817     }
818 
819     bool rc = false;
820 
821     const potion_type ptype = static_cast<potion_type>(potion->sub_type);
822 
823     if (mons.can_drink_potion(ptype) && mons.should_drink_potion(ptype))
824     {
825         const bool was_visible = you.can_see(mons);
826 
827         // XXX: this is mostly to prevent a funny message order:
828         // "$foo drinks a potion. $foo wields a great mace. $foo goes berserk!"
829         if (ptype == POT_BERSERK_RAGE)
830             mons.wield_melee_weapon();
831 
832         // Drink the potion, and identify it.
833         if (mons.drink_potion_effect(ptype) && was_visible)
834             set_ident_type(OBJ_POTIONS, ptype, true);
835 
836         // Remove it from inventory.
837         if (dec_mitm_item_quantity(potion->index(), 1))
838             mons.inv[MSLOT_POTION] = NON_ITEM;
839 
840         mons.lose_energy(EUT_ITEM);
841         rc = true;
842     }
843 
844     return rc;
845 }
846 
847 /**
848  * Check if the monster has a swooping attack and is in a position to
849  * use it, and do so if they can.
850  *
851  * Specifically, this seems to try to move to the opposite side of the target
852  * (if there's space) and perform a melee attack, then set a cooldown for
853  * 4-8 turns.
854  *
855  * @param mons The monster who might be swooping.
856  * @return Whether they performed a swoop attack. False if the monster
857  *         can't swoop, the foe isn't hostile, the positioning doesn't
858  *         work, etc.
859  */
_handle_swoop(monster & mons)860 static bool _handle_swoop(monster& mons)
861 {
862     // TODO: check for AF_SWOOP in other slots and/or make it work there?
863     if (mons_attack_spec(mons, 0, true).flavour != AF_SWOOP)
864         return false;
865 
866     actor *defender = mons.get_foe();
867     if (mons.confused() || !defender || !mons.can_see(*defender))
868         return false;
869 
870     if (mons.foe_distance() >= 5 || mons.foe_distance() == 1)
871         return false;
872 
873     if (!one_chance_in(4))
874         return false;
875 
876     if (mons.props.exists("swoop_cooldown")
877         && (you.elapsed_time < mons.props["swoop_cooldown"].get_int()))
878     {
879         return false;
880     }
881 
882     coord_def target = defender->pos();
883 
884     bolt tracer;
885     tracer.source = mons.pos();
886     tracer.target = target;
887     tracer.is_tracer = true;
888     tracer.pierce = true;
889     tracer.range = LOS_RADIUS;
890     tracer.fire();
891 
892     for (unsigned int j = 0; j < tracer.path_taken.size() - 1; ++j)
893     {
894         if (tracer.path_taken[j] != target)
895             continue;
896 
897         if (!monster_habitable_grid(&mons, env.grid(tracer.path_taken[j+1]))
898             || actor_at(tracer.path_taken[j+1]))
899         {
900             continue;
901         }
902 
903         if (you.can_see(mons))
904         {
905             mprf("%s swoops through the air toward %s!",
906                  mons.name(DESC_THE).c_str(),
907                  defender->name(DESC_THE).c_str());
908         }
909         mons.move_to_pos(tracer.path_taken[j+1]);
910         fight_melee(&mons, defender);
911         mons.props["swoop_cooldown"].get_int() = you.elapsed_time
912                                                   + 40 + random2(51);
913         return true;
914     }
915 
916     return false;
917 }
918 
919 /**
920  * Check whether this monster can make a reaching attack, and do so if
921  * they can.
922  *
923  * @param mons The monster who might be reaching.
924  * @return Whether they attempted a reaching attack. False if the monster
925  *         doesn't have a reaching weapon, the foe isn't hostile, the foe
926  *         is too near or too far, etc.
927  */
_handle_reaching(monster & mons)928 static bool _handle_reaching(monster& mons)
929 {
930     bool       ret = false;
931     const reach_type range = mons.reach_range();
932     actor *foe = mons.get_foe();
933 
934     if (mons.caught()
935         || mons_is_confused(mons)
936         || !foe
937         || range <= REACH_NONE
938         || is_sanctuary(mons.pos())
939         || is_sanctuary(foe->pos())
940         || mons.submerged()
941         || (mons_aligned(&mons, foe) && !mons.has_ench(ENCH_INSANE))
942         || mons_is_fleeing(mons)
943         || mons.pacified())
944     {
945         return false;
946     }
947 
948     const coord_def foepos(foe->pos());
949     if (can_reach_attack_between(mons.pos(), foepos)
950         // The monster has to be attacking the correct position.
951         && mons.target == foepos)
952     {
953         ret = true;
954 
955         ASSERT(foe->is_player() || foe->is_monster());
956 
957         fight_melee(&mons, foe);
958     }
959 
960     return ret;
961 }
962 
_handle_scroll(monster & mons)963 static bool _handle_scroll(monster& mons)
964 {
965     item_def* scroll = mons.mslot_item(MSLOT_SCROLL);
966 
967     // Yes, there is a logic to this ordering {dlb}:
968     if (mons.asleep()
969         || mons_is_confused(mons)
970         || mons.submerged()
971         || !scroll
972         || mons.has_ench(ENCH_BLIND)
973         || !one_chance_in(3)
974         || mons_itemuse(mons) < MONUSE_STARTING_EQUIPMENT
975         || silenced(mons.pos())
976         || scroll->base_type != OBJ_SCROLLS)
977     {
978         return false;
979     }
980 
981     bool read        = false;
982     bool was_visible = you.can_see(mons);
983 
984     // Notice how few cases are actually accounted for here {dlb}:
985     const int scroll_type = scroll->sub_type;
986     switch (scroll_type)
987     {
988     case SCR_TELEPORTATION:
989         if (!mons.has_ench(ENCH_TP) && !mons.no_tele(true, false) && mons.pacified())
990         {
991             simple_monster_message(mons, " reads a scroll.");
992             read = true;
993             monster_teleport(&mons, false);
994         }
995         break;
996 
997     case SCR_BLINKING:
998         if (mons.pacified() && mons.can_see(you) && !mons.no_tele(true, false))
999         {
1000             simple_monster_message(mons, " reads a scroll.");
1001             read = true;
1002             if (mons.caught())
1003                 monster_blink(&mons);
1004             else
1005                 blink_away(&mons);
1006         }
1007         break;
1008 
1009     case SCR_SUMMONING:
1010         if (mons.can_see(you))
1011         {
1012             simple_monster_message(mons, " reads a scroll.");
1013             mprf("Wisps of shadow swirl around %s.", mons.name(DESC_THE).c_str());
1014             read = true;
1015             int count = roll_dice(2, 2);
1016             for (int i = 0; i < count; ++i)
1017             {
1018                 create_monster(
1019                     mgen_data(RANDOM_MOBILE_MONSTER, SAME_ATTITUDE((&mons)),
1020                               mons.pos(), mons.foe)
1021                     .set_summoned(&mons, 3, MON_SUMM_SCROLL));
1022             }
1023         }
1024         break;
1025     }
1026 
1027     if (read)
1028     {
1029         if (dec_mitm_item_quantity(mons.inv[MSLOT_SCROLL], 1))
1030             mons.inv[MSLOT_SCROLL] = NON_ITEM;
1031 
1032         if (was_visible)
1033             set_ident_type(OBJ_SCROLLS, scroll_type, true);
1034 
1035         mons.lose_energy(EUT_ITEM);
1036     }
1037 
1038     return read;
1039 }
1040 
_mons_fire_wand(monster & mons,item_def & wand,bolt & beem,bool was_visible)1041 static void _mons_fire_wand(monster& mons, item_def &wand, bolt &beem,
1042                             bool was_visible)
1043 {
1044     if (!simple_monster_message(mons, " zaps a wand."))
1045     {
1046         if (!silenced(you.pos()))
1047             mprf(MSGCH_SOUND, "You hear a zap.");
1048     }
1049 
1050     // charge expenditure {dlb}
1051     wand.charges--;
1052     const spell_type mzap =
1053         spell_in_wand(static_cast<wand_type>(wand.sub_type));
1054 
1055     mons_cast(&mons, beem, mzap, MON_SPELL_EVOKE, false);
1056 
1057     if (was_visible)
1058     {
1059         if (wand.charges <= 0)
1060             mprf("The now-empty wand crumbles to dust.");
1061         else
1062             mons.flags |= MF_SEEN_RANGED;
1063     }
1064 
1065     if (wand.charges <= 0)
1066         dec_mitm_item_quantity(wand.index(), 1);
1067 
1068     mons.lose_energy(EUT_ITEM);
1069 }
1070 
_handle_wand(monster & mons)1071 static bool _handle_wand(monster& mons)
1072 {
1073     item_def *wand = mons.mslot_item(MSLOT_WAND);
1074     // Yes, there is a logic to this ordering {dlb}:
1075     // (no there's not -- pf)
1076     if (!wand
1077         || wand->base_type != OBJ_WANDS
1078 
1079         || !mons.get_foe()
1080         || !cell_see_cell(mons.pos(), mons.get_foe()->pos(), LOS_SOLID_SEE)
1081 
1082         || mons.asleep()
1083         || mons_is_fleeing(mons)
1084         || mons.pacified()
1085         || mons.confused()
1086         || mons_itemuse(mons) < MONUSE_STARTING_EQUIPMENT
1087         || mons.has_ench(ENCH_SUBMERGED)
1088 
1089         || x_chance_in_y(3, 4))
1090     {
1091         return false;
1092     }
1093 
1094     if (wand->charges <= 0)
1095         return false;
1096 
1097     if (item_type_removed(wand->base_type, wand->sub_type))
1098         return false;
1099 
1100     // Digging is handled elsewhere so that sensible (wall) targets are
1101     // chosen.
1102     if (wand->sub_type == WAND_DIGGING)
1103         return false;
1104 
1105     bolt beem;
1106 
1107     const spell_type mzap =
1108         spell_in_wand(static_cast<wand_type>(wand->sub_type));
1109 
1110     if (!setup_mons_cast(&mons, beem, mzap, true))
1111         return false;
1112 
1113     beem.source     = mons.pos();
1114     beem.aux_source =
1115         wand->name(DESC_QUALNAME, false, true, false, false);
1116 
1117     bool should_fire = false;
1118     const wand_type kind = (wand_type)wand->sub_type;
1119     switch (kind)
1120     {
1121     case WAND_MINDBURST:
1122         // Dial down damage from wands of disintegration, since
1123         // disintegration beams can do large amounts of damage.
1124         beem.damage.size = beem.damage.size * 2 / 3;
1125 
1126         // Intentional fallthrough
1127     default:
1128         fire_tracer(&mons, beem);
1129         should_fire = mons_should_fire(beem);
1130         break;
1131     }
1132 
1133     if (should_fire)
1134     {
1135         _mons_fire_wand(mons, *wand, beem, you.see_cell(mons.pos()));
1136         return true;
1137     }
1138 
1139     return false;
1140 }
1141 
handle_throw(monster * mons,bolt & beem,bool teleport,bool check_only)1142 bool handle_throw(monster* mons, bolt & beem, bool teleport, bool check_only)
1143 {
1144     // Yes, there is a logic to this ordering {dlb}:
1145     if (mons->incapacitated()
1146         || mons->submerged()
1147         || mons->caught()
1148         || mons_is_confused(*mons))
1149     {
1150         return false;
1151     }
1152 
1153     if (mons_itemuse(*mons) < MONUSE_STARTING_EQUIPMENT
1154         && mons->type != MONS_SPECTRAL_THING
1155         && !mons_class_is_animated_object(mons->type))
1156     {
1157         return false;
1158     }
1159 
1160     const bool prefer_ranged_attack
1161         = mons_class_flag(mons->type, M_PREFER_RANGED);
1162 
1163     // Don't allow offscreen throwing for now.
1164     if (mons->foe == MHITYOU && !you.see_cell(mons->pos()))
1165         return false;
1166 
1167     // Most monsters won't shoot in melee range, largely for balance reasons.
1168     // Specialist archers are an exception to this rule, though most archers
1169     // lack the M_PREFER_RANGED flag.
1170     // Monsters who only can attack with ranged still should. Keep in mind
1171     // that M_PREFER_RANGED only applies if the monster has ammo.
1172     if (adjacent(beem.target, mons->pos()) && !prefer_ranged_attack)
1173         return false;
1174 
1175     // Don't let fleeing (or pacified creatures) stop to shoot at things
1176     if (mons_is_fleeing(*mons) || mons->pacified())
1177         return false;
1178 
1179     item_def *launcher = nullptr;
1180     const item_def *weapon = nullptr;
1181     const int mon_item = mons_usable_missile(mons, &launcher);
1182 
1183     if (mon_item == NON_ITEM || !env.item[mon_item].defined())
1184         return false;
1185 
1186     if (player_or_mon_in_sanct(*mons))
1187         return false;
1188 
1189     item_def *missile = &env.item[mon_item];
1190 
1191     const actor *act = actor_at(beem.target);
1192     ASSERT(missile->base_type == OBJ_MISSILES);
1193     if (act && missile->sub_type == MI_THROWING_NET)
1194     {
1195         // Throwing a net at a target that is already caught would be
1196         // completely useless, so bail out.
1197         if (act->caught())
1198             return false;
1199         // Netting targets that are already permanently stuck in place
1200         // is similarly useless.
1201         if (mons_class_is_stationary(act->type))
1202             return false;
1203     }
1204 
1205     if (prefer_ranged_attack)
1206     {
1207         // Master archers are always quite likely to shoot you, if they can.
1208         if (one_chance_in(10))
1209             return false;
1210     }
1211     else if (launcher)
1212     {
1213         // Fellas with ranged weapons are likely to use them, though slightly
1214         // less likely than master archers. XXX: this is a bit silly and we
1215         // could probably collapse this chance and master archers' together.
1216         if (one_chance_in(5))
1217             return false;
1218     }
1219     else if (!one_chance_in(3))
1220     {
1221         // Monsters with throwing weapons only use them one turn in three
1222         // if they're not master archers.
1223         return false;
1224     }
1225 
1226     if (launcher)
1227     {
1228         // If the attack needs a launcher that we can't wield, bail out.
1229         weapon = mons->mslot_item(MSLOT_WEAPON);
1230         if (weapon && weapon != launcher && weapon->cursed())
1231             return false;
1232     }
1233 
1234     // Ok, we'll try it.
1235     setup_monster_throw_beam(mons, beem);
1236 
1237     // Set fake damage for the tracer.
1238     beem.damage = dice_def(10, 10);
1239 
1240     // Set item for tracer, even though it probably won't be used
1241     beem.item = missile;
1242 
1243     ru_interference interference = DO_NOTHING;
1244     // See if Ru worshippers block or redirect the attack.
1245     if (does_ru_wanna_redirect(mons))
1246     {
1247         interference = get_ru_attack_interference_level();
1248         if (interference == DO_BLOCK_ATTACK)
1249         {
1250             simple_monster_message(*mons,
1251                                 " is stunned by your conviction and fails to attack.",
1252                                 MSGCH_GOD);
1253             return false;
1254         }
1255         else if (interference == DO_REDIRECT_ATTACK)
1256         {
1257             mprf(MSGCH_GOD, "You redirect %s's attack!",
1258                     mons->name(DESC_THE).c_str());
1259             int pfound = 0;
1260             for (radius_iterator ri(you.pos(),
1261                 LOS_DEFAULT); ri; ++ri)
1262             {
1263                 monster* new_target = monster_at(*ri);
1264 
1265                 if (new_target == nullptr
1266                     || mons_is_projectile(new_target->type)
1267                     || mons_is_firewood(*new_target)
1268                     || new_target->friendly())
1269                 {
1270                     continue;
1271                 }
1272 
1273                 ASSERT(new_target);
1274 
1275                 if (one_chance_in(++pfound))
1276                 {
1277                     mons->target = new_target->pos();
1278                     mons->foe = new_target->mindex();
1279                     beem.target = mons->target;
1280                 }
1281             }
1282         }
1283     }
1284 
1285     // Fire tracer.
1286     if (!teleport)
1287         fire_tracer(mons, beem);
1288 
1289     // Clear fake damage (will be set correctly in mons_throw).
1290     beem.damage = dice_def();
1291 
1292     // Good idea?
1293     if (teleport || mons_should_fire(beem) || interference != DO_NOTHING)
1294     {
1295         if (check_only)
1296             return true;
1297 
1298         // Monsters shouldn't shoot if fleeing, so let them "turn to attack".
1299         make_mons_stop_fleeing(mons);
1300 
1301         if (launcher && launcher != weapon)
1302             mons->swap_weapons();
1303 
1304         beem.name.clear();
1305         return mons_throw(mons, beem, mon_item, teleport);
1306     }
1307 
1308     return false;
1309 }
1310 
1311 // Give the monster its action energy (aka speed_increment).
_monster_add_energy(monster & mons)1312 static void _monster_add_energy(monster& mons)
1313 {
1314     if (mons.speed > 0)
1315     {
1316         // Randomise to make counting off monster moves harder:
1317         const int energy_gained =
1318             max(1, div_rand_round(mons.speed * you.time_taken, 10));
1319         mons.speed_increment += energy_gained;
1320     }
1321 }
1322 
1323 #ifdef DEBUG
1324 #    define DEBUG_ENERGY_USE(problem) \
1325     if (mons->speed_increment == old_energy && mons->alive()) \
1326              mprf(MSGCH_DIAGNOSTICS, \
1327                   problem " for monster '%s' consumed no energy", \
1328                   mons->name(DESC_PLAIN).c_str());
1329 #    define DEBUG_ENERGY_USE_REF(problem) \
1330     if (mons.speed_increment == old_energy && mons.alive()) \
1331              mprf(MSGCH_DIAGNOSTICS, \
1332                   problem " for monster '%s' consumed no energy", \
1333                   mons.name(DESC_PLAIN).c_str());
1334 #else
1335 #    define DEBUG_ENERGY_USE(problem) ((void) 0)
1336 #    define DEBUG_ENERGY_USE_REF(problem) ((void) 0)
1337 #endif
1338 
_confused_move_dir(monster * mons)1339 static void _confused_move_dir(monster *mons)
1340 {
1341     mmov.reset();
1342     int pfound = 0;
1343     for (adjacent_iterator ai(mons->pos(), false); ai; ++ai)
1344         if (mons->can_pass_through(*ai) && one_chance_in(++pfound))
1345             mmov = *ai - mons->pos();
1346 }
1347 
_tentacle_move_speed(monster_type type)1348 static int _tentacle_move_speed(monster_type type)
1349 {
1350     if (type == MONS_KRAKEN)
1351         return 10;
1352     else if (type == MONS_TENTACLED_STARSPAWN)
1353         return 18;
1354     else
1355         return 0;
1356 }
1357 
_pre_monster_move(monster & mons)1358 static void _pre_monster_move(monster& mons)
1359 {
1360     mons.hit_points = min(mons.max_hit_points, mons.hit_points);
1361 
1362     if (mons.type == MONS_SPATIAL_MAELSTROM
1363         && !player_in_branch(BRANCH_ABYSS)
1364         && !player_in_branch(BRANCH_ZIGGURAT))
1365     {
1366         for (int i = 0; i < you.time_taken; ++i)
1367         {
1368             if (one_chance_in(100))
1369             {
1370                 mons.banish(&mons);
1371                 return;
1372             }
1373         }
1374     }
1375 
1376     if (mons.has_ench(ENCH_HEXED))
1377     {
1378         const actor* const agent =
1379             actor_by_mid(mons.get_ench(ENCH_HEXED).source);
1380         if (!agent || !agent->alive())
1381             mons.del_ench(ENCH_HEXED);
1382     }
1383 
1384     if (mons.type == MONS_SNAPLASHER_VINE
1385         && mons.props.exists("vine_awakener"))
1386     {
1387         monster* awakener = monster_by_mid(mons.props["vine_awakener"].get_int());
1388         if (awakener && !awakener->can_see(mons))
1389         {
1390             simple_monster_message(mons, " falls limply to the ground.");
1391             monster_die(mons, KILL_RESET, NON_MONSTER);
1392             return;
1393         }
1394     }
1395 
1396     // Dissipate player ball lightnings and foxfires
1397     // that have left the player's sight
1398     // (monsters are allowed to 'cheat', as with orb of destruction)
1399     if ((mons.type == MONS_BALL_LIGHTNING || mons.type == MONS_FOXFIRE)
1400         && mons.summoner == MID_PLAYER
1401         && !cell_see_cell(you.pos(), mons.pos(), LOS_SOLID))
1402     {
1403         if (mons.type == MONS_FOXFIRE)
1404             check_place_cloud(CLOUD_FLAME, mons.pos(), 2, &mons);
1405         monster_die(mons, KILL_RESET, NON_MONSTER);
1406         return;
1407     }
1408 
1409     if (mons_stores_tracking_data(mons))
1410     {
1411         actor* foe = mons.get_foe();
1412         if (foe)
1413         {
1414             if (!mons.props.exists("foe_pos"))
1415                 mons.props["foe_pos"].get_coord() = foe->pos();
1416             else
1417             {
1418                 if (mons.props["foe_pos"].get_coord().distance_from(mons.pos())
1419                     > foe->pos().distance_from(mons.pos()))
1420                 {
1421                     mons.props["foe_approaching"].get_bool() = true;
1422                 }
1423                 else
1424                     mons.props["foe_approaching"].get_bool() = false;
1425 
1426                 mons.props["foe_pos"].get_coord() = foe->pos();
1427             }
1428         }
1429         else
1430             mons.props.erase("foe_pos");
1431     }
1432 
1433     reset_battlesphere(&mons);
1434 
1435     fedhas_neutralise(&mons);
1436     slime_convert(&mons);
1437 
1438     // Monster just summoned (or just took stairs), skip this action.
1439     if (testbits(mons.flags, MF_JUST_SUMMONED))
1440     {
1441         mons.flags &= ~MF_JUST_SUMMONED;
1442         return;
1443     }
1444 
1445     mon_acting mact(&mons);
1446 
1447     _monster_add_energy(mons);
1448 
1449     // Handle clouds on nonmoving monsters.
1450     if (mons.speed == 0)
1451     {
1452         _mons_in_cloud(mons);
1453 
1454         // Update constriction durations
1455         mons.accum_has_constricted();
1456 
1457         if (mons.type == MONS_NO_MONSTER)
1458             return;
1459     }
1460 
1461     // Apply monster enchantments once for every normal-speed
1462     // player turn.
1463     mons.ench_countdown -= you.time_taken;
1464     while (mons.ench_countdown < 0)
1465     {
1466         mons.ench_countdown += 10;
1467         mons.apply_enchantments();
1468 
1469         // If the monster *merely* died just break from the loop
1470         // rather than quit altogether, since we have to deal with
1471         // ballistomycete spores and ball lightning exploding at the end of the
1472         // function, but do return if the monster's data has been
1473         // reset, since then the monster type is invalid.
1474         if (mons.type == MONS_NO_MONSTER)
1475             return;
1476         else if (mons.hit_points < 1)
1477             break;
1478     }
1479 
1480     // Memory is decremented here for a reason -- we only want it
1481     // decrementing once per monster "move".
1482     if (mons.foe_memory > 0 && !you.penance[GOD_ASHENZARI])
1483         mons.foe_memory -= you.time_taken;
1484 
1485     // Otherwise there are potential problems with summonings.
1486     if (mons.type == MONS_GLOWING_SHAPESHIFTER)
1487         mons.add_ench(ENCH_GLOWING_SHAPESHIFTER);
1488 
1489     if (mons.type == MONS_SHAPESHIFTER)
1490         mons.add_ench(ENCH_SHAPESHIFTER);
1491 
1492     mons.check_speed();
1493 }
1494 
1495 // Handle weird stuff like spells/special abilities, item use,
1496 // reaching, swooping, etc.
1497 // Returns true iff the monster used up their turn.
_mons_take_special_action(monster & mons,int old_energy)1498 static bool _mons_take_special_action(monster &mons, int old_energy)
1499 {
1500     if ((mons.asleep() || mons_is_wandering(mons))
1501         // Slime creatures can split while wandering or resting.
1502         && mons.type != MONS_SLIME_CREATURE)
1503     {
1504         return false;
1505     }
1506 
1507     // Berserking monsters are limited to running up and
1508     // hitting their foes.
1509     if (mons.berserk_or_insane())
1510     {
1511         if (_handle_reaching(mons))
1512         {
1513             DEBUG_ENERGY_USE_REF("_handle_reaching()");
1514             return true;
1515         }
1516 
1517         return false;
1518     }
1519 
1520     // Prevents unfriendlies from nuking you from offscreen.
1521     // How nice!
1522     const bool friendly_or_near =
1523             mons.friendly() && mons.foe == MHITYOU
1524             || mons.near_foe();
1525 
1526     // Activate spells or abilities?
1527     if (friendly_or_near
1528         || mons.type == MONS_TEST_SPAWNER
1529         // Slime creatures can split when offscreen.
1530         || mons.type == MONS_SLIME_CREATURE
1531         // Let monsters who have Awaken Earth use it off-screen.
1532         // :( -- pf
1533         || mons.has_spell(SPELL_AWAKEN_EARTH)
1534         )
1535     {
1536         // [ds] Special abilities shouldn't overwhelm
1537         // spellcasting in monsters that have both. This aims
1538         // to give them both roughly the same weight.
1539         if (coinflip() ? mon_special_ability(&mons) || _do_mon_spell(&mons)
1540                        : _do_mon_spell(&mons) || mon_special_ability(&mons))
1541         {
1542             DEBUG_ENERGY_USE_REF("spell or special");
1543             mmov.reset();
1544             return true;
1545         }
1546     }
1547 
1548     if (friendly_or_near)
1549     {
1550         if (_handle_potion(mons))
1551         {
1552             DEBUG_ENERGY_USE_REF("_handle_potion()");
1553             return true;
1554         }
1555 
1556         if (_handle_scroll(mons))
1557         {
1558             DEBUG_ENERGY_USE_REF("_handle_scroll()");
1559             return true;
1560         }
1561     }
1562 
1563     if (_handle_wand(mons))
1564     {
1565         DEBUG_ENERGY_USE_REF("_handle_wand()");
1566         return true;
1567     }
1568 
1569     if (_handle_swoop(mons))
1570     {
1571         DEBUG_ENERGY_USE_REF("_handle_swoop()");
1572         return true;
1573     }
1574 
1575     if (friendly_or_near)
1576     {
1577         bolt beem = setup_targetting_beam(mons);
1578         if (handle_throw(&mons, beem, false, false))
1579         {
1580             DEBUG_ENERGY_USE_REF("_handle_throw()");
1581             return true;
1582         }
1583     }
1584 
1585     if (_handle_reaching(mons))
1586     {
1587         DEBUG_ENERGY_USE_REF("_handle_reaching()");
1588         return true;
1589     }
1590 
1591 #ifndef DEBUG
1592     UNUSED(old_energy);
1593 #endif
1594 
1595     return false;
1596 }
1597 
handle_monster_move(monster * mons)1598 void handle_monster_move(monster* mons)
1599 {
1600     ASSERT(mons); // XXX: change to monster &mons
1601     const monsterentry* entry = get_monster_data(mons->type);
1602     if (!entry)
1603         return;
1604 
1605     const bool disabled = crawl_state.disables[DIS_MON_ACT]
1606                           && _unfriendly_or_impaired(*mons);
1607 
1608     const int old_energy = mons->speed_increment;
1609     int non_move_energy = min(entry->energy_usage.move,
1610                               entry->energy_usage.swim);
1611 
1612 #ifdef DEBUG_MONS_SCAN
1613     bool monster_was_floating = env.mgrid(mons->pos()) != mons->mindex();
1614 #endif
1615     coord_def old_pos = mons->pos();
1616 
1617     if (!mons->has_action_energy())
1618         return;
1619 
1620     if (!disabled)
1621         move_solo_tentacle(mons);
1622 
1623     if (!mons->alive())
1624         return;
1625 
1626     if (!disabled && mons_is_tentacle_head(mons_base_type(*mons)))
1627         move_child_tentacles(mons);
1628 
1629     old_pos = mons->pos();
1630 
1631 #ifdef DEBUG_MONS_SCAN
1632     if (!monster_was_floating
1633         && env.mgrid(mons->pos()) != mons->mindex())
1634     {
1635         mprf(MSGCH_ERROR, "Monster %s became detached from env.mgrid "
1636                           "in handle_monster_move() loop",
1637              mons->name(DESC_PLAIN, true).c_str());
1638         mprf(MSGCH_WARN, "[[[[[[[[[[[[[[[[[[");
1639         debug_mons_scan();
1640         mprf(MSGCH_WARN, "]]]]]]]]]]]]]]]]]]");
1641         monster_was_floating = true;
1642     }
1643     else if (monster_was_floating
1644              && env.mgrid(mons->pos()) == mons->mindex())
1645     {
1646         mprf(MSGCH_DIAGNOSTICS, "Monster %s re-attached itself to env.mgrid "
1647                                 "in handle_monster_move() loop",
1648              mons->name(DESC_PLAIN, true).c_str());
1649         monster_was_floating = false;
1650     }
1651 #endif
1652 
1653     if (mons_is_projectile(*mons))
1654     {
1655         if (iood_act(*mons))
1656             return;
1657         mons->lose_energy(EUT_MOVE);
1658         return;
1659     }
1660 
1661     if (mons->type == MONS_BATTLESPHERE)
1662     {
1663         if (fire_battlesphere(mons))
1664             mons->lose_energy(EUT_SPECIAL);
1665     }
1666 
1667     if (mons->type == MONS_FULMINANT_PRISM)
1668     {
1669         ++mons->prism_charge;
1670         if (mons->prism_charge == 2)
1671             mons->suicide();
1672         else
1673         {
1674             if (player_can_hear(mons->pos()))
1675             {
1676                 if (you.can_see(*mons))
1677                 {
1678                     simple_monster_message(*mons, " crackles loudly.",
1679                                            MSGCH_WARN);
1680                 }
1681                 else
1682                     mprf(MSGCH_SOUND, "You hear a loud crackle.");
1683             }
1684             // Done this way to keep the detonation timer predictable
1685             mons->speed_increment -= BASELINE_DELAY;
1686         }
1687         return;
1688     }
1689 
1690     if (mons->type == MONS_FOXFIRE)
1691     {
1692         if (mons->steps_remaining == 0)
1693         {
1694             check_place_cloud(CLOUD_FLAME, mons->pos(), 2, mons);
1695             mons->suicide();
1696             return;
1697         }
1698     }
1699 
1700     mons->shield_blocks = 0;
1701 
1702     _mons_in_cloud(*mons);
1703     actor_apply_toxic_bog(mons);
1704 
1705     if (!mons->alive())
1706         return;
1707 
1708     if (env.level_state & LSTATE_SLIMY_WALL)
1709         slime_wall_damage(mons, speed_to_duration(mons->speed));
1710 
1711     if (!mons->alive())
1712         return;
1713 
1714     if (env.level_state & LSTATE_ICY_WALL)
1715         ice_wall_damage(*mons, speed_to_duration(mons->speed));
1716 
1717     if (!mons->alive())
1718         return;
1719 
1720     if (mons->type == MONS_TIAMAT && one_chance_in(3))
1721         draconian_change_colour(mons);
1722 
1723     _monster_regenerate(mons);
1724 
1725     // Please change _slouch_damage to match!
1726     if (mons->cannot_act()
1727         || mons->type == MONS_SIXFIRHY // these move only 8 of 24 turns
1728             && ++mons->move_spurt / 8 % 3 != 2  // but are not helpless
1729         || mons->type == MONS_JIANGSHI // similarly, but more irregular (48 of 90)
1730             && (++mons->move_spurt / 6 % 3 == 1 || mons->move_spurt / 3 % 5 == 1))
1731     {
1732         mons->speed_increment -= non_move_energy;
1733         return;
1734     }
1735 
1736     // Continue reciting.
1737     if (mons->has_ench(ENCH_WORD_OF_RECALL))
1738     {
1739         mons->speed_increment -= non_move_energy;
1740         return;
1741     }
1742 
1743     if (mons->has_ench(ENCH_DAZED) && one_chance_in(4))
1744     {
1745         simple_monster_message(*mons, " is lost in a daze.");
1746         mons->speed_increment -= non_move_energy;
1747         return;
1748     }
1749 
1750     if (mons->has_ench(ENCH_GOLD_LUST))
1751     {
1752         mons->speed_increment -= non_move_energy;
1753         return;
1754     }
1755 
1756     if (mons->has_ench(ENCH_BRILLIANCE_AURA))
1757         aura_of_brilliance(mons);
1758 
1759     if (you.duration[DUR_GOZAG_GOLD_AURA]
1760         && have_passive(passive_t::gold_aura)
1761         && you.see_cell(mons->pos())
1762         && !mons->asleep()
1763         && !mons_is_conjured(mons->type)
1764         && !mons_is_tentacle_or_tentacle_segment(mons->type)
1765         && !mons_is_firewood(*mons)
1766         && !mons->wont_attack())
1767     {
1768         const int gold = you.props[GOZAG_GOLD_AURA_KEY].get_int();
1769         if (bernoulli(gold, 3.0/100.0))
1770         {
1771             simple_monster_message(*mons,
1772                 " is distracted by your dazzling golden aura.");
1773 
1774             mons->add_ench(
1775                 mon_enchant(ENCH_GOLD_LUST, 1, nullptr,
1776                             random_range(1, 5) * BASELINE_DELAY));
1777             mons->foe = MHITNOT;
1778             mons->target = mons->pos();
1779             mons->speed_increment -= non_move_energy;
1780             return;
1781         }
1782     }
1783 
1784     if (disabled)
1785     {
1786         mons->speed_increment -= non_move_energy;
1787         return;
1788     }
1789 
1790     handle_behaviour(mons);
1791 
1792     // handle_behaviour() could make the monster leave the level.
1793     if (!mons->alive())
1794         return;
1795 
1796     ASSERT(!crawl_state.game_is_arena() || mons->foe != MHITYOU);
1797     ASSERT_IN_BOUNDS_OR_ORIGIN(mons->target);
1798 
1799     if (mons->speed >= 100)
1800     {
1801         mons->speed_increment -= non_move_energy;
1802         return;
1803     }
1804 
1805     if (_handle_pickup(mons))
1806     {
1807         DEBUG_ENERGY_USE("handle_pickup()");
1808         return;
1809     }
1810 
1811     // Lurking monsters only stop lurking if their target is right
1812     // next to them, otherwise they just sit there.
1813     if (mons->has_ench(ENCH_SUBMERGED))
1814     {
1815         if (mons->foe != MHITNOT
1816             && grid_distance(mons->target, mons->pos()) <= 1)
1817         {
1818             if (mons->submerged())
1819             {
1820                 if (!mons->del_ench(ENCH_SUBMERGED))
1821                 {
1822                     // Couldn't unsubmerge.
1823                     mons->speed_increment -= non_move_energy;
1824                     return;
1825                 }
1826             }
1827             mons->behaviour = BEH_SEEK;
1828         }
1829         else
1830         {
1831             mons->speed_increment -= non_move_energy;
1832             return;
1833         }
1834     }
1835 
1836     if (mons->caught())
1837     {
1838         // Struggling against the net takes time.
1839         _swim_or_move_energy(*mons);
1840     }
1841     else if (!mons->petrified())
1842     {
1843         // Calculates mmov based on monster target.
1844         _handle_movement(mons);
1845 
1846         // Confused monsters sometimes stumble about instead of moving with
1847         // purpose.
1848         if (mons_is_confused(*mons) && !one_chance_in(3))
1849         {
1850             set_random_target(mons);
1851             _confused_move_dir(mons);
1852         }
1853     }
1854     if (!mons->asleep() && !mons->submerged())
1855         maybe_mons_speaks(mons);
1856 
1857     if (!mons->alive())
1858         return;
1859 
1860     // XXX: A bit hacky, but stores where we WILL move, if we don't take
1861     //      another action instead (used for decision-making)
1862     if (mons_stores_tracking_data(*mons))
1863         mons->props["mmov"].get_coord() = mmov;
1864 
1865     if (_mons_take_special_action(*mons, old_energy))
1866         return;
1867 
1868     if (!mons->caught())
1869     {
1870         if (mons->pos() + mmov == you.pos())
1871         {
1872             ASSERT(!crawl_state.game_is_arena());
1873 
1874             if (_unfriendly_or_impaired(*mons)
1875                 && !mons->has_ench(ENCH_CHARM)
1876                 && !mons->has_ench(ENCH_HEXED))
1877             {
1878                 monster* new_target = 0;
1879                 if (!mons->wont_attack())
1880                 {
1881                     // Otherwise, if it steps into you, cancel other targets.
1882                     mons->foe = MHITYOU;
1883                     mons->target = you.pos();
1884 
1885                     // Check to see if your religion redirects the attack
1886                     if (does_ru_wanna_redirect(mons))
1887                     {
1888                         ru_interference interference =
1889                                 get_ru_attack_interference_level();
1890                         if (interference == DO_BLOCK_ATTACK)
1891                         {
1892                             simple_monster_message(*mons,
1893                                 " is stunned by your conviction and fails to attack.",
1894                                 MSGCH_GOD);
1895                             mons->speed_increment -= non_move_energy;
1896                             return;
1897                         }
1898                         else if (interference == DO_REDIRECT_ATTACK)
1899                         {
1900                             // get a target
1901                             int pfound = 0;
1902                             for (adjacent_iterator ai(mons->pos(), false); ai; ++ai)
1903                             {
1904                                 monster* candidate = monster_at(*ai);
1905                                 if (candidate == nullptr
1906                                     || mons_is_projectile(candidate->type)
1907                                     || mons_is_firewood(*candidate)
1908                                     || candidate->friendly())
1909                                 {
1910                                     continue;
1911                                 }
1912                                 ASSERT(candidate);
1913                                 if (one_chance_in(++pfound))
1914                                     new_target = candidate;
1915                             }
1916                         }
1917                     }
1918                 }
1919 
1920                 if (new_target)
1921                 {
1922                     // attack that target
1923                     mons->target = new_target->pos();
1924                     mons->foe = new_target->mindex();
1925                     mprf(MSGCH_GOD, "You redirect %s's attack!",
1926                          mons->name(DESC_THE).c_str());
1927                     fight_melee(mons, new_target);
1928                 }
1929                 else
1930                     fight_melee(mons, &you);
1931 
1932                 if (mons_is_batty(*mons))
1933                     _be_batty(*mons);
1934                 DEBUG_ENERGY_USE("fight_melee()");
1935                 mmov.reset();
1936                 return;
1937             }
1938         }
1939 
1940         // See if we move into (and fight) an unfriendly monster.
1941         monster* targ = monster_at(mons->pos() + mmov);
1942 
1943         //If a tentacle owner is attempting to move into an adjacent
1944         //segment, kill the segment and adjust connectivity data.
1945         if (targ && mons_tentacle_adjacent(mons, targ))
1946         {
1947             const bool basis = targ->props.exists("outwards");
1948             monster* outward =  basis ? monster_by_mid(targ->props["outwards"].get_int()) : nullptr;
1949             if (outward)
1950                 outward->props["inwards"].get_int() = mons->mid;
1951 
1952             monster_die(*targ, KILL_MISC, NON_MONSTER, true);
1953             targ = nullptr;
1954         }
1955 
1956         if (targ
1957             && targ != mons
1958             && mons->behaviour != BEH_WITHDRAW
1959             && (!(mons_aligned(mons, targ) || targ->type == MONS_FOXFIRE)
1960                 || mons->has_ench(ENCH_INSANE))
1961             && monster_can_hit_monster(mons, targ))
1962         {
1963             // Maybe they can swap places?
1964             if (_swap_monsters(*mons, *targ))
1965             {
1966                 _swim_or_move_energy(*mons);
1967                 return;
1968             }
1969             // Figure out if they fight.
1970             else if ((!mons_is_firewood(*targ)
1971                       || mons->is_child_tentacle())
1972                           && fight_melee(mons, targ))
1973             {
1974                 if (mons_is_batty(*mons))
1975                     _be_batty(*mons);
1976 
1977                 mmov.reset();
1978                 DEBUG_ENERGY_USE("fight_melee()");
1979                 return;
1980             }
1981         }
1982         else if (mons->behaviour == BEH_WITHDRAW
1983                  && ((targ && targ != mons && targ->friendly())
1984                       || (you.pos() == mons->pos() + mmov)))
1985         {
1986             // Don't count turns spent blocked by friendly creatures
1987             // (or the player) as an indication that we're stuck
1988             mons->props.erase("blocked_deadline");
1989         }
1990 
1991         if (invalid_monster(mons) || mons->is_stationary())
1992         {
1993             if (mons->speed_increment == old_energy)
1994                 mons->speed_increment -= non_move_energy;
1995             return;
1996         }
1997 
1998         if (mons->cannot_move() || !_monster_move(mons))
1999             mons->speed_increment -= non_move_energy;
2000     }
2001     you.update_beholder(mons);
2002     you.update_fearmonger(mons);
2003 
2004     // Reevaluate behaviour, since the monster's surroundings have
2005     // changed (it may have moved, or died for that matter). Don't
2006     // bother for dead monsters.  :)
2007     if (mons->alive())
2008     {
2009         handle_behaviour(mons);
2010         ASSERT_IN_BOUNDS_OR_ORIGIN(mons->target);
2011     }
2012 
2013     if (mons_is_tentacle_head(mons_base_type(*mons)))
2014     {
2015         move_child_tentacles(mons);
2016 
2017         mons->move_spurt += (old_energy - mons->speed_increment)
2018                              * _tentacle_move_speed(mons_base_type(*mons));
2019         ASSERT(mons->move_spurt > 0);
2020         while (mons->move_spurt >= 100)
2021         {
2022             move_child_tentacles(mons);
2023             mons->move_spurt -= 100;
2024         }
2025     }
2026 }
2027 
catch_breath()2028 void monster::catch_breath()
2029 {
2030     decay_enchantment(ENCH_BREATH_WEAPON);
2031 }
2032 
2033 /**
2034  * Let trapped monsters struggle against nets, webs, etc.
2035  */
struggle_against_net()2036 void monster::struggle_against_net()
2037 {
2038     if (is_stationary() || cannot_act() || asleep())
2039         return;
2040 
2041     if (props.exists(NEWLY_TRAPPED_KEY))
2042     {
2043         props.erase(NEWLY_TRAPPED_KEY);
2044         return; // don't try to escape on the same turn you were trapped!
2045     }
2046 
2047     int net = get_trapping_net(pos(), true);
2048 
2049     if (net == NON_ITEM)
2050     {
2051         trap_def *trap = trap_at(pos());
2052         if (trap && trap->type == TRAP_WEB)
2053         {
2054             if (coinflip())
2055             {
2056                 if (you.see_cell(pos()))
2057                 {
2058                     if (!visible_to(&you))
2059                         mpr("Something you can't see is thrashing in a web.");
2060                     else
2061                         simple_monster_message(*this,
2062                                            " struggles to get unstuck from the web.");
2063                 }
2064                 return;
2065             }
2066         }
2067         monster_web_cleanup(*this);
2068         del_ench(ENCH_HELD);
2069         return;
2070     }
2071 
2072     if (you.see_cell(pos()))
2073     {
2074         if (!visible_to(&you))
2075             mpr("Something wriggles in the net.");
2076         else
2077             simple_monster_message(*this, " struggles against the net.");
2078     }
2079 
2080     int damage = 1 + random2(2);
2081 
2082     // Faster monsters can damage the net more often per
2083     // time period.
2084     if (speed != 0)
2085         damage = div_rand_round(damage * speed, 10);
2086 
2087     env.item[net].net_durability -= damage;
2088 
2089     if (env.item[net].net_durability < NET_MIN_DURABILITY)
2090     {
2091         if (you.see_cell(pos()))
2092         {
2093             if (visible_to(&you))
2094             {
2095                 mprf("The net rips apart, and %s comes free!",
2096                      name(DESC_THE).c_str());
2097             }
2098             else
2099                 mpr("All of a sudden the net rips apart!");
2100         }
2101         destroy_item(net);
2102 
2103         del_ench(ENCH_HELD, true);
2104     }
2105 }
2106 
_ancient_zyme_sicken(monster * mons)2107 static void _ancient_zyme_sicken(monster* mons)
2108 {
2109     if (is_sanctuary(mons->pos()))
2110         return;
2111 
2112     if (!is_sanctuary(you.pos())
2113         && !mons->wont_attack()
2114         && !you.res_miasma()
2115         && !you.duration[DUR_DIVINE_STAMINA]
2116         && cell_see_cell(you.pos(), mons->pos(), LOS_SOLID_SEE))
2117     {
2118         if (!you.duration[DUR_SICKNESS])
2119         {
2120             if (!you.duration[DUR_SICKENING])
2121             {
2122                 mprf(MSGCH_WARN, "You feel yourself growing ill in the "
2123                                  "presence of %s.",
2124                     mons->name(DESC_THE).c_str());
2125             }
2126 
2127             you.duration[DUR_SICKENING] += (2 + random2(4)) * BASELINE_DELAY;
2128             if (you.duration[DUR_SICKENING] > 100)
2129             {
2130                 you.sicken(40 + random2(30));
2131                 you.duration[DUR_SICKENING] = 0;
2132             }
2133         }
2134         else
2135         {
2136             if (x_chance_in_y(you.time_taken, 60))
2137                 you.sicken(15 + random2(30));
2138         }
2139     }
2140 
2141     for (radius_iterator ri(mons->pos(), LOS_RADIUS, C_SQUARE); ri; ++ri)
2142     {
2143         monster *m = monster_at(*ri);
2144         if (m && !mons_aligned(mons, m)
2145             && cell_see_cell(mons->pos(), *ri, LOS_SOLID_SEE)
2146             && !is_sanctuary(*ri))
2147         {
2148             m->sicken(2 * you.time_taken);
2149         }
2150     }
2151 }
2152 
2153 /**
2154  * Apply the torpor snail slowing effect.
2155  *
2156  * @param mons      The snail applying the effect.
2157  */
_torpor_snail_slow(monster * mons)2158 static void _torpor_snail_slow(monster* mons)
2159 {
2160     // XXX: might be nice to refactor together with _ancient_zyme_sicken().
2161     // XXX: also with torpor_slowed().... so many duplicated checks :(
2162 
2163     if (is_sanctuary(mons->pos())
2164         || mons->attitude != ATT_HOSTILE
2165         || mons->has_ench(ENCH_CHARM))
2166     {
2167         return;
2168     }
2169 
2170     if (!is_sanctuary(you.pos())
2171         && !you.stasis()
2172         && cell_see_cell(you.pos(), mons->pos(), LOS_SOLID_SEE))
2173     {
2174         if (!you.duration[DUR_SLOW])
2175         {
2176             mprf("Being near %s leaves you feeling lethargic.",
2177                  mons->name(DESC_THE).c_str());
2178         }
2179 
2180         if (you.duration[DUR_SLOW] <= 1)
2181             you.set_duration(DUR_SLOW, 1);
2182         you.props[TORPOR_SLOWED_KEY] = true;
2183     }
2184 
2185     for (monster_near_iterator ri(mons->pos(), LOS_SOLID_SEE); ri; ++ri)
2186     {
2187         monster *m = *ri;
2188         if (m && !mons_aligned(mons, m) && !m->stasis()
2189             && !mons_is_conjured(m->type) && !m->is_stationary()
2190             && !is_sanctuary(m->pos()))
2191         {
2192             m->add_ench(mon_enchant(ENCH_SLOW, 0, mons, 1));
2193             m->props[TORPOR_SLOWED_KEY] = true;
2194         }
2195     }
2196 }
2197 
_post_monster_move(monster * mons)2198 static void _post_monster_move(monster* mons)
2199 {
2200     if (invalid_monster(mons))
2201         return;
2202 
2203     mons->handle_constriction();
2204 
2205     if (mons->has_ench(ENCH_HELD))
2206         mons->struggle_against_net();
2207 
2208     if (mons->has_ench(ENCH_BREATH_WEAPON))
2209         mons->catch_breath();
2210 
2211     if (mons->type == MONS_ANCIENT_ZYME)
2212         _ancient_zyme_sicken(mons);
2213 
2214     if (mons->type == MONS_TORPOR_SNAIL)
2215         _torpor_snail_slow(mons);
2216 
2217     if (mons->type == MONS_WATER_NYMPH
2218         || mons->type == MONS_ELEMENTAL_WELLSPRING)
2219     {
2220         for (adjacent_iterator ai(mons->pos(), false); ai; ++ai)
2221             if (feat_has_solid_floor(env.grid(*ai))
2222                 && (coinflip() || *ai == mons->pos()))
2223             {
2224                 if (env.grid(*ai) != DNGN_SHALLOW_WATER
2225                     && env.grid(*ai) != DNGN_FLOOR
2226                     && you.see_cell(*ai))
2227                 {
2228                     mprf("%s watery aura covers %s.",
2229                          apostrophise(mons->name(DESC_THE)).c_str(),
2230                          feature_description_at(*ai, false, DESC_THE).c_str());
2231                 }
2232                 temp_change_terrain(*ai, DNGN_SHALLOW_WATER,
2233                                     random_range(50, 80),
2234                                     TERRAIN_CHANGE_FLOOD, mons);
2235             }
2236     }
2237 
2238     if (mons->type == MONS_GUARDIAN_GOLEM)
2239         guardian_golem_bond(*mons);
2240 
2241     // A rakshasa that has regained full health dismisses its emergency clones
2242     // (if they're somehow still alive) and regains the ability to summon new ones.
2243     if (mons->type == MONS_RAKSHASA && mons->hit_points == mons->max_hit_points
2244         && !mons->has_ench(ENCH_PHANTOM_MIRROR)
2245         && mons->props.exists("emergency_clone"))
2246     {
2247         mons->props.erase("emergency_clone");
2248         for (monster_iterator mi; mi; ++mi)
2249         {
2250             if (mi->type == MONS_RAKSHASA && mi->summoner == mons->mid)
2251                 mi->del_ench(ENCH_ABJ);
2252         }
2253     }
2254 
2255     update_mons_cloud_ring(mons);
2256 
2257     const item_def * weapon = mons->mslot_item(MSLOT_WEAPON);
2258     if (weapon && get_weapon_brand(*weapon) == SPWPN_SPECTRAL
2259         && !mons_is_avatar(mons->type))
2260     {
2261         // TODO: implement monster spectral ego
2262     }
2263 
2264     if (mons->foe != MHITNOT && mons_is_wandering(*mons) && mons_is_batty(*mons))
2265     {
2266         int &bat_turns = mons->props[BATTY_TURNS_KEY].get_int();
2267         bat_turns++;
2268         int turns_to_bat = div_rand_round(mons_base_speed(*mons), 10);
2269         if (turns_to_bat < 2)
2270             turns_to_bat = 2;
2271         if (bat_turns >= turns_to_bat)
2272             mons->behaviour = BEH_SEEK;
2273     }
2274 
2275     if (mons->type != MONS_NO_MONSTER && mons->hit_points < 1)
2276         monster_die(*mons, KILL_MISC, NON_MONSTER);
2277 }
2278 
2279 priority_queue<pair<monster *, int>,
2280                vector<pair<monster *, int> >,
2281                MonsterActionQueueCompare> monster_queue;
2282 
2283 // Inserts a monster into the monster queue (needed to ensure that any monsters
2284 // given energy or an action by a effect can actually make use of that energy
2285 // this round)
queue_monster_for_action(monster * mons)2286 void queue_monster_for_action(monster* mons)
2287 {
2288     monster_queue.emplace(mons, mons->speed_increment);
2289 }
2290 
_clear_monster_flags()2291 static void _clear_monster_flags()
2292 {
2293     // Clear any summoning flags so that lower indiced
2294     // monsters get their actions in the next round.
2295     // Also clear one-turn deep sleep flag.
2296     // XXX: MF_JUST_SLEPT only really works for player-cast hibernation.
2297     for (auto &mons : menv_real)
2298         mons.flags &= ~MF_JUST_SUMMONED & ~MF_JUST_SLEPT;
2299 }
2300 
2301 /**
2302 * On each monster turn, check to see if we need to update monster attitude.
2303 * At the time of writing, it just checks for MUT_NO_LOVE from Ru Sacrifice Love.
2304 *
2305 * @param mon     The targeted monster
2306 * @return        Void
2307 **/
_update_monster_attitude(monster * mon)2308 static void _update_monster_attitude(monster *mon)
2309 {
2310     if (you.get_mutation_level(MUT_NO_LOVE)
2311         && !mons_is_conjured(mon->type))
2312     {
2313         mon->attitude = ATT_HOSTILE;
2314     }
2315 }
2316 
2317 vector<monster *> just_seen_queue;
2318 
mons_set_just_seen(monster * mon)2319 void mons_set_just_seen(monster *mon)
2320 {
2321     mon->seen_context = SC_JUST_SEEN;
2322     just_seen_queue.push_back(mon);
2323 }
2324 
mons_reset_just_seen()2325 void mons_reset_just_seen()
2326 {
2327     // this may be called when the pointers are not valid, so don't mess with
2328     // seen_context.
2329     just_seen_queue.clear();
2330 }
2331 
_display_just_seen()2332 static void _display_just_seen()
2333 {
2334     // these are monsters that were marked as SC_JUST_SEEN at some point since
2335     // last time this was called. We announce any that leave all at once so
2336     // as to handle monsters that may move multiple times per world_reacts.
2337     if (in_bounds(you.pos()))
2338     {
2339         for (auto m : just_seen_queue)
2340         {
2341             if (!m || invalid_monster(m) || !m->alive())
2342                 continue;
2343             // can't use simple_monster_message here, because m is out of view.
2344             // The monster should be visible to be in this queue.
2345             if (in_bounds(m->pos()) && !you.see_cell(m->pos()))
2346             {
2347                 mprf(MSGCH_PLAIN, "%s moves out of view.",
2348                      m->name(DESC_THE, true).c_str());
2349             }
2350         }
2351     }
2352     mons_reset_just_seen();
2353 }
2354 
2355 /**
2356  * Get all monsters to make an action, if they can/want to.
2357  *
2358  * @param with_noise whether to process noises after the loop.
2359  */
handle_monsters(bool with_noise)2360 void handle_monsters(bool with_noise)
2361 {
2362     for (monster_iterator mi; mi; ++mi)
2363     {
2364         _pre_monster_move(**mi);
2365         if (!invalid_monster(*mi) && mi->alive() && mi->has_action_energy())
2366             monster_queue.emplace(*mi, mi->speed_increment);
2367     }
2368 
2369     int tries = 0; // infinite loop protection, shouldn't be ever needed
2370     while (!monster_queue.empty())
2371     {
2372         if (tries++ > 32767)
2373         {
2374             die("infinite handle_monsters() loop, mons[0 of %d] is %s",
2375                 (int)monster_queue.size(),
2376                 monster_queue.top().first->name(DESC_PLAIN, true).c_str());
2377         }
2378 
2379         monster *mon = monster_queue.top().first;
2380         const int oldspeed = monster_queue.top().second;
2381         monster_queue.pop();
2382 
2383         if (invalid_monster(mon) || !mon->alive() || !mon->has_action_energy())
2384             continue;
2385 
2386         _update_monster_attitude(mon);
2387 
2388         // Only move the monster if nothing else has played with its energy
2389         // during their turn.
2390         // If something's played with the energy, they get added back to
2391         // the queue just after this.
2392         if (oldspeed == mon->speed_increment)
2393         {
2394             handle_monster_move(mon);
2395             _post_monster_move(mon);
2396             fire_final_effects();
2397         }
2398 
2399         if (mon->has_action_energy())
2400             monster_queue.emplace(mon, mon->speed_increment);
2401 
2402         // If the player got banished, discard pending monster actions.
2403         if (you.banished)
2404         {
2405             // Clear list of mesmerising monsters.
2406             you.clear_beholders();
2407             you.clear_fearmongers();
2408             you.stop_constricting_all();
2409             you.stop_being_constricted();
2410             break;
2411         }
2412     }
2413     _display_just_seen();
2414 
2415     // Process noises now (before clearing the sleep flag).
2416     if (with_noise)
2417         apply_noises();
2418 
2419     _clear_monster_flags();
2420 }
2421 
_jelly_divide(monster & parent)2422 static bool _jelly_divide(monster& parent)
2423 {
2424     if (!mons_class_flag(parent.type, M_SPLITS))
2425         return false;
2426 
2427     const int reqd = max(parent.get_experience_level() * 8, 50);
2428     if (parent.hit_points < reqd)
2429         return false;
2430 
2431     monster* child = nullptr;
2432     coord_def child_spot;
2433     int num_spots = 0;
2434 
2435     // First, find a suitable spot for the child {dlb}:
2436     for (adjacent_iterator ai(parent.pos()); ai; ++ai)
2437         if (actor_at(*ai) == nullptr && parent.can_pass_through(*ai)
2438             && one_chance_in(++num_spots))
2439         {
2440             child_spot = *ai;
2441         }
2442 
2443     if (num_spots == 0)
2444         return false;
2445 
2446     // Now that we have a spot, find a monster slot {dlb}:
2447     child = get_free_monster();
2448     if (!child)
2449         return false;
2450 
2451     // Handle impact of split on parent {dlb}:
2452     parent.max_hit_points /= 2;
2453 
2454     if (parent.hit_points > parent.max_hit_points)
2455         parent.hit_points = parent.max_hit_points;
2456 
2457     parent.init_experience();
2458     parent.experience = parent.experience * 3 / 5 + 1;
2459 
2460     // Create child {dlb}:
2461     // This is terribly partial and really requires
2462     // more thought as to generation ... {dlb}
2463     *child = parent;
2464     child->max_hit_points  = child->hit_points;
2465     child->speed_increment = 70 + random2(5);
2466     child->set_new_monster_id();
2467     child->move_to_pos(child_spot);
2468 
2469     if (!simple_monster_message(parent, " splits in two!")
2470         && (player_can_hear(parent.pos()) || player_can_hear(child->pos())))
2471     {
2472         mprf(MSGCH_SOUND, "You hear a squelching noise.");
2473     }
2474 
2475     if (crawl_state.game_is_arena())
2476         arena_placed_monster(child);
2477 
2478     return true;
2479 }
2480 
2481 // Only Jiyva jellies eat items.
_monster_eat_item(monster * mons)2482 static bool _monster_eat_item(monster* mons)
2483 {
2484     if (!mons_eats_items(*mons))
2485         return false;
2486 
2487     // Off-limit squares are off-limit.
2488     if (testbits(env.pgrid(mons->pos()), FPROP_NO_JIYVA))
2489         return false;
2490 
2491     int hps_changed = 0;
2492     int max_eat = roll_dice(1, 10);
2493     int eaten = 0;
2494     bool shown_msg = false;
2495 
2496     // Jellies can swim, so don't check water
2497     for (stack_iterator si(mons->pos());
2498          si && eaten < max_eat && hps_changed < 50; ++si)
2499     {
2500         if (!item_is_jelly_edible(*si))
2501             continue;
2502 
2503         dprf("%s eating %s", mons->name(DESC_PLAIN, true).c_str(),
2504              si->name(DESC_PLAIN).c_str());
2505 
2506         int quant = si->quantity;
2507 
2508         if (si->base_type != OBJ_GOLD)
2509         {
2510             quant = min(quant, max_eat - eaten);
2511 
2512             hps_changed += quant * 3;
2513             eaten += quant;
2514         }
2515         else
2516         {
2517             // Shouldn't be much trouble to digest a huge pile of gold!
2518             if (quant > 500)
2519                 quant = 500 + roll_dice(2, (quant - 500) / 2);
2520 
2521             hps_changed += quant / 10 + 1;
2522             eaten++;
2523         }
2524 
2525         if (eaten && !shown_msg && player_can_hear(mons->pos()))
2526         {
2527             mprf(MSGCH_SOUND, "You hear a%s slurping noise.",
2528                  you.see_cell(mons->pos()) ? "" : " distant");
2529             shown_msg = true;
2530         }
2531 
2532         if (you_worship(GOD_JIYVA))
2533             jiyva_slurp_item_stack(*si, quant);
2534 
2535         if (quant >= si->quantity)
2536             item_was_destroyed(*si);
2537         dec_mitm_item_quantity(si.index(), quant);
2538     }
2539 
2540     if (eaten > 0)
2541     {
2542         hps_changed = max(hps_changed, 1);
2543         hps_changed = min(hps_changed, 50);
2544 
2545         // This is done manually instead of using heal_monster(),
2546         // because that function doesn't work quite this way. - bwr
2547         const int avg_hp = mons_avg_hp(mons->type);
2548         mons->hit_points += hps_changed;
2549         mons->hit_points = min(MAX_MONSTER_HP,
2550                                min(avg_hp * 4, mons->hit_points));
2551         mons->max_hit_points = max(mons->hit_points, mons->max_hit_points);
2552 
2553         _jelly_divide(*mons);
2554     }
2555 
2556     return eaten > 0;
2557 }
2558 
2559 
_handle_pickup(monster * mons)2560 static bool _handle_pickup(monster* mons)
2561 {
2562     if (env.igrid(mons->pos()) == NON_ITEM
2563         // Summoned monsters never pick anything up.
2564         || mons->is_summoned() || mons->is_perm_summoned()
2565         || mons->asleep() || mons->submerged())
2566     {
2567         return false;
2568     }
2569 
2570     // Flying over water doesn't let you pick up stuff. This is inexact, as
2571     // a merfolk could be flying, but that's currently impossible except for
2572     // being polar vortex'd, and with *that* low life expectancy let's not care.
2573     dungeon_feature_type feat = env.grid(mons->pos());
2574 
2575     if ((feat == DNGN_LAVA || feat == DNGN_DEEP_WATER) && mons->airborne())
2576         return false;
2577 
2578     int count_pickup = 0;
2579 
2580     if (mons_eats_items(*mons) && _monster_eat_item(mons))
2581         return false;
2582 
2583     if (mons_itemuse(*mons) < MONUSE_WEAPONS_ARMOUR)
2584         return false;
2585 
2586     // Keep neutral, charmed, and friendly monsters from
2587     // picking up stuff.
2588     const bool never_pickup
2589         = mons->neutral() || mons->friendly()
2590           || have_passive(passive_t::neutral_slimes) && mons_is_slime(*mons)
2591           || mons->has_ench(ENCH_CHARM) || mons->has_ench(ENCH_HEXED);
2592 
2593 
2594     // Note: Monsters only look at stuff near the top of stacks.
2595     //
2596     // XXX: Need to put in something so that monster picks up
2597     // multiple items (e.g. ammunition) identical to those it's
2598     // carrying.
2599     //
2600     // Monsters may now pick up up to two items in the same turn.
2601     // (jpeg)
2602     for (stack_iterator si(mons->pos()); si; ++si)
2603     {
2604         if (!crawl_state.game_is_arena()
2605             && (never_pickup
2606                 // Monsters being able to pick up items you've seen encourages
2607                 // tediously moving everything away from a place where they
2608                 // could use them. Maurice being able to pick up such items
2609                 // encourages killing Maurice, since there's just one of him.
2610                 // Usually.
2611                 || (testbits(si->flags, ISFLAG_SEEN)
2612                     && !mons->has_attack_flavour(AF_STEAL)))
2613             // ...but it's ok if it dropped the item itself.
2614             && !(si->props.exists(DROPPER_MID_KEY)
2615                  && si->props[DROPPER_MID_KEY].get_int() == (int)mons->mid))
2616         {
2617             // don't pick up any items beneath one that the player's seen,
2618             // to prevent seemingly-buggy behavior (monsters picking up items
2619             // from the middle of a stack while the player is watching)
2620             return false;
2621         }
2622 
2623         if (si->flags & ISFLAG_NO_PICKUP)
2624             continue;
2625 
2626         if (mons->pickup_item(*si, you.see_cell(mons->pos()), false))
2627             count_pickup++;
2628 
2629         if (count_pickup > 1 || coinflip())
2630             break;
2631     }
2632 
2633     return count_pickup > 0;
2634 }
2635 
_mons_open_door(monster & mons,const coord_def & pos)2636 static void _mons_open_door(monster& mons, const coord_def &pos)
2637 {
2638     const char *adj = "", *noun = "door";
2639 
2640     bool was_seen   = false;
2641 
2642     set<coord_def> all_door;
2643     find_connected_identical(pos, all_door);
2644     get_door_description(all_door.size(), &adj, &noun);
2645 
2646     for (const auto &dc : all_door)
2647     {
2648         if (you.see_cell(dc))
2649             was_seen = true;
2650 
2651         dgn_open_door(dc);
2652         set_terrain_changed(dc);
2653     }
2654 
2655     if (was_seen)
2656     {
2657         viewwindow();
2658         update_screen();
2659 
2660         string open_str = "opens the ";
2661         open_str += adj;
2662         open_str += noun;
2663         open_str += ".";
2664 
2665         // Should this be conditionalized on you.can_see(mons?)
2666         mons.seen_context = (all_door.size() <= 2) ? SC_DOOR : SC_GATE;
2667 
2668         if (!you.can_see(mons))
2669         {
2670             mprf("Something unseen %s", open_str.c_str());
2671             interrupt_activity(activity_interrupt::force);
2672         }
2673         else if (!you_are_delayed())
2674         {
2675             mprf("%s %s", mons.name(DESC_A).c_str(),
2676                  open_str.c_str());
2677         }
2678     }
2679 
2680     mons.lose_energy(EUT_MOVE);
2681 
2682     dungeon_events.fire_position_event(DET_DOOR_OPENED, pos);
2683 }
2684 
_no_habitable_adjacent_grids(const monster * mon)2685 static bool _no_habitable_adjacent_grids(const monster* mon)
2686 {
2687     for (adjacent_iterator ai(mon->pos()); ai; ++ai)
2688         if (monster_habitable_grid(mon, env.grid(*ai)))
2689             return false;
2690 
2691     return true;
2692 }
2693 
_same_tentacle_parts(const monster * mpusher,const monster * mpushee)2694 static bool _same_tentacle_parts(const monster* mpusher,
2695                                const monster* mpushee)
2696 {
2697     if (!mons_is_tentacle_head(mons_base_type(*mpusher)))
2698         return false;
2699 
2700     if (mpushee->is_child_tentacle_of(mpusher))
2701         return true;
2702 
2703     if (mons_tentacle_adjacent(mpusher, mpushee))
2704         return true;
2705 
2706     return false;
2707 }
2708 
_mons_can_displace(const monster * mpusher,const monster * mpushee)2709 static bool _mons_can_displace(const monster* mpusher,
2710                                const monster* mpushee)
2711 {
2712     if (invalid_monster(mpusher) || invalid_monster(mpushee))
2713         return false;
2714 
2715     const int ipushee = mpushee->mindex();
2716     if (invalid_monster_index(ipushee))
2717         return false;
2718 
2719     // Foxfires can always be pushed
2720     if (mpushee->type == MONS_FOXFIRE)
2721         return !mons_aligned(mpushee, mpusher); // But allies won't do it
2722 
2723     if (!mpushee->has_action_energy()
2724         && !_same_tentacle_parts(mpusher, mpushee))
2725     {
2726         return false;
2727     }
2728 
2729     // Confused monsters can't be pushed past, sleeping monsters
2730     // can't push. Note that sleeping monsters can't be pushed
2731     // past, either, but they may be woken up by a crowd trying to
2732     // elbow past them, and the wake-up check happens downstream.
2733     // Monsters caught in a net also can't be pushed past.
2734     if (mons_is_confused(*mpusher) || mons_is_confused(*mpushee)
2735         || mpusher->cannot_move() || mpusher->is_stationary()
2736         || mpusher->is_constricted() || mpushee->is_constricted()
2737         || (!_same_tentacle_parts(mpusher, mpushee)
2738            && (mpushee->cannot_move() || mpushee->is_stationary()))
2739         || mpusher->asleep() || mpushee->caught())
2740     {
2741         return false;
2742     }
2743 
2744     // OODs should crash into things, not push them around.
2745     if (mons_is_projectile(*mpusher) || mons_is_projectile(*mpushee))
2746         return false;
2747 
2748     // Likewise, OOBs (orbs of beetle)
2749     if (mpusher->has_ench(ENCH_ROLLING) || mpushee->has_ench(ENCH_ROLLING))
2750         return false;
2751 
2752     // Fleeing monsters cannot push past other fleeing monsters
2753     // (This helps to prevent some traffic jams in confined spaces)
2754     if (mons_is_fleeing(*mpusher) && mons_is_fleeing(*mpushee))
2755         return false;
2756 
2757     // Batty monsters are unpushable.
2758     if (mons_is_batty(*mpusher) || mons_is_batty(*mpushee))
2759         return false;
2760 
2761     // Anyone can displace a submerged monster.
2762     if (mpushee->submerged())
2763         return true;
2764 
2765     if (!monster_shover(*mpusher))
2766         return false;
2767 
2768     // Fleeing monsters of the same type may push past higher ranking ones.
2769     if (!monster_senior(*mpusher, *mpushee, mons_is_retreating(*mpusher)))
2770         return false;
2771 
2772     return true;
2773 }
2774 
2775 // Returns true if the monster should try to avoid that position
2776 // because of taking damage from damaging walls.
_check_damaging_walls(const monster * mon,const coord_def & targ)2777 static bool _check_damaging_walls(const monster *mon,
2778                                   const coord_def &targ)
2779 {
2780     const bool have_slimy = env.level_state & LSTATE_SLIMY_WALL;
2781     const bool have_icy   = env.level_state & LSTATE_ICY_WALL;
2782 
2783     if (!have_slimy && !have_icy)
2784         return false;
2785 
2786     if (!have_icy && actor_slime_wall_immune(mon)
2787         || mons_intel(*mon) <= I_BRAINLESS)
2788     {
2789         return false;
2790     }
2791 
2792     // Monsters are only ever affected by one wall at a time, so we don't care
2793     // about wall counts past 1.
2794     const bool target_damages = count_adjacent_slime_walls(targ)
2795         + count_adjacent_icy_walls(targ);
2796 
2797     // Entirely safe.
2798     if (!target_damages)
2799         return false;
2800 
2801     const bool current_damages = count_adjacent_slime_walls(mon->pos())
2802         + count_adjacent_icy_walls(mon->pos());
2803 
2804     // We're already taking damage, so moving into damage is fine.
2805     if (current_damages)
2806         return false;
2807 
2808     // The monster needs to have a purpose to risk taking damage.
2809     if (!mons_is_seeking(*mon))
2810         return true;
2811 
2812     // With enough hit points monsters will consider moving
2813     // onto more dangerous squares.
2814     return mon->hit_points < mon->max_hit_points / 2;
2815 }
2816 
2817 // Check whether a monster can move to given square (described by its relative
2818 // coordinates to the current monster position). just_check is true only for
2819 // calls from is_trap_safe when checking the surrounding squares of a trap.
mon_can_move_to_pos(const monster * mons,const coord_def & delta,bool just_check)2820 bool mon_can_move_to_pos(const monster* mons, const coord_def& delta,
2821                          bool just_check)
2822 {
2823     const coord_def targ = mons->pos() + delta;
2824 
2825     // Bounds check: don't consider moving out of grid!
2826     if (!in_bounds(targ))
2827         return false;
2828 
2829     // Non-friendly and non-good neutral monsters won't enter
2830     // sanctuaries.
2831     if (is_sanctuary(targ)
2832         && !is_sanctuary(mons->pos())
2833         && !mons->wont_attack())
2834     {
2835         return false;
2836     }
2837 
2838     // Inside a sanctuary don't attack anything!
2839     if (is_sanctuary(mons->pos()) && actor_at(targ))
2840         return false;
2841 
2842     const dungeon_feature_type target_grid = env.grid(targ);
2843     const habitat_type habitat = mons_primary_habitat(*mons);
2844 
2845     // No monster may enter the open sea.
2846     if (feat_is_endless(target_grid))
2847         return false;
2848 
2849     // A confused monster will happily go wherever it can, regardless of
2850     // consequences.
2851     if (mons->confused() && mons->can_pass_through(targ))
2852         return true;
2853 
2854     if (mons_avoids_cloud(mons, targ))
2855         return false;
2856 
2857     if (_check_damaging_walls(mons, targ))
2858         return false;
2859 
2860     const bool digs = _mons_can_cast_dig(mons, false)
2861                       || _mons_can_zap_dig(mons);
2862     if ((target_grid == DNGN_ROCK_WALL || target_grid == DNGN_CLEAR_ROCK_WALL)
2863            && (mons->can_burrow() || digs)
2864         || mons->type == MONS_SPATIAL_MAELSTROM
2865            && feat_is_solid(target_grid) && !feat_is_permarock(target_grid)
2866            && !feat_is_critical(target_grid)
2867         || feat_is_tree(target_grid) && mons_flattens_trees(*mons)
2868         || target_grid == DNGN_GRATE && digs)
2869     {
2870     }
2871     else if (!mons_can_traverse(*mons, targ, false, false)
2872              && !monster_habitable_grid(mons, target_grid))
2873     {
2874         // If the monster somehow ended up in this habitat (and is
2875         // not dead by now), give it a chance to get out again.
2876         if (env.grid(mons->pos()) == target_grid && mons->ground_level()
2877             && _no_habitable_adjacent_grids(mons))
2878         {
2879             return true;
2880         }
2881 
2882         return false;
2883     }
2884 
2885     if (mons->type == MONS_MERFOLK_AVATAR)
2886     {
2887         // Don't voluntarily break LoS with a player we're mesmerising
2888         if (you.beheld_by(*mons) && !you.see_cell(targ))
2889             return false;
2890 
2891         // And path around players instead of into them
2892         if (you.pos() == targ)
2893             return false;
2894     }
2895 
2896     // Try to avoid deliberately blocking the player's line of fire.
2897     if (mons->type == MONS_SPELLFORGED_SERVITOR)
2898     {
2899         const actor * const summoner = actor_by_mid(mons->summoner);
2900 
2901         if (!summoner) // XXX
2902             return false;
2903 
2904         // Only check if our target is something the caster could theoretically
2905         // be aiming at.
2906         if (mons->get_foe() && mons->target != summoner->pos()
2907                             && summoner->see_cell_no_trans(mons->target))
2908         {
2909             ray_def ray;
2910             if (find_ray(summoner->pos(), mons->target, ray, opc_immob))
2911             {
2912                 while (ray.advance())
2913                 {
2914                     // Either we've reached the end of the ray, or we're already
2915                     // (maybe) in the player's way and shouldn't care if our
2916                     // next step also is.
2917                     if (ray.pos() == mons->target || ray.pos() == mons->pos())
2918                         break;
2919                     else if (ray.pos() == targ)
2920                         return false;
2921                 }
2922             }
2923         }
2924     }
2925 
2926     // Submerged water creatures avoid the shallows where
2927     // they would be forced to surface. -- bwr
2928     // [dshaligram] Monsters now prefer to head for deep water only if
2929     // they're low on hitpoints. No point in hiding if they want a
2930     // fight.
2931     if (habitat == HT_WATER
2932         && targ != you.pos()
2933         && target_grid != DNGN_DEEP_WATER
2934         && env.grid(mons->pos()) == DNGN_DEEP_WATER
2935         && mons->hit_points < (mons->max_hit_points * 3) / 4)
2936     {
2937         return false;
2938     }
2939 
2940     // Smacking the player is always a good move if we're
2941     // hostile (even if we're heading somewhere else).
2942     // Also friendlies want to keep close to the player
2943     // so it's okay as well.
2944 
2945     // Smacking another monster is good, if the monsters
2946     // are aligned differently.
2947     if (monster* targmonster = monster_at(targ))
2948     {
2949         if (just_check)
2950         {
2951             if (targ == mons->pos())
2952                 return true;
2953 
2954             return false; // blocks square
2955         }
2956 
2957         if (!summon_can_attack(mons, targ))
2958             return false;
2959 
2960         // Cut down plants only when no alternative, or they're
2961         // our target.
2962         if (mons_is_firewood(*targmonster) && mons->target != targ)
2963             return false;
2964 
2965         if ((mons_aligned(mons, targmonster)
2966              || targmonster->type == MONS_FOXFIRE)
2967             && !mons->has_ench(ENCH_INSANE)
2968             && !_mons_can_displace(mons, targmonster))
2969         {
2970             return false;
2971         }
2972         // Prefer to move past enemies instead of hit them, if we're retreating
2973         else if ((!mons_aligned(mons, targmonster)
2974                   || mons->has_ench(ENCH_INSANE))
2975                  && mons->behaviour == BEH_WITHDRAW)
2976         {
2977             return false;
2978         }
2979     }
2980 
2981     // Friendlies shouldn't try to move onto the player's
2982     // location, if they are aiming for some other target.
2983     if (mons->foe != MHITYOU
2984         && targ == you.pos()
2985         && (mons->foe != MHITNOT || mons->is_patrolling())
2986         && !_unfriendly_or_impaired(*mons))
2987     {
2988         return false;
2989     }
2990 
2991     // Wandering through a trap is OK if we're pretty healthy,
2992     // really stupid, or immune to the trap.
2993     if (!mons->is_trap_safe(targ, just_check))
2994         return false;
2995 
2996     // If we end up here the monster can safely move.
2997     return true;
2998 }
2999 
3000 // May mons attack targ if it's in its way, despite
3001 // possibly aligned attitudes?
3002 // The aim of this is to have monsters cut down plants
3003 // to get to the player if necessary.
_may_cutdown(monster * mons,monster * targ)3004 static bool _may_cutdown(monster* mons, monster* targ)
3005 {
3006     // Save friendly plants from allies.
3007     // [ds] I'm deliberately making the alignment checks symmetric here.
3008     // The previous check involved good-neutrals never attacking friendlies
3009     // and friendlies never attacking anything other than hostiles.
3010     if ((mons->friendly() || mons->good_neutral())
3011          && (targ->friendly() || targ->good_neutral()))
3012     {
3013         return false;
3014     }
3015     // Outside of that case, can always cut mundane plants
3016     // (but don't try to attack briars unless their damage will be insignificant)
3017     return mons_is_firewood(*targ)
3018         && (targ->type != MONS_BRIAR_PATCH
3019             || (targ->friendly() && !mons_aligned(mons, targ))
3020             || mons->type == MONS_THORN_HUNTER
3021             || mons->armour_class() * mons->hit_points >= 400);
3022 }
3023 
3024 // Uses, and updates the global variable mmov.
_find_good_alternate_move(monster * mons,const move_array & good_move)3025 static void _find_good_alternate_move(monster* mons,
3026                                       const move_array& good_move)
3027 {
3028     const coord_def target = mons->firing_pos.zero() ? mons->target
3029                                                      : mons->firing_pos;
3030     const int current_distance = distance2(mons->pos(), target);
3031 
3032     int dir = _compass_idx(mmov);
3033 
3034     // Only handle if the original move is to an adjacent square.
3035     if (dir == -1)
3036         return;
3037 
3038     int dist[2];
3039 
3040     // First 1 away, then 2 (3 is silly).
3041     for (int j = 1; j <= 2; j++)
3042     {
3043         const int FAR_AWAY = 1000000;
3044 
3045         // Try both directions (but randomise which one is first).
3046         const int sdir = random_choose(j, -j);
3047         const int inc = -2 * sdir;
3048 
3049         for (int mod = sdir, i = 0; i < 2; mod += inc, i++)
3050         {
3051             const int newdir = (dir + 8 + mod) % 8;
3052             if (good_move[mon_compass[newdir].x+1][mon_compass[newdir].y+1])
3053                 dist[i] = distance2(mons->pos()+mon_compass[newdir], target);
3054             else
3055             {
3056                 // If we can cut firewood there, it's still not a good move,
3057                 // but update mmov so we can fall back to it.
3058                 monster* targ = monster_at(mons->pos() + mon_compass[newdir]);
3059                 const bool retreating = mons_is_retreating(*mons);
3060 
3061                 dist[i] = (targ && _may_cutdown(mons, targ))
3062                           ? current_distance
3063                           : retreating ? -FAR_AWAY : FAR_AWAY;
3064             }
3065         }
3066 
3067         const int dir0 = ((dir + 8 + sdir) % 8);
3068         const int dir1 = ((dir + 8 - sdir) % 8);
3069 
3070         // Now choose.
3071         if (dist[0] == dist[1] && abs(dist[0]) == FAR_AWAY)
3072             continue;
3073 
3074         // Which one was better? -- depends on FLEEING or not.
3075         if (mons_is_retreating(*mons))
3076         {
3077             if (dist[0] >= dist[1] && dist[0] >= current_distance)
3078             {
3079                 mmov = mon_compass[dir0];
3080                 break;
3081             }
3082             if (dist[1] >= dist[0] && dist[1] >= current_distance)
3083             {
3084                 mmov = mon_compass[dir1];
3085                 break;
3086             }
3087         }
3088         else
3089         {
3090             if (dist[0] <= dist[1] && dist[0] <= current_distance)
3091             {
3092                 mmov = mon_compass[dir0];
3093                 break;
3094             }
3095             if (dist[1] <= dist[0] && dist[1] <= current_distance)
3096             {
3097                 mmov = mon_compass[dir1];
3098                 break;
3099             }
3100         }
3101     }
3102 }
3103 
_jelly_grows(monster & mons)3104 static void _jelly_grows(monster& mons)
3105 {
3106     if (player_can_hear(mons.pos()))
3107     {
3108         mprf(MSGCH_SOUND, "You hear a%s slurping noise.",
3109              you.see_cell(mons.pos()) ? "" : " distant");
3110     }
3111 
3112     const int avg_hp = mons_avg_hp(mons.type);
3113     mons.hit_points += 5;
3114     mons.hit_points = min(MAX_MONSTER_HP,
3115                           min(avg_hp * 4, mons.hit_points));
3116 
3117     // note here, that this makes jellies "grow" {dlb}:
3118     if (mons.hit_points > mons.max_hit_points)
3119         mons.max_hit_points = mons.hit_points;
3120 
3121     _jelly_divide(mons);
3122 }
3123 
_monster_swaps_places(monster * mon,const coord_def & delta)3124 static bool _monster_swaps_places(monster* mon, const coord_def& delta)
3125 {
3126     if (delta.origin())
3127         return false;
3128 
3129     monster* const m2 = monster_at(mon->pos() + delta);
3130 
3131     if (!m2)
3132         return false;
3133 
3134     if (!_mons_can_displace(mon, m2))
3135         return false;
3136 
3137     if (m2->asleep())
3138     {
3139         if (coinflip())
3140         {
3141             dprf("Alerting monster %s at (%d,%d)",
3142                  m2->name(DESC_PLAIN).c_str(), m2->pos().x, m2->pos().y);
3143             behaviour_event(m2, ME_ALERT);
3144         }
3145         return false;
3146     }
3147 
3148     if (!mon->swap_with(m2))
3149         return false;
3150 
3151     _swim_or_move_energy(*mon);
3152     _swim_or_move_energy(*m2);
3153 
3154     mon->check_redraw(m2->pos(), false);
3155     mon->apply_location_effects(m2->pos());
3156 
3157     m2->check_redraw(mon->pos(), false);
3158     m2->apply_location_effects(mon->pos());
3159 
3160     // The seen context no longer applies if the monster is moving normally.
3161     mon->seen_context = SC_NONE;
3162     m2->seen_context = SC_NONE;
3163 
3164     _handle_manticore_barbs(*mon);
3165     _handle_manticore_barbs(*m2);
3166 
3167     // Pushing past a foxfire gets you burned regardless of alignment
3168     if (m2->type == MONS_FOXFIRE)
3169     {
3170         foxfire_attack(m2, mon);
3171         monster_die(*m2, KILL_DISMISSED, NON_MONSTER, true);
3172     }
3173 
3174     return false;
3175 }
3176 
_do_move_monster(monster & mons,const coord_def & delta)3177 static bool _do_move_monster(monster& mons, const coord_def& delta)
3178 {
3179     const coord_def f = mons.pos() + delta;
3180 
3181     if (!in_bounds(f))
3182         return false;
3183 
3184     if (f == you.pos())
3185     {
3186         fight_melee(&mons, &you);
3187         return true;
3188     }
3189 
3190     // This includes the case where the monster attacks itself.
3191     if (monster* def = monster_at(f))
3192     {
3193         fight_melee(&mons, def);
3194         return true;
3195     }
3196 
3197     if (mons.is_constricted())
3198     {
3199         if (mons.attempt_escape())
3200             simple_monster_message(mons, " escapes!");
3201         else
3202         {
3203             simple_monster_message(mons, " struggles to escape constriction.");
3204             _swim_or_move_energy(mons);
3205             return true;
3206         }
3207     }
3208 
3209     ASSERT(!cell_is_runed(f)); // should be checked in mons_can_traverse
3210 
3211     if (feat_is_closed_door(env.grid(f)))
3212     {
3213         if (mons_can_destroy_door(mons, f))
3214         {
3215             env.grid(f) = DNGN_FLOOR;
3216             set_terrain_changed(f);
3217 
3218             if (you.see_cell(f))
3219             {
3220                 viewwindow();
3221                 update_screen();
3222 
3223                 if (!you.can_see(mons))
3224                 {
3225                     mpr("The door bursts into shrapnel!");
3226                     interrupt_activity(activity_interrupt::force);
3227                 }
3228                 else
3229                     simple_monster_message(mons, " bursts through the door, destroying it!");
3230             }
3231         }
3232         else if (mons_can_open_door(mons, f))
3233         {
3234             _mons_open_door(mons, f);
3235             return true;
3236         }
3237         else if (mons_can_eat_door(mons, f))
3238         {
3239             env.grid(f) = DNGN_FLOOR;
3240             set_terrain_changed(f);
3241 
3242             _jelly_grows(mons);
3243 
3244             if (you.see_cell(f))
3245             {
3246                 viewwindow();
3247                 update_screen();
3248 
3249                 if (!you.can_see(mons))
3250                 {
3251                     mpr("The door mysteriously vanishes.");
3252                     interrupt_activity(activity_interrupt::force);
3253                 }
3254                 else
3255                     simple_monster_message(mons, " eats the door!");
3256             }
3257         } // done door-eating jellies
3258     }
3259 
3260     // The monster gave a "comes into view" message and then immediately
3261     // moved back out of view, leaing the player nothing to see, so give
3262     // this message to avoid confusion.
3263     else if (crawl_state.game_is_hints() && mons.flags & MF_WAS_IN_VIEW
3264              && !you.see_cell(f))
3265     {
3266         learned_something_new(HINT_MONSTER_LEFT_LOS, mons.pos());
3267     }
3268 
3269     // The seen context no longer applies if the monster is moving normally.
3270     mons.seen_context = SC_NONE;
3271 
3272     // This appears to be the real one, ie where the movement occurs:
3273     _swim_or_move_energy(mons);
3274 
3275     if (mons.type == MONS_FOXFIRE)
3276         --mons.steps_remaining;
3277 
3278     _escape_water_hold(mons);
3279 
3280     if (env.grid(mons.pos()) == DNGN_DEEP_WATER && env.grid(f) != DNGN_DEEP_WATER
3281         && !monster_habitable_grid(&mons, DNGN_DEEP_WATER))
3282     {
3283         // er, what?  Seems impossible.
3284         mons.seen_context = SC_NONSWIMMER_SURFACES_FROM_DEEP;
3285     }
3286 
3287     mons.move_to_pos(f, false);
3288 
3289     // Let go of all constrictees; only stop *being* constricted if we are now
3290     // too far away (done in move_to_pos above).
3291     mons.stop_directly_constricting_all(false);
3292 
3293     mons.check_redraw(mons.pos() - delta);
3294     mons.apply_location_effects(mons.pos() - delta);
3295     if (!invalid_monster(&mons) && you.can_see(mons))
3296     {
3297         handle_seen_interrupt(&mons);
3298         seen_monster(&mons);
3299     }
3300 
3301     _handle_manticore_barbs(mons);
3302 
3303     return true;
3304 }
3305 
_monster_move(monster * mons)3306 static bool _monster_move(monster* mons)
3307 {
3308     ASSERT(mons); // XXX: change to monster &mons
3309     move_array good_move;
3310 
3311     const habitat_type habitat = mons_primary_habitat(*mons);
3312     bool deep_water_available = false;
3313 
3314     // Berserking monsters make a lot of racket.
3315     if (mons->berserk_or_insane())
3316     {
3317         int noise_level = get_shout_noise_level(mons_shouts(mons->type));
3318         if (noise_level > 0)
3319         {
3320             if (you.can_see(*mons) && mons->berserk())
3321             {
3322                 if (one_chance_in(10))
3323                 {
3324                     mprf(MSGCH_TALK_VISUAL, "%s rages.",
3325                          mons->name(DESC_THE).c_str());
3326                 }
3327                 noisy(noise_level, mons->pos(), mons->mid);
3328             }
3329             else if (one_chance_in(5))
3330                 monster_attempt_shout(*mons);
3331             else
3332             {
3333                 // Just be noisy without messaging the player.
3334                 noisy(noise_level, mons->pos(), mons->mid);
3335             }
3336         }
3337     }
3338 
3339     // If a water (or lava) monster is currently flopping around on land, it
3340     // cannot really control where it wants to move, though there's a 50%
3341     // chance of flopping into an adjacent water (or lava) grid.
3342     if (mons->has_ench(ENCH_AQUATIC_LAND))
3343     {
3344         vector<coord_def> adj_water;
3345         vector<coord_def> adj_move;
3346         for (adjacent_iterator ai(mons->pos()); ai; ++ai)
3347         {
3348             if (!cell_is_solid(*ai))
3349             {
3350                 adj_move.push_back(*ai);
3351                 if (habitat == HT_WATER && feat_is_watery(env.grid(*ai))
3352                     || habitat == HT_LAVA && feat_is_lava(env.grid(*ai)))
3353                 {
3354                     adj_water.push_back(*ai);
3355                 }
3356             }
3357         }
3358         if (adj_move.empty())
3359         {
3360             simple_monster_message(*mons, " flops around on dry land!");
3361             return false;
3362         }
3363 
3364         vector<coord_def> moves = adj_water;
3365         if (adj_water.empty() || coinflip())
3366             moves = adj_move;
3367 
3368         const coord_def newpos = moves.empty() ? mons->pos()
3369                                                : moves[random2(moves.size())];
3370 
3371         const monster* mon2 = monster_at(newpos);
3372         if (!mons->has_ench(ENCH_INSANE)
3373             && (newpos == you.pos() && mons->wont_attack()
3374                 || (mon2 && mons->wont_attack() == mon2->wont_attack())))
3375         {
3376             simple_monster_message(*mons, " flops around on dry land!");
3377             return false;
3378         }
3379 
3380         return _do_move_monster(*mons, newpos - mons->pos());
3381     }
3382 
3383     // Let's not even bother with this if mmov is zero.
3384     if (mmov.origin())
3385         return false;
3386 
3387     for (int count_x = 0; count_x < 3; count_x++)
3388         for (int count_y = 0; count_y < 3; count_y++)
3389         {
3390             const int targ_x = mons->pos().x + count_x - 1;
3391             const int targ_y = mons->pos().y + count_y - 1;
3392 
3393             // Bounds check: don't consider moving out of grid!
3394             if (!in_bounds(targ_x, targ_y))
3395             {
3396                 good_move[count_x][count_y] = false;
3397                 continue;
3398             }
3399             dungeon_feature_type target_grid = env.grid[targ_x][targ_y];
3400 
3401             if (target_grid == DNGN_DEEP_WATER)
3402                 deep_water_available = true;
3403 
3404             good_move[count_x][count_y] =
3405                 mon_can_move_to_pos(mons, coord_def(count_x-1, count_y-1));
3406         }
3407 
3408     // Now we know where we _can_ move.
3409 
3410     const coord_def newpos = mons->pos() + mmov;
3411     // Water creatures have a preference for water they can hide in -- bwr
3412     // [ds] Weakened the powerful attraction to deep water if the monster
3413     // is in good health.
3414     if (habitat == HT_WATER
3415         && deep_water_available
3416         && env.grid(mons->pos()) != DNGN_DEEP_WATER
3417         && env.grid(newpos) != DNGN_DEEP_WATER
3418         && newpos != you.pos()
3419         && (one_chance_in(3)
3420             || mons->hit_points <= (mons->max_hit_points * 3) / 4))
3421     {
3422         int count = 0;
3423 
3424         for (int cx = 0; cx < 3; cx++)
3425             for (int cy = 0; cy < 3; cy++)
3426             {
3427                 if (good_move[cx][cy]
3428                     && env.grid[mons->pos().x + cx - 1][mons->pos().y + cy - 1]
3429                             == DNGN_DEEP_WATER)
3430                 {
3431                     if (one_chance_in(++count))
3432                     {
3433                         mmov.x = cx - 1;
3434                         mmov.y = cy - 1;
3435                     }
3436                 }
3437             }
3438     }
3439 
3440     // Now, if a monster can't move in its intended direction, try
3441     // either side. If they're both good, move in whichever dir
3442     // gets it closer (farther for fleeing monsters) to its target.
3443     // If neither does, do nothing.
3444     if (good_move[mmov.x + 1][mmov.y + 1] == false)
3445         _find_good_alternate_move(mons, good_move);
3446 
3447     // ------------------------------------------------------------------
3448     // If we haven't found a good move by this point, we're not going to.
3449     // ------------------------------------------------------------------
3450 
3451     if (mons->type == MONS_SPATIAL_MAELSTROM)
3452     {
3453         const dungeon_feature_type feat = env.grid(mons->pos() + mmov);
3454         if (!feat_is_permarock(feat) && feat_is_solid(feat))
3455         {
3456             const coord_def target(mons->pos() + mmov);
3457             create_monster(
3458                     mgen_data(MONS_SPATIAL_VORTEX, SAME_ATTITUDE(mons), target)
3459                     .set_summoned(mons, 2, MON_SUMM_ANIMATE, GOD_LUGONU));
3460             destroy_wall(target);
3461         }
3462     }
3463 
3464     const bool burrows = mons->can_burrow();
3465     const bool flattens_trees = mons_flattens_trees(*mons);
3466     const bool digs = _mons_can_cast_dig(mons, false)
3467                       || _mons_can_zap_dig(mons);
3468     // Take care of Dissolution burrowing, lerny, etc
3469     if (burrows || flattens_trees || digs)
3470     {
3471         const dungeon_feature_type feat = env.grid(mons->pos() + mmov);
3472         if ((feat == DNGN_ROCK_WALL || feat == DNGN_CLEAR_ROCK_WALL)
3473                 && !burrows && digs
3474             || feat == DNGN_GRATE && digs)
3475         {
3476             bolt beem;
3477             if (_mons_can_cast_dig(mons, true))
3478             {
3479                 setup_mons_cast(mons, beem, SPELL_DIG);
3480                 beem.target = mons->pos() + mmov;
3481                 mons_cast(mons, beem, SPELL_DIG,
3482                           mons->spell_slot_flags(SPELL_DIG));
3483             }
3484             else if (_mons_can_zap_dig(mons))
3485             {
3486                 ASSERT(mons->mslot_item(MSLOT_WAND));
3487                 item_def &wand = *mons->mslot_item(MSLOT_WAND);
3488                 setup_mons_cast(mons, beem, SPELL_DIG, true);
3489                 beem.target = mons->pos() + mmov;
3490                 _mons_fire_wand(*mons, wand, beem, you.can_see(*mons));
3491 
3492                 //_setup_wand_beam(beem, *mons, wand);
3493                 //_mons_fire_wand(*mons, wand, beem, you.can_see(*mons), false);
3494             }
3495             else
3496                 simple_monster_message(*mons, " falters for a moment.");
3497             mons->lose_energy(EUT_SPELL);
3498             return true;
3499         }
3500         else if ((((feat == DNGN_ROCK_WALL || feat == DNGN_CLEAR_ROCK_WALL)
3501                   && burrows)
3502                   || (flattens_trees && feat_is_tree(feat)))
3503                  && good_move[mmov.x + 1][mmov.y + 1] == true)
3504         {
3505             const coord_def target(mons->pos() + mmov);
3506             destroy_wall(target);
3507 
3508             if (flattens_trees)
3509             {
3510                 // Flattening trees has a movement cost to the monster
3511                 // - 100% over and above its normal move cost.
3512                 _swim_or_move_energy(*mons);
3513                 if (you.see_cell(target))
3514                 {
3515                     const bool actor_visible = you.can_see(*mons);
3516                     mprf("%s knocks down a tree!",
3517                          actor_visible?
3518                          mons->name(DESC_THE).c_str() : "Something");
3519                     noisy(25, target);
3520                 }
3521                 else
3522                     noisy(25, target, "You hear a crashing sound.");
3523             }
3524             // Dissolution dissolves walls.
3525             else if (player_can_hear(mons->pos() + mmov))
3526                 mprf(MSGCH_SOUND, "You hear a sizzling sound.");
3527         }
3528     }
3529 
3530     bool ret = false;
3531     if (good_move[mmov.x + 1][mmov.y + 1] && !mmov.origin())
3532     {
3533         // Check for attacking player.
3534         if (mons->pos() + mmov == you.pos())
3535         {
3536             ret = fight_melee(mons, &you);
3537             mmov.reset();
3538         }
3539 
3540         // If we're following the player through stairs, the only valid
3541         // movement is towards the player. -- bwr
3542         if (testbits(mons->flags, MF_TAKING_STAIRS))
3543         {
3544             if (!player_stair_delay())
3545             {
3546                 mons->flags &= ~MF_TAKING_STAIRS;
3547 
3548                 dprf("BUG: %s was marked as follower when not following!",
3549                      mons->name(DESC_PLAIN).c_str());
3550             }
3551             else
3552             {
3553                 ret    = true;
3554                 mmov.reset();
3555 
3556                 dprf("%s is skipping movement in order to follow.",
3557                      mons->name(DESC_THE).c_str());
3558             }
3559         }
3560 
3561         // Check for attacking another monster.
3562         if (monster* targ = monster_at(mons->pos() + mmov))
3563         {
3564             if ((mons_aligned(mons, targ) || targ->type == MONS_FOXFIRE)
3565                 && !(mons->has_ench(ENCH_INSANE)
3566                      || mons->confused()))
3567             {
3568                 ret = _monster_swaps_places(mons, mmov);
3569             }
3570             else
3571             {
3572                 fight_melee(mons, targ);
3573                 ret = true;
3574             }
3575 
3576             // If the monster swapped places, the work's already done.
3577             mmov.reset();
3578         }
3579 
3580         // The monster could die after a melee attack due to a mummy
3581         // death curse or something.
3582         if (!mons->alive())
3583             return true;
3584 
3585         if (mons_genus(mons->type) == MONS_EFREET
3586             || mons->type == MONS_FIRE_ELEMENTAL)
3587         {
3588             place_cloud(CLOUD_FIRE, mons->pos(), 2 + random2(4), mons);
3589         }
3590 
3591         if (mons->has_ench(ENCH_ROLLING))
3592             place_cloud(CLOUD_DUST, mons->pos(), 2, mons);
3593 
3594         if (mons->type == MONS_FOXFIRE)
3595             check_place_cloud(CLOUD_FLAME, mons->pos(), 2, mons);
3596 
3597         if (mons->type == MONS_CURSE_TOE)
3598             place_cloud(CLOUD_MIASMA, mons->pos(), 2 + random2(3), mons);
3599     }
3600     else
3601     {
3602         monster* targ = monster_at(mons->pos() + mmov);
3603         if (!mmov.origin() && targ && _may_cutdown(mons, targ))
3604         {
3605             fight_melee(mons, targ);
3606             ret = true;
3607         }
3608 
3609         mmov.reset();
3610 
3611         // Don't let boulder beetles roll in place.
3612         if (mons->has_ench(ENCH_ROLLING))
3613             mons->del_ench(ENCH_ROLLING);
3614 
3615         // Fleeing monsters that can't move will panic and possibly
3616         // turn to face their attacker.
3617         make_mons_stop_fleeing(mons);
3618     }
3619 
3620     // This handles the chance for the monster to hit itself.
3621     if (mmov.x || mmov.y || (mons->confused() && one_chance_in(6)))
3622         return _do_move_monster(*mons, mmov);
3623 
3624     // Battlespheres need to preserve their tracking targets after each move
3625     if (mons_is_wandering(*mons)
3626         && mons->type != MONS_BATTLESPHERE)
3627     {
3628         // trigger a re-evaluation of our wander target on our next move -cao
3629         mons->target = mons->pos();
3630         if (!mons->is_patrolling())
3631         {
3632             mons->travel_target = MTRAV_NONE;
3633             mons->travel_path.clear();
3634         }
3635         mons->firing_pos.reset();
3636     }
3637 
3638     return ret;
3639 }
3640 
_mons_in_cloud(monster & mons)3641 static void _mons_in_cloud(monster& mons)
3642 {
3643     // Submerging in water or lava saves from clouds.
3644     if (mons.submerged() && env.grid(mons.pos()) != DNGN_FLOOR)
3645         return;
3646 
3647     actor_apply_cloud(&mons);
3648 }
3649