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