Add two new Ru sacrifices: resistance and eye
[crawl.git] / crawl-ref / source / beam.cc
1 /**
2  * @file
3  * @brief Functions related to ranged attacks.
4 **/
5
6 #include "AppHdr.h"
7
8 #include "beam.h"
9
10 #include <algorithm>
11 #include <cmath>
12 #include <cstdarg>
13 #include <cstdio>
14 #include <cstdlib>
15 #include <cstring>
16 #include <iostream>
17 #include <set>
18
19 #include "act-iter.h"
20 #include "areas.h"
21 #include "attitude-change.h"
22 #include "bloodspatter.h"
23 #include "branch.h"
24 #include "cloud.h"
25 #include "colour.h"
26 #include "coordit.h"
27 #include "delay.h"
28 #include "directn.h"
29 #include "dungeon.h"
30 #include "english.h"
31 #include "exercise.h"
32 #include "fight.h"
33 #include "godabil.h"
34 #include "godconduct.h"
35 #include "itemprop.h"
36 #include "items.h"
37 #include "libutil.h"
38 #include "losglobal.h"
39 #include "los.h"
40 #include "message.h"
41 #include "misc.h"
42 #include "mon-behv.h"
43 #include "mon-death.h"
44 #include "mon-place.h"
45 #include "mon-poly.h"
46 #include "mutation.h"
47 #include "potion.h"
48 #include "prompt.h"
49 #include "ranged_attack.h"
50 #include "religion.h"
51 #include "shout.h"
52 #include "spl-clouds.h"
53 #include "spl-damage.h"
54 #include "spl-goditem.h"
55 #include "spl-monench.h"
56 #include "spl-other.h"
57 #include "spl-summoning.h"
58 #include "spl-transloc.h"
59 #include "spl-util.h"
60 #include "spl-zap.h"
61 #include "state.h"
62 #include "stepdown.h"
63 #include "stringutil.h"
64 #include "target.h"
65 #include "teleport.h"
66 #include "terrain.h"
67 #include "throw.h"
68 #ifdef USE_TILE
69  #include "tilepick.h"
70 #endif
71 #include "transform.h"
72 #include "traps.h"
73 #include "viewchar.h"
74 #include "view.h"
75 #include "xom.h"
76
77 #define SAP_MAGIC_CHANCE() x_chance_in_y(7, 10)
78
79 // Helper functions (some of these should probably be public).
80 static void _ench_animation(int flavour, const monster* mon = nullptr,
81                             bool force = false);
82 static beam_type _chaos_beam_flavour(bolt* beam);
83 static string _beam_type_name(beam_type type);
84
85 tracer_info::tracer_info()
86 {
87     reset();
88 }
89
90 void tracer_info::reset()
91 {
92     count = power = hurt = helped = 0;
93     dont_stop = false;
94 }
95
96 const tracer_info& tracer_info::operator+=(const tracer_info &other)
97 {
98     count  += other.count;
99     power  += other.power;
100     hurt   += other.hurt;
101     helped += other.helped;
102
103     dont_stop = dont_stop || other.dont_stop;
104
105     return *this;
106 }
107
108 bool bolt::is_blockable() const
109 {
110     // BEAM_ELECTRICITY is added here because chain lightning is not
111     // a true beam (stops at the first target it gets to and redirects
112     // from there)... but we don't want it shield blockable.
113     return !pierce && !is_explosion && flavour != BEAM_ELECTRICITY
114            && hit != AUTOMATIC_HIT && flavour != BEAM_VISUAL;
115 }
116
117 void bolt::emit_message(const char* m)
118 {
119     const string message = m;
120     if (!message_cache.count(message))
121         mpr(m);
122
123     message_cache.insert(message);
124 }
125
126 kill_category bolt::whose_kill() const
127 {
128     if (YOU_KILL(thrower) || source_id == MID_YOU_FAULTLESS)
129         return KC_YOU;
130     else if (MON_KILL(thrower))
131     {
132         if (source_id == MID_ANON_FRIEND)
133             return KC_FRIENDLY;
134         const monster* mon = monster_by_mid(source_id);
135         if (mon && mon->friendly())
136             return KC_FRIENDLY;
137     }
138     return KC_OTHER;
139 }
140
141 // A simple animated flash from Rupert Smith (expanded to be more
142 // generic).
143 static void _zap_animation(int colour, const monster* mon = nullptr,
144                            bool force = false)
145 {
146     coord_def p = you.pos();
147
148     if (mon)
149     {
150         if (!force && !mon->visible_to(&you))
151             return;
152
153         p = mon->pos();
154     }
155
156     if (!you.see_cell(p))
157         return;
158
159     const coord_def drawp = grid2view(p);
160
161     if (in_los_bounds_v(drawp))
162     {
163 #ifdef USE_TILE
164         tiles.add_overlay(p, tileidx_zap(colour));
165 #endif
166 #ifndef USE_TILE_LOCAL
167         view_update();
168         cgotoxy(drawp.x, drawp.y, GOTO_DNGN);
169         put_colour_ch(colour, dchar_glyph(DCHAR_FIRED_ZAP));
170 #endif
171
172         update_screen();
173
174         scaled_delay(50);
175     }
176 }
177
178 // Special front function for zap_animation to interpret enchantment flavours.
179 static void _ench_animation(int flavour, const monster* mon, bool force)
180 {
181     element_type elem;
182     switch (flavour)
183     {
184     case BEAM_HEALING:
185         elem = ETC_HEAL;
186         break;
187     case BEAM_PAIN:
188         elem = ETC_UNHOLY;
189         break;
190     case BEAM_DISPEL_UNDEAD:
191         elem = ETC_HOLY;
192         break;
193     case BEAM_POLYMORPH:
194     case BEAM_MALMUTATE:
195     case BEAM_CORRUPT_BODY:
196         elem = ETC_MUTAGENIC;
197         break;
198     case BEAM_CHAOS:
199     case BEAM_CHAOTIC_REFLECTION:
200         elem = ETC_RANDOM;
201         break;
202     case BEAM_TELEPORT:
203     case BEAM_BANISH:
204     case BEAM_BLINK:
205     case BEAM_BLINK_CLOSE:
206         elem = ETC_WARP;
207         break;
208     case BEAM_MAGIC:
209         elem = ETC_MAGIC;
210         break;
211     default:
212         elem = ETC_ENCHANT;
213         break;
214     }
215
216     _zap_animation(element_colour(elem), mon, force);
217 }
218
219 // If needs_tracer is true, we need to check the beam path for friendly
220 // monsters.
221 spret_type zapping(zap_type ztype, int power, bolt &pbolt,
222                    bool needs_tracer, const char* msg, bool fail)
223 {
224     dprf(DIAG_BEAM, "zapping: power=%d", power);
225
226     pbolt.thrower = KILL_YOU_MISSILE;
227
228     // Check whether tracer goes through friendlies.
229     // NOTE: Whenever zapping() is called with a randomised value for power
230     // (or effect), player_tracer should be called directly with the highest
231     // power possible respecting current skill, experience level, etc.
232     if (needs_tracer && !player_tracer(ztype, power, pbolt))
233         return SPRET_ABORT;
234
235     fail_check();
236     // Fill in the bolt structure.
237     zappy(ztype, power, pbolt);
238
239     if (msg)
240         mpr(msg);
241
242     if (ztype == ZAP_LIGHTNING_BOLT)
243     {
244         noisy(spell_effect_noise(SPELL_LIGHTNING_BOLT),
245                you.pos(), "You hear a mighty clap of thunder!");
246         pbolt.heard = true;
247     }
248
249     if (ztype == ZAP_DIG)
250         pbolt.aimed_at_spot = false;
251
252     pbolt.fire();
253
254     return SPRET_SUCCESS;
255 }
256
257 // Returns true if the path is considered "safe", and false if there are
258 // monsters in the way the player doesn't want to hit.
259 bool player_tracer(zap_type ztype, int power, bolt &pbolt, int range)
260 {
261     // Non-controlleable during confusion.
262     // (We'll shoot in a different direction anyway.)
263     if (you.confused())
264         return true;
265
266     zappy(ztype, power, pbolt);
267
268     pbolt.is_tracer     = true;
269     pbolt.source        = you.pos();
270     pbolt.source_id     = MID_PLAYER;
271     pbolt.smart_monster = true;
272     pbolt.attitude      = ATT_FRIENDLY;
273     pbolt.thrower       = KILL_YOU_MISSILE;
274
275
276     // Init tracer variables.
277     pbolt.friend_info.reset();
278     pbolt.foe_info.reset();
279
280     pbolt.foe_ratio        = 100;
281     pbolt.beam_cancelled   = false;
282     pbolt.dont_stop_player = false;
283
284     // Clear misc
285     pbolt.seen          = false;
286     pbolt.heard         = false;
287     pbolt.reflections   = 0;
288     pbolt.bounces       = 0;
289
290     // Save range before overriding it
291     const int old_range = pbolt.range;
292     if (range)
293         pbolt.range = range;
294
295     pbolt.fire();
296
297     if (range)
298         pbolt.range = old_range;
299
300     // Should only happen if the player answered 'n' to one of those
301     // "Fire through friendly?" prompts.
302     if (pbolt.beam_cancelled)
303     {
304         dprf(DIAG_BEAM, "Beam cancelled.");
305         you.turn_is_over = false;
306         return false;
307     }
308
309     // Set to non-tracing for actual firing.
310     pbolt.is_tracer = false;
311     return true;
312 }
313
314 template<typename T>
315 class power_deducer
316 {
317 public:
318     virtual T operator()(int pow) const = 0;
319     virtual ~power_deducer() {}
320 };
321
322 typedef power_deducer<int> tohit_deducer;
323
324 template<int adder, int mult_num = 0, int mult_denom = 1>
325 class tohit_calculator : public tohit_deducer
326 {
327 public:
328     int operator()(int pow) const
329     {
330         return adder + pow * mult_num / mult_denom;
331     }
332 };
333
334 typedef power_deducer<dice_def> dam_deducer;
335
336 template<int numdice, int adder, int mult_num, int mult_denom>
337 class dicedef_calculator : public dam_deducer
338 {
339 public:
340     dice_def operator()(int pow) const
341     {
342         return dice_def(numdice, adder + pow * mult_num / mult_denom);
343     }
344 };
345
346 template<int numdice, int adder, int mult_num, int mult_denom>
347 class calcdice_calculator : public dam_deducer
348 {
349 public:
350     dice_def operator()(int pow) const
351     {
352         return calc_dice(numdice, adder + pow * mult_num / mult_denom);
353     }
354 };
355
356 struct zap_info
357 {
358     zap_type ztype;
359     const char* name;           // nullptr means handled specially
360     int power_cap;
361     dam_deducer* damage;
362     tohit_deducer* tohit;       // Enchantments have power modifier here
363     colour_t colour;
364     bool is_enchantment;
365     beam_type flavour;
366     dungeon_char_type glyph;
367     bool always_obvious;
368     bool can_beam;
369     bool is_explosion;
370     int hit_loudness;
371 };
372
373 #include "zap-data.h"
374
375 static int zap_index[NUM_ZAPS];
376
377 void init_zap_index()
378 {
379     for (int i = 0; i < NUM_ZAPS; ++i)
380         zap_index[i] = -1;
381
382     for (unsigned int i = 0; i < ARRAYSZ(zap_data); ++i)
383         zap_index[zap_data[i].ztype] = i;
384 }
385
386 static const zap_info* _seek_zap(zap_type z_type)
387 {
388     ASSERT_RANGE(z_type, 0, NUM_ZAPS);
389     if (zap_index[z_type] == -1)
390         return nullptr;
391     else
392         return &zap_data[zap_index[z_type]];
393 }
394
395 int zap_power_cap(zap_type z_type)
396 {
397     const zap_info* zinfo = _seek_zap(z_type);
398
399     return zinfo ? zinfo->power_cap : 0;
400 }
401
402 int zap_ench_power(zap_type z_type, int pow)
403 {
404     const zap_info* zinfo = _seek_zap(z_type);
405     if (!zinfo)
406         return pow;
407
408     if (zinfo->power_cap > 0)
409         pow = min(zinfo->power_cap, pow);
410
411     if (zinfo->is_enchantment && zinfo->tohit)
412         return (*zinfo->tohit)(pow);
413     else
414         return pow;
415 }
416
417 void zappy(zap_type z_type, int power, bolt &pbolt)
418 {
419     const zap_info* zinfo = _seek_zap(z_type);
420
421     // None found?
422     if (zinfo == nullptr)
423     {
424         dprf("Couldn't find zap type %d", z_type);
425         return;
426     }
427
428     // Fill
429     pbolt.name           = zinfo->name;
430     pbolt.flavour        = zinfo->flavour;
431     pbolt.real_flavour   = zinfo->flavour;
432     pbolt.colour         = zinfo->colour;
433     pbolt.glyph          = dchar_glyph(zinfo->glyph);
434     pbolt.obvious_effect = zinfo->always_obvious;
435     pbolt.pierce         = zinfo->can_beam;
436     pbolt.is_explosion   = zinfo->is_explosion;
437
438     if (zinfo->power_cap > 0)
439         power = min(zinfo->power_cap, power);
440
441     ASSERT(zinfo->is_enchantment == pbolt.is_enchantment());
442
443     pbolt.ench_power = zap_ench_power(z_type, power);
444
445     if (zinfo->is_enchantment)
446         pbolt.hit = AUTOMATIC_HIT;
447     else
448     {
449         pbolt.hit = (*zinfo->tohit)(power);
450         if (pbolt.hit != AUTOMATIC_HIT)
451             pbolt.hit = max(0, pbolt.hit - 5 * you.inaccuracy());
452     }
453
454     if (zinfo->damage)
455         pbolt.damage = (*zinfo->damage)(power);
456
457     pbolt.origin_spell = zap_to_spell(z_type);
458
459     if (z_type == ZAP_BREATHE_FIRE && you.species == SP_RED_DRACONIAN)
460         pbolt.origin_spell = SPELL_SEARING_BREATH;
461
462     if (pbolt.loudness == 0)
463         pbolt.loudness = zinfo->hit_loudness;
464 }
465
466 bool bolt::can_affect_actor(const actor *act) const
467 {
468     // Blinkbolt doesn't hit its caster, since they are the bolt.
469     if (origin_spell == SPELL_BLINKBOLT && act->mid == source_id)
470         return false;
471     auto cnt = hit_count.find(act->mid);
472     if (cnt != hit_count.end() && cnt->second >= 2)
473     {
474         // Note: this is done for balance, even if it hurts realism a bit.
475         // It is arcane knowledge which wall patterns will cause lightning
476         // to bounce thrice, double damage for ordinary bounces is enough.
477 #ifdef DEBUG_DIAGNOSTICS
478         if (!quiet_debug)
479             dprf(DIAG_BEAM, "skipping beam hit, affected them twice already");
480 #endif
481         return false;
482     }
483
484     return !act->submerged();
485 }
486
487 static beam_type _chaos_beam_flavour(bolt* beam)
488 {
489     beam_type flavour;
490     do
491     {
492         flavour = random_choose_weighted(
493             10, BEAM_FIRE,
494             10, BEAM_COLD,
495             10, BEAM_ELECTRICITY,
496             10, BEAM_POISON,
497             10, BEAM_NEG,
498             10, BEAM_ACID,
499             10, BEAM_HELLFIRE,
500             10, BEAM_STICKY_FLAME,
501             10, BEAM_SLOW,
502             10, BEAM_HASTE,
503             10, BEAM_MIGHT,
504             10, BEAM_BERSERK,
505             10, BEAM_HEALING,
506             10, BEAM_PARALYSIS,
507             10, BEAM_CONFUSION,
508             10, BEAM_INVISIBILITY,
509             10, BEAM_POLYMORPH,
510             10, BEAM_BANISH,
511             10, BEAM_DISINTEGRATION,
512             10, BEAM_PETRIFY,
513             10, BEAM_AGILITY,
514              2, BEAM_ENSNARE,
515             0);
516     }
517     while (beam->origin_spell == SPELL_CHAIN_OF_CHAOS
518            && (flavour == BEAM_BANISH
519                || flavour == BEAM_POLYMORPH));
520
521     return flavour;
522 }
523
524 static beam_type _chaotic_reflection_flavour(bolt* beam)
525 {
526     return random_choose_weighted(
527             10, BEAM_SLOW,
528             10, BEAM_HASTE,
529             10, BEAM_MIGHT,
530             10, BEAM_BERSERK,
531             10, BEAM_PARALYSIS,
532             10, BEAM_CONFUSION,
533             10, BEAM_DISINTEGRATION,
534             10, BEAM_PETRIFY,
535             10, BEAM_AGILITY,
536             10, BEAM_BLINK,
537             10, BEAM_SLEEP,
538             10, BEAM_VULNERABILITY,
539             10, BEAM_RESISTANCE,
540              2, BEAM_ENSNARE,
541              0);
542 }
543
544 bool bolt::visible() const
545 {
546     return !is_tracer && glyph != 0 && !is_enchantment();
547 }
548
549 void bolt::initialise_fire()
550 {
551     // Fix some things which the tracer might have set.
552     extra_range_used   = 0;
553     in_explosion_phase = false;
554     use_target_as_pos  = false;
555     hit_count.clear();
556
557     if (special_explosion != nullptr)
558     {
559         ASSERT(!is_explosion);
560         ASSERT(special_explosion->is_explosion);
561         ASSERT(special_explosion->special_explosion == nullptr);
562         special_explosion->in_explosion_phase = false;
563         special_explosion->use_target_as_pos  = false;
564     }
565
566     if (chose_ray)
567     {
568         ASSERT_IN_BOUNDS(ray.pos());
569
570         if (source == coord_def())
571             source = ray.pos();
572     }
573
574     if (target == source)
575     {
576         range             = 0;
577         aimed_at_feet     = true;
578         auto_hit          = true;
579         aimed_at_spot     = true;
580         use_target_as_pos = true;
581     }
582
583     ASSERT_IN_BOUNDS(source);
584     ASSERT_RANGE(flavour, BEAM_NONE + 1, BEAM_FIRST_PSEUDO);
585     ASSERT(!drop_item || item && item->defined());
586     ASSERTM(range >= 0, "beam '%s', source '%s', item '%s'; has range -1",
587             name.c_str(),
588             (source_id == MID_PLAYER ? "player" :
589                           monster_by_mid(source_id) ?
590                              monster_by_mid(source_id)->name(DESC_PLAIN, true) :
591                           "unknown").c_str(),
592             (item ? item->name(DESC_PLAIN, false, true) : "none").c_str());
593     ASSERT(!aimed_at_feet || source == target);
594
595     real_flavour = flavour;
596
597     message_cache.clear();
598
599     // seen might be set by caller to suppress this.
600     if (!seen && you.see_cell(source) && range > 0 && visible())
601     {
602         seen = true;
603         const monster* mon = monster_at(source);
604
605         if (flavour != BEAM_VISUAL
606             && !YOU_KILL(thrower)
607             && !crawl_state.is_god_acting()
608             && (!mon || !mon->observable()))
609         {
610             mprf("%s appears from out of thin air!",
611                  article_a(name, false).c_str());
612         }
613     }
614
615     // Visible self-targeted beams are always seen, even though they don't
616     // leave a path.
617     if (you.see_cell(source) && target == source && visible())
618         seen = true;
619
620     // XXX: Should non-agents count as seeing invisible?
621     // The agent may die during the beam's firing, need to save these now.
622     nightvision = agent() && agent()->nightvision();
623     can_see_invis = agent() && agent()->can_see_invisible();
624
625 #ifdef DEBUG_DIAGNOSTICS
626     // Not a "real" tracer, merely a range/reachability check.
627     if (quiet_debug)
628         return;
629
630     dprf(DIAG_BEAM, "%s%s%s [%s] (%d,%d) to (%d,%d): "
631           "gl=%d col=%d flav=%d hit=%d dam=%dd%d range=%d",
632           (pierce) ? "beam" : "missile",
633           (is_explosion) ? "*" :
634           (is_big_cloud()) ? "+" : "",
635           (is_tracer) ? " tracer" : "",
636           name.c_str(),
637           source.x, source.y,
638           target.x, target.y,
639           glyph, colour, flavour,
640           hit, damage.num, damage.size,
641           range);
642 #endif
643 }
644
645 // Catch the bolt part of explosive bolt.
646 static bool _is_explosive_bolt(const bolt *beam)
647 {
648     return beam->origin_spell == SPELL_EXPLOSIVE_BOLT
649            && !beam->in_explosion_phase;
650 }
651
652 void bolt::apply_beam_conducts()
653 {
654     if (!is_tracer && YOU_KILL(thrower))
655     {
656         switch (flavour)
657         {
658         case BEAM_HELLFIRE:
659             did_god_conduct(DID_UNHOLY, 2 + random2(3), god_cares());
660             did_god_conduct(DID_FIRE, 10 + random2(5), god_cares());
661             break;
662         case BEAM_FIRE:
663         case BEAM_HOLY_FLAME:
664         case BEAM_STICKY_FLAME:
665             did_god_conduct(DID_FIRE,
666                             pierce || is_explosion ? 6 + random2(3)
667                                                    : 2 + random2(3),
668                             god_cares());
669             break;
670         default:
671             // Fire comes from a side-effect of the beam, not the beam itself.
672             if (_is_explosive_bolt(this))
673                 did_god_conduct(DID_FIRE, 6 + random2(3), god_cares());
674             break;
675         }
676     }
677 }
678
679 void bolt::choose_ray()
680 {
681     if ((!chose_ray || reflections > 0)
682         && !find_ray(source, target, ray, opc_solid_see)
683         // If fire is blocked, at least try a visible path so the
684         // error message is better.
685         && !find_ray(source, target, ray, opc_default))
686     {
687         fallback_ray(source, target, ray);
688     }
689 }
690
691 // Draw the bolt at p if needed.
692 void bolt::draw(const coord_def& p)
693 {
694     if (is_tracer || is_enchantment() || !you.see_cell(p))
695         return;
696
697     // We don't clean up the old position.
698     // First, most people like to see the full path,
699     // and second, it is hard to do it right with
700     // respect to killed monsters, cloud trails, etc.
701
702     const coord_def drawpos = grid2view(p);
703
704     if (!in_los_bounds_v(drawpos))
705         return;
706
707 #ifdef USE_TILE
708     if (tile_beam == -1)
709         tile_beam = tileidx_bolt(*this);
710
711     if (tile_beam != -1)
712     {
713         int dist = (p - source).rdist();
714         tiles.add_overlay(p, vary_bolt_tile(tile_beam, dist));
715     }
716 #endif
717 #ifndef USE_TILE_LOCAL
718     cgotoxy(drawpos.x, drawpos.y, GOTO_DNGN);
719     put_colour_ch(colour == BLACK ? random_colour(true)
720                                   : element_colour(colour),
721                   glyph);
722
723     // Get curses to update the screen so we can see the beam.
724     update_screen();
725 #endif
726     scaled_delay(draw_delay);
727 }
728
729 // Bounce a bolt off a solid feature.
730 // The ray is assumed to have just been advanced into
731 // the feature.
732 void bolt::bounce()
733 {
734     // Don't bounce player tracers off unknown cells, or cells that we
735     // incorrectly thought were non-bouncy.
736     if (is_tracer && agent() == &you)
737     {
738         const dungeon_feature_type feat = env.map_knowledge(ray.pos()).feat();
739
740         if (feat == DNGN_UNSEEN || !feat_is_solid(feat) || !is_bouncy(feat))
741         {
742             ray.regress();
743             finish_beam();
744             return;
745         }
746     }
747
748     do
749     {
750         ray.regress();
751     }
752     while (cell_is_solid(ray.pos()));
753
754     extra_range_used += range_used(true);
755     bounce_pos = ray.pos();
756     bounces++;
757     reflect_grid rg;
758     for (adjacent_iterator ai(ray.pos(), false); ai; ++ai)
759         rg(*ai - ray.pos()) = cell_is_solid(*ai);
760     ray.bounce(rg);
761     extra_range_used += 2;
762
763     ASSERT(!cell_is_solid(ray.pos()));
764 }
765
766 void bolt::fake_flavour()
767 {
768     if (real_flavour == BEAM_RANDOM)
769         flavour = static_cast<beam_type>(random_range(BEAM_FIRE, BEAM_ACID));
770     else if (real_flavour == BEAM_CHAOS)
771         flavour = _chaos_beam_flavour(this);
772     else if (real_flavour == BEAM_CHAOTIC_REFLECTION)
773         flavour = _chaotic_reflection_flavour(this);
774     else if (real_flavour == BEAM_CRYSTAL && flavour == BEAM_CRYSTAL)
775     {
776         flavour = coinflip() ? BEAM_FIRE : BEAM_COLD;
777         hit_verb = (flavour == BEAM_FIRE) ? "burns" :
778                    (flavour == BEAM_COLD) ? "freezes"
779                                           : "bugs";
780     }
781 }
782
783 void bolt::digging_wall_effect()
784 {
785     const dungeon_feature_type feat = grd(pos());
786     switch (feat)
787     {
788     case DNGN_ROCK_WALL:
789     case DNGN_CLEAR_ROCK_WALL:
790     case DNGN_SLIMY_WALL:
791     case DNGN_GRATE:
792         destroy_wall(pos());
793         if (!msg_generated)
794         {
795             if (!you.see_cell(pos()))
796             {
797                 if (!silenced(you.pos()))
798                 {
799                     mprf(MSGCH_SOUND, "You hear a grinding noise.");
800                     obvious_effect = true; // You may still see the caster.
801                     msg_generated = true;
802                 }
803                 break;
804             }
805
806             obvious_effect = true;
807             msg_generated = true;
808
809             string wall;
810             if (feat == DNGN_GRATE)
811             {
812                 // XXX: should this change for monsters?
813                 mpr("The damaged grate falls apart.");
814                 return;
815             }
816             else if (feat == DNGN_SLIMY_WALL)
817                 wall = "slime";
818             else if (player_in_branch(BRANCH_PANDEMONIUM))
819                 wall = "weird stuff";
820             else
821                 wall = "rock";
822
823             mprf("%s %s shatters into small pieces.",
824                  agent() && agent()->is_player() ? "The" : "Some",
825                  wall.c_str());
826         }
827         break;
828
829     default:
830         if (feat_is_wall(feat))
831             finish_beam();
832     }
833 }
834
835 void bolt::burn_wall_effect()
836 {
837     dungeon_feature_type feat = grd(pos());
838     // Fire only affects trees.
839     if (!feat_is_tree(feat)
840         || env.markers.property_at(pos(), MAT_ANY, "veto_fire") == "veto"
841         || !is_superhot())
842     {
843         finish_beam();
844         return;
845     }
846
847     // Destroy the wall.
848     destroy_wall(pos());
849     if (you.see_cell(pos()))
850     {
851         if (player_in_branch(BRANCH_SWAMP))
852             emit_message("The tree smolders and burns.");
853         else
854             emit_message("The tree burns like a torch!");
855     }
856     else if (you.can_smell())
857         emit_message("You smell burning wood.");
858     if (whose_kill() == KC_YOU)
859     {
860         did_god_conduct(DID_KILL_PLANT, 1, god_cares());
861         did_god_conduct(DID_FIRE, 6, god_cares()); // guaranteed penance
862     }
863     else if (whose_kill() == KC_FRIENDLY && !crawl_state.game_is_arena())
864         did_god_conduct(DID_KILL_PLANT, 1, god_cares());
865
866     // Trees do not burn so readily in a wet environment.
867     if (player_in_branch(BRANCH_SWAMP))
868         place_cloud(CLOUD_FIRE, pos(), random2(12)+5, agent());
869     else
870         place_cloud(CLOUD_FOREST_FIRE, pos(), random2(30)+25, agent());
871     obvious_effect = true;
872
873     finish_beam();
874 }
875
876 static bool _destroy_wall_msg(dungeon_feature_type feat, const coord_def& p)
877 {
878     const char *msg = nullptr;
879     msg_channel_type chan = MSGCH_PLAIN;
880     bool hear = player_can_hear(p);
881     bool see = you.see_cell(p);
882
883     switch (feat)
884     {
885     case DNGN_ROCK_WALL:
886     case DNGN_SLIMY_WALL:
887     case DNGN_CLEAR_ROCK_WALL:
888     case DNGN_GRANITE_STATUE:
889     case DNGN_CLOSED_DOOR:
890     case DNGN_RUNED_DOOR:
891     case DNGN_SEALED_DOOR:
892         if (see)
893         {
894             msg = (feature_description_at(p, false, DESC_THE, false)
895                    + " explodes into countless fragments.").c_str();
896         }
897         else if (hear)
898         {
899             msg = "You hear a grinding noise.";
900             chan = MSGCH_SOUND;
901         }
902         break;
903
904     case DNGN_GRATE:
905         if (hear)
906         {
907             if (see)
908                 msg = "The grate screeches as it bends and collapses.";
909             else
910                 msg = "You hear the screech of bent metal.";
911             chan = MSGCH_SOUND;
912         }
913         else if (see)
914             msg = "The grate bends and collapses.";
915         break;
916
917     case DNGN_ORCISH_IDOL:
918         if (hear)
919         {
920             if (see)
921                 msg = "The idol screams as its substance crumbles away!";
922             else
923                 msg = "You hear a hideous screaming!";
924             chan = MSGCH_SOUND;
925         }
926         else if (see)
927             msg = "The idol twists and shakes as its substance crumbles away!";
928         break;
929
930     case DNGN_TREE:
931         if (see)
932             msg = "The tree breaks and falls down!";
933         else if (hear)
934         {
935             msg = "You hear timber falling.";
936             chan = MSGCH_SOUND;
937         }
938         break;
939
940     default:
941         break;
942     }
943
944     if (msg)
945     {
946         mprf(chan, "%s", msg);
947         return true;
948     }
949     else
950         return false;
951 }
952
953 void bolt::destroy_wall_effect()
954 {
955     if (env.markers.property_at(pos(), MAT_ANY, "veto_disintegrate") == "veto")
956     {
957         finish_beam();
958         return;
959     }
960
961     const dungeon_feature_type feat = grd(pos());
962
963     switch (feat)
964     {
965     case DNGN_ROCK_WALL:
966     case DNGN_SLIMY_WALL:
967     case DNGN_CLEAR_ROCK_WALL:
968     case DNGN_GRATE:
969     case DNGN_GRANITE_STATUE:
970     case DNGN_ORCISH_IDOL:
971     case DNGN_TREE:
972         destroy_wall(pos());
973         break;
974
975     case DNGN_CLOSED_DOOR:
976     case DNGN_RUNED_DOOR:
977     case DNGN_SEALED_DOOR:
978     {
979         set<coord_def> doors;
980         find_connected_identical(pos(), doors);
981         for (coord_def c : doors)
982             destroy_wall(c);
983         break;
984     }
985
986     default:
987         finish_beam();
988         return;
989     }
990
991     obvious_effect = _destroy_wall_msg(feat, pos());
992
993     if (feat == DNGN_ORCISH_IDOL)
994     {
995         if (source_id == MID_PLAYER)
996             did_god_conduct(DID_DESTROY_ORCISH_IDOL, 8);
997     }
998     else if (feat_is_tree(feat))
999     {
1000         if (whose_kill() == KC_YOU)
1001             did_god_conduct(DID_KILL_PLANT, 1);
1002         else if (whose_kill() == KC_FRIENDLY && !crawl_state.game_is_arena())
1003             did_god_conduct(DID_KILL_PLANT, 1, god_cares(), 0);
1004     }
1005
1006     finish_beam();
1007 }
1008
1009 int bolt::range_used(bool leg_only) const
1010 {
1011     const int leg_length = pos().range(leg_source());
1012     return leg_only ? leg_length : leg_length + extra_range_used;
1013 }
1014
1015 void bolt::finish_beam()
1016 {
1017     extra_range_used = BEAM_STOP;
1018 }
1019
1020 void bolt::affect_wall()
1021 {
1022     if (is_tracer)
1023     {
1024         if (!can_affect_wall(grd(pos())))
1025             finish_beam();
1026         // The only thing that doesn't stop at walls.
1027         if (flavour != BEAM_DIGGING)
1028             finish_beam();
1029         return;
1030     }
1031
1032     if (flavour == BEAM_DIGGING)
1033         digging_wall_effect();
1034     else if (is_fiery() || flavour == BEAM_ELECTRICITY)
1035         burn_wall_effect();
1036     else if (flavour == BEAM_DISINTEGRATION || flavour == BEAM_DEVASTATION)
1037         destroy_wall_effect();
1038
1039     if (cell_is_solid(pos()))
1040         finish_beam();
1041 }
1042
1043 coord_def bolt::pos() const
1044 {
1045     if (in_explosion_phase || use_target_as_pos)
1046         return target;
1047     else
1048         return ray.pos();
1049 }
1050
1051 bool bolt::need_regress() const
1052 {
1053     // XXX: The affects_wall check probably makes some of the
1054     //      others obsolete.
1055     return (is_explosion && !in_explosion_phase)
1056            || drop_item
1057            || cell_is_solid(pos()) && !can_affect_wall(grd(pos()))
1058            || origin_spell == SPELL_PRIMAL_WAVE;
1059 }
1060
1061 // Returns true if the beam ended due to hitting the wall.
1062 bool bolt::hit_wall()
1063 {
1064     const dungeon_feature_type feat = grd(pos());
1065
1066 #ifdef ASSERTS
1067     if (!feat_is_solid(feat))
1068         die("beam::hit_wall yet not solid: %s", dungeon_feature_name(feat));
1069 #endif
1070
1071     if (is_tracer && !is_targeting && YOU_KILL(thrower)
1072         && in_bounds(target) && !passed_target && pos() != target
1073         && pos() != source && foe_info.count == 0
1074         && flavour != BEAM_DIGGING && flavour <= BEAM_LAST_REAL
1075         && bounces == 0 && reflections == 0 && you.see_cell(target)
1076         && !cell_is_solid(target))
1077     {
1078         // Okay, with all those tests passed, this is probably an instance
1079         // of the player manually targeting something whose line of fire
1080         // is blocked, even though its line of sight isn't blocked. Give
1081         // a warning about this fact.
1082         string prompt = "Your line of fire to ";
1083         const monster* mon = monster_at(target);
1084
1085         if (mon && mon->observable())
1086             prompt += mon->name(DESC_THE);
1087         else
1088         {
1089             prompt += "the targeted "
1090                     + feature_description_at(target, false, DESC_PLAIN, false);
1091         }
1092
1093         prompt += " is blocked by "
1094                 + feature_description_at(pos(), false, DESC_A, false);
1095
1096         prompt += ". Continue anyway?";
1097
1098         if (!yesno(prompt.c_str(), false, 'n'))
1099         {
1100             canned_msg(MSG_OK);
1101             beam_cancelled = true;
1102             finish_beam();
1103             return false;
1104         }
1105
1106         // Well, we warned them.
1107     }
1108
1109     if (in_bounds(pos()) && can_affect_wall(feat))
1110         affect_wall();
1111     else if (is_bouncy(feat) && !in_explosion_phase)
1112         bounce();
1113     else
1114     {
1115         // Regress for explosions: blow up in an open grid (if regressing
1116         // makes any sense). Also regress when dropping items.
1117         if (pos() != source && need_regress())
1118         {
1119             do
1120             {
1121                 ray.regress();
1122             }
1123             while (ray.pos() != source && cell_is_solid(ray.pos()));
1124
1125             // target is where the explosion is centered, so update it.
1126             if (is_explosion && !is_tracer)
1127                 target = ray.pos();
1128         }
1129         finish_beam();
1130
1131         return true;
1132     }
1133
1134     return false;
1135 }
1136
1137 void bolt::affect_cell()
1138 {
1139     // Shooting through clouds affects accuracy.
1140     if (env.cgrid(pos()) != EMPTY_CLOUD && hit != AUTOMATIC_HIT)
1141         hit = max(hit - 2, 0);
1142
1143     fake_flavour();
1144
1145     const coord_def old_pos = pos();
1146     const bool was_solid = cell_is_solid(pos());
1147
1148     // Note that this can change the ray position and the solidity
1149     // of the wall.
1150     if (was_solid && hit_wall())
1151     {
1152         // Beam ended due to hitting wall, so don't hit the player
1153         // or monster with the regressed beam.
1154         return;
1155     }
1156
1157     // If the player can ever walk through walls, this will need
1158     // special-casing too.
1159     bool hit_player = found_player();
1160     if (hit_player && can_affect_actor(&you))
1161     {
1162         affect_player();
1163         if (hit == AUTOMATIC_HIT && !pierce)
1164             finish_beam();
1165     }
1166
1167     // We don't want to hit a monster in a wall square twice. Also,
1168     // stop single target beams from affecting a monster if they already
1169     // affected the player on this square. -cao
1170     const bool still_wall = (was_solid && old_pos == pos());
1171     if ((!hit_player || pierce || is_explosion) && !still_wall)
1172     {
1173         monster *m = monster_at(pos());
1174         if (m && can_affect_actor(m))
1175         {
1176             affect_monster(m);
1177             if ((hit == AUTOMATIC_HIT && !pierce && !ignores_monster(m))
1178                 && (!is_tracer || m->visible_to(agent())))
1179             {
1180                 finish_beam();
1181             }
1182         }
1183     }
1184
1185     if (!cell_is_solid(pos()))
1186         affect_ground();
1187 }
1188
1189 static void _undo_tracer(bolt &orig, bolt &copy)
1190 {
1191     // FIXME: we should have a better idea of what gets changed!
1192     orig.target           = copy.target;
1193     orig.source           = copy.source;
1194     orig.aimed_at_spot    = copy.aimed_at_spot;
1195     orig.extra_range_used = copy.extra_range_used;
1196     orig.auto_hit         = copy.auto_hit;
1197     orig.ray              = copy.ray;
1198     orig.colour           = copy.colour;
1199     orig.flavour          = copy.flavour;
1200     orig.real_flavour     = copy.real_flavour;
1201     orig.bounces          = copy.bounces;
1202     orig.bounce_pos       = copy.bounce_pos;
1203 }
1204
1205 // This saves some important things before calling fire().
1206 void bolt::fire()
1207 {
1208     path_taken.clear();
1209
1210     if (special_explosion)
1211         special_explosion->is_tracer = is_tracer;
1212
1213     if (is_tracer)
1214     {
1215         bolt boltcopy = *this;
1216         if (special_explosion != nullptr)
1217             boltcopy.special_explosion = new bolt(*special_explosion);
1218
1219         do_fire();
1220
1221         if (special_explosion != nullptr)
1222         {
1223             _undo_tracer(*special_explosion, *boltcopy.special_explosion);
1224             delete boltcopy.special_explosion;
1225         }
1226
1227         _undo_tracer(*this, boltcopy);
1228     }
1229     else
1230         do_fire();
1231
1232     if (special_explosion != nullptr)
1233     {
1234         seen           = seen  || special_explosion->seen;
1235         heard          = heard || special_explosion->heard;
1236     }
1237 }
1238
1239 void bolt::do_fire()
1240 {
1241     initialise_fire();
1242
1243     if (range < extra_range_used && range > 0)
1244     {
1245 #ifdef DEBUG
1246         dprf(DIAG_BEAM, "fire_beam() called on already done beam "
1247              "'%s' (item = '%s')", name.c_str(),
1248              item ? item->name(DESC_PLAIN).c_str() : "none");
1249 #endif
1250         return;
1251     }
1252
1253     apply_beam_conducts();
1254     cursor_control coff(false);
1255
1256 #ifdef USE_TILE
1257     tile_beam = -1;
1258
1259     if (item && !is_tracer && (flavour == BEAM_MISSILE
1260                                || flavour == BEAM_VISUAL))
1261     {
1262         const coord_def diff = target - source;
1263         tile_beam = tileidx_item_throw(get_item_info(*item), diff.x, diff.y);
1264     }
1265 #endif
1266
1267     msg_generated = false;
1268     if (!aimed_at_feet)
1269     {
1270         choose_ray();
1271         // Take *one* step, so as not to hurt the source.
1272         ray.advance();
1273     }
1274
1275     while (map_bounds(pos()))
1276     {
1277         if (range_used() > range)
1278         {
1279             ray.regress();
1280             extra_range_used++;
1281             ASSERT(range_used() >= range);
1282             break;
1283         }
1284
1285         if (!affects_nothing)
1286             affect_cell();
1287
1288         if (path_taken.empty() || pos() != path_taken.back())
1289             path_taken.push_back(pos());
1290
1291         if (range_used() > range)
1292             break;
1293
1294         if (beam_cancelled)
1295             return;
1296
1297         // Weapons of returning should find an inverse ray
1298         // through find_ray and setup_retrace, but they didn't
1299         // always in the past, and we don't want to crash
1300         // if they accidentally pass through a corner.
1301         ASSERT(!cell_is_solid(pos())
1302                || is_tracer && can_affect_wall(grd(pos()))
1303                || affects_nothing); // returning weapons
1304
1305         const bool was_seen = seen;
1306         if (!was_seen && range > 0 && visible() && you.see_cell(pos()))
1307             seen = true;
1308
1309         if (flavour != BEAM_VISUAL && !was_seen && seen && !is_tracer)
1310         {
1311             mprf("%s appears from out of your range of vision.",
1312                  article_a(name, false).c_str());
1313         }
1314
1315         // Reset chaos beams so that it won't be considered an invisible
1316         // enchantment beam for the purposes of animation.
1317         if (real_flavour == BEAM_CHAOS
1318             || real_flavour == BEAM_CHAOTIC_REFLECTION)
1319         {
1320             flavour = real_flavour;
1321         }
1322
1323         // Actually draw the beam/missile/whatever, if the player can see
1324         // the cell.
1325         if (animate)
1326             draw(pos());
1327
1328         if (pos() == target)
1329         {
1330             passed_target = true;
1331             if (stop_at_target())
1332                 break;
1333         }
1334
1335         noise_generated = false;
1336         ray.advance();
1337     }
1338
1339     if (!map_bounds(pos()))
1340     {
1341         ASSERT(!aimed_at_spot);
1342
1343         int tries = max(GXM, GYM);
1344         while (!map_bounds(ray.pos()) && tries-- > 0)
1345             ray.regress();
1346
1347         // Something bizarre happening if we can't get back onto the map.
1348         ASSERT(map_bounds(pos()));
1349     }
1350
1351     // The beam has terminated.
1352     if (!affects_nothing)
1353         affect_endpoint();
1354
1355     // Tracers need nothing further.
1356     if (is_tracer || affects_nothing)
1357         return;
1358
1359     // Canned msg for enchantments that affected no-one, but only if the
1360     // enchantment is yours (and it wasn't a chaos beam, since with chaos
1361     // enchantments are entirely random, and if it randomly attempts
1362     // something which ends up having no obvious effect then the player
1363     // isn't going to realise it).
1364     if (!msg_generated && !obvious_effect && is_enchantment()
1365         && real_flavour != BEAM_CHAOS
1366         && real_flavour != BEAM_CHAOTIC_REFLECTION
1367         && YOU_KILL(thrower))
1368     {
1369         canned_msg(MSG_NOTHING_HAPPENS);
1370     }
1371
1372     // Reactions if a monster zapped the beam.
1373     if (monster_by_mid(source_id))
1374     {
1375         if (foe_info.hurt == 0 && friend_info.hurt > 0)
1376             xom_is_stimulated(100);
1377         else if (foe_info.helped > 0 && friend_info.helped == 0)
1378             xom_is_stimulated(100);
1379
1380         // Allow friendlies to react to projectiles, except when in
1381         // sanctuary when pet_target can only be explicitly changed by
1382         // the player.
1383         const monster* mon = monster_by_mid(source_id);
1384         if (foe_info.hurt > 0 && !mon->wont_attack() && !crawl_state.game_is_arena()
1385             && you.pet_target == MHITNOT && env.sanctuary_time <= 0)
1386         {
1387             you.pet_target = mon->mindex();
1388         }
1389     }
1390 }
1391
1392 // Returns damage taken by a monster from a "flavoured" (fire, ice, etc.)
1393 // attack -- damage from clouds and branded weapons handled elsewhere.
1394 int mons_adjust_flavoured(monster* mons, bolt &pbolt, int hurted,
1395                           bool doFlavouredEffects)
1396 {
1397     // If we're not doing flavoured effects, must be preliminary
1398     // damage check only.
1399     // Do not print messages or apply any side effects!
1400     int original = hurted;
1401
1402     switch (pbolt.flavour)
1403     {
1404     case BEAM_FIRE:
1405     case BEAM_STEAM:
1406         hurted = resist_adjust_damage(mons, pbolt.flavour, hurted);
1407
1408         if (!hurted)
1409         {
1410             if (doFlavouredEffects)
1411             {
1412                 simple_monster_message(mons,
1413                                        (original > 0) ? " completely resists."
1414                                                       : " appears unharmed.");
1415             }
1416         }
1417         else if (original > hurted)
1418         {
1419             if (doFlavouredEffects)
1420                 simple_monster_message(mons, " resists.");
1421         }
1422         else if (original < hurted && doFlavouredEffects)
1423         {
1424             if (mons->is_icy())
1425                 simple_monster_message(mons, " melts!");
1426             else if (mons_species(mons->type) == MONS_BUSH
1427                      && mons->res_fire() < 0)
1428             {
1429                 simple_monster_message(mons, " is on fire!");
1430             }
1431             else if (pbolt.flavour == BEAM_FIRE)
1432                 simple_monster_message(mons, " is burned terribly!");
1433             else
1434                 simple_monster_message(mons, " is scalded terribly!");
1435         }
1436         break;
1437
1438     case BEAM_WATER:
1439         hurted = resist_adjust_damage(mons, pbolt.flavour, hurted);
1440         if (doFlavouredEffects)
1441         {
1442             if (!hurted)
1443                 simple_monster_message(mons, " shrugs off the wave.");
1444             else if (hurted > original)
1445                 simple_monster_message(mons, " is doused terribly!");
1446         }
1447         break;
1448
1449     case BEAM_COLD:
1450         hurted = resist_adjust_damage(mons, pbolt.flavour, hurted);
1451         if (!hurted)
1452         {
1453             if (doFlavouredEffects)
1454             {
1455                 simple_monster_message(mons,
1456                                        (original > 0) ? " completely resists."
1457                                                       : " appears unharmed.");
1458             }
1459         }
1460         else if (original > hurted)
1461         {
1462             if (doFlavouredEffects)
1463                 simple_monster_message(mons, " resists.");
1464         }
1465         else if (original < hurted)
1466         {
1467             if (doFlavouredEffects)
1468                 simple_monster_message(mons, " is frozen!");
1469         }
1470         break;
1471
1472     case BEAM_ELECTRICITY:
1473         hurted = resist_adjust_damage(mons, pbolt.flavour, hurted);
1474         if (!hurted)
1475         {
1476             if (doFlavouredEffects)
1477             {
1478                 simple_monster_message(mons,
1479                                        (original > 0) ? " completely resists."
1480                                                       : " appears unharmed.");
1481             }
1482         }
1483         else if (original > hurted)
1484         {
1485             if (doFlavouredEffects)
1486                 simple_monster_message(mons, " resists.");
1487         }
1488         else if (original < hurted)
1489         {
1490             if (doFlavouredEffects)
1491                 simple_monster_message(mons, " is electrocuted!");
1492         }
1493         break;
1494
1495     case BEAM_ACID:
1496     {
1497         hurted = resist_adjust_damage(mons, pbolt.flavour, hurted);
1498         if (!hurted)
1499         {
1500             if (doFlavouredEffects)
1501             {
1502                 simple_monster_message(mons,
1503                                        (original > 0) ? " completely resists."
1504                                                       : " appears unharmed.");
1505             }
1506         }
1507         else if (mons->res_acid() <= 0 && doFlavouredEffects)
1508             mons->splash_with_acid(pbolt.agent());
1509         break;
1510     }
1511
1512     case BEAM_POISON:
1513     {
1514         hurted = resist_adjust_damage(mons, pbolt.flavour, hurted);
1515
1516         if (!hurted && doFlavouredEffects)
1517         {
1518             simple_monster_message(mons,
1519                                    (original > 0) ? " completely resists."
1520                                                   : " appears unharmed.");
1521         }
1522         else if (doFlavouredEffects && !one_chance_in(3))
1523             poison_monster(mons, pbolt.agent());
1524
1525         break;
1526     }
1527
1528     case BEAM_POISON_ARROW:
1529         hurted = resist_adjust_damage(mons, pbolt.flavour, hurted);
1530         if (hurted < original)
1531         {
1532             if (doFlavouredEffects)
1533             {
1534                 simple_monster_message(mons, " partially resists.");
1535
1536                 poison_monster(mons, pbolt.agent(), 2, true);
1537             }
1538         }
1539         else if (doFlavouredEffects)
1540             poison_monster(mons, pbolt.agent(), 4, true);
1541
1542         break;
1543
1544     case BEAM_NEG:
1545         if (mons->res_negative_energy() == 3)
1546         {
1547             if (doFlavouredEffects)
1548                 simple_monster_message(mons, " completely resists.");
1549
1550             hurted = 0;
1551         }
1552         else
1553         {
1554             hurted = resist_adjust_damage(mons, pbolt.flavour, hurted);
1555
1556             // Early out if no side effects.
1557             if (!doFlavouredEffects)
1558                 return hurted;
1559
1560             if (original > hurted)
1561                 simple_monster_message(mons, " resists.");
1562             else if (original < hurted)
1563                 simple_monster_message(mons, " is drained terribly!");
1564
1565             if (mons->observable())
1566                 pbolt.obvious_effect = true;
1567
1568             mons->drain_exp(pbolt.agent());
1569
1570             if (YOU_KILL(pbolt.thrower))
1571                 did_god_conduct(DID_NECROMANCY, 2, pbolt.god_cares());
1572         }
1573         break;
1574
1575     case BEAM_MIASMA:
1576         if (mons->res_rotting())
1577         {
1578             if (doFlavouredEffects)
1579                 simple_monster_message(mons, " completely resists.");
1580
1581             hurted = 0;
1582         }
1583         else
1584         {
1585             // Early out for tracer/no side effects.
1586             if (!doFlavouredEffects)
1587                 return hurted;
1588
1589             miasma_monster(mons, pbolt.agent());
1590
1591             if (YOU_KILL(pbolt.thrower))
1592                 did_god_conduct(DID_UNCLEAN, 2, pbolt.god_cares());
1593         }
1594         break;
1595
1596     case BEAM_HOLY:
1597     {
1598         // Cleansing flame.
1599         const int rhe = mons->res_holy_energy(pbolt.agent());
1600         if (rhe > 0)
1601             hurted = 0;
1602         else if (rhe == 0)
1603             hurted /= 2;
1604         else if (rhe < -1)
1605             hurted = hurted * 3 / 2;
1606
1607         if (doFlavouredEffects)
1608         {
1609             simple_monster_message(mons,
1610                                    hurted == 0 ? " appears unharmed."
1611                                                : " writhes in agony!");
1612         }
1613         break;
1614     }
1615
1616     case BEAM_ICE:
1617         // ice - 40% of damage is cold, other 60% is impact and
1618         // can't be resisted (except by AC, of course)
1619         hurted = resist_adjust_damage(mons, pbolt.flavour, hurted);
1620         if (hurted < original)
1621         {
1622             if (doFlavouredEffects)
1623                 simple_monster_message(mons, " partially resists.");
1624         }
1625         else if (hurted > original)
1626         {
1627             if (doFlavouredEffects)
1628                 simple_monster_message(mons, " is frozen!");
1629         }
1630         break;
1631
1632     case BEAM_LAVA:
1633         hurted = resist_adjust_damage(mons, pbolt.flavour, hurted);
1634
1635         if (hurted < original)
1636         {
1637             if (doFlavouredEffects)
1638                 simple_monster_message(mons, " partially resists.");
1639         }
1640         else if (hurted > original)
1641         {
1642             if (mons->is_icy())
1643             {
1644                 if (doFlavouredEffects)
1645                     simple_monster_message(mons, " melts!");
1646             }
1647             else
1648             {
1649                 if (doFlavouredEffects)
1650                     simple_monster_message(mons, " is burned terribly!");
1651             }
1652         }
1653         break;
1654
1655     case BEAM_HELLFIRE:
1656         if (mons->res_hellfire())
1657         {
1658             if (doFlavouredEffects)
1659             {
1660                 simple_monster_message(mons,
1661                                        hurted ? " completely resists."
1662                                               : " appears unharmed.");
1663             }
1664
1665             hurted = 0;
1666         }
1667         break;
1668
1669     case BEAM_SPORE:
1670         if (mons->type == MONS_BALLISTOMYCETE)
1671             hurted = 0;
1672         break;
1673
1674     case BEAM_AIR:
1675         if (mons->res_wind())
1676             hurted = 0;
1677         else if (mons->airborne())
1678             hurted += hurted / 2;
1679         if (!hurted)
1680         {
1681             if (doFlavouredEffects)
1682                 simple_monster_message(mons, " is harmlessly tossed around.");
1683         }
1684         else if (original < hurted)
1685         {
1686             if (doFlavouredEffects)
1687                 simple_monster_message(mons, " gets badly buffeted.");
1688         }
1689         break;
1690
1691     case BEAM_ENSNARE:
1692         if (doFlavouredEffects)
1693             ensnare(mons);
1694         break;
1695
1696     case BEAM_GHOSTLY_FLAME:
1697         if (mons->holiness() == MH_UNDEAD)
1698             hurted = 0;
1699         else
1700         {
1701             hurted = resist_adjust_damage(mons, pbolt.flavour, hurted);
1702
1703             if (!doFlavouredEffects)
1704                 break;
1705
1706             if (!hurted)
1707             {
1708                 simple_monster_message(mons,
1709                                        (original > 0) ? " completely resists."
1710                                                       : " appears unharmed.");
1711             }
1712             else if (hurted < original)
1713                 simple_monster_message(mons, " partially resists.");
1714             else if (hurted > original)
1715                 simple_monster_message(mons, " is drained terribly!");
1716         }
1717         break;
1718
1719     default:
1720         break;
1721     }
1722
1723     if (pbolt.affects_items && doFlavouredEffects)
1724     {
1725         const int burn_power = (pbolt.is_explosion) ? 5 :
1726                                (pbolt.pierce)       ? 3
1727                                                     : 2;
1728         mons->expose_to_element(pbolt.flavour, burn_power, false);
1729     }
1730
1731     return hurted;
1732 }
1733
1734 static bool _monster_resists_mass_enchantment(monster* mons,
1735                                               enchant_type wh_enchant,
1736                                               int pow,
1737                                               bool* did_msg)
1738 {
1739     // Assuming that the only mass charm is control undead.
1740     if (wh_enchant == ENCH_CHARM)
1741     {
1742         if (player_mutation_level(MUT_NO_LOVE))
1743             return true;
1744
1745         if (mons->friendly())
1746             return true;
1747
1748         if (mons->holiness() != MH_UNDEAD)
1749             return true;
1750
1751         int res_margin = mons->check_res_magic(pow);
1752         if (res_margin > 0)
1753         {
1754             if (simple_monster_message(mons,
1755                     mons->resist_margin_phrase(res_margin).c_str()))
1756             {
1757                 *did_msg = true;
1758             }
1759             return true;
1760         }
1761     }
1762     else if (wh_enchant == ENCH_CONFUSION
1763              && mons->check_clarity(false))
1764     {
1765         *did_msg = true;
1766         return true;
1767     }
1768     else if (wh_enchant == ENCH_CONFUSION
1769              || wh_enchant == ENCH_INSANE
1770              || mons->holiness() == MH_NATURAL)
1771     {
1772         if (wh_enchant == ENCH_FEAR
1773             && mons->friendly())
1774         {
1775             return true;
1776         }
1777
1778         if (wh_enchant == ENCH_INSANE
1779             && !mons->can_go_frenzy())
1780         {
1781             return true;
1782         }
1783
1784         int res_margin = mons->check_res_magic(pow);
1785         if (res_margin > 0)
1786         {
1787             if (simple_monster_message(mons,
1788                     mons->resist_margin_phrase(res_margin).c_str()))
1789             {
1790                 *did_msg = true;
1791             }
1792             return true;
1793         }
1794     }
1795     // Mass enchantments around lots of plants/fungi shouldn't cause a flood
1796     // of "is unaffected" messages. --Eino
1797     else if (mons_is_firewood(mons))
1798         return true;
1799     else  // trying to enchant an unnatural creature doesn't work
1800     {
1801         if (simple_monster_message(mons, " is unaffected."))
1802             *did_msg = true;
1803         return true;
1804     }
1805
1806     // If monster was affected, then there was a message.
1807     *did_msg = true;
1808     return false;
1809 }
1810
1811 // Enchants all monsters in player's sight.
1812 // If m_succumbed is non-nullptr, will be set to the number of monsters that
1813 // were enchanted. If m_attempted is not nullptr, will be set to the number of
1814 // monsters that we tried to enchant.
1815 spret_type mass_enchantment(enchant_type wh_enchant, int pow, bool fail)
1816 {
1817     fail_check();
1818     bool did_msg = false;
1819
1820     // Give mass enchantments a power multiplier.
1821     pow *= 3;
1822     pow /= 2;
1823
1824     pow = min(pow, 200);
1825
1826     for (monster_iterator mi; mi; ++mi)
1827     {
1828         if (!you.see_cell_no_trans(mi->pos()))
1829             continue;
1830
1831         if (mi->has_ench(wh_enchant))
1832             continue;
1833
1834         bool resisted = _monster_resists_mass_enchantment(*mi, wh_enchant,
1835                                                           pow, &did_msg);
1836
1837         if (resisted)
1838             continue;
1839
1840         if ((wh_enchant == ENCH_INSANE && mi->go_frenzy(&you))
1841             || (wh_enchant == ENCH_CHARM && mi->has_ench(ENCH_HEXED))
1842             || (wh_enchant != ENCH_INSANE
1843                 && mi->add_ench(mon_enchant(wh_enchant, 0, &you))))
1844         {
1845             // Do messaging.
1846             const char* msg;
1847             switch (wh_enchant)
1848             {
1849             case ENCH_FEAR:      msg = " looks frightened!";      break;
1850             case ENCH_CONFUSION: msg = " looks rather confused."; break;
1851             case ENCH_CHARM:     msg = " submits to your will.";  break;
1852             default:             msg = nullptr;                   break;
1853             }
1854             if (msg && simple_monster_message(*mi, msg))
1855                 did_msg = true;
1856
1857             // Reassert control over hexed undead.
1858             if (wh_enchant == ENCH_CHARM && mi->has_ench(ENCH_HEXED))
1859                 mi->del_ench(ENCH_HEXED);
1860
1861             // Extra check for fear (monster needs to reevaluate behaviour).
1862             if (wh_enchant == ENCH_FEAR)
1863                 behaviour_event(*mi, ME_SCARE, &you);
1864         }
1865     }
1866
1867     if (!did_msg)
1868         canned_msg(MSG_NOTHING_HAPPENS);
1869
1870     if (wh_enchant == ENCH_INSANE)
1871         did_god_conduct(DID_HASTY, 8, true);
1872
1873     return SPRET_SUCCESS;
1874 }
1875
1876 void bolt::apply_bolt_paralysis(monster* mons)
1877 {
1878     if (mons->paralysed() || mons->check_stasis(false))
1879         return;
1880     // asleep monsters can still be paralysed (and will be always woken by
1881     // trying to resist); the message might seem wrong but paralysis is
1882     // always visible.
1883     if (!mons_is_immotile(mons)
1884         && simple_monster_message(mons, " suddenly stops moving!"))
1885     {
1886         mons->stop_constricting_all();
1887         obvious_effect = true;
1888     }
1889
1890     mons->add_ench(mon_enchant(ENCH_PARALYSIS, 0, agent(),
1891                                ench_power * BASELINE_DELAY));
1892 }
1893
1894 // Petrification works in two stages. First the monster is slowed down in
1895 // all of its actions, and when that times out it remains properly petrified
1896 // (no movement or actions). The second part is similar to paralysis,
1897 // except that insubstantial monsters can't be affected and damage is
1898 // drastically reduced.
1899 void bolt::apply_bolt_petrify(monster* mons)
1900 {
1901     if (mons->petrified())
1902         return;
1903
1904     if (mons->petrifying())
1905     {
1906         // If the petrifying is not yet finished, we can force it to happen
1907         // right away by casting again. Otherwise, the spell has no further
1908         // effect.
1909         mons->del_ench(ENCH_PETRIFYING, true, false);
1910         // del_ench() would do it, but let's call it ourselves for proper agent
1911         // blaming and messaging.
1912         if (mons->fully_petrify(agent()))
1913             obvious_effect = true;
1914     }
1915     else if (mons->add_ench(mon_enchant(ENCH_PETRIFYING, 0, agent())))
1916     {
1917         if (!mons_is_immotile(mons)
1918             && simple_monster_message(mons, " is moving more slowly."))
1919         {
1920             obvious_effect = true;
1921         }
1922     }
1923 }
1924
1925 static bool _curare_hits_monster(actor *agent, monster* mons, int levels)
1926 {
1927     if (!mons->alive())
1928         return false;
1929
1930     if (mons->res_poison() > 0)
1931         return false;
1932
1933     poison_monster(mons, agent, levels, false);
1934
1935     int hurted = 0;
1936
1937     if (!mons->is_unbreathing())
1938     {
1939         hurted = roll_dice(levels, 6);
1940
1941         if (hurted)
1942         {
1943             simple_monster_message(mons, " convulses.");
1944             mons->hurt(agent, hurted, BEAM_POISON);
1945         }
1946     }
1947
1948     if (mons->alive())
1949     {
1950         if (!mons->cannot_move())
1951         {
1952             simple_monster_message(mons, mons->has_ench(ENCH_SLOW)
1953                                          ? " seems to be slow for longer."
1954                                          : " seems to slow down.");
1955         }
1956         // FIXME: calculate the slow duration more cleanly
1957         mon_enchant me(ENCH_SLOW, 0, agent);
1958         levels -= 2;
1959         while (levels > 0)
1960         {
1961             mon_enchant me2(ENCH_SLOW, 0, agent);
1962             me.set_duration(mons, &me2);
1963             levels -= 2;
1964         }
1965         mons->add_ench(me);
1966     }
1967
1968     // Deities take notice.
1969     if (agent->is_player())
1970         did_god_conduct(DID_POISON, 5 + random2(3));
1971
1972     return hurted > 0;
1973 }
1974
1975 // Actually poisons a monster (with message).
1976 bool poison_monster(monster* mons, const actor *who, int levels,
1977                     bool force, bool verbose)
1978 {
1979     if (!mons->alive() || levels <= 0)
1980         return false;
1981
1982     const int res = mons->res_poison();
1983     if (res >= 3)
1984         return false;
1985     if (!force && res >= 1 && x_chance_in_y(2, 3))
1986         return false;
1987
1988     const mon_enchant old_pois = mons->get_ench(ENCH_POISON);
1989     mons->add_ench(mon_enchant(ENCH_POISON, levels, who));
1990     const mon_enchant new_pois = mons->get_ench(ENCH_POISON);
1991
1992     // Actually do the poisoning. The order is important here.
1993     if (new_pois.degree > old_pois.degree)
1994     {
1995         if (verbose)
1996         {
1997             simple_monster_message(mons,
1998                                    old_pois.degree > 0 ? " looks even sicker."
1999                                                        : " is poisoned.");
2000         }
2001     }
2002
2003     // Finally, take care of deity preferences.
2004     if (who && who->is_player())
2005         did_god_conduct(DID_POISON, 5 + random2(3));
2006
2007     return new_pois.duration > old_pois.duration;
2008 }
2009
2010 // Actually poisons, rots, and/or slows a monster with miasma (with
2011 // message).
2012 bool miasma_monster(monster* mons, const actor* who)
2013 {
2014     if (!mons->alive())
2015         return false;
2016
2017     if (mons->res_rotting())
2018         return false;
2019
2020     bool success = poison_monster(mons, who);
2021
2022     if (who && who->is_player()
2023         && is_good_god(you.religion)
2024         && !(success && you_worship(GOD_SHINING_ONE))) // already penalized
2025     {
2026         did_god_conduct(DID_NECROMANCY, 5 + random2(3));
2027     }
2028
2029     if (mons->max_hit_points > 4 && coinflip())
2030     {
2031         mons->max_hit_points--;
2032         mons->hit_points = min(mons->max_hit_points, mons->hit_points);
2033         success = true;
2034     }
2035
2036     if (one_chance_in(3))
2037     {
2038         bolt beam;
2039         beam.flavour = BEAM_SLOW;
2040         beam.apply_enchantment_to_monster(mons);
2041         success = true;
2042     }
2043
2044     return success;
2045 }
2046
2047 // Actually napalms a monster (with message).
2048 bool napalm_monster(monster* mons, const actor *who, int levels, bool verbose)
2049 {
2050     if (!mons->alive())
2051         return false;
2052
2053     if (mons->res_sticky_flame() || levels <= 0 || mons->has_ench(ENCH_WATER_HOLD))
2054         return false;
2055
2056     const mon_enchant old_flame = mons->get_ench(ENCH_STICKY_FLAME);
2057     mons->add_ench(mon_enchant(ENCH_STICKY_FLAME, levels, who));
2058     const mon_enchant new_flame = mons->get_ench(ENCH_STICKY_FLAME);
2059
2060     // Actually do the napalming. The order is important here.
2061     if (new_flame.degree > old_flame.degree)
2062     {
2063         if (verbose)
2064             simple_monster_message(mons, " is covered in liquid flames!");
2065         ASSERT(who);
2066         behaviour_event(mons, ME_WHACK, who);
2067     }
2068
2069     return new_flame.degree > old_flame.degree;
2070 }
2071
2072 static bool _curare_hits_player(actor* agent, int levels, string name,
2073                                 string source_name)
2074 {
2075     ASSERT(!crawl_state.game_is_arena());
2076
2077     if (player_res_poison() >= 3
2078         || player_res_poison() > 0 && !one_chance_in(3))
2079     {
2080         return false;
2081     }
2082
2083     poison_player(roll_dice(levels, 12) + 1, source_name, name);
2084
2085     int hurted = 0;
2086
2087     if (!you.is_unbreathing())
2088     {
2089         hurted = roll_dice(levels, 6);
2090
2091         if (hurted)
2092         {
2093             mpr("You have difficulty breathing.");
2094             ouch(hurted, KILLED_BY_CURARE, agent->mid,
2095                  "curare-induced apnoea");
2096         }
2097     }
2098
2099     slow_player(10 + random2(levels + random2(3 * levels)));
2100
2101     return hurted > 0;
2102 }
2103
2104
2105 bool curare_actor(actor* source, actor* target, int levels, string name,
2106                   string source_name)
2107 {
2108     if (target->is_player())
2109         return _curare_hits_player(source, levels, name, source_name);
2110     else
2111         return _curare_hits_monster(source, target->as_monster(), levels);
2112 }
2113
2114 // XXX: This is a terrible place for this, but it at least does go with
2115 // curare_actor().
2116 int silver_damages_victim(actor* victim, int damage, string &dmg_msg)
2117 {
2118     int ret = 0;
2119     if (victim->how_chaotic()
2120         || victim->is_player() && player_is_shapechanged())
2121     {
2122         ret = damage * 3 / 4;
2123     }
2124     else if (victim->is_player())
2125     {
2126         // For mutation damage, we want to count innate mutations for
2127         // demonspawn but not other species.
2128         int multiplier = 5 * how_mutated(you.species == SP_DEMONSPAWN, true);
2129         if (multiplier == 0)
2130             return 0;
2131
2132         if (multiplier > 75)
2133             multiplier = 75;
2134
2135         ret = damage * multiplier / 100;
2136     }
2137     else
2138         return 0;
2139
2140     dmg_msg = "The silver sears " + victim->name(DESC_THE) + "!";
2141     return ret;
2142 }
2143
2144 //  Used by monsters in "planning" which spell to cast. Fires off a "tracer"
2145 //  which tells the monster what it'll hit if it breathes/casts etc.
2146 //
2147 //  The output from this tracer function is written into the
2148 //  tracer_info variables (friend_info and foe_info).
2149 //
2150 //  Note that beam properties must be set, as the tracer will take them
2151 //  into account, as well as the monster's intelligence.
2152 void fire_tracer(const monster* mons, bolt &pbolt, bool explode_only)
2153 {
2154     // Don't fiddle with any input parameters other than tracer stuff!
2155     pbolt.is_tracer     = true;
2156     pbolt.source        = mons->pos();
2157     pbolt.source_id     = mons->mid;
2158     pbolt.smart_monster = (mons_intel(mons) >= I_NORMAL);
2159     pbolt.attitude      = mons_attitude(mons);
2160
2161     // Init tracer variables.
2162     pbolt.foe_info.reset();
2163     pbolt.friend_info.reset();
2164
2165     // Clear misc
2166     pbolt.reflections   = 0;
2167     pbolt.bounces       = 0;
2168
2169     // If there's a specifically requested foe_ratio, honour it.
2170     if (!pbolt.foe_ratio)
2171     {
2172         pbolt.foe_ratio     = 80;        // default - see mons_should_fire()
2173
2174         // Foe ratio for summoning greater demons & undead -- they may be
2175         // summoned, but they're hostile and would love nothing better
2176         // than to nuke the player and his minions.
2177         if (mons_att_wont_attack(pbolt.attitude)
2178             && !mons_att_wont_attack(mons->attitude))
2179         {
2180             pbolt.foe_ratio = 25;
2181         }
2182     }
2183
2184     pbolt.in_explosion_phase = false;
2185
2186     // Fire!
2187     if (explode_only)
2188         pbolt.explode(false);
2189     else
2190         pbolt.fire();
2191
2192     // Unset tracer flag (convenience).
2193     pbolt.is_tracer = false;
2194 }
2195
2196 static coord_def _random_point_hittable_from(const coord_def &c,
2197                                             int radius,
2198                                             int margin = 1,
2199                                             int tries = 5)
2200 {
2201     while (tries-- > 0)
2202     {
2203         const coord_def point = dgn_random_point_from(c, radius, margin);
2204         if (point.origin())
2205             continue;
2206         if (!cell_see_cell(c, point, LOS_SOLID))
2207             continue;
2208         return point;
2209     }
2210     return coord_def();
2211 }
2212
2213 void create_feat_splash(coord_def center,
2214                                 int radius,
2215                                 int nattempts)
2216 {
2217     // Always affect center, if compatible
2218     if ((grd(center) == DNGN_FLOOR || grd(center) == DNGN_SHALLOW_WATER))
2219     {
2220         temp_change_terrain(center, DNGN_SHALLOW_WATER, 100 + random2(100),
2221                             TERRAIN_CHANGE_FLOOD);
2222     }
2223
2224     for (int i = 0; i < nattempts; ++i)
2225     {
2226         const coord_def newp(_random_point_hittable_from(center, radius));
2227         if (newp.origin() || (grd(newp) != DNGN_FLOOR && grd(newp) != DNGN_SHALLOW_WATER))
2228             continue;
2229         temp_change_terrain(newp, DNGN_SHALLOW_WATER, 100 + random2(100),
2230                             TERRAIN_CHANGE_FLOOD);
2231     }
2232 }
2233
2234 bool imb_can_splash(coord_def origin, coord_def center,
2235                     vector<coord_def> path_taken, coord_def target)
2236 {
2237     // Don't go back along the path of the beam (the explosion doesn't
2238     // reverse direction). We do this to avoid hitting the caster and
2239     // also because we don't want aiming one
2240     // square past a lone monster to be optimal.
2241     if (origin == target)
2242         return false;
2243     if (find(begin(path_taken), end(path_taken), target) != end(path_taken))
2244         return false;
2245
2246     // Don't go far away from the caster (not enough momentum).
2247     if (distance2(origin, center + (target - center)*2)
2248         > sqr(you.current_vision) + 1)
2249     {
2250         return false;
2251     }
2252
2253     return true;
2254 }
2255
2256 void bolt_parent_init(const bolt &parent, bolt &child)
2257 {
2258     child.name           = parent.name;
2259     child.short_name     = parent.short_name;
2260     child.aux_source     = parent.aux_source;
2261     child.source_id      = parent.source_id;
2262     child.origin_spell   = parent.origin_spell;
2263     child.glyph          = parent.glyph;
2264     child.colour         = parent.colour;
2265
2266     child.flavour        = parent.flavour;
2267     child.origin_spell   = parent.origin_spell;
2268
2269     // We don't copy target since that is often overriden.
2270     child.thrower        = parent.thrower;
2271     child.source         = parent.source;
2272     child.source_name    = parent.source_name;
2273     child.attitude       = parent.attitude;
2274
2275     child.pierce         = parent.pierce ;
2276     child.is_explosion   = parent.is_explosion;
2277     child.ex_size        = parent.ex_size;
2278     child.foe_ratio      = parent.foe_ratio;
2279
2280     child.is_tracer      = parent.is_tracer;
2281     child.is_targeting   = parent.is_targeting;
2282
2283     child.range          = parent.range;
2284     child.hit            = parent.hit;
2285     child.damage         = parent.damage;
2286     if (parent.ench_power != -1)
2287         child.ench_power = parent.ench_power;
2288
2289     child.friend_info.dont_stop = parent.friend_info.dont_stop;
2290     child.foe_info.dont_stop    = parent.foe_info.dont_stop;
2291     child.dont_stop_player      = parent.dont_stop_player;
2292
2293 #ifdef DEBUG_DIAGNOSTICS
2294     child.quiet_debug    = parent.quiet_debug;
2295 #endif
2296 }
2297
2298 static void _maybe_imb_explosion(bolt *parent, coord_def center)
2299 {
2300     if (parent->origin_spell != SPELL_ISKENDERUNS_MYSTIC_BLAST
2301         || parent->in_explosion_phase)
2302     {
2303         return;
2304     }
2305     const int dist = grid_distance(parent->source, center);
2306     if (dist == 0 || (!parent->is_tracer && !x_chance_in_y(3, 2 + 2 * dist)))
2307         return;
2308     bolt beam;
2309
2310     bolt_parent_init(*parent, beam);
2311     beam.name           = "mystic blast";
2312     beam.aux_source     = "orb of energy";
2313     beam.range          = 3;
2314     beam.hit            = AUTOMATIC_HIT;
2315     beam.colour         = MAGENTA;
2316     beam.obvious_effect = true;
2317     beam.pierce         = false;
2318     beam.is_explosion   = false;
2319     // So as not to recur infinitely
2320     beam.origin_spell = SPELL_NO_SPELL;
2321     beam.passed_target  = true; // The centre was the target.
2322     beam.aimed_at_spot  = true;
2323     if (you.see_cell(center))
2324         beam.seen = true;
2325     beam.source         = center;
2326
2327     bool first = true;
2328     for (adjacent_iterator ai(center); ai; ++ai)
2329     {
2330         if (!imb_can_splash(parent->source, center, parent->path_taken, *ai))
2331             continue;
2332         if (beam.is_tracer || x_chance_in_y(3, 4))
2333         {
2334             if (first && !beam.is_tracer)
2335             {
2336                 if (you.see_cell(center))
2337                     mpr("The orb of energy explodes!");
2338                 noisy(spell_effect_noise(SPELL_ISKENDERUNS_MYSTIC_BLAST),
2339                       center);
2340                 first = false;
2341             }
2342             beam.friend_info.reset();
2343             beam.foe_info.reset();
2344             beam.friend_info.dont_stop = parent->friend_info.dont_stop;
2345             beam.foe_info.dont_stop = parent->foe_info.dont_stop;
2346             beam.target = center + (*ai - center) * 2;
2347             beam.fire();
2348             parent->friend_info += beam.friend_info;
2349             parent->foe_info    += beam.foe_info;
2350             if (beam.is_tracer)
2351             {
2352                 if (beam.beam_cancelled)
2353                 {
2354                     parent->beam_cancelled = true;
2355                     return;
2356                 }
2357             }
2358         }
2359     }
2360 }
2361
2362 static void _malign_offering_effect(actor* victim, const actor* agent, int damage)
2363 {
2364     if (!agent || damage < 1)
2365         return;
2366
2367     // The victim may die.
2368     coord_def c = victim->pos();
2369
2370     mprf("%s life force is offered up.", victim->name(DESC_ITS).c_str());
2371     damage = victim->hurt(agent, damage, BEAM_NEG, KILLED_BY_BEAM,
2372                           "", "by a malign offering");
2373
2374     // Actors that had LOS to the victim (blocked by glass, clouds, etc),
2375     // even if they couldn't actually see each other because of blindness
2376     // or invisibility.
2377     for (actor_near_iterator ai(c, LOS_NO_TRANS); ai; ++ai)
2378     {
2379         if (mons_aligned(agent, *ai) && ai->holiness() != MH_NONLIVING)
2380         {
2381             if (ai->heal(max(1, damage * 2 / 3)) && you.can_see(*ai))
2382             {
2383                 mprf("%s %s healed.", ai->name(DESC_THE).c_str(),
2384                                       ai->conj_verb("are").c_str());
2385             }
2386         }
2387     }
2388 }
2389
2390 static void _explosive_bolt_explode(bolt *parent, coord_def pos)
2391 {
2392     bolt beam;
2393
2394     bolt_parent_init(*parent, beam);
2395     beam.name         = "fiery explosion";
2396     beam.aux_source   = "explosive bolt";
2397     beam.damage       = dice_def(3, 9 + parent->ench_power / 6);
2398     beam.colour       = RED;
2399     beam.flavour      = BEAM_FIRE;
2400     beam.is_explosion = true;
2401     beam.source       = pos;
2402     beam.target       = pos;
2403     beam.origin_spell = SPELL_EXPLOSIVE_BOLT;
2404     beam.refine_for_explosion();
2405     beam.explode();
2406     parent->friend_info += beam.friend_info;
2407     parent->foe_info    += beam.foe_info;
2408     if (beam.is_tracer && beam.beam_cancelled)
2409         parent->beam_cancelled = true;
2410 }
2411
2412 bool bolt::is_bouncy(dungeon_feature_type feat) const
2413 {
2414     if ((real_flavour == BEAM_CHAOS
2415          || real_flavour == BEAM_CHAOTIC_REFLECTION)
2416          && feat_is_solid(feat))
2417     {
2418         return true;
2419     }
2420
2421     if ((flavour == BEAM_CRYSTAL || real_flavour == BEAM_CRYSTAL
2422          || flavour == BEAM_BOUNCY_TRACER)
2423         && feat_is_solid(feat)
2424         && !feat_is_tree(feat))
2425     {
2426         return true;
2427     }
2428
2429     if (is_enchantment())
2430         return false;
2431
2432     if (flavour == BEAM_ELECTRICITY && !feat_is_metal(feat)
2433         && !feat_is_tree(feat))
2434     {
2435         return true;
2436     }
2437
2438     if ((flavour == BEAM_FIRE || flavour == BEAM_COLD)
2439         && feat == DNGN_CRYSTAL_WALL)
2440     {
2441         return true;
2442     }
2443
2444     return false;
2445 }
2446
2447 cloud_type bolt::get_cloud_type() const
2448 {
2449     if (origin_spell == SPELL_NOXIOUS_CLOUD)
2450         return CLOUD_MEPHITIC;
2451
2452     if (origin_spell == SPELL_POISONOUS_CLOUD)
2453         return CLOUD_POISON;
2454
2455     if (origin_spell == SPELL_HOLY_BREATH)
2456         return CLOUD_HOLY_FLAMES;
2457
2458     if (origin_spell == SPELL_FLAMING_CLOUD)
2459         return CLOUD_FIRE;
2460
2461     if (origin_spell == SPELL_CHAOS_BREATH)
2462         return CLOUD_CHAOS;
2463
2464     if (origin_spell == SPELL_MIASMA_BREATH)
2465         return CLOUD_MIASMA;
2466
2467     if (origin_spell == SPELL_FREEZING_CLOUD)
2468         return CLOUD_COLD;
2469
2470     if (origin_spell == SPELL_GHOSTLY_FLAMES)
2471         return CLOUD_GHOSTLY_FLAME;
2472
2473     return CLOUD_NONE;
2474 }
2475
2476 int bolt::get_cloud_pow() const
2477 {
2478     if (origin_spell == SPELL_FREEZING_CLOUD
2479         || origin_spell == SPELL_POISONOUS_CLOUD)
2480     {
2481         return random_range(10, 15);
2482     }
2483
2484     if (origin_spell == SPELL_GHOSTLY_FLAMES)
2485         return random_range(12, 20);
2486
2487     return 0;
2488 }
2489
2490 int bolt::get_cloud_size(bool min, bool max) const
2491 {
2492     if (origin_spell == SPELL_MEPHITIC_CLOUD
2493         || origin_spell == SPELL_MIASMA_BREATH
2494         || origin_spell == SPELL_FREEZING_CLOUD)
2495     {
2496         return 10;
2497     }
2498
2499     if (min)
2500         return 8;
2501     if (max)
2502         return 12;
2503
2504     return 8 + random2(5);
2505 }
2506
2507 void bolt::affect_endpoint()
2508 {
2509     if (special_explosion)
2510     {
2511         special_explosion->target = pos();
2512         special_explosion->refine_for_explosion();
2513         special_explosion->explode();
2514
2515         // XXX: we're significantly overcounting here.
2516         foe_info      += special_explosion->foe_info;
2517         friend_info   += special_explosion->friend_info;
2518         beam_cancelled = beam_cancelled || special_explosion->beam_cancelled;
2519     }
2520
2521     // Leave an object, if applicable.
2522     if (drop_item && item)
2523         drop_object();
2524
2525     if (is_explosion)
2526     {
2527         target = pos();
2528         refine_for_explosion();
2529         explode();
2530         return;
2531     }
2532
2533     cloud_type cloud = get_cloud_type();
2534
2535     if (is_tracer)
2536     {
2537         _maybe_imb_explosion(this, pos());
2538         if (cloud != CLOUD_NONE)
2539         {
2540             targetter_cloud tgt(agent(), range, get_cloud_size(true),
2541                                                 get_cloud_size(false, true));
2542             tgt.set_aim(pos());
2543             for (const auto &entry : tgt.seen)
2544             {
2545                 if (entry.second != AFF_YES && entry.second != AFF_MAYBE)
2546                     continue;
2547
2548                 if (entry.first == you.pos())
2549                     tracer_affect_player();
2550                 else if (monster* mon = monster_at(entry.first))
2551                     tracer_affect_monster(mon);
2552
2553                 if (agent()->is_player() && beam_cancelled)
2554                     return;
2555             }
2556         }
2557         return;
2558     }
2559
2560     if (!is_explosion && !noise_generated && loudness)
2561     {
2562         // Digging can target squares on the map boundary, though it
2563         // won't remove them of course.
2564         const coord_def noise_position = clamp_in_bounds(pos());
2565         noisy(loudness, noise_position, source_id);
2566         noise_generated = true;
2567     }
2568
2569     if (origin_spell == SPELL_PRIMAL_WAVE) // &&coinflip()
2570     {
2571         if (you.see_cell(pos()))
2572         {
2573             mpr("The wave splashes down.");
2574             noisy(spell_effect_noise(SPELL_PRIMAL_WAVE), pos());
2575         }
2576         else
2577             noisy(spell_effect_noise(SPELL_PRIMAL_WAVE),
2578                   pos(), "You hear a splash.");
2579         create_feat_splash(pos(), 2, random_range(3, 12, 2));
2580     }
2581
2582     if (origin_spell == SPELL_BLINKBOLT)
2583     {
2584         if (agent() && agent()->alive())
2585         {
2586             for (vector<coord_def>::reverse_iterator citr = path_taken.rbegin();
2587                  citr != path_taken.rend(); ++citr)
2588             {
2589                 if (agent()->is_habitable(*citr) &&
2590                     agent()->blink_to(*citr, false))
2591                 {
2592                     return;
2593                 }
2594             }
2595         }
2596         return;
2597     }
2598
2599     // FIXME: why doesn't this just have is_explosion set?
2600     if (origin_spell == SPELL_ORB_OF_ELECTRICITY)
2601     {
2602         target = pos();
2603         refine_for_explosion();
2604         explode();
2605     }
2606
2607     if (cloud != CLOUD_NONE)
2608         big_cloud(cloud, agent(), pos(), get_cloud_pow(), get_cloud_size());
2609
2610     if (origin_spell == SPELL_SEARING_BREATH)
2611         place_cloud(CLOUD_FIRE, pos(), 5 + random2(5), agent());
2612
2613     _maybe_imb_explosion(this, pos());
2614 }
2615
2616 bool bolt::stop_at_target() const
2617 {
2618     return is_explosion || is_big_cloud() || aimed_at_spot;
2619 }
2620
2621 void bolt::drop_object()
2622 {
2623     ASSERT(item != nullptr);
2624     ASSERT(item->defined());
2625
2626     // Conditions: beam is missile and not tracer.
2627     if (is_tracer || !was_missile)
2628         return;
2629
2630     // Summoned creatures' thrown items disappear.
2631     if (item->flags & ISFLAG_SUMMONED)
2632     {
2633         if (you.see_cell(pos()))
2634         {
2635             mprf("%s %s!",
2636                  item->name(DESC_THE).c_str(),
2637                  summoned_poof_msg(agent() ? agent()->as_monster() : nullptr,
2638                                    *item).c_str());
2639         }
2640         item_was_destroyed(*item);
2641         return;
2642     }
2643
2644     if (!thrown_object_destroyed(item, pos()))
2645     {
2646         if (item->sub_type == MI_THROWING_NET)
2647         {
2648             monster* m = monster_at(pos());
2649             // Player or monster at position is caught in net.
2650             if (you.pos() == pos() && you.attribute[ATTR_HELD]
2651                 || m && m->caught())
2652             {
2653                 // If no trapping net found mark this one.
2654                 if (get_trapping_net(pos(), true) == NON_ITEM)
2655                     set_net_stationary(*item);
2656             }
2657         }
2658
2659         copy_item_to_grid(*item, pos(), 1);
2660     }
2661     else
2662         item_was_destroyed(*item);
2663 }
2664
2665 // Returns true if the beam hits the player, fuzzing the beam if necessary
2666 // for monsters without see invis firing tracers at the player.
2667 bool bolt::found_player() const
2668 {
2669     const bool needs_fuzz = (is_tracer && !can_see_invis
2670                              && you.invisible() && !YOU_KILL(thrower));
2671     const int dist = needs_fuzz? 2 : 0;
2672
2673     return grid_distance(pos(), you.pos()) <= dist;
2674 }
2675
2676 void bolt::affect_ground()
2677 {
2678     // Explosions only have an effect during their explosion phase.
2679     // Special cases can be handled here.
2680     if (is_explosion && !in_explosion_phase)
2681         return;
2682
2683     if (is_tracer)
2684         return;
2685
2686     // Spore explosions might spawn a fungus. The spore explosion
2687     // covers 21 tiles in open space, so the expected number of spores
2688     // produced is the x in x_chance_in_y() in the conditional below.
2689     if (is_explosion && flavour == BEAM_SPORE
2690         && agent() && !agent()->is_summoned())
2691     {
2692         if (env.grid(pos()) == DNGN_FLOOR)
2693             env.pgrid(pos()) |= FPROP_MOLD;
2694
2695         if (x_chance_in_y(2, 21)
2696            && !crawl_state.game_is_zotdef() // Turn off in Zotdef
2697            && mons_class_can_pass(MONS_BALLISTOMYCETE, env.grid(pos()))
2698            && !actor_at(pos()))
2699         {
2700             beh_type beh = attitude_creation_behavior(attitude);
2701
2702             const god_type god = agent() ? agent()->deity() : GOD_NO_GOD;
2703
2704             if (create_monster(mgen_data(MONS_BALLISTOMYCETE,
2705                                          beh,
2706                                          nullptr,
2707                                          0,
2708                                          0,
2709                                          pos(),
2710                                          MHITNOT,
2711                                          MG_FORCE_PLACE,
2712                                          god)))
2713             {
2714                 remove_mold(pos());
2715                 if (you.see_cell(pos()))
2716                     mpr("A fungus suddenly grows.");
2717
2718             }
2719         }
2720     }
2721
2722     affect_place_clouds();
2723 }
2724
2725 bool bolt::is_fiery() const
2726 {
2727     return flavour == BEAM_FIRE
2728            || flavour == BEAM_HELLFIRE
2729            || flavour == BEAM_LAVA;
2730 }
2731
2732 bool bolt::is_superhot() const
2733 {
2734     if (!is_fiery() && flavour != BEAM_ELECTRICITY)
2735         return false;
2736
2737     return origin_spell == SPELL_BOLT_OF_FIRE
2738            || origin_spell == SPELL_BOLT_OF_MAGMA
2739            || origin_spell == SPELL_FIREBALL
2740            || origin_spell == SPELL_LIGHTNING_BOLT
2741            || flavour == BEAM_HELLFIRE
2742               && in_explosion_phase;
2743 }
2744
2745 bool bolt::can_affect_wall(dungeon_feature_type wall) const
2746 {
2747     // digging
2748     if (flavour == BEAM_DIGGING
2749         && (wall == DNGN_ROCK_WALL || wall == DNGN_CLEAR_ROCK_WALL
2750             || wall == DNGN_SLIMY_WALL || wall == DNGN_GRATE))
2751     {
2752         return true;
2753     }
2754
2755     if (is_fiery() && feat_is_tree(wall))
2756         return is_superhot();
2757
2758     if (flavour == BEAM_ELECTRICITY && feat_is_tree(wall))
2759         return is_superhot();
2760
2761     if (flavour == BEAM_DISINTEGRATION && damage.num >= 3
2762         || flavour == BEAM_DEVASTATION)
2763     {
2764         return wall == DNGN_ROCK_WALL
2765                || wall == DNGN_SLIMY_WALL
2766                || wall == DNGN_CLEAR_ROCK_WALL
2767                || wall == DNGN_GRATE
2768                || wall == DNGN_CLOSED_DOOR
2769                || wall == DNGN_RUNED_DOOR
2770                || feat_is_statuelike(wall)
2771                || feat_is_tree(wall);
2772     }
2773
2774     // Lee's Rapid Deconstruction
2775     if (flavour == BEAM_FRAG)
2776         return true; // smite targeting, we don't care
2777
2778     return false;
2779 }
2780
2781 void bolt::affect_place_clouds()
2782 {
2783     if (in_explosion_phase)
2784         affect_place_explosion_clouds();
2785
2786     const coord_def p = pos();
2787
2788     // Is there already a cloud here?
2789     const int cloudidx = env.cgrid(p);
2790     if (cloudidx != EMPTY_CLOUD)
2791     {
2792         cloud_type& ctype = env.cloud[cloudidx].type;
2793
2794         // fire cancelling cold & vice versa
2795         if ((ctype == CLOUD_COLD
2796              && (flavour == BEAM_FIRE || flavour == BEAM_LAVA))
2797             || (ctype == CLOUD_FIRE && flavour == BEAM_COLD))
2798         {
2799             if (player_can_hear(p))
2800                 mprf(MSGCH_SOUND, "You hear a sizzling sound!");
2801
2802             delete_cloud(cloudidx);
2803             extra_range_used += 5;
2804         }
2805         return;
2806     }
2807
2808     // No clouds here, free to make new ones.
2809     const dungeon_feature_type feat = grd(p);
2810
2811     if (origin_spell == SPELL_POISONOUS_CLOUD)
2812         place_cloud(CLOUD_POISON, p, random2(5) + 3, agent());
2813
2814     if (origin_spell == SPELL_HOLY_BREATH)
2815         place_cloud(CLOUD_HOLY_FLAMES, p, random2(4) + 2, agent());
2816
2817     if (origin_spell == SPELL_FLAMING_CLOUD)
2818         place_cloud(CLOUD_FIRE, p, random2(4) + 2, agent());
2819
2820     // Fire/cold over water/lava
2821     if (feat == DNGN_LAVA && flavour == BEAM_COLD
2822         || feat_is_watery(feat) && is_fiery())
2823     {
2824         place_cloud(CLOUD_STEAM, p, 2 + random2(5), agent());
2825     }
2826
2827     if (feat_is_watery(feat) && flavour == BEAM_COLD
2828         && damage.num * damage.size > 35)
2829     {
2830         place_cloud(CLOUD_COLD, p, damage.num * damage.size / 30 + 1, agent());
2831     }
2832
2833     if (flavour == BEAM_MIASMA)
2834         place_cloud(CLOUD_MIASMA, p, random2(5) + 2, agent());
2835
2836     //XXX: these use the name for a gameplay effect.
2837     if (name == "ball of steam")
2838         place_cloud(CLOUD_STEAM, p, random2(5) + 2, agent());
2839
2840     if (name == "poison gas")
2841         place_cloud(CLOUD_POISON, p, random2(4) + 3, agent());
2842
2843     if (name == "blast of choking fumes")
2844         place_cloud(CLOUD_MEPHITIC, p, random2(4) + 3, agent());
2845
2846     if (name == "trail of fire")
2847         place_cloud(CLOUD_FIRE, p, random2(ench_power) + ench_power, agent());
2848
2849     if (origin_spell == SPELL_PETRIFYING_CLOUD)
2850         place_cloud(CLOUD_PETRIFY, p, random2(4) + 4, agent());
2851
2852     if (origin_spell == SPELL_GHOSTLY_FLAMES)
2853         place_cloud(CLOUD_GHOSTLY_FLAME, p, random2(6) + 5, agent());
2854
2855     if (origin_spell == SPELL_DEATH_RATTLE)
2856         place_cloud(CLOUD_NEGATIVE_ENERGY, p, random2(4) + 4, agent());
2857 }
2858
2859 void bolt::affect_place_explosion_clouds()
2860 {
2861     const coord_def p = pos();
2862
2863     // First check: fire/cold over water/lava.
2864     if (grd(p) == DNGN_LAVA && flavour == BEAM_COLD
2865         || feat_is_watery(grd(p)) && is_fiery())
2866     {
2867         place_cloud(CLOUD_STEAM, p, 2 + random2(5), agent());
2868         return;
2869     }
2870
2871     if (flavour == BEAM_MEPHITIC)
2872     {
2873         const coord_def center = (aimed_at_feet ? source : ray.pos());
2874         if (p == center || x_chance_in_y(125 + ench_power, 225))
2875         {
2876             place_cloud(CLOUD_MEPHITIC, p, roll_dice(2, 3 + ench_power / 20),
2877                         agent());
2878         }
2879     }
2880
2881     if (origin_spell == SPELL_FIRE_STORM)
2882     {
2883         place_cloud(CLOUD_FIRE, p, 2 + random2avg(5,2), agent());
2884
2885         if (grd(p) == DNGN_FLOOR && !monster_at(p) && one_chance_in(4))
2886         {
2887             const god_type god =
2888                 (crawl_state.is_god_acting()) ? crawl_state.which_god_acting()
2889                                               : GOD_NO_GOD;
2890             const beh_type att =
2891                 (whose_kill() == KC_OTHER ? BEH_HOSTILE : BEH_FRIENDLY);
2892
2893             actor* summ = agent();
2894             mgen_data mg(MONS_FIRE_VORTEX, att, summ, 1, SPELL_FIRE_STORM,
2895                          p, MHITNOT, 0, god);
2896
2897             // Spell-summoned monsters need to have a live summoner.
2898             if (summ == nullptr || !summ->alive())
2899             {
2900                 if (!source_name.empty())
2901                     mg.non_actor_summoner = source_name;
2902                 else if (god != GOD_NO_GOD)
2903                     mg.non_actor_summoner = god_name(god);
2904             }
2905
2906             mons_place(mg);
2907         }
2908     }
2909 }
2910
2911 // A little helper function to handle the calling of ouch()...
2912 void bolt::internal_ouch(int dam)
2913 {
2914     monster* monst = nullptr;
2915     monst = monster_by_mid(source_id);
2916
2917     const char *what = aux_source.empty() ? name.c_str() : aux_source.c_str();
2918
2919     if (YOU_KILL(thrower) && you.duration[DUR_QUAD_DAMAGE])
2920         dam *= 4;
2921
2922     // The order of this is important.
2923     if (monst && monst->type == MONS_PLAYER_SHADOW
2924         && !monst->mname.empty())
2925     {
2926         ouch(dam, KILLED_BY_DIVINE_WRATH, MID_NOBODY,
2927              aux_source.empty() ? nullptr : aux_source.c_str(), true,
2928              source_name.empty() ? nullptr : source_name.c_str());
2929     }
2930     else if (monst && (monst->type == MONS_GIANT_SPORE
2931                        || monst->type == MONS_BALL_LIGHTNING
2932                        || monst->type == MONS_HYPERACTIVE_BALLISTOMYCETE
2933                        || monst->type == MONS_FULMINANT_PRISM
2934                        || monst->type == MONS_BENNU // death flames
2935                        ))
2936     {
2937         ouch(dam, KILLED_BY_SPORE, source_id,
2938              aux_source.c_str(), true,
2939              source_name.empty() ? nullptr : source_name.c_str());
2940     }
2941     else if (flavour == BEAM_DISINTEGRATION || flavour == BEAM_DEVASTATION)
2942     {
2943         ouch(dam, KILLED_BY_DISINT, source_id, what, true,
2944              source_name.empty() ? nullptr : source_name.c_str());
2945     }
2946     else if (YOU_KILL(thrower) && aux_source.empty())
2947     {
2948         if (reflections > 0)
2949             ouch(dam, KILLED_BY_REFLECTION, reflector, name.c_str());
2950         else if (bounces > 0)
2951             ouch(dam, KILLED_BY_BOUNCE, MID_PLAYER, name.c_str());
2952         else
2953         {
2954             if (aimed_at_feet && effect_known)
2955                 ouch(dam, KILLED_BY_SELF_AIMED, MID_PLAYER, name.c_str());
2956             else
2957                 ouch(dam, KILLED_BY_TARGETING, MID_PLAYER, name.c_str());
2958         }
2959     }
2960     else if (MON_KILL(thrower) || aux_source == "exploding inner flame")
2961         ouch(dam, KILLED_BY_BEAM, source_id,
2962              aux_source.c_str(), true,
2963              source_name.empty() ? nullptr : source_name.c_str());
2964     else // KILL_MISC || (YOU_KILL && aux_source)
2965         ouch(dam, KILLED_BY_WILD_MAGIC, source_id, aux_source.c_str());
2966 }
2967
2968 // [ds] Apply a fuzz if the monster lacks see invisible and is trying to target
2969 // an invisible player. This makes invisibility slightly more powerful.
2970 bool bolt::fuzz_invis_tracer()
2971 {
2972     // Did the monster have a rough idea of where you are?
2973     int dist = grid_distance(target, you.pos());
2974
2975     // No, ditch this.
2976     if (dist > 2)
2977         return false;
2978
2979     // Apply fuzz now.
2980     coord_def fuzz(random_range(-2, 2), random_range(-2, 2));
2981     coord_def newtarget = target + fuzz;
2982
2983     if (in_bounds(newtarget))
2984         target = newtarget;
2985
2986     // Fire away!
2987     return true;
2988 }
2989
2990 // A first step towards to-hit sanity for beams. We're still being
2991 // very kind to the player, but it should be fairer to monsters than
2992 // 4.0.
2993 static bool _test_beam_hit(int attack, int defence, bool pierce,
2994                            int defl, defer_rand &r)
2995 {
2996     if (attack == AUTOMATIC_HIT)
2997         return true;
2998
2999     if (pierce)
3000     {
3001         if (defl > 1)
3002             attack = r[0].random2(attack * 2) / 3;
3003         else if (defl && attack >= 2) // don't increase acc of 0
3004             attack = r[0].random_range((attack + 1) / 2 + 1, attack);
3005     }
3006     else if (defl)
3007         attack = r[0].random2(attack / defl);
3008
3009     dprf(DIAG_BEAM, "Beam attack: %d, defence: %d", attack, defence);
3010
3011     attack = r[1].random2(attack);
3012     defence = r[2].random2avg(defence, 2);
3013
3014     dprf(DIAG_BEAM, "Beam new attack: %d, defence: %d", attack, defence);
3015
3016     return attack >= defence;
3017 }
3018
3019 bool bolt::is_harmless(const monster* mon) const
3020 {
3021     // For enchantments, this is already handled in nasty_to().
3022     if (is_enchantment())
3023         return !nasty_to(mon);
3024
3025     // The others are handled here.
3026     switch (flavour)
3027     {
3028     case BEAM_VISUAL:
3029     case BEAM_DIGGING:
3030         return true;
3031
3032     case BEAM_HOLY:
3033         return mon->res_holy_energy(agent()) > 0;
3034
3035     case BEAM_STEAM:
3036         return mon->res_steam() >= 3;
3037
3038     case BEAM_FIRE:
3039         return mon->res_fire() >= 3;
3040
3041     case BEAM_COLD:
3042         return mon->res_cold() >= 3;
3043
3044     case BEAM_MIASMA:
3045         return mon->res_rotting();
3046
3047     case BEAM_NEG:
3048         return mon->res_negative_energy() == 3;
3049
3050     case BEAM_ELECTRICITY:
3051         return mon->res_elec() >= 3;
3052
3053     case BEAM_POISON:
3054         return mon->res_poison() >= 3;
3055
3056     case BEAM_ACID:
3057         return mon->res_acid() >= 3;
3058
3059     case BEAM_PETRIFY:
3060         return mon->res_petrify() || mon->petrified();
3061
3062     case BEAM_MEPHITIC:
3063         return mon->res_poison() > 0 || mon->is_unbreathing();
3064
3065     case BEAM_GHOSTLY_FLAME:
3066         return mon->holiness() == MH_UNDEAD;
3067
3068     default:
3069         return false;
3070     }
3071 }
3072
3073 // N.b. only called for player-originated beams; if that is changed,
3074 // be sure to adjust the Qazlal cloud immunity below, and various other
3075 // assumptions based on the spells/abilities available to the player.
3076 bool bolt::harmless_to_player() const
3077 {
3078     dprf(DIAG_BEAM, "beam flavour: %d", flavour);
3079
3080     if (in_good_standing(GOD_QAZLAL) && is_big_cloud())
3081         return true;
3082
3083     switch (flavour)
3084     {
3085     case BEAM_VISUAL:
3086     case BEAM_DIGGING:
3087         return true;
3088
3089     // Positive enchantments.
3090     case BEAM_HASTE:
3091     case BEAM_HEALING:
3092     case BEAM_MIGHT:
3093     case BEAM_AGILITY:
3094     case BEAM_INVISIBILITY:
3095     case BEAM_RESISTANCE:
3096         return true;
3097
3098     case BEAM_HOLY:
3099         return you.res_holy_energy(&you);
3100
3101     case BEAM_STEAM:
3102         return player_res_steam(false) >= 3;
3103
3104     case BEAM_MIASMA:
3105         return you.res_rotting();
3106
3107     case BEAM_NEG:
3108         return player_prot_life(false) >= 3;
3109
3110     case BEAM_POISON:
3111         return player_res_poison(false) >= 3
3112                || is_big_cloud() && player_res_poison(false) > 0;
3113
3114     case BEAM_MEPHITIC:
3115         return player_res_poison(false) > 0 || you.clarity(false)
3116                || you.is_unbreathing();
3117
3118     case BEAM_ELECTRICITY:
3119         return player_res_electricity(false);
3120
3121     case BEAM_PETRIFY:
3122         return you.res_petrify() || you.petrified();
3123
3124     case BEAM_COLD:
3125         return is_big_cloud() && you.mutation[MUT_FREEZING_CLOUD_IMMUNITY];
3126
3127 #if TAG_MAJOR_VERSION == 34
3128     case BEAM_FIRE:
3129     case BEAM_HELLFIRE:
3130     case BEAM_HOLY_FLAME:
3131     case BEAM_STICKY_FLAME:
3132         return you.species == SP_DJINNI;
3133 #endif
3134
3135     case BEAM_VIRULENCE:
3136         return player_res_poison(false) >= 3;
3137
3138     case BEAM_IGNITE_POISON:
3139         return !ignite_poison_affects(&you);
3140
3141     default:
3142         return false;
3143     }
3144 }
3145
3146 bool bolt::is_reflectable(const item_def *it) const
3147 {
3148     if (range_used() > range)
3149         return false;
3150
3151     return it && is_shield(*it) && shield_reflects(*it);
3152 }
3153
3154 bool bolt::is_big_cloud() const
3155 {
3156     return get_spell_flags(origin_spell) & SPFLAG_CLOUD;
3157 }
3158
3159 coord_def bolt::leg_source() const
3160 {
3161     if (bounces > 0 && map_bounds(bounce_pos))
3162         return bounce_pos;
3163     else
3164         return source;
3165 }
3166
3167 // Reflect a beam back the direction it came. This is used
3168 // by shields of reflection.
3169 void bolt::reflect()
3170 {
3171     reflections++;
3172
3173     target = leg_source();
3174     source = pos();
3175
3176     // Reset bounce_pos, so that if we somehow reflect again before reaching
3177     // the wall that we won't keep heading towards the wall.
3178     bounce_pos.reset();
3179
3180     if (pos() == you.pos())
3181         reflector = MID_PLAYER;
3182     else if (monster* m = monster_at(pos()))
3183         reflector = m->mid;
3184     else
3185     {
3186         reflector = MID_NOBODY;
3187 #ifdef DEBUG
3188         dprf(DIAG_BEAM, "Bolt reflected by neither player nor "
3189              "monster (bolt = %s, item = %s)", name.c_str(),
3190              item ? item->name(DESC_PLAIN).c_str() : "none");
3191 #endif
3192     }
3193
3194     flavour = real_flavour;
3195     choose_ray();
3196 }
3197
3198 void bolt::tracer_affect_player()
3199 {
3200     // Check whether thrower can see player, unless thrower == player.
3201     if (YOU_KILL(thrower))
3202     {
3203         // Don't ask if we're aiming at ourselves.
3204         if (!aimed_at_feet && !dont_stop_player && !harmless_to_player())
3205         {
3206             string prompt = make_stringf("That %s is likely to hit you. Continue anyway?",
3207                                          item ? name.c_str() : "beam");
3208
3209             if (yesno(prompt.c_str(), false, 'n'))
3210             {
3211                 friend_info.count++;
3212                 friend_info.power += you.experience_level;
3213                 dont_stop_player = true;
3214             }
3215             else
3216             {
3217                 canned_msg(MSG_OK);
3218                 beam_cancelled = true;
3219                 finish_beam();
3220             }
3221         }
3222     }
3223     else if (can_see_invis || !you.invisible() || fuzz_invis_tracer())
3224     {
3225         if (mons_att_wont_attack(attitude))
3226         {
3227             friend_info.count++;
3228             friend_info.power += you.experience_level;
3229         }
3230         else
3231         {
3232             foe_info.count++;
3233             foe_info.power += you.experience_level;
3234         }
3235     }
3236
3237     extra_range_used += range_used_on_hit();
3238
3239     if (_is_explosive_bolt(this))
3240         _explosive_bolt_explode(this, you.pos());
3241 }
3242
3243 // Magical penetrating projectiles should pass through shields.
3244 bool bolt::pierces_shields() const
3245 {
3246     return range_used_on_hit() == 0;
3247 }
3248
3249 bool bolt::misses_player()
3250 {
3251     if (flavour == BEAM_VISUAL)
3252         return true;
3253
3254     if (is_enchantment())
3255         return false;
3256
3257     const bool engulfs = is_explosion || is_big_cloud();
3258
3259     if (is_explosion || aimed_at_feet || auto_hit)
3260     {
3261         if (hit_verb.empty())
3262             hit_verb = engulfs ? "engulfs" : "hits";
3263         if (flavour != BEAM_VISUAL)
3264             mprf("The %s %s you!", name.c_str(), hit_verb.c_str());
3265         return false;
3266     }
3267
3268     const int dodge = you.evasion();
3269     const int dodge_less = you.evasion(EV_IGNORE_PHASESHIFT);
3270     int real_tohit  = hit;
3271
3272     if (real_tohit != AUTOMATIC_HIT)
3273     {
3274         // Monsters shooting at an invisible player are very inaccurate.
3275         if (you.invisible() && !can_see_invis)
3276             real_tohit /= 2;
3277
3278         // Backlit is easier to hit:
3279         if (you.backlit(false))
3280             real_tohit += 2 + random2(8);
3281
3282         // Umbra is harder to hit:
3283         if (!nightvision && you.umbra())
3284             real_tohit -= 2 + random2(4);
3285     }
3286
3287     bool train_shields_more = false;
3288
3289     if (is_blockable()
3290         && you.shielded()
3291         && !aimed_at_feet
3292         && player_shield_class() > 0)
3293     {
3294         // We use the original to-hit here.
3295         const int testhit = random2(hit * 130 / 100
3296                                     + you.shield_block_penalty());
3297
3298         const int block = you.shield_bonus();
3299
3300         dprf(DIAG_BEAM, "Beamshield: hit: %d, block %d", testhit, block);
3301         if (testhit < block)
3302         {
3303             bool penet = false;
3304             if (is_reflectable(you.shield()))
3305             {
3306                 mprf("Your %s reflects the %s!",
3307                       you.shield()->name(DESC_PLAIN).c_str(),
3308                       name.c_str());
3309                 reflect();
3310             }
3311             else if (pierces_shields())
3312             {
3313                 penet = true;
3314                 mprf("The %s pierces through your %s!",
3315                       name.c_str(),
3316                       you.shield() ? you.shield()->name(DESC_PLAIN).c_str()
3317                                    : "shielding");
3318             }
3319             else
3320             {
3321                 mprf("You block the %s.", name.c_str());
3322                 finish_beam();
3323             }
3324             you.shield_block_succeeded(agent());
3325             hit_shield(&you);
3326             if (!penet)
3327                 return true;
3328         }
3329
3330         // Some training just for the "attempt".
3331         train_shields_more = true;
3332     }
3333
3334     if (!aimed_at_feet)
3335         practise(EX_BEAM_MAY_HIT);
3336
3337     defer_rand r;
3338     bool miss = true;
3339
3340     int defl = you.missile_deflection();
3341
3342     if (!_test_beam_hit(real_tohit, dodge_less, pierce, 0, r))
3343         mprf("The %s misses you.", name.c_str());
3344     else if (defl && !_test_beam_hit(real_tohit, dodge_less, pierce, defl, r))
3345     {
3346         // active voice to imply stronger effect
3347         mprf(defl == 1 ? "The %s is repelled." : "You deflect the %s!",
3348              name.c_str());
3349         you.ablate_deflection();
3350     }
3351     else if (!_test_beam_hit(real_tohit, dodge, pierce, defl, r))
3352     {
3353         mprf("You momentarily phase out as the %s "
3354              "passes through you.", name.c_str());
3355     }
3356     else
3357     {
3358         int dodge_more = you.evasion(EV_IGNORE_HELPLESS);
3359
3360         if (hit_verb.empty())
3361             hit_verb = engulfs ? "engulfs" : "hits";
3362
3363         if (_test_beam_hit(real_tohit, dodge_more, pierce, defl, r))
3364             mprf("The %s %s you!", name.c_str(), hit_verb.c_str());
3365         else
3366             mprf("Helpless, you fail to dodge the %s.", name.c_str());
3367
3368         miss = false;
3369     }
3370
3371     if (train_shields_more)
3372         practise(EX_SHIELD_BEAM_FAIL);
3373
3374     return miss;
3375 }
3376
3377 void bolt::affect_player_enchantment(bool resistible)
3378 {
3379     if (resistible
3380         && ((flavour != BEAM_MALMUTATE
3381              && flavour != BEAM_CORRUPT_BODY
3382              && flavour != BEAM_SAP_MAGIC
3383              && has_saving_throw())
3384               || flavour == BEAM_VIRULENCE)
3385         && you.check_res_magic(ench_power) > 0)
3386     {
3387         // You resisted it.
3388
3389         // Give a message.
3390         bool need_msg = true;
3391         if (thrower != KILL_YOU_MISSILE)
3392         {
3393             const monster* mon = monster_by_mid(source_id);
3394             if (mon && !mon->observable())
3395             {
3396                 mprf("Something tries to affect you, but you %s.",
3397                      you.res_magic() == MAG_IMMUNE ? "are unaffected"
3398                                                    : "resist");
3399                 need_msg = false;
3400             }
3401         }
3402         if (need_msg)
3403         {
3404             if (you.res_magic() == MAG_IMMUNE)
3405                 canned_msg(MSG_YOU_UNAFFECTED);
3406             else
3407             {
3408                 // the message reflects the level of difficulty resisting.
3409                 const int margin = you.res_magic() - ench_power;
3410                 mprf("You%s", you.resist_margin_phrase(margin).c_str());
3411             }
3412         }
3413         // You *could* have gotten a free teleportation in the Abyss,
3414         // but no, you resisted.
3415         if (flavour == BEAM_TELEPORT && player_in_branch(BRANCH_ABYSS))
3416             xom_is_stimulated(200);
3417
3418         extra_range_used += range_used_on_hit();
3419         return;
3420     }
3421
3422     // You didn't resist it.
3423     if (animate)
3424         _ench_animation(effect_known ? real_flavour : BEAM_MAGIC);
3425
3426     bool nasty = true, nice = false;
3427
3428     const bool blame_player = god_cares() && YOU_KILL(thrower);
3429
3430     switch (flavour)
3431     {
3432     case BEAM_HIBERNATION:
3433     case BEAM_SLEEP:
3434         you.put_to_sleep(nullptr, ench_power, flavour == BEAM_HIBERNATION);
3435         break;
3436
3437     case BEAM_CORONA:
3438         you.backlight();
3439         obvious_effect = true;
3440         break;
3441
3442     case BEAM_POLYMORPH:
3443         obvious_effect = you.polymorph(ench_power);
3444         break;
3445
3446     case BEAM_MALMUTATE:
3447         mpr("Strange energies course through your body.");
3448         you.malmutate(aux_source.empty() ? get_source_name() :
3449                       (get_source_name() + "/" + aux_source));
3450         obvious_effect = true;
3451         break;
3452
3453     case BEAM_SLOW:
3454         slow_player(10 + random2(ench_power));
3455         obvious_effect = true;
3456         break;
3457
3458     case BEAM_HASTE:
3459         haste_player(40 + random2(ench_power));
3460         did_god_conduct(DID_HASTY, 10, blame_player);
3461         contaminate_player(1000, blame_player);
3462         obvious_effect = true;
3463         nasty = false;
3464         nice  = true;
3465         break;
3466
3467     case BEAM_HEALING:
3468         potionlike_effect(POT_HEAL_WOUNDS, ench_power);
3469         obvious_effect = true;
3470         nasty = false;
3471         nice  = true;
3472         break;
3473
3474     case BEAM_MIGHT:
3475         potionlike_effect(POT_MIGHT, ench_power);
3476         obvious_effect = true;
3477         nasty = false;
3478         nice  = true;
3479         break;
3480
3481     case BEAM_INVISIBILITY:
3482         you.attribute[ATTR_INVIS_UNCANCELLABLE] = 1;
3483         potionlike_effect(POT_INVISIBILITY, ench_power);
3484         contaminate_player(1000 + random2(1000), blame_player);
3485         obvious_effect = true;
3486         nasty = false;
3487         nice  = true;
3488         break;
3489
3490     case BEAM_PARALYSIS:
3491         you.paralyse(agent(), 2 + random2(6));
3492         obvious_effect = true;
3493         break;
3494
3495     case BEAM_PETRIFY:
3496         you.petrify(agent());
3497         obvious_effect = true;
3498         break;
3499
3500     case BEAM_CONFUSION:
3501         confuse_player(5 + random2(3));
3502         obvious_effect = true;
3503         break;
3504
3505     case BEAM_TELEPORT:
3506         you_teleport();
3507
3508         // An enemy helping you escape while in the Abyss, or an
3509         // enemy stabilizing a teleport that was about to happen.
3510         if (!mons_att_wont_attack(attitude) && player_in_branch(BRANCH_ABYSS))
3511             xom_is_stimulated(200);
3512
3513         obvious_effect = true;
3514         break;
3515
3516     case BEAM_BLINK:
3517         uncontrolled_blink();
3518         obvious_effect = true;
3519         break;
3520
3521     case BEAM_BLINK_CLOSE:
3522         blink_other_close(&you, source);
3523         obvious_effect = true;
3524         break;
3525
3526     case BEAM_ENSLAVE:
3527         mprf(MSGCH_WARN, "Your will is overpowered!");
3528         confuse_player(5 + random2(3));
3529         obvious_effect = true;
3530         break;     // enslavement - confusion?
3531
3532     case BEAM_BANISH:
3533         if (YOU_KILL(thrower))
3534         {
3535             mpr("This spell isn't strong enough to banish yourself.");
3536             break;
3537         }
3538         you.banish(agent(), get_source_name());
3539         obvious_effect = true;
3540         break;
3541
3542     case BEAM_PAIN:
3543         if (player_res_torment())
3544         {
3545             canned_msg(MSG_YOU_UNAFFECTED);
3546             break;
3547         }
3548
3549         if (aux_source.empty())
3550             aux_source = "by nerve-wracking pain";
3551
3552         if (origin_spell == SPELL_AGONY)
3553         {
3554             if (you.res_negative_energy()) // Agony has no effect with rN.
3555             {
3556                 canned_msg(MSG_YOU_UNAFFECTED);
3557                 break;
3558             }
3559
3560             mpr("Your body is wracked with pain!");
3561
3562             // On the player, Agony acts like single-target torment.
3563             internal_ouch(max(0, you.hp / 2 - 1));
3564         }
3565         else
3566         {
3567             mpr("Pain shoots through your body!");
3568
3569             internal_ouch(damage.roll());
3570         }
3571         obvious_effect = true;
3572         break;
3573
3574     case BEAM_DISPEL_UNDEAD:
3575         if (you.undead_state() == US_ALIVE)
3576         {
3577             canned_msg(MSG_YOU_UNAFFECTED);
3578             break;
3579         }
3580
3581         mpr("You convulse!");
3582
3583         if (aux_source.empty())
3584             aux_source = "by dispel undead";
3585
3586         internal_ouch(damage.roll());
3587         obvious_effect = true;
3588         break;
3589
3590     case BEAM_DISINTEGRATION:
3591         mpr("You are blasted!");
3592
3593         if (aux_source.empty())
3594             aux_source = "disintegration bolt";
3595
3596         {
3597             int amt = damage.roll();
3598             internal_ouch(amt);
3599
3600             if (you.can_bleed())
3601                 blood_spray(you.pos(), MONS_PLAYER, amt / 5);
3602         }
3603
3604         obvious_effect = true;
3605         break;
3606
3607     case BEAM_PORKALATOR:
3608         if (!transform(ench_power, TRAN_PIG, true))
3609         {
3610             mpr("You feel a momentary urge to oink.");
3611             break;
3612         }
3613
3614         you.transform_uncancellable = true;
3615         obvious_effect = true;
3616         break;
3617
3618     case BEAM_BERSERK:
3619         you.go_berserk(blame_player, true);
3620         obvious_effect = true;
3621         break;
3622
3623     case BEAM_SENTINEL_MARK:
3624         you.sentinel_mark();
3625         obvious_effect = true;