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