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