1 /**
2  * @file
3  * @brief Monster behaviour functions.
4 **/
5 
6 #include "AppHdr.h"
7 
8 #include "mon-behv.h"
9 
10 #include "ability.h"
11 #include "act-iter.h"
12 #include "areas.h"
13 #include "attitude-change.h"
14 #include "coordit.h"
15 #include "database.h"
16 #include "dgn-overview.h"
17 #include "dungeon.h"
18 #include "fineff.h"
19 #include "god-passive.h"
20 #include "hints.h"
21 #include "item-prop.h"
22 #include "losglobal.h"
23 #include "macro.h"
24 #include "message.h"
25 #include "mon-act.h"
26 #include "mon-death.h"
27 #include "mon-movetarget.h"
28 #include "mon-speak.h"
29 #include "ouch.h"
30 #include "religion.h"
31 #include "shout.h"
32 #include "spl-summoning.h"
33 #include "state.h"
34 #include "stringutil.h"
35 #include "terrain.h"
36 #include "traps.h"
37 
_guess_invis_foe_pos(monster * mon)38 static void _guess_invis_foe_pos(monster* mon)
39 {
40     const actor* foe          = mon->get_foe();
41     const int    guess_radius = 2;
42 
43     vector<coord_def> possibilities;
44 
45     // NOTE: This depends on ignoring clouds, so that cells hidden by
46     // opaque clouds are included as a possibility for the foe's location.
47     for (radius_iterator ri(mon->pos(), guess_radius, C_SQUARE, LOS_SOLID); ri; ++ri)
48     {
49         if (foe->is_habitable(*ri))
50             possibilities.push_back(*ri);
51     }
52 
53     if (!possibilities.empty())
54         mon->target = possibilities[random2(possibilities.size())];
55     else
56         mon->target = dgn_random_point_from(mon->pos(), guess_radius);
57 }
58 
_mon_check_foe_invalid(monster * mon)59 static void _mon_check_foe_invalid(monster* mon)
60 {
61     // Assume a spectral weapon has a valid target
62     // Ideally this is not outside special cased like this
63     if (mons_is_avatar(mon->type))
64         return;
65 
66     if (mon->foe != MHITNOT && mon->foe != MHITYOU)
67     {
68         if (actor *foe = mon->get_foe())
69         {
70             const monster* foe_mons = foe->as_monster();
71             if (foe_mons->alive() && summon_can_attack(mon, foe)
72                 && (mon->has_ench(ENCH_INSANE)
73                     || mon->friendly() != foe_mons->friendly()
74                     || mon->neutral() != foe_mons->neutral()))
75             {
76                 return;
77             }
78         }
79 
80         mon->foe = MHITNOT;
81     }
82 }
83 
_mon_tries_regain_los(monster * mon)84 static bool _mon_tries_regain_los(monster* mon)
85 {
86     // Only intelligent monsters with ranged attack will try to regain LOS.
87     if (mons_intel(*mon) < I_HUMAN || !mons_has_ranged_attack(*mon))
88         return false;
89 
90     // Any special case should go here.
91     if (mons_class_flag(mon->type, M_FIGHTER)
92         && !(mon->type == MONS_CENTAUR_WARRIOR)
93         && !(mon->type == MONS_YAKTAUR_CAPTAIN))
94     {
95         return false;
96     }
97 
98     // Randomize it to make it less predictable, and reduce flip-flopping.
99     return !one_chance_in(3);
100 }
101 
102 // Monster tries to get into a firing position. Among the cells which have
103 // a line of fire to the target, we choose the closest one to regain LOS as
104 // fast as possible. If several cells are eligible, we choose the one closest
105 // to ideal_range (too far = easier to escape, too close = easier to ambush).
_set_firing_pos(monster * mon,coord_def target)106 static void _set_firing_pos(monster* mon, coord_def target)
107 {
108     const int ideal_range = LOS_DEFAULT_RANGE / 2;
109     const int current_distance = mon->pos().distance_from(target);
110 
111     // We don't consider getting farther away unless already very close.
112     const int max_range = max(ideal_range, current_distance);
113 
114     int best_distance = INT_MAX;
115     int best_distance_to_ideal_range = INT_MAX;
116     coord_def best_pos(0, 0);
117 
118     for (distance_iterator di(mon->pos(), true, true, LOS_RADIUS);
119          di; ++di)
120     {
121         const coord_def p(*di);
122         const int range = p.distance_from(target);
123 
124         if (!mon->see_cell(*di))
125             continue;
126 
127         if (!in_bounds(p) || range > max_range
128             || !cell_see_cell(p, target, LOS_NO_TRANS)
129             || !mon_can_move_to_pos(mon, p - mon->pos()))
130         {
131             continue;
132         }
133 
134         const int distance = p.distance_from(mon->pos());
135 
136         if (distance < best_distance
137             || distance == best_distance
138                && abs(range - ideal_range) < best_distance_to_ideal_range)
139         {
140             best_pos = p;
141             best_distance = distance;
142             best_distance_to_ideal_range = abs(range - ideal_range);
143         }
144     }
145 
146     mon->firing_pos = best_pos;
147 }
148 
_decide_monster_firing_position(monster * mon,actor * owner)149 static void _decide_monster_firing_position(monster* mon, actor* owner)
150 {
151     // Monster can see foe: continue 'tracking'
152     // by updating target x,y.
153     if (mon->foe == MHITYOU)
154     {
155         const bool ignore_special_firing_AI = mon->friendly()
156                                               || mon->berserk_or_insane();
157 
158         // The foe is the player.
159         if (mons_class_flag(mon->type, M_MAINTAIN_RANGE)
160             && !ignore_special_firing_AI)
161             {
162                 // Get to firing range even if we are close.
163                 _set_firing_pos(mon, you.pos());
164             }
165         else if (mon->type == MONS_MERFOLK_AVATAR && !ignore_special_firing_AI)
166             find_merfolk_avatar_water_target(mon);
167         else if (!mon->firing_pos.zero()
168                  && mon->see_cell_no_trans(mon->target))
169         {
170             // If monster is currently getting into firing position and
171             // sees the player and can attack him, clear firing_pos.
172             mon->firing_pos.reset();
173         }
174 
175         if (!(mon->firing_pos.zero() && try_pathfind(mon)))
176         {
177             // Whew. If we arrived here, path finding didn't yield anything
178             // (or wasn't even attempted) and we need to set our target
179             // the traditional way.
180 
181             mon->target = you.pos();
182         }
183     }
184     else
185     {
186         // We have a foe but it's not the player.
187         monster* target = &env.mons[mon->foe];
188         mon->target = target->pos();
189 
190         if (mons_class_flag(mon->type, M_MAINTAIN_RANGE)
191             && !mon->berserk_or_insane()
192             && !(mons_is_avatar(mon->type)
193                  && owner && mon->foe == owner->mindex()))
194         {
195             _set_firing_pos(mon, mon->target);
196         }
197         // Hold position if we've reached our ideal range
198         else if (mon->type == MONS_SPELLFORGED_SERVITOR
199                  && (mon->pos() - target->pos()).rdist()
200                  <= mon->props["ideal_range"].get_int()
201                  && !one_chance_in(8))
202         {
203             mon->firing_pos = mon->pos();
204         }
205     }
206 }
207 
208 /**
209  * Should the player be treated as if they were normally visible to a given
210  * monster, even though they're currently invisible?
211  *
212  * Random per-call; depends on proximity, monster intelligence, and Ash wrath.
213  *
214  * @param mon     The monster in question.
215  * @param         Whether the monster correctly guessed the player's presence.
216  */
_monster_guesses_invis_player(const monster & mon)217 static bool _monster_guesses_invis_player(const monster &mon)
218 {
219     // Sometimes, if a player is right next to a monster, they will 'see'.
220     if (grid_distance(you.pos(), mon.pos()) == 1 && one_chance_in(3))
221         return true;
222 
223     // [dshaligram] Smart monsters have a chance of clueing in to
224     // invisible players in various ways.
225     if (mons_intel(mon) == I_HUMAN && one_chance_in(12))
226         return true;
227 
228     // Ash penance makes monsters very likely to target you through invis.
229     if (player_under_penance(GOD_ASHENZARI) && coinflip())
230         return true;
231 
232     return false;
233 }
234 
235 /**
236  * Evaluates the monster's AI state, and sets its target based on its foe.
237  */
handle_behaviour(monster * mon)238 void handle_behaviour(monster* mon)
239 {
240     // Test spawners should always be BEH_SEEK against a foe, since
241     // their only purpose is to spew out monsters for testing
242     // purposes.
243     if (mon->type == MONS_TEST_SPAWNER)
244     {
245         for (monster_iterator mi; mi; ++mi)
246         {
247             if (mon->attitude != mi->attitude)
248             {
249                 mon->foe       = mi->mindex();
250                 mon->target    = mi->pos();
251                 mon->behaviour = BEH_SEEK;
252                 return;
253             }
254         }
255     }
256 
257     bool changed = true;
258     bool isFriendly = mon->friendly();
259     bool isNeutral  = mon->neutral();
260     bool wontAttack = mon->wont_attack() && !mon->has_ench(ENCH_INSANE);
261 
262     // Whether the player position is in LOS of the monster.
263     bool proxPlayer = !crawl_state.game_is_arena() && mon->see_cell(you.pos());
264 
265     // If set, pretend the player isn't there, but only for hostile monsters.
266     if (proxPlayer && crawl_state.disables[DIS_MON_SIGHT] && !mon->wont_attack())
267         proxPlayer = false;
268 
269     bool proxFoe;
270     bool isHealthy  = (mon->hit_points > mon->max_hit_points / 2);
271     bool isSmart    = (mons_intel(*mon) >= I_HUMAN);
272     bool isScared   = mon->has_ench(ENCH_FEAR);
273     bool isPacified = mon->pacified();
274     bool patrolling = mon->is_patrolling();
275     static vector<level_exit> e;
276     static int                e_index = -1;
277 
278     //mprf("AI debug: mon %d behv=%d foe=%d pos=%d %d target=%d %d",
279     //     mon->mindex(), mon->behaviour, mon->foe, mon->pos().x,
280     //     mon->pos().y, mon->target.x, mon->target.y);
281 
282     // Check for permanent confusion, early out.
283     if (mons_class_flag(mon->type, M_CONFUSED))
284     {
285         set_random_target(mon);
286         return;
287     }
288 
289     if (mons_is_fleeing_sanctuary(*mon)
290         && mons_is_fleeing(*mon)
291         && is_sanctuary(you.pos()))
292     {
293         return;
294     }
295 
296     // Make sure monsters are not targeting the player in arena mode.
297     ASSERT(!crawl_state.game_is_arena() || mon->foe != MHITYOU);
298 
299     // Validate current target exists.
300     _mon_check_foe_invalid(mon);
301 
302     actor *owner = (mon->summoner ? actor_by_mid(mon->summoner) : nullptr);
303     if (mon->type == MONS_SPECTRAL_WEAPON)
304     {
305         if (mon->summoner && (!owner || !owner->alive()))
306             end_spectral_weapon(mon, false);
307 
308         // Spectral weapons never do anything on their own. They just attack
309         // on command. A sad existence, really.
310         return;
311     }
312 
313     // Change proxPlayer depending on invisibility and standing
314     // in shallow water.
315     if (proxPlayer && !you.visible_to(mon))
316         proxPlayer = _monster_guesses_invis_player(*mon);
317 
318     // Set friendly target, if they don't already have one.
319     // Berserking allies ignore your commands!
320     if (isFriendly
321         && (mon->foe == MHITNOT || mon->foe == MHITYOU)
322         && !mon->berserk_or_insane()
323         && mon->behaviour != BEH_WITHDRAW
324         && !mons_self_destructs(*mon)
325         && !mons_is_avatar(mon->type))
326     {
327         if (you.pet_target != MHITNOT)
328             mon->foe = you.pet_target;
329         else if (mons_class_is_stationary(mon->type))
330             set_nearest_monster_foe(mon);
331     }
332 
333     // Instead, berserkers attack nearest monsters.
334     if (mon->behaviour != BEH_SLEEP
335         && (mon->has_ench(ENCH_INSANE)
336             || ((mon->berserk() || mons_self_destructs(*mon))
337                 && (mon->foe == MHITNOT
338                     || isFriendly && mon->foe == MHITYOU))))
339     {
340         // Intelligent monsters prefer to attack the player,
341         // even when berserking.
342         if (!isFriendly
343             && !mon->has_ench(ENCH_INSANE)
344             && proxPlayer
345             && mons_intel(*mon) >= I_HUMAN)
346         {
347             mon->foe = MHITYOU;
348         }
349         else
350             set_nearest_monster_foe(mon);
351     }
352 
353     // Pacified monsters leaving the level prefer not to attack.
354     // Others choose the nearest foe.
355     // XXX: This is currently expensive, so we don't want to do it
356     //      every turn for every monster.
357     if (!isPacified && mon->foe == MHITNOT
358         && mon->behaviour != BEH_SLEEP
359         && (proxPlayer || one_chance_in(3)))
360     {
361         set_nearest_monster_foe(mon);
362     }
363 
364     // Friendly summons will come back to the player if they go out of sight.
365     if (!summon_can_attack(mon))
366         mon->target = you.pos();
367 
368     // Monsters do not attack themselves. {dlb}
369     if (mon->foe == mon->mindex())
370         mon->foe = MHITNOT;
371 
372     // Friendly and good neutral monsters do not attack other friendly
373     // and good neutral monsters.
374     if (!mons_is_avatar(mon->type) && mon->foe != MHITNOT && mon->foe != MHITYOU
375         && wontAttack && env.mons[mon->foe].wont_attack())
376     {
377         mon->foe = MHITNOT;
378     }
379 
380     // Neutral monsters prefer not to attack players, or other neutrals.
381     if (isNeutral
382         && !mon->has_ench(ENCH_INSANE)
383         && mon->foe != MHITNOT
384         && (mon->foe == MHITYOU || env.mons[mon->foe].neutral()))
385     {
386         mon->foe = MHITNOT;
387     }
388 
389     // Unfriendly monsters fighting other monsters will usually
390     // target the player, if they're healthy.
391     if (!isFriendly && !isNeutral
392         && !mons_is_avatar(mon->type)
393         && mon->foe != MHITYOU && mon->foe != MHITNOT
394         && proxPlayer && !mon->berserk_or_insane()
395         && isHealthy
396         && !one_chance_in(3))
397     {
398         mon->foe = MHITYOU;
399     }
400 
401     // Validate current target again.
402     _mon_check_foe_invalid(mon);
403 
404     if (mon->has_ench(ENCH_HAUNTING))
405     {
406         actor* targ = mon->get_ench(ENCH_HAUNTING).agent();
407         if (targ && targ->alive())
408         {
409             mon->foe = targ->mindex();
410             mon->target = targ->pos();
411         }
412     }
413 
414     while (changed)
415     {
416         const actor* afoe = mon->get_foe();
417         proxFoe = afoe && mon->can_see(*afoe);
418 
419         if (mon->foe == MHITYOU)
420         {
421             // monster::get_foe returns nullptr for friendly monsters with
422             // foe == MHITYOU, so make afoe point to the player here.
423             // -cao
424             afoe = &you;
425             proxFoe = proxPlayer;   // Take invis into account.
426         }
427 
428         coord_def foepos = coord_def(0,0);
429         if (afoe)
430             foepos = afoe->pos();
431 
432         if (mon->pos() == mon->firing_pos)
433             mon->firing_pos.reset();
434 
435         // Track changes to state; attitude never changes here.
436         beh_type new_beh       = mon->behaviour;
437         unsigned short new_foe = mon->foe;
438 
439         // Take care of monster state changes.
440         switch (mon->behaviour)
441         {
442         case BEH_SLEEP:
443             // default sleep state
444             mon->target = mon->pos();
445             new_foe = MHITNOT;
446             break;
447 
448         case BEH_SEEK:
449             // No foe?  Then wander or seek the player.
450             if (mon->foe == MHITNOT)
451             {
452                 if (crawl_state.game_is_arena()
453                     || !proxPlayer && !isFriendly
454                     || isNeutral && !mon->has_ench(ENCH_INSANE)
455                     || patrolling
456                     || mons_self_destructs(*mon))
457                 {
458                     new_beh = BEH_WANDER;
459                 }
460                 else
461                 {
462                     new_foe = MHITYOU;
463                     mon->target = you.pos();
464                 }
465                 break;
466             }
467 
468             // just because a move takes us closer to the target doesn't mean
469             // that the move will stay in los of the target, and if it leaves
470             // los of the target, it's possible for just naively moving toward
471             // the target will not let us reach it (due to walls or whatever)
472             if (!mon->see_cell(mon->target))
473                 try_pathfind(mon);
474 
475             // Foe gone out of LOS?
476             if (!proxFoe
477                 && !(mon->friendly()
478                      && mon->foe == MHITYOU
479                      && mon->is_travelling()
480                      && mon->travel_target == MTRAV_FOE))
481             {
482                 // If their foe is marked, the monster always knows exactly
483                 // where they are.
484                 if (afoe && (mons_foe_is_marked(*mon)
485                                             || mon->has_ench(ENCH_HAUNTING)))
486                 {
487                     mon->target = afoe->pos();
488                     try_pathfind(mon);
489                     break;
490                 }
491 
492                 // Maybe the foe is just invisible.
493                 if (mon->target.origin() && afoe && mon->near_foe())
494                 {
495                     _guess_invis_foe_pos(mon);
496                     if (mon->target.origin())
497                     {
498                         // Having a seeking mon with a foe who's target is
499                         // (0, 0) can lead to asserts, so lets try to
500                         // avoid that.
501                         set_nearest_monster_foe(mon);
502                         if (mon->foe == MHITNOT)
503                         {
504                             new_beh = BEH_WANDER;
505                             break;
506                         }
507                         mon->target = mon->get_foe()->pos();
508                     }
509                 }
510 
511                 if (mon->travel_target == MTRAV_MERFOLK_AVATAR)
512                     mon->travel_target = MTRAV_NONE;
513 
514                 // Spectral weapons simply seek back to their owner if
515                 // they can't see their seek target.
516                 if (mons_is_avatar(mon->type))
517                 {
518                     // XXX: should owner ever not be set here?
519                     new_foe = owner ? owner->mindex() : MHITNOT;
520                     mon->target = owner ? owner->pos() : mon->pos();
521                     break;
522                 }
523                 else if (isFriendly && mon->foe != MHITYOU)
524                 {
525                     if (patrolling || crawl_state.game_is_arena())
526                     {
527                         new_foe = MHITNOT;
528                         new_beh = BEH_WANDER;
529                     }
530                     // If the player can see the target location, do not reset
531                     // our target, even if this monster cannot (we'll assume
532                     // the player passes along this information to allies)
533                     // EXCEPTION: invisible enemies for allies without sinv
534                     // (otherwise your allies get stuck doing nothing)
535                     else if (!foepos.origin() && you.see_cell(foepos)
536                              && afoe->visible_to(mon))
537                     {
538                         try_pathfind(mon);
539                     }
540                     else
541                     {
542                         new_foe = MHITYOU;
543                         mon->target = foepos;
544                     }
545                     break;
546                 }
547 
548                 ASSERT(mon->foe != MHITNOT);
549                 if (mon->foe_memory > 0)
550                 {
551                     // If we've arrived at our target x,y
552                     // do a stealth check. If the foe
553                     // fails, monster will then start
554                     // tracking foe's CURRENT position,
555                     // but only for a few moves (smell and
556                     // intuition only go so far).
557 
558                   if (mon->pos() == mon->target)
559                     {
560                         if (mon->foe == MHITYOU)
561                         {
562                             if (x_chance_in_y(50, you.stealth())
563                                 || you.penance[GOD_ASHENZARI] && coinflip())
564                             {
565                                 mon->target = you.pos();
566                             }
567                             else
568                                 mon->foe_memory = 0;
569                         }
570                         else
571                         {
572                             if (coinflip())     // XXX: cheesy!
573                                 mon->target = env.mons[mon->foe].pos();
574                             else
575                                 mon->foe_memory = 0;
576                         }
577                     }
578                 }
579 
580                 if (mon->foe_memory <= 0
581                     && !(mon->friendly() && mon->foe == MHITYOU))
582                 {
583                     new_beh = BEH_WANDER;
584                 }
585                 // If the player walk out of the LOS of a monster with a ranged
586                 // attack, we assume it sees in which direction the player went
587                 // and it tries to find a line of fire instead of following the
588                 // player.
589                 else if (grid_distance(mon->target, you.pos()) == 1
590                          && _mon_tries_regain_los(mon))
591                 {
592                     _set_firing_pos(mon, you.pos());
593                 }
594                 else //
595                     mon->firing_pos.reset();
596 
597                 if (!isFriendly)
598                     break;
599                 else if (mons_is_avatar(mon->type)
600                          && owner
601                          && !owner->is_player())
602                 {
603                     mon->foe = owner->mindex();
604                     break;
605                 }
606             }
607 
608             ASSERT(proxFoe || isFriendly);
609             ASSERT(mon->foe != MHITNOT);
610 
611             // Monster can see foe: set memory in case it loses sight.
612             // Hack: smarter monsters will tend to pursue the player longer.
613             switch (mons_intel(*mon))
614             {
615             case I_HUMAN:
616                 mon->foe_memory = random_range(450, 1000);
617                 break;
618             case I_ANIMAL:
619                 mon->foe_memory = random_range(250, 550);
620                 break;
621             case I_BRAINLESS:
622                 mon->foe_memory = random_range(100, 300);
623                 break;
624             }
625 
626             _decide_monster_firing_position(mon, owner);
627 
628             break;
629 
630         case BEH_WANDER:
631             if (isPacified)
632             {
633                 // If a pacified monster isn't travelling toward
634                 // someplace from which it can leave the level, make it
635                 // start doing so. If there's no such place, either
636                 // search the level for such a place again, or travel
637                 // randomly.
638                 if (mon->travel_target != MTRAV_PATROL)
639                 {
640                     new_foe = MHITNOT;
641                     mon->travel_path.clear();
642 
643                     e_index = mons_find_nearest_level_exit(mon, e);
644 
645                     if (e_index == -1 || one_chance_in(20))
646                         e_index = mons_find_nearest_level_exit(mon, e, true);
647 
648                     if (e_index != -1)
649                     {
650                         mon->travel_target = MTRAV_PATROL;
651                         patrolling = true;
652                         mon->patrol_point = e[e_index].target;
653                         mon->target = e[e_index].target;
654                     }
655                     else
656                     {
657                         mon->travel_target = MTRAV_NONE;
658                         patrolling = false;
659                         mon->patrol_point.reset();
660                         set_random_target(mon);
661                     }
662                 }
663 
664                 if (pacified_leave_level(mon, e, e_index))
665                     return;
666             }
667 
668             if (mon->strict_neutral() && mons_is_slime(*mon)
669                 && have_passive(passive_t::neutral_slimes))
670             {
671                 set_random_slime_target(mon);
672             }
673 
674             // Is our foe in LOS?
675             // Batty monsters don't automatically reseek so that
676             // they'll flitter away, we'll reset them just before
677             // they get movement in handle_monsters() instead. -- bwr
678             if (proxFoe && !mons_is_batty(*mon) || mons_foe_is_marked(*mon))
679             {
680                 new_beh = BEH_SEEK;
681                 break;
682             }
683 
684             // Creatures not currently pursuing another foe are
685             // alerted by a sentinel's mark
686             if (mon->foe == MHITNOT && you.duration[DUR_SENTINEL_MARK]
687                 && (!isFriendly && !mons_is_avatar(mon->type) && !isNeutral
688                     && !isPacified
689                     || mon->has_ench(ENCH_INSANE)))
690             {
691                 new_foe = MHITYOU;
692                 new_beh = BEH_SEEK;
693                 break;
694             }
695 
696             check_wander_target(mon, isPacified);
697 
698             // During their wanderings, monsters will eventually relax
699             // their guard (stupid ones will do so faster, smart
700             // monsters have longer memories). Pacified monsters will
701             // also eventually switch the place from which they want to
702             // leave the level, in case their current choice is blocked.
703             if (!proxFoe && !mons_is_avatar(mon->type) && mon->foe != MHITNOT
704                    && one_chance_in(isSmart ? 60 : 20)
705                    && !mons_foe_is_marked(*mon)
706                 || isPacified && one_chance_in(isSmart ? 40 : 120))
707             {
708                 new_foe = MHITNOT;
709                 if (mon->is_travelling() && mon->travel_target != MTRAV_PATROL
710                     || isPacified)
711                 {
712 #ifdef DEBUG_PATHFIND
713                     mpr("It's been too long! Stop travelling.");
714 #endif
715                     mon->travel_path.clear();
716                     mon->travel_target = MTRAV_NONE;
717 
718                     if (isPacified && e_index != -1)
719                         e[e_index].unreachable = true;
720                 }
721             }
722             break;
723 
724         case BEH_RETREAT:
725             // If the target can be reached, there is a chance the monster will
726             // try to attack. The chance is low to prevent the player from
727             // dancing in and out of the water.
728             try_pathfind(mon);
729             if (one_chance_in(10) && !target_is_unreachable(mon)
730                 || mons_can_attack(*mon))
731             {
732                 new_beh = BEH_SEEK;
733             }
734             else if (!proxPlayer && one_chance_in(5))
735                 new_beh = BEH_WANDER;
736             else if (proxPlayer)
737                 mon->target = foepos;
738             break;
739 
740         case BEH_FLEE:
741             // Check for healed.
742             if (isHealthy && !isScared)
743                 new_beh = BEH_SEEK;
744 
745             // Smart monsters flee until they can flee no more...
746             // possible to get a 'CORNERED' event, at which point
747             // we can jump back to WANDER if the foe isn't present.
748 
749             if (isFriendly)
750             {
751                 // Special-cased below so that it will flee *towards* you.
752                 if (mon->foe == MHITYOU)
753                     mon->target = you.pos();
754             }
755             else if (proxFoe)
756             {
757                 // Special-cased below so that it will flee *from* the
758                 // correct position.
759                 mon->target = foepos;
760             }
761             break;
762 
763         case BEH_CORNERED:
764 
765             // If we were able to move since becoming cornered, resume fleeing
766             if (mon->pos() != mon->props["last_pos"].get_coord())
767             {
768                 new_beh = BEH_FLEE;
769                 mon->props.erase("last_pos");
770             }
771 
772             // Foe gone out of LOS?
773             if (!proxFoe)
774             {
775                 if ((isFriendly || proxPlayer)
776                     && (!isNeutral || mon->has_ench(ENCH_INSANE))
777                     && !patrolling
778                     && !crawl_state.game_is_arena())
779                 {
780                     new_foe = MHITYOU;
781                 }
782                 else
783                     new_beh = BEH_WANDER;
784             }
785             else
786                 mon->target = foepos;
787             break;
788 
789         case BEH_WITHDRAW:
790         {
791             if (!isFriendly)
792             {
793                 new_beh = BEH_WANDER;
794                 break;
795             }
796 
797             bool stop_retreat = false;
798             // We've approached our next destination, re-evaluate
799             if (grid_distance(mon->target, mon->pos()) <= 1)
800             {
801                 // Continue on to the rally point
802                 if (mon->target != mon->patrol_point)
803                     mon->target = mon->patrol_point;
804                 // Reached rally point, stop withdrawing
805                 else
806                     stop_retreat = true;
807 
808             }
809             else if (grid_distance(mon->pos(), you.pos()) >
810                      LOS_DEFAULT_RANGE + 2)
811             {
812                 // We're too far from the player. Idle around and wait for
813                 // them to catch up.
814                 if (!mon->props.exists("idle_point"))
815                 {
816                     mon->props["idle_point"] = mon->pos();
817                     mon->props["idle_deadline"] = you.elapsed_time + 200;
818                 }
819 
820                 coord_def target_rnd;
821                 target_rnd.x = random_range(-2, 2);
822                 target_rnd.y = random_range(-2, 2);
823                 mon->target = clamp_in_bounds(
824                                     mon->props["idle_point"].get_coord()
825                                     + target_rnd);
826 
827                 if (you.elapsed_time >= mon->props["idle_deadline"].get_int())
828                     stop_retreat = true;
829             }
830             else
831             {
832                 // Be more lenient about player distance if a monster is
833                 // idling (to prevent it from repeatedly resetting idle
834                 // time if its own wanderings bring it closer to the player)
835                 if (mon->props.exists("idle_point")
836                     && grid_distance(mon->pos(), you.pos()) < LOS_DEFAULT_RANGE)
837                 {
838                     mon->props.erase("idle_point");
839                     mon->props.erase("idle_deadline");
840                     mon->target = mon->patrol_point;
841                 }
842 
843                 if (mon->pos() == mon->props["last_pos"].get_coord())
844                 {
845                     if (!mon->props.exists("blocked_deadline"))
846                         mon->props["blocked_deadline"] = you.elapsed_time + 30;
847 
848                     if (!mon->props.exists("idle_deadline"))
849                         mon->props["idle_deadline"] = you.elapsed_time + 200;
850 
851                     if (you.elapsed_time >= mon->props["blocked_deadline"].get_int()
852                         || you.elapsed_time >= mon->props["idle_deadline"].get_int())
853                     {
854                         stop_retreat = true;
855                     }
856                 }
857                 else
858                 {
859                     mon->props.erase("blocked_deadline");
860                     mon->props.erase("idle_deadline");
861                 }
862             }
863 
864             if (stop_retreat)
865             {
866                 new_beh = BEH_SEEK;
867                 new_foe = MHITYOU;
868                 mon->props.erase("last_pos");
869                 mon->props.erase("idle_point");
870                 mon->props.erase("blocked_deadline");
871                 mon->props.erase("idle_deadline");
872             }
873             else
874                 mon->props["last_pos"] = mon->pos();
875 
876             break;
877         }
878 
879         default:
880             return;     // uh oh
881         }
882 
883         changed = (new_beh != mon->behaviour || new_foe != mon->foe);
884         mon->behaviour = new_beh;
885 
886         if (mon->foe != new_foe)
887             mon->foe_memory = 0;
888 
889         mon->foe = new_foe;
890     }
891 }
892 
_mons_check_foe(monster * mon,const coord_def & p,bool friendly,bool neutral,bool ignore_sight)893 static bool _mons_check_foe(monster* mon, const coord_def& p,
894                             bool friendly, bool neutral, bool ignore_sight)
895 {
896     // We don't check for the player here because otherwise wandering
897     // monsters will always attack you.
898 
899     // -- But why should they always attack monsters? -- 1KB
900 
901     monster* foe = monster_at(p);
902     return foe && foe != mon
903            && (ignore_sight || mon->can_see(*foe))
904            && (foe->friendly() != friendly
905                || neutral && !foe->neutral()
906                || mon->has_ench(ENCH_INSANE))
907            && !mons_is_projectile(*foe)
908            && summon_can_attack(mon, p)
909            && (friendly || !is_sanctuary(p))
910            && !mons_is_firewood(*foe)
911            || p == you.pos() && mon->has_ench(ENCH_INSANE);
912 }
913 
914 // Choose random nearest monster as a foe.
set_nearest_monster_foe(monster * mon,bool near_player)915 void set_nearest_monster_foe(monster* mon, bool near_player)
916 {
917     // These don't look for foes.
918     if (mon->good_neutral() || mon->strict_neutral()
919         || mon->behaviour == BEH_WITHDRAW
920         || mons_is_avatar(mon->type)
921         || mon->has_ench(ENCH_HAUNTING))
922     {
923         return;
924     }
925 
926     const bool friendly = mon->friendly();
927     const bool neutral  = mon->neutral();
928 
929     coord_def center = mon->pos();
930     bool second_pass = false;
931     vector<coord_def> monster_pos;
932 
933     while (true)
934     {
935         for (auto di = distance_iterator(center, true, true,
936                                          second_pass ? you.current_vision :
937                                          LOS_DEFAULT_RANGE);
938              di; ++di)
939         {
940             if (!cell_see_cell(center, *di, LOS_NO_TRANS)
941                 || (near_player && !you.see_cell(*di)))
942             {
943                 continue;
944             }
945 
946             if (_mons_check_foe(mon, *di, friendly, neutral, second_pass))
947             {
948                 if (*di == you.pos())
949                     mon->foe = MHITYOU;
950                 else
951                     mon->foe = env.mgrid(*di);
952                 return;
953             }
954         }
955 
956         // If we're selecting a new summon's autofoe and we were unable to
957         // find a foe in los of the monster, try a second pass using the
958         // player's los instead.
959         if (near_player && !second_pass)
960         {
961             center = you.pos();
962             second_pass = true;
963         }
964         else
965             break;
966     }
967 }
968 
969 /**
970  * Make a monster react to an event, possibly re-evaluating its attitude,
971  * foe, AI state, or target.
972  *
973  * @param mon the monster getting updated
974  * @param event what it's reacting to
975  * @param src who did it
976  * @param src_pos and where
977  * @param allow_shout whether the monster can shout in reaction.
978  */
behaviour_event(monster * mon,mon_event_type event,const actor * src,coord_def src_pos,bool allow_shout)979 void behaviour_event(monster* mon, mon_event_type event, const actor *src,
980                      coord_def src_pos, bool allow_shout)
981 {
982     if (!mon->alive())
983         return;
984 
985     ASSERT(!crawl_state.game_is_arena() || src != &you);
986     ASSERT_IN_BOUNDS_OR_ORIGIN(src_pos);
987     if (mons_is_projectile(mon->type))
988         return; // projectiles have no AI
989 
990     const beh_type old_behaviour = mon->behaviour;
991 
992     bool isSmart          = (mons_intel(*mon) >= I_HUMAN);
993     bool setTarget        = false;
994     bool breakCharm       = false;
995     bool was_unaware      = mon->asleep() || mon->foe == MHITNOT;
996     string msg;
997     int src_idx           = src ? src->mindex() : MHITNOT; // AXE ME
998 
999     // Monsters know to blame you for reflecting things at them.
1000     if (src_idx == YOU_FAULTLESS)
1001         src_idx = MHITYOU;
1002 
1003     if (is_sanctuary(mon->pos()) && mons_is_fleeing_sanctuary(*mon))
1004     {
1005         mon->behaviour = BEH_FLEE;
1006         mon->foe       = MHITYOU;
1007         mon->target    = env.sanctuary_pos;
1008         return;
1009     }
1010 
1011     switch (event)
1012     {
1013     case ME_DISTURB:
1014 #ifdef DEBUG_NOISE_PROPAGATION
1015         dprf("Disturbing %s", mon->name(DESC_A, true).c_str());
1016 #endif
1017         // Assumes disturbed by noise...
1018         if (mon->asleep())
1019             mon->behaviour = BEH_WANDER;
1020 
1021         // A bit of code to make Projected Noise actually do
1022         // something again. Basically, dumb monsters and
1023         // monsters who aren't otherwise occupied will at
1024         // least consider the (apparent) source of the noise
1025         // interesting for a moment. -- bwr
1026         if (!isSmart || mon->foe == MHITNOT || mons_is_wandering(*mon))
1027         {
1028             if (mon->is_patrolling())
1029                 break;
1030 
1031             ASSERT(!src_pos.origin());
1032             mon->target = src_pos;
1033         }
1034         break;
1035 
1036     case ME_WHACK:
1037     case ME_ANNOY:
1038         if (mon->has_ench(ENCH_GOLD_LUST))
1039             mon->del_ench(ENCH_GOLD_LUST);
1040 
1041         // Will turn monster against <src>.
1042         // Orders to withdraw take precedence over interruptions
1043         if (mon->behaviour == BEH_WITHDRAW && src != &you)
1044             break;
1045 
1046         // Monster types that you can't gain experience from cannot
1047         // fight back, so don't bother having them do so. If you
1048         // worship Fedhas, create a ring of friendly plants, and try
1049         // to break out of the ring by killing a plant, you'll get
1050         // a warning prompt and penance only once. Without the
1051         // hostility check, the plant will remain friendly until it
1052         // dies, and you'll get a warning prompt and penance once
1053         // *per hit*. This may not be the best way to address the
1054         // issue, though. -cao
1055         if (!mons_is_threatening(*mon)
1056             && mon->attitude != ATT_FRIENDLY
1057             && mon->attitude != ATT_GOOD_NEUTRAL)
1058         {
1059             return;
1060         }
1061 
1062         mon->foe = src_idx;
1063 
1064         // If the monster can't reach its target and can't attack it
1065         // either, retreat.
1066         try_pathfind(mon);
1067         if (mons_intel(*mon) > I_BRAINLESS && !mons_can_attack(*mon)
1068             && target_is_unreachable(mon))
1069         {
1070             mon->behaviour = BEH_RETREAT;
1071         }
1072         else if (mon->has_ench(ENCH_FEAR))
1073         {
1074             // self-attacks probably shouldn't break fear.
1075             if (src == mon)
1076                 break;
1077 
1078             if (you.can_see(*mon))
1079             {
1080                 mprf("%s attack snaps %s out of %s fear.",
1081                         src ? src->name(DESC_ITS).c_str() : "the",
1082                         mon->name(DESC_THE).c_str(),
1083                         mon->pronoun(PRONOUN_POSSESSIVE).c_str());
1084             }
1085             mon->del_ench(ENCH_FEAR, true);
1086         }
1087         else if (!mons_is_fleeing(*mon))
1088             mon->behaviour = BEH_SEEK;
1089 
1090         if (src == &you && mon->angered_by_attacks())
1091         {
1092             if (mon->attitude == ATT_FRIENDLY && mon->is_summoned())
1093             {
1094                 summon_dismissal_fineff::schedule(mon);
1095                 return;
1096             }
1097             else
1098             {
1099                 mon->attitude = ATT_HOSTILE;
1100                 breakCharm    = true;
1101             }
1102         }
1103 
1104         // XXX: Somewhat hacky, this being here.
1105         if (mons_is_elven_twin(mon))
1106             elven_twins_unpacify(mon);
1107 
1108         // Now set target so that monster can whack back (once) at an
1109         // invisible foe.
1110         if (event == ME_WHACK)
1111             setTarget = true;
1112 
1113         break;
1114 
1115     case ME_ALERT:
1116 #ifdef DEBUG_NOISE_PROPAGATION
1117         dprf("Alerting %s", mon->name(DESC_A, true).c_str());
1118 #endif
1119         // Allow monsters falling asleep while patrolling (can happen if
1120         // they're left alone for a long time) to be woken by this event.
1121         if (mon->friendly() && mon->is_patrolling()
1122             && !mon->asleep())
1123         {
1124             break;
1125         }
1126 
1127         // Orders to withdraw take precedence over interruptions
1128         if (mon->behaviour == BEH_WITHDRAW)
1129             break;
1130 
1131         // Avoid moving friendly explodey things out of BEH_WANDER.
1132         if (mon->friendly() && mons_self_destructs(*mon))
1133             break;
1134 
1135         // [ds] Neutral monsters don't react to your presence.
1136         // XXX: Neutral monsters are a tangled mess of arbitrary logic.
1137         // It's not even clear any more what behaviours are intended for
1138         // neutral monsters and what are merely accidents of the code.
1139         if (mon->neutral() && !mon->has_ench(ENCH_INSANE))
1140         {
1141             if (mon->asleep())
1142                 mon->behaviour = BEH_WANDER;
1143             break;
1144         }
1145 
1146         // Will alert monster to <src> and turn them
1147         // against them, unless they have a current foe.
1148         // It won't turn friends hostile either.
1149         if (!mons_is_retreating(*mon))
1150             mon->behaviour = BEH_SEEK;
1151 
1152         if (mon->foe == MHITNOT)
1153             mon->foe = src_idx;
1154 
1155         if (!src_pos.origin()
1156             && (mon->foe == MHITNOT || src && mon->foe == src->mindex()
1157                 || mons_is_wandering(*mon)))
1158         {
1159             if (mon->is_patrolling())
1160                 break;
1161 
1162             mon->target = src_pos;
1163 
1164             // XXX: Should this be done in _handle_behaviour()?
1165             if (src == &you && src_pos == you.pos()
1166                 && !you.see_cell(mon->pos()))
1167             {
1168                 try_pathfind(mon);
1169             }
1170         }
1171         break;
1172 
1173     case ME_SCARE:
1174         // Stationary monsters can't flee, and berserking monsters
1175         // are too enraged.
1176         if (mon->is_stationary() || mon->berserk_or_insane())
1177         {
1178             mon->del_ench(ENCH_FEAR, true, true);
1179             break;
1180         }
1181 
1182         // Neither do plants or nonliving beings.
1183         if (mon->holiness() & (MH_PLANT | MH_NONLIVING))
1184         {
1185             mon->del_ench(ENCH_FEAR, true, true);
1186             break;
1187         }
1188 
1189         msg = getSpeakString(mon->name(DESC_PLAIN) + " flee");
1190 
1191         // Assume monsters know where to run from, even if player is
1192         // invisible.
1193         mon->behaviour = BEH_FLEE;
1194         mon->foe       = src_idx;
1195         mon->target    = src_pos;
1196         if (src == &you)
1197                 setTarget = true;
1198         else if (mon->friendly() && !crawl_state.game_is_arena())
1199             mon->foe = MHITYOU;
1200 
1201         if (you.see_cell(mon->pos()))
1202             learned_something_new(HINT_FLEEING_MONSTER);
1203 
1204         break;
1205 
1206     case ME_CORNERED:
1207         // We only care about this event if we were actually running away
1208         if (!mons_is_retreating(*mon))
1209             break;
1210 
1211         // Pacified monsters shouldn't change their behaviour.
1212         if (mon->pacified())
1213             break;
1214 
1215         // If we were already cornered last turn, give up on trying to flee
1216         // and turn to fight instead. Otherwise, pause a turn in hope that
1217         // an escape route will open up.
1218         if (mons_is_cornered(*mon))
1219         {
1220             if (mon->friendly() && !crawl_state.game_is_arena())
1221             {
1222                 mon->foe = MHITYOU;
1223                 msg = "PLAIN:@The_monster@ returns to your side!";
1224             }
1225             else if (!mon->is_child_tentacle())
1226             {
1227                 msg = getSpeakString(mon->name(DESC_PLAIN) + " cornered");
1228                 if (msg.empty())
1229                     msg = "PLAIN:Cornered, @The_monster@ turns to fight!";
1230             }
1231             mon->del_ench(ENCH_FEAR, true);
1232             mon->behaviour = BEH_SEEK;
1233         }
1234         else if (mons_is_fleeing(*mon))
1235         {
1236             // Save their current position so we know if they manage to move
1237             // on the following turn (and thus resume BEH_FLEE)
1238             mon->props["last_pos"].get_coord() = mon->pos();
1239             mon->behaviour = BEH_CORNERED;
1240         }
1241         else
1242             mon->behaviour = BEH_SEEK;
1243         break;
1244 
1245     case ME_EVAL:
1246         break;
1247     }
1248 
1249     if (setTarget && src)
1250     {
1251         mon->target = src_pos;
1252         if (src->is_player() && mon->angered_by_attacks())
1253         {
1254             // Why only attacks by the player change attitude? -- 1KB
1255             mon->attitude = ATT_HOSTILE;
1256             // Non-hostile uniques might be removed from dungeon annotation
1257             // so we add them back.
1258             if (mon->props.exists("no_annotate"))
1259                 mon->props["no_annotate"] = false;
1260             set_unique_annotation(mon);
1261             mons_att_changed(mon);
1262         }
1263     }
1264 
1265     // Now, break charms if appropriate.
1266     if (breakCharm)
1267     {
1268         mon->del_ench(ENCH_CHARM);
1269         gozag_break_bribe(mon);
1270         mons_att_changed(mon);
1271     }
1272 
1273     // Do any resultant foe or state changes.
1274     handle_behaviour(mon);
1275 
1276     // That might have made the monster leave the level.
1277     if (!mon->alive())
1278         return;
1279 
1280     ASSERT_IN_BOUNDS_OR_ORIGIN(mon->target);
1281 
1282     // If it was unaware of you and you're its new foe, it might shout.
1283 #ifdef DEBUG_NOISE_PROPAGATION
1284     dprf("%s could shout in behavioural event, allow_shout: %d, foe: %d", mon->name(DESC_A, true).c_str(), allow_shout, mon->foe);
1285 #endif
1286     if (was_unaware && allow_shout
1287         && mon->foe == MHITYOU && !mon->wont_attack())
1288     {
1289         monster_consider_shouting(*mon);
1290     }
1291 
1292     const bool isPacified = mon->pacified();
1293 
1294     if (isPacified
1295         && (event == ME_DISTURB || event == ME_ALERT || event == ME_EVAL))
1296     {
1297         // Pacified monsters leaving the level won't stop doing so just because
1298         // they noticed something.
1299         mon->behaviour = old_behaviour;
1300     }
1301 
1302     // mons_speaks_msg already handles the LOS check.
1303     if (!msg.empty() && mon->visible_to(&you))
1304         mons_speaks_msg(mon, msg, MSGCH_TALK, silenced(mon->pos()));
1305 
1306     if (mons_allows_beogh_now(*mon))
1307     {
1308         const bool first = !you.attribute[ATTR_SEEN_BEOGH];
1309         if (first || one_chance_in(10))
1310         {
1311             mons_speaks_msg(mon, getSpeakString("orc_priest_preaching"),
1312                             MSGCH_TALK);
1313             if (first)
1314             {
1315                 ASSERT_RANGE(get_talent(ABIL_CONVERT_TO_BEOGH, false).hotkey,
1316                              'A', 'z' + 1);
1317                 mprf("(press <w>%c</w> on the <w>%s</w>bility menu to convert to Beogh)",
1318                      get_talent(ABIL_CONVERT_TO_BEOGH, false).hotkey,
1319                      command_to_string(CMD_USE_ABILITY).c_str());
1320                 you.attribute[ATTR_SEEN_BEOGH] = 1;
1321             }
1322         }
1323     }
1324 
1325     ASSERT(!crawl_state.game_is_arena()
1326            || mon->foe != MHITYOU && mon->target != you.pos());
1327 }
1328 
make_mons_stop_fleeing(monster * mon)1329 void make_mons_stop_fleeing(monster* mon)
1330 {
1331     if (mons_is_retreating(*mon))
1332         behaviour_event(mon, ME_CORNERED);
1333 }
1334 
attitude_creation_behavior(mon_attitude_type att)1335 beh_type attitude_creation_behavior(mon_attitude_type att)
1336 {
1337     switch (att)
1338     {
1339     case ATT_NEUTRAL:
1340         return BEH_NEUTRAL;
1341     case ATT_GOOD_NEUTRAL:
1342         return BEH_GOOD_NEUTRAL;
1343     case ATT_STRICT_NEUTRAL:
1344         return BEH_STRICT_NEUTRAL;
1345     case ATT_FRIENDLY:
1346         return BEH_FRIENDLY;
1347     default:
1348         return BEH_HOSTILE;
1349     }
1350 }
1351 
1352 // If you're invis and throw/zap whatever, alerts env.mons to your position.
alert_nearby_monsters()1353 void alert_nearby_monsters()
1354 {
1355     // Judging from the above comment, this function isn't
1356     // intended to wake up monsters, so we're only going to
1357     // alert monsters that aren't sleeping. For cases where an
1358     // event should wake up monsters and alert them, I'd suggest
1359     // calling noisy() before calling this function. - bwr
1360     for (monster_near_iterator mi(you.pos()); mi; ++mi)
1361         if (!mi->asleep())
1362              behaviour_event(*mi, ME_ALERT, &you);
1363 }
1364 
1365 //Make all monsters lose track of a given target after a few turns
shake_off_monsters(const actor * target)1366 void shake_off_monsters(const actor* target)
1367 {
1368     //If the player is under Ashenzari penance, monsters will not
1369     //lose track of them so easily
1370     if (target->is_player() && you.penance[GOD_ASHENZARI])
1371         return;
1372 
1373     for (monster_iterator mi; mi; ++mi)
1374     {
1375         monster* m = mi->as_monster();
1376         if (m->foe == target->mindex() && m->foe_memory > 0)
1377         {
1378             // Set foe_memory to a small non-zero amount so that monsters can
1379             // still close in on your old location, rather than immediately
1380             // realizing their target is gone, even if they took stairs while
1381             // out of sight
1382             dprf("Monster %d forgot about foe %d. (Previous foe_memory: %d)",
1383                     m->mindex(), target->mindex(), m->foe_memory);
1384             m->foe_memory = min(m->foe_memory, 7);
1385         }
1386     }
1387 }
1388 
1389 // If _mons_find_level_exits() is ever expanded to handle more grid
1390 // types, this should be expanded along with it.
_mons_indicate_level_exit(const monster * mon)1391 static void _mons_indicate_level_exit(const monster* mon)
1392 {
1393     const dungeon_feature_type feat = env.grid(mon->pos());
1394     const bool is_shaft = (get_trap_type(mon->pos()) == TRAP_SHAFT);
1395 
1396     if (feat_is_gate(feat))
1397         simple_monster_message(*mon, " passes through the gate.");
1398     else if (feat_is_travelable_stair(feat))
1399     {
1400         command_type dir = feat_stair_direction(feat);
1401         simple_monster_message(*mon,
1402             make_stringf(" %s the %s.",
1403                 dir == CMD_GO_UPSTAIRS     ? "goes up" :
1404                 dir == CMD_GO_DOWNSTAIRS   ? "goes down"
1405                                            : "takes",
1406                 feat_is_escape_hatch(feat) ? "escape hatch"
1407                                            : "stairs").c_str());
1408     }
1409     else if (is_shaft)
1410     {
1411         simple_monster_message(*mon,
1412             make_stringf(" %s the shaft.",
1413                 mon->airborne() ? "goes down"
1414                                 : "jumps into").c_str());
1415     }
1416 }
1417 
make_mons_leave_level(monster * mon)1418 void make_mons_leave_level(monster* mon)
1419 {
1420     if (mon->pacified())
1421     {
1422         if (you.can_see(*mon))
1423             _mons_indicate_level_exit(mon);
1424 
1425         // Pacified monsters leaving the level take their stuff with
1426         // them.
1427         mon->flags |= MF_HARD_RESET;
1428         monster_die(*mon, KILL_DISMISSED, NON_MONSTER);
1429     }
1430 }
1431 
1432 // Given an adjacent monster, returns true if the monster can hit it
1433 // (the monster should not be submerged, be submerged in shallow water
1434 // if the monster has a polearm, or be submerged in anything if the
1435 // monster has tentacles).
monster_can_hit_monster(monster * mons,const monster * targ)1436 bool monster_can_hit_monster(monster* mons, const monster* targ)
1437 {
1438     if (!summon_can_attack(mons, targ))
1439         return false;
1440 
1441     if (!targ->submerged() || mons->has_damage_type(DVORP_TENTACLE))
1442         return true;
1443 
1444     if (env.grid(targ->pos()) != DNGN_SHALLOW_WATER)
1445         return false;
1446 
1447     const item_def *weapon = mons->weapon();
1448     return weapon && item_attack_skill(*weapon) == SK_POLEARMS;
1449 }
1450 
1451 // Friendly summons can't attack out of the player's LOS, it's too abusable.
summon_can_attack(const monster * mons)1452 bool summon_can_attack(const monster* mons)
1453 {
1454     return crawl_state.game_is_arena()
1455            || !mons->friendly()
1456            || !mons->is_summoned()
1457               && !mons->has_ench(ENCH_FAKE_ABJURATION)
1458               && !mons->has_ench(ENCH_PORTAL_PACIFIED)
1459               && !mons_is_hepliaklqana_ancestor(mons->type)
1460            || you.see_cell_no_trans(mons->pos());
1461 }
1462 
summon_can_attack(const monster * mons,const coord_def & p)1463 bool summon_can_attack(const monster* mons, const coord_def &p)
1464 {
1465     if (crawl_state.game_is_arena())
1466         return true;
1467 
1468     // Spectral weapons only attack their target
1469     if (mons->type == MONS_SPECTRAL_WEAPON)
1470         return false;
1471 
1472     if (!mons->friendly()
1473         || !mons->is_summoned()
1474             && !mons->has_ench(ENCH_FAKE_ABJURATION)
1475             && !mons_is_hepliaklqana_ancestor(mons->type)
1476             && !mons->has_ench(ENCH_PORTAL_PACIFIED)
1477             && mons->type != MONS_FOXFIRE)
1478     {
1479         return true;
1480     }
1481 
1482     return you.see_cell_no_trans(mons->pos()) && you.see_cell_no_trans(p);
1483 }
1484 
summon_can_attack(const monster * mons,const actor * targ)1485 bool summon_can_attack(const monster* mons, const actor* targ)
1486 {
1487     return summon_can_attack(mons, targ->pos());
1488 }
1489 
find_allies_targeting(const actor & a)1490 vector<monster *> find_allies_targeting(const actor &a)
1491 {
1492     vector<monster *> result;
1493     for (monster* m : monster_near_iterator(you.pos(), LOS_DEFAULT))
1494         if (m->friendly() && m->foe == a.mindex())
1495             result.push_back(m);
1496     return result;
1497 }
1498 
is_ally_target(const actor & a)1499 bool is_ally_target(const actor &a)
1500 {
1501     for (monster* m : monster_near_iterator(you.pos(), LOS_DEFAULT))
1502         if (m->friendly() && m->foe == a.mindex())
1503             return true;
1504     return false;
1505 }
1506