1 /**
2 * @file
3 * @brief Monster abilities.
4 **/
5
6 #include "AppHdr.h"
7
8 #include "mon-abil.h"
9
10 #include <algorithm>
11 #include <cmath>
12 #include <map>
13 #include <queue>
14 #include <set>
15
16 #include "act-iter.h"
17 #include "actor.h"
18 #include "areas.h"
19 #include "arena.h"
20 #include "beam.h"
21 #include "cloud.h"
22 #include "colour.h"
23 #include "coordit.h"
24 #include "delay.h"
25 #include "dgn-overview.h"
26 #include "directn.h"
27 #include "english.h"
28 #include "env.h"
29 #include "fprop.h"
30 #include "item-prop.h"
31 #include "libutil.h"
32 #include "losglobal.h"
33 #include "message.h"
34 #include "mgen-data.h"
35 #include "mon-act.h"
36 #include "mon-behv.h"
37 #include "mon-cast.h"
38 #include "mon-death.h"
39 #include "mon-pathfind.h"
40 #include "mon-place.h"
41 #include "mon-poly.h"
42 #include "mon-util.h"
43 #include "ouch.h"
44 #include "random.h"
45 #include "religion.h"
46 #include "spl-damage.h"
47 #include "spl-util.h"
48 #include "state.h"
49 #include "stringutil.h"
50 #include "target.h"
51 #include "teleport.h"
52 #include "terrain.h"
53 #include "view.h"
54
55 static bool _slime_split_merge(monster* thing);
56
57 // Currently only used for Tiamat.
draconian_change_colour(monster * drac)58 void draconian_change_colour(monster* drac)
59 {
60 if (mons_genus(drac->type) != MONS_DRACONIAN)
61 return;
62
63 drac->base_monster = random_choose(MONS_RED_DRACONIAN,
64 MONS_WHITE_DRACONIAN,
65 MONS_BLACK_DRACONIAN,
66 MONS_GREEN_DRACONIAN,
67 MONS_PURPLE_DRACONIAN,
68 MONS_YELLOW_DRACONIAN);
69 drac->colour = mons_class_colour(drac->base_monster);
70
71 // Get rid of the old breath weapon first.
72 monster_spells oldspells = drac->spells;
73 drac->spells.clear();
74 for (const mon_spell_slot &slot : oldspells)
75 if (!(slot.flags & MON_SPELL_BREATH))
76 drac->spells.push_back(slot);
77
78 drac->spells.push_back(drac_breath(draco_or_demonspawn_subspecies(*drac)));
79 }
80
boris_covet_orb(monster * boris)81 void boris_covet_orb(monster* boris)
82 {
83 if (boris->type != MONS_BORIS || !player_has_orb())
84 return;
85
86 if (boris->observable())
87 simple_monster_message(*boris, " is empowered by the presence of the orb!");
88
89 boris->add_ench(mon_enchant(ENCH_HASTE, 1, boris, INFINITE_DURATION));
90 boris->add_ench(mon_enchant(ENCH_EMPOWERED_SPELLS, 1, boris,
91 INFINITE_DURATION));
92 }
93
ugly_thing_mutate(monster & ugly,bool force)94 bool ugly_thing_mutate(monster& ugly, bool force)
95 {
96 if (!(one_chance_in(9) || force))
97 return false;
98
99 const char* msg = nullptr;
100 // COLOUR_UNDEF means "pick a random colour".
101 colour_t new_colour = COLOUR_UNDEF;
102
103 for (fair_adjacent_iterator ai(ugly.pos()); ai && !msg; ++ai)
104 {
105 const actor* act = actor_at(*ai);
106
107 if (!act)
108 continue;
109
110 if (act->is_player() && get_contamination_level())
111 msg = " basks in your mutagenic energy and changes!";
112 else if (mons_genus(act->type) == MONS_UGLY_THING)
113 {
114 msg = " basks in the mutagenic energy from its kin and changes!";
115 const colour_t other_colour =
116 make_low_colour(act->as_monster()->colour);
117 if (make_low_colour(ugly.colour) != other_colour)
118 new_colour = other_colour;
119 }
120 }
121
122 if (force)
123 msg = " basks in the mutagenic energy and changes!";
124
125 if (!msg) // didn't find anything to mutate off of
126 return false;
127
128 simple_monster_message(ugly, msg);
129 ugly.uglything_mutate(new_colour);
130 return true;
131 }
132
133 // Inflict any enchantments the parent slime has on its offspring,
134 // leaving durations unchanged, I guess. -cao
_split_ench_durations(monster * initial_slime,monster * split_off)135 static void _split_ench_durations(monster* initial_slime, monster* split_off)
136 {
137 for (const auto &entry : initial_slime->enchantments)
138 // Don't let new slimes inherit being held by a web or net
139 if (entry.second.ench != ENCH_HELD)
140 split_off->add_ench(entry.second);
141 }
142
143 // What to do about any enchantments these two creatures may have?
144 // For now, we are averaging the durations, weighted by slime size
145 // or by hit dice, depending on usehd.
merge_ench_durations(monster & initial,monster & merge_to,bool usehd)146 void merge_ench_durations(monster& initial, monster& merge_to, bool usehd)
147 {
148 int initial_count = usehd ? initial.get_hit_dice() : initial.blob_size;
149 int merge_to_count = usehd ? merge_to.get_hit_dice() : merge_to.blob_size;
150 int total_count = initial_count + merge_to_count;
151
152 mon_enchant_list &from_ench = initial.enchantments;
153
154 for (auto &entry : from_ench)
155 {
156 // Does the other creature have this enchantment as well?
157 const mon_enchant temp = merge_to.get_ench(entry.first);
158 // If not, use duration 0 for their part of the average.
159 const bool no_initial = temp.ench == ENCH_NONE;
160 const int duration = no_initial ? 0 : temp.duration;
161
162 entry.second.duration = (entry.second.duration * initial_count
163 + duration * merge_to_count)/total_count;
164
165 if (!entry.second.duration)
166 entry.second.duration = 1;
167
168 if (no_initial)
169 merge_to.add_ench(entry.second);
170 else
171 merge_to.update_ench(entry.second);
172 }
173
174 for (auto &entry : merge_to.enchantments)
175 {
176 if (from_ench.find(entry.first) == from_ench.end()
177 && entry.second.duration > 1)
178 {
179 entry.second.duration = (merge_to_count * entry.second.duration)
180 / total_count;
181
182 merge_to.update_ench(entry.second);
183 }
184 }
185 }
186
187 // Calculate slime creature hp based on how many are merged.
_stats_from_blob_count(monster * slime,float max_per_blob,float current_per_blob)188 static void _stats_from_blob_count(monster* slime, float max_per_blob,
189 float current_per_blob)
190 {
191 slime->max_hit_points = (int)(slime->blob_size * max_per_blob);
192 slime->hit_points = (int)(slime->blob_size * current_per_blob);
193 }
194
195 // Create a new slime creature at 'target', and split 'thing''s hp and
196 // merge count with the new monster.
197 // Now it returns index of new slime (-1 if it fails).
_do_split(monster * thing,const coord_def & target)198 static monster* _do_split(monster* thing, const coord_def & target)
199 {
200 ASSERT(thing); // XXX: change to monster &thing
201 ASSERT(thing->alive());
202
203 // Create a new slime.
204 mgen_data new_slime_data = mgen_data(thing->type,
205 SAME_ATTITUDE(thing),
206 target,
207 thing->foe,
208 MG_FORCE_PLACE);
209
210 // Don't explicitly announce the child slime coming into view if you
211 // saw the split that created it
212 if (you.can_see(*thing))
213 new_slime_data.extra_flags |= MF_WAS_IN_VIEW;
214
215 monster *new_slime = create_monster(new_slime_data);
216
217 if (!new_slime)
218 return 0;
219
220 if (you.can_see(*thing))
221 mprf("%s splits.", thing->name(DESC_A).c_str());
222
223 // Inflict the new slime with any enchantments on the parent.
224 _split_ench_durations(thing, new_slime);
225 new_slime->attitude = thing->attitude;
226 new_slime->behaviour = thing->behaviour;
227 new_slime->flags = thing->flags;
228 new_slime->props = thing->props;
229 new_slime->summoner = thing->summoner;
230 if (thing->props.exists("blame"))
231 new_slime->props["blame"] = thing->props["blame"].get_vector();
232
233 int split_off = thing->blob_size / 2;
234 float max_per_blob = thing->max_hit_points / float(thing->blob_size);
235 float current_per_blob = thing->hit_points / float(thing->blob_size);
236
237 thing->blob_size -= split_off;
238 new_slime->blob_size = split_off;
239
240 new_slime->set_hit_dice(thing->get_experience_level());
241
242 _stats_from_blob_count(thing, max_per_blob, current_per_blob);
243 _stats_from_blob_count(new_slime, max_per_blob, current_per_blob);
244
245 if (crawl_state.game_is_arena())
246 arena_split_monster(thing, new_slime);
247
248 ASSERT(thing->alive());
249 ASSERT(new_slime->alive());
250
251 return new_slime;
252 }
253
254 // Cause a monster to lose a turn. has_gone should be true if the
255 // monster has already moved this turn.
_lose_turn(monster * mons,bool has_gone)256 static void _lose_turn(monster* mons, bool has_gone)
257 {
258 const monsterentry* entry = get_monster_data(mons->type);
259
260 // We want to find out if mons will move next time it has a turn
261 // (assuming for the sake of argument the next delay is 10). If it's
262 // already going to lose a turn we don't need to do anything.
263 mons->speed_increment += entry->speed;
264 if (!mons->has_action_energy())
265 return;
266 mons->speed_increment -= entry->speed;
267
268 mons->speed_increment -= entry->energy_usage.move;
269
270 // So we subtracted some energy above, but if mons hasn't moved yet
271 // /this turn, that will just cancel its turn in this round of
272 // world_reacts().
273 if (!has_gone)
274 mons->speed_increment -= entry->energy_usage.move;
275 }
276
277 // Actually merge two slime creatures, pooling their hp, etc.
278 // initial_slime is the one that gets killed off by this process.
_do_merge_slimes(monster * initial_slime,monster * merge_to)279 static void _do_merge_slimes(monster* initial_slime, monster* merge_to)
280 {
281 const string old_name = merge_to->name(DESC_A);
282 const bool merge_to_was_visible = you.can_see(*merge_to);
283
284 // Combine enchantment durations.
285 merge_ench_durations(*initial_slime, *merge_to);
286
287 merge_to->blob_size += initial_slime->blob_size;
288 merge_to->max_hit_points += initial_slime->max_hit_points;
289 merge_to->hit_points += initial_slime->hit_points;
290
291 // Merge monster flags (mostly so that MF_CREATED_NEUTRAL, etc. are
292 // passed on if the merged slime subsequently splits. Hopefully
293 // this won't do anything weird.
294 merge_to->flags |= initial_slime->flags;
295
296 // Merging costs the combined slime some energy. The idea is that if 2
297 // slimes merge you can gain a space by moving away the turn after (maybe
298 // this is too nice but there will probably be a lot of complaints about
299 // the damage on higher level slimes). We see if mons has gone already by
300 // checking its mindex (this works because handle_monsters just iterates
301 // over env.mons in ascending order).
302 _lose_turn(merge_to, merge_to->mindex() < initial_slime->mindex());
303
304 // Overwrite the state of the slime getting merged into, because it
305 // might have been resting or something.
306 merge_to->behaviour = initial_slime->behaviour;
307 merge_to->foe = initial_slime->foe;
308
309 behaviour_event(merge_to, ME_EVAL);
310
311 // Messaging cases:
312 // 1. MT & I were both visible & still are
313 // 2. MT was visible, I wasn't but now both are
314 // 3. MT was visible, I wasn't and now both aren't
315 // 4. MT wasn't visible, I was and now both are
316 // 5. MT and I weren't visible & still aren't
317 if (merge_to_was_visible)
318 {
319 if (you.can_see(*merge_to))
320 {
321 // cases 1 and 2
322 mprf("Two slime creatures merge to form %s.",
323 merge_to->name(DESC_A).c_str());
324 }
325 else
326 {
327 // case 3
328 mprf("Something merges into %s, and it vanishes!",
329 old_name.c_str());
330 }
331
332 flash_view_delay(UA_MONSTER, LIGHTGREEN, 150);
333 }
334 else if (you.can_see(*initial_slime))
335 {
336 // case 4
337 mprf("%s merges with something you can't see.",
338 initial_slime->name(DESC_A).c_str());
339 }
340 // case 5 (no-op)
341
342 // Have to 'kill' the slime doing the merging.
343 monster_die(*initial_slime, KILL_DISMISSED, NON_MONSTER, true);
344 }
345
346 // Slime creatures can split but not merge under these conditions.
_unoccupied_slime(monster * thing)347 static bool _unoccupied_slime(monster* thing)
348 {
349 return thing->asleep() || mons_is_wandering(*thing)
350 || thing->foe == MHITNOT;
351 }
352
353 // Slime creatures cannot split or merge under these conditions.
_disabled_merge(monster * thing)354 static bool _disabled_merge(monster* thing)
355 {
356 return !thing
357 || mons_is_fleeing(*thing)
358 || mons_is_confused(*thing)
359 || thing->paralysed();
360 }
361
362 // See if there are any appropriate adjacent slime creatures for 'thing'
363 // to merge with. If so, carry out the merge.
364 //
365 // A slime creature will merge if there is an adjacent slime, merging
366 // onto that slime would reduce the distance to the original slime's
367 // target, and there are no empty squares that would also reduce the
368 // distance to the target.
_slime_merge(monster * thing)369 static bool _slime_merge(monster* thing)
370 {
371 if (!thing || _disabled_merge(thing) || _unoccupied_slime(thing))
372 return false;
373
374 int max_slime_merge = 5;
375 int target_distance = grid_distance(thing->target, thing->pos());
376
377 // Check for adjacent slime creatures.
378 monster* merge_target = nullptr;
379 for (fair_adjacent_iterator ai(thing->pos()); ai; ++ai)
380 {
381 // If this square won't reduce the distance to our target, don't
382 // look for a potential merge, and don't allow this square to
383 // prevent a merge if empty.
384 if (grid_distance(thing->target, *ai) >= target_distance)
385 continue;
386
387 // Don't merge if there is an open square that reduces distance
388 // to target, even if we found a possible slime to merge with.
389 if (!actor_at(*ai)
390 && mons_class_can_pass(MONS_SLIME_CREATURE, env.grid(*ai)))
391 {
392 return false;
393 }
394
395 // Is there a slime creature on this square we can consider
396 // merging with?
397 monster* other_thing = monster_at(*ai);
398 if (!merge_target
399 && other_thing
400 && other_thing->type == MONS_SLIME_CREATURE
401 && other_thing->attitude == thing->attitude
402 && other_thing->has_ench(ENCH_CHARM) == thing->has_ench(ENCH_CHARM)
403 && other_thing->has_ench(ENCH_HEXED) == thing->has_ench(ENCH_HEXED)
404 && other_thing->is_summoned() == thing->is_summoned()
405 && !other_thing->is_shapeshifter()
406 && !_disabled_merge(other_thing))
407 {
408 // We can potentially merge if doing so won't take us over
409 // the merge cap.
410 int new_blob_count = other_thing->blob_size + thing->blob_size;
411 if (new_blob_count <= max_slime_merge)
412 merge_target = other_thing;
413 }
414 }
415
416 // We found a merge target and didn't find an open square that
417 // would reduce distance to target, so we can actually merge.
418 if (merge_target)
419 {
420 _do_merge_slimes(thing, merge_target);
421 return true;
422 }
423
424 // No adjacent slime creatures we could merge with.
425 return false;
426 }
427
_slime_can_spawn(const coord_def target)428 static bool _slime_can_spawn(const coord_def target)
429 {
430 return mons_class_can_pass(MONS_SLIME_CREATURE, env.grid(target))
431 && !actor_at(target);
432 }
433
434 // See if slime creature 'thing' can split, and carry out the split if
435 // we can find a square to place the new slime creature on.
_slime_split(monster * thing,bool force_split)436 static monster *_slime_split(monster* thing, bool force_split)
437 {
438 if (!thing || thing->blob_size <= 1 || thing->hit_points < 4
439 || (coinflip() && !force_split) // Don't make splitting quite so reliable. (jpeg)
440 || _disabled_merge(thing))
441 {
442 return 0;
443 }
444
445 const coord_def origin = thing->pos();
446
447 const actor* foe = thing->get_foe();
448 const bool has_foe = (foe != nullptr && thing->can_see(*foe));
449 const coord_def foe_pos = (has_foe ? foe->position : coord_def(0,0));
450 const int old_dist = (has_foe ? grid_distance(origin, foe_pos) : 0);
451
452 if ((has_foe && old_dist > 1) && !force_split)
453 {
454 // If we're not already adjacent to the foe, check whether we can
455 // move any closer. If so, do that rather than splitting.
456 for (adjacent_iterator ri(origin); ri; ++ri)
457 {
458 if (_slime_can_spawn(*ri)
459 && grid_distance(*ri, foe_pos) < old_dist)
460 {
461 return 0;
462 }
463 }
464 }
465
466 // Anywhere we can place an offspring?
467 for (fair_adjacent_iterator ai(origin); ai; ++ai)
468 {
469 // Don't split if this increases the distance to the target.
470 if (has_foe && grid_distance(*ai, foe_pos) > old_dist
471 && !force_split)
472 {
473 continue;
474 }
475
476 if (_slime_can_spawn(*ai))
477 {
478 // This can fail if placing a new monster fails. That
479 // probably means we have too many monsters on the level,
480 // so just return in that case.
481 return _do_split(thing, *ai);
482 }
483 }
484
485 // No free squares.
486 return 0;
487 }
488
489 // See if a given slime creature can split or merge.
_slime_split_merge(monster * thing)490 static bool _slime_split_merge(monster* thing)
491 {
492 // No merging/splitting shapeshifters.
493 if (!thing
494 || thing->is_shapeshifter()
495 || thing->type != MONS_SLIME_CREATURE)
496 {
497 return false;
498 }
499
500 if (_slime_split(thing, false))
501 return true;
502
503 return _slime_merge(thing);
504 }
505
506 // Splits and polymorphs merged slime creatures.
slime_creature_polymorph(monster & slime,poly_power_type power)507 bool slime_creature_polymorph(monster& slime, poly_power_type power)
508 {
509 ASSERT(slime.type == MONS_SLIME_CREATURE);
510
511 if (slime.blob_size > 1 && x_chance_in_y(4, 5))
512 {
513 int count = 0;
514 while (slime.blob_size > 1 && count <= 10)
515 {
516 if (monster *splinter = _slime_split(&slime, true))
517 slime_creature_polymorph(*splinter, power);
518 else
519 break;
520 count++;
521 }
522 }
523
524 return monster_polymorph(&slime, RANDOM_POLYMORPH_MONSTER, power);
525 }
526
_starcursed_split(monster * mon)527 static bool _starcursed_split(monster* mon)
528 {
529 if (!mon || mon->blob_size <= 1 || mon->type != MONS_STARCURSED_MASS)
530 return false;
531
532 // Anywhere we can place an offspring?
533 for (fair_adjacent_iterator ai(mon->pos()); ai; ++ai)
534 {
535 if (mons_class_can_pass(MONS_STARCURSED_MASS, env.grid(*ai))
536 && !actor_at(*ai))
537 {
538 return _do_split(mon, *ai);
539 }
540 }
541
542 // No free squares.
543 return false;
544 }
545
_starcursed_scream(monster * mon,actor * target)546 static void _starcursed_scream(monster* mon, actor* target)
547 {
548 if (!target || !target->alive())
549 return;
550
551 //Gather the chorus
552 vector<monster*> chorus;
553
554 for (monster_near_iterator mi(target->pos(), LOS_NO_TRANS); mi; ++mi)
555 {
556 if (mi->type == MONS_STARCURSED_MASS)
557 chorus.push_back(*mi);
558 }
559
560 int n = chorus.size();
561 int dam = 0; int stun = 0;
562 const char* message = nullptr;
563
564 dprf("Chorus size: %d", n);
565
566 if (n > 7)
567 {
568 message = "A cacophony of accursed wailing tears at your sanity!";
569 if (coinflip())
570 stun = 2;
571 }
572 else if (n > 4)
573 {
574 message = "A deafening chorus of shrieks assaults your mind!";
575 if (one_chance_in(3))
576 stun = 1;
577 }
578 else if (n > 1)
579 message = "A chorus of shrieks assaults your mind.";
580 else
581 message = "The starcursed mass shrieks in your mind.";
582
583 dam = 4 + random2(5) + random2(n * 3 / 2);
584
585 if (!target->is_player())
586 {
587 if (you.see_cell(target->pos()))
588 {
589 mprf(target->as_monster()->friendly() ? MSGCH_FRIEND_SPELL
590 : MSGCH_MONSTER_SPELL,
591 "%s writhes in pain as voices assail %s mind.",
592 target->name(DESC_THE).c_str(),
593 target->pronoun(PRONOUN_POSSESSIVE).c_str());
594 }
595 }
596 else
597 mprf(MSGCH_MONSTER_SPELL, "%s", message);
598 target->hurt(mon, dam, BEAM_MISSILE, KILLED_BY_BEAM, "",
599 "accursed screaming");
600
601 if (stun && target->alive())
602 target->paralyse(mon, stun, "accursed screaming");
603
604 for (monster *voice : chorus)
605 if (voice->alive())
606 voice->add_ench(mon_enchant(ENCH_SCREAMED, 1, voice, 1));
607 }
608
_will_starcursed_scream(monster * mon)609 static bool _will_starcursed_scream(monster* mon)
610 {
611 int n = 0;
612
613 for (monster_near_iterator mi(mon->pos(), LOS_NO_TRANS); mi; ++mi)
614 {
615 if (mi->type != MONS_STARCURSED_MASS)
616 continue;
617
618 // Don't scream if any part of the chorus has a scream timeout
619 // (This prevents it being staggered into a bunch of mini-screams)
620 if (mi->has_ench(ENCH_SCREAMED))
621 return false;
622 else
623 n++;
624 }
625
626 return one_chance_in(n);
627 }
628
629 /**
630 * Can a lost soul revive the given monster, assuming one is nearby?
631 *
632 * @param mons The monster potentially being revived.
633 * @return Whether it's a possible target for lost souls.
634 */
_lost_soul_affectable(const monster & mons)635 static bool _lost_soul_affectable(const monster &mons)
636 {
637 // zombies are boring
638 if (mons_is_zombified(mons))
639 return false;
640
641 // undead can be reknit, naturals ghosted, everyone else is out of luck
642 if (!(mons.holiness() & (MH_UNDEAD | MH_NATURAL)))
643 return false;
644
645 // already been revived once
646 if (testbits(mons.flags, MF_SPECTRALISED))
647 return false;
648
649 // just silly
650 if (mons.type == MONS_LOST_SOUL)
651 return false;
652
653 // for ely, I guess?
654 if (is_good_god(mons.god))
655 return false;
656
657 if (mons.is_summoned())
658 return false;
659
660 if (!mons_class_gives_xp(mons.type))
661 return false;
662
663 return true;
664 }
665
666 // Is it worth sacrificing ourselves to revive this monster? This is based
667 // on monster HD, with a lower chance for weaker monsters so long as other
668 // monsters are present, but always true if there are only as many valid
669 // targets as nearby lost souls.
_worthy_sacrifice(monster * soul,const monster * target)670 static bool _worthy_sacrifice(monster* soul, const monster* target)
671 {
672 int count = 0;
673 for (monster_near_iterator mi(soul, LOS_NO_TRANS); mi; ++mi)
674 {
675 if (_lost_soul_affectable(**mi))
676 ++count;
677 else if (mi->type == MONS_LOST_SOUL)
678 --count;
679 }
680
681 const int target_hd = target->get_experience_level();
682 return count <= -1 || target_hd > 9
683 || x_chance_in_y(target_hd * target_hd * target_hd, 1200);
684 }
685
686 /**
687 * Check to see if the given monster can be revived by lost souls (if it's a
688 * valid target for revivication & if there are any lost souls nearby), and
689 * revive it if so.
690 *
691 * @param mons The monster in question.
692 * @return Whether the monster was revived/reknitted, or whether it
693 * remains dead (dying?).
694 */
lost_soul_revive(monster & mons,killer_type killer)695 bool lost_soul_revive(monster& mons, killer_type killer)
696 {
697 if (killer == KILL_RESET
698 || killer == KILL_DISMISSED
699 || killer == KILL_BANISHED)
700 {
701 return false;
702 }
703
704 if (!_lost_soul_affectable(mons))
705 return false;
706
707 for (monster_near_iterator mi(&mons, LOS_NO_TRANS); mi; ++mi)
708 {
709 if (mi->type != MONS_LOST_SOUL || !mons_aligned(&mons, *mi))
710 continue;
711
712 if (!_worthy_sacrifice(*mi, &mons))
713 continue;
714
715 // save this before we revive it
716 const string revivee_name = mons.name(DESC_THE);
717 const bool was_alive = bool(mons.holiness() & MH_NATURAL);
718
719 // In this case the old monster will be replaced by
720 // a ghostly version, so we should record defeat now.
721 if (was_alive)
722 {
723 record_monster_defeat(&mons, killer);
724 remove_unique_annotation(&mons);
725 }
726
727 targeter_radius hitfunc(*mi, LOS_SOLID);
728 flash_view_delay(UA_MONSTER, GREEN, 200, &hitfunc);
729
730 mons.heal(mons.max_hit_points);
731 mons.del_ench(ENCH_CONFUSION, true);
732 mons.timeout_enchantments(10);
733
734 coord_def newpos = mi->pos();
735 if (was_alive)
736 {
737 mons.move_to_pos(newpos);
738 mons.flags |= (MF_SPECTRALISED | MF_FAKE_UNDEAD);
739 }
740
741 // check if you can see the monster *after* it maybe moved
742 if (you.can_see(mons))
743 {
744 if (!was_alive)
745 {
746 mprf("%s sacrifices itself to reknit %s!",
747 mi->name(DESC_THE).c_str(),
748 revivee_name.c_str());
749 }
750 else
751 {
752 mprf("%s assumes the form of %s%s!",
753 mi->name(DESC_THE).c_str(),
754 revivee_name.c_str(),
755 (mi->is_summoned() ? " and becomes anchored to this"
756 " world" : ""));
757 }
758 }
759
760 monster_die(**mi, KILL_MISC, -1, true);
761
762 return true;
763 }
764
765 return false;
766 }
767
treant_release_fauna(monster & mons)768 void treant_release_fauna(monster& mons)
769 {
770 // FIXME: this should be a fineff, at least when called from monster_die.
771 int count = mons.mangrove_pests;
772 bool created = false;
773
774 monster_type fauna_t = MONS_HORNET;
775
776 mon_enchant abj = mons.get_ench(ENCH_ABJ);
777
778 for (int i = 0; i < count; ++i)
779 {
780 mgen_data fauna_data(fauna_t, SAME_ATTITUDE(&mons),
781 mons.pos(), mons.foe);
782 fauna_data.set_summoned(&mons, 0, SPELL_NO_SPELL);
783 fauna_data.extra_flags |= MF_WAS_IN_VIEW;
784 monster* fauna = create_monster(fauna_data);
785
786 if (fauna)
787 {
788 fauna->props["band_leader"].get_int() = mons.mid;
789
790 // Give released fauna the same summon duration as their 'parent'
791 if (abj.ench != ENCH_NONE)
792 fauna->add_ench(abj);
793
794 created = true;
795 mons.mangrove_pests--;
796 }
797 }
798
799 if (created && you.can_see(mons))
800 {
801 mprf("Angry insects surge out from beneath %s foliage!",
802 mons.name(DESC_ITS).c_str());
803 }
804 }
805
_adj_to_tree(coord_def p)806 static bool _adj_to_tree(coord_def p)
807 {
808 for (adjacent_iterator ai(p); ai; ++ai)
809 if (feat_is_tree(env.grid(*ai)))
810 return true;
811 return false;
812 }
813
_find_nearer_tree(coord_def cur_loc,coord_def target)814 static coord_def _find_nearer_tree(coord_def cur_loc, coord_def target)
815 {
816 coord_def p = {0, 0};
817 int seen = 0;
818 // don't bother teleporting to something that's at the same distance
819 // from the target as you already are
820 int closest = grid_distance(cur_loc, target) - 1;
821 for (distance_iterator di(target); di; ++di)
822 {
823 const int dist = grid_distance(target, *di);
824 if (dist > closest)
825 break;
826
827 if (!cell_see_cell(target, *di, LOS_NO_TRANS) // there might be a better iterator
828 || !_adj_to_tree(*di)
829 || !monster_habitable_grid(MONS_ELEIONOMA, env.grid(*di)))
830 {
831 continue;
832 }
833 // XXX: also check for dangerous clouds?
834
835 closest = dist;
836
837 seen++;
838 if (x_chance_in_y(1, seen))
839 p = *di;
840 }
841 return p;
842 }
843
_mons_cast_abil(monster * mons,bolt & pbolt,spell_type spell_cast)844 static inline void _mons_cast_abil(monster* mons, bolt &pbolt,
845 spell_type spell_cast)
846 {
847 mons_cast(mons, pbolt, spell_cast, MON_SPELL_NATURAL);
848 }
849
mon_special_ability(monster * mons)850 bool mon_special_ability(monster* mons)
851 {
852 bool used = false;
853
854 const monster_type mclass = (mons_genus(mons->type) == MONS_DRACONIAN)
855 ? draco_or_demonspawn_subspecies(*mons)
856 : mons->type;
857
858 // Slime creatures can split while out of sight.
859 if ((!mons->near_foe() || mons->asleep() || mons->submerged())
860 && mons->type != MONS_SLIME_CREATURE
861 && mons->type != MONS_LOST_SOUL)
862 {
863 return false;
864 }
865
866 switch (mclass)
867 {
868 case MONS_UGLY_THING:
869 case MONS_VERY_UGLY_THING:
870 // A (very) ugly thing may mutate if it's next to other ones (or
871 // next to you if you're contaminated).
872 used = ugly_thing_mutate(*mons, false);
873 break;
874
875 case MONS_SLIME_CREATURE:
876 // Slime creatures may split or merge depending on the
877 // situation.
878 used = _slime_split_merge(mons);
879 if (!mons->alive())
880 return true;
881 break;
882
883 case MONS_BALL_LIGHTNING:
884 if (is_sanctuary(mons->pos()))
885 break;
886
887 if (mons->attitude == ATT_HOSTILE
888 && grid_distance(you.pos(), mons->pos()) <= 2)
889 {
890 mons->suicide();
891 used = true;
892 break;
893 }
894
895 for (monster_near_iterator targ(mons, LOS_NO_TRANS); targ; ++targ)
896 {
897 if (mons_aligned(mons, *targ) || mons_is_firewood(**targ)
898 || grid_distance(mons->pos(), targ->pos()) > 2
899 || !you.see_cell(targ->pos()))
900 {
901 continue;
902 }
903
904 if (!cell_is_solid(targ->pos()))
905 {
906 mons->suicide();
907 used = true;
908 break;
909 }
910 }
911 break;
912
913 case MONS_FOXFIRE:
914 if (is_sanctuary(mons->pos()))
915 break;
916
917 if (mons->attitude == ATT_HOSTILE
918 && grid_distance(you.pos(), mons->pos()) == 1)
919 {
920 foxfire_attack(mons, &you);
921 check_place_cloud(CLOUD_FLAME, mons->pos(), 2, mons);
922 mons->suicide();
923 used = true;
924 break;
925 }
926
927 for (monster_near_iterator targ(mons, LOS_NO_TRANS); targ; ++targ)
928 {
929 if (mons_aligned(mons, *targ) || mons_is_firewood(**targ)
930 || grid_distance(mons->pos(), targ->pos()) > 1
931 || !you.see_cell(targ->pos()))
932 {
933 continue;
934 }
935
936 if (!cell_is_solid(targ->pos()))
937 {
938 foxfire_attack(mons, *targ);
939 mons->suicide();
940 used = true;
941 break;
942 }
943 }
944 break;
945
946 case MONS_SKY_BEAST:
947 if (one_chance_in(8))
948 {
949 // If we're invisible, become visible.
950 if (mons->invisible())
951 {
952 mons->del_ench(ENCH_INVIS);
953 place_cloud(CLOUD_RAIN, mons->pos(), 2, mons);
954 }
955 // Otherwise, go invisible.
956 else
957 enchant_monster_invisible(mons, "flickers out of sight");
958 }
959 break;
960
961 case MONS_STARCURSED_MASS:
962 if (x_chance_in_y(mons->blob_size,8) && x_chance_in_y(2,3)
963 && mons->hit_points >= 8)
964 {
965 _starcursed_split(mons), used = true;
966 }
967
968 if (!mons_is_confused(*mons)
969 && !is_sanctuary(mons->pos()) && !is_sanctuary(mons->target)
970 && _will_starcursed_scream(mons)
971 && coinflip())
972 {
973 _starcursed_scream(mons, actor_at(mons->target));
974 used = true;
975 }
976 break;
977
978 case MONS_THORN_HUNTER:
979 {
980 // If we would try to move into a briar (that we might have just created
981 // defensively), let's see if we can shoot our foe through it instead
982 if (actor_at(mons->pos() + mons->props["mmov"].get_coord())
983 && actor_at(mons->pos() + mons->props["mmov"].get_coord())->type == MONS_BRIAR_PATCH
984 && !one_chance_in(3))
985 {
986 actor *foe = mons->get_foe();
987 if (foe && mons->can_see(*foe))
988 {
989 bolt beem = setup_targetting_beam(*mons);
990 beem.target = foe->pos();
991 setup_mons_cast(mons, beem, SPELL_THORN_VOLLEY);
992
993 fire_tracer(mons, beem);
994 if (mons_should_fire(beem))
995 {
996 make_mons_stop_fleeing(mons);
997 _mons_cast_abil(mons, beem, SPELL_THORN_VOLLEY);
998 used = true;
999 }
1000 }
1001 }
1002 // Otherwise, if our foe is approaching us, we might want to raise a
1003 // defensive wall of brambles (use the number of brambles in the area
1004 // as some indication if we've already done this, and shouldn't repeat)
1005 else if (mons->props["foe_approaching"].get_bool() == true
1006 && !mons_is_confused(*mons)
1007 && coinflip())
1008 {
1009 int briar_count = 0;
1010 for (monster_near_iterator mi(mons, LOS_NO_TRANS); mi; ++mi)
1011 {
1012 if (mi->type == MONS_BRIAR_PATCH
1013 && grid_distance(mons->pos(), mi->pos()) > 3)
1014 {
1015 briar_count++;
1016 }
1017 }
1018 if (briar_count < 4) // Probably no solid wall here
1019 {
1020 bolt beem; // unused
1021 _mons_cast_abil(mons, beem, SPELL_WALL_OF_BRAMBLES);
1022 used = true;
1023 }
1024 }
1025 }
1026 break;
1027
1028 case MONS_WATER_NYMPH:
1029 {
1030 if (one_chance_in(5))
1031 {
1032 actor *foe = mons->get_foe();
1033 if (foe && !feat_is_water(env.grid(foe->pos())))
1034 {
1035 coord_def spot;
1036 if (find_habitable_spot_near(foe->pos(), MONS_ELECTRIC_EEL, 3, false, spot)
1037 && foe->pos().distance_from(spot)
1038 < foe->pos().distance_from(mons->pos()))
1039 {
1040 if (mons->move_to_pos(spot))
1041 {
1042 simple_monster_message(*mons, " flows with the water.");
1043 used = true;
1044 }
1045 }
1046 }
1047 }
1048 }
1049 break;
1050
1051 case MONS_ELEIONOMA:
1052 {
1053 if (!one_chance_in(3))
1054 break;
1055
1056 actor *foe = mons->get_foe();
1057 if (!foe)
1058 break;
1059
1060 const int dist = grid_distance(foe->pos(), mons->pos());
1061 if (dist < 3)
1062 break;
1063
1064 const coord_def target = _find_nearer_tree(mons->pos(), foe->pos());
1065 if (target.origin() || !mons->move_to_pos(target))
1066 break;
1067
1068 simple_monster_message(*mons, " flows through the trees.");
1069 used = true;
1070 }
1071 break;
1072
1073 case MONS_SHAMBLING_MANGROVE:
1074 {
1075 if (mons->hit_points * 2 < mons->max_hit_points
1076 && mons->mangrove_pests > 0)
1077 {
1078 treant_release_fauna(*mons);
1079 // Intentionally takes no energy; the creatures are flying free
1080 // on their own time.
1081 }
1082 }
1083 break;
1084
1085 case MONS_GUARDIAN_GOLEM:
1086 if (mons->hit_points * 2 < mons->max_hit_points
1087 && !mons->has_ench(ENCH_INNER_FLAME))
1088 {
1089 simple_monster_message(*mons, " overheats!");
1090 mid_t act = mons->summoner == MID_PLAYER ? MID_YOU_FAULTLESS :
1091 mons->summoner;
1092 mons->add_ench(mon_enchant(ENCH_INNER_FLAME, 0, actor_by_mid(act),
1093 INFINITE_DURATION));
1094 }
1095 break;
1096
1097 default:
1098 break;
1099 }
1100
1101 if (used)
1102 mons->lose_energy(EUT_SPECIAL);
1103
1104 return used;
1105 }
1106
guardian_golem_bond(monster & mons)1107 void guardian_golem_bond(monster& mons)
1108 {
1109 for (monster_near_iterator mi(&mons, LOS_NO_TRANS); mi; ++mi)
1110 {
1111 if (mons_aligned(&mons, *mi) && !mi->has_ench(ENCH_CHARM)
1112 && *mi != &mons)
1113 {
1114 mi->add_ench(mon_enchant(ENCH_INJURY_BOND, 1, &mons, INFINITE_DURATION));
1115 }
1116 }
1117 }
1118