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