Apply the new ground_level method.
[crawl.git] / crawl-ref / source / beam.cc
blobc28de02b084d506a3bf93ced4cb75f96cf4926f4
1 /*
2 * File: beam.cc
3 * Summary: Functions related to ranged attacks.
4 * Written by: Linley Henzell
5 */
7 #include "AppHdr.h"
9 #include "beam.h"
11 #include <cstdlib>
12 #include <cstdio>
13 #include <cstring>
14 #include <cstdarg>
15 #include <iostream>
16 #include <set>
17 #include <algorithm>
18 #include <cmath>
20 #include "externs.h"
21 #include "options.h"
23 #include "areas.h"
24 #include "attitude-change.h"
25 #include "cio.h"
26 #include "cloud.h"
27 #include "colour.h"
28 #include "coord.h"
29 #include "coordit.h"
30 #include "delay.h"
31 #include "dungeon.h"
32 #include "dgnevent.h"
33 #include "effects.h"
34 #include "env.h"
35 #include "enum.h"
36 #include "exercise.h"
37 #include "godabil.h"
38 #include "map_knowledge.h"
39 #include "fprop.h"
40 #include "fight.h"
41 #include "item_use.h"
42 #include "it_use2.h"
43 #include "items.h"
44 #include "itemname.h"
45 #include "itemprop.h"
46 #include "libutil.h"
47 #include "los.h"
48 #include "message.h"
49 #include "mgen_data.h"
50 #include "misc.h"
51 #include "mon-behv.h"
52 #include "mon-death.h"
53 #include "mon-iter.h"
54 #include "mon-place.h"
55 #include "mon-stuff.h"
56 #include "mon-util.h"
57 #include "mutation.h"
58 #include "ouch.h"
59 #include "player.h"
60 #include "religion.h"
61 #include "godconduct.h"
62 #include "skills.h"
63 #include "spl-clouds.h"
64 #include "spl-goditem.h"
65 #include "spl-monench.h"
66 #include "spl-transloc.h"
67 #include "state.h"
68 #include "stuff.h"
69 #include "teleport.h"
70 #include "terrain.h"
71 #ifdef USE_TILE
72 #include "tilepick.h"
73 #endif
74 #include "transform.h"
75 #include "traps.h"
76 #include "view.h"
77 #include "shout.h"
78 #include "viewchar.h"
79 #include "viewgeom.h"
80 #include "xom.h"
82 //#define DEBUG_BEAM
83 //#define DEBUG_CHAOS_BOUNCE
85 #define BEAM_STOP 1000 // all beams stopped by subtracting this
86 // from remaining range
88 // Helper functions (some of these should probably be public).
89 static void _ench_animation(int flavour, const monster* mon = NULL,
90 bool force = false);
91 static void _zappy(zap_type z_type, int power, bolt &pbolt);
92 static beam_type _chaos_beam_flavour();
93 static std::string _beam_type_name(beam_type type);
95 tracer_info::tracer_info()
97 reset();
100 void tracer_info::reset()
102 count = power = hurt = helped = 0;
103 dont_stop = false;
106 const tracer_info& tracer_info::operator+=(const tracer_info &other)
108 count += other.count;
109 power += other.power;
110 hurt += other.hurt;
111 helped += other.helped;
113 dont_stop = dont_stop || other.dont_stop;
115 return (*this);
118 bool bolt::is_blockable() const
120 // BEAM_ELECTRICITY is added here because chain lightning is not
121 // a true beam (stops at the first target it gets to and redirects
122 // from there)... but we don't want it shield blockable.
123 return (!is_beam && !is_explosion && flavour != BEAM_ELECTRICITY);
126 void bolt::emit_message(msg_channel_type chan, const char* m)
128 const std::string message = m;
129 if (message_cache.find(message) == message_cache.end())
130 mpr(m, chan);
132 message_cache.insert(message);
135 kill_category bolt::whose_kill() const
137 if (YOU_KILL(thrower))
138 return (KC_YOU);
139 else if (MON_KILL(thrower))
141 if (beam_source == ANON_FRIENDLY_MONSTER)
142 return (KC_FRIENDLY);
143 if (!invalid_monster_index(beam_source))
145 const monster* mon = &menv[beam_source];
146 if (mon->friendly())
147 return (KC_FRIENDLY);
150 return (KC_OTHER);
153 // A simple animated flash from Rupert Smith (expanded to be more
154 // generic).
155 static void _zap_animation(int colour, const monster* mon = NULL,
156 bool force = false)
158 coord_def p = you.pos();
160 if (mon)
162 if (!force && !mon->visible_to(&you))
163 return;
165 p = mon->pos();
168 if (!you.see_cell(p))
169 return;
171 const coord_def drawp = grid2view(p);
173 if (in_los_bounds_v(drawp))
175 // Default to whatever colour magic is today.
176 if (colour == -1)
177 colour = ETC_MAGIC;
179 #ifdef USE_TILE
180 tiles.add_overlay(p, tileidx_zap(colour));
181 #else
182 view_update();
183 cgotoxy(drawp.x, drawp.y, GOTO_DNGN);
184 put_colour_ch(colour, dchar_glyph(DCHAR_FIRED_ZAP));
185 #endif
187 update_screen();
189 int zap_delay = 50;
190 // Scale delay to match change in arena_delay.
191 if (crawl_state.game_is_arena())
193 zap_delay *= Options.arena_delay;
194 zap_delay /= 600;
197 delay(zap_delay);
201 // Special front function for zap_animation to interpret enchantment flavours.
202 static void _ench_animation(int flavour, const monster* mon, bool force)
204 element_type elem;
205 switch (flavour)
207 case BEAM_HEALING:
208 elem = ETC_HEAL;
209 break;
210 case BEAM_PAIN:
211 elem = ETC_UNHOLY;
212 break;
213 case BEAM_DISPEL_UNDEAD:
214 elem = ETC_HOLY;
215 break;
216 case BEAM_POLYMORPH:
217 elem = ETC_MUTAGENIC;
218 break;
219 case BEAM_CHAOS:
220 elem = ETC_RANDOM;
221 break;
222 case BEAM_TELEPORT:
223 case BEAM_BANISH:
224 case BEAM_BLINK:
225 case BEAM_BLINK_CLOSE:
226 elem = ETC_WARP;
227 break;
228 default:
229 elem = ETC_ENCHANT;
230 break;
233 _zap_animation(element_colour(elem), mon, force);
236 // If needs_tracer is true, we need to check the beam path for friendly
237 // monsters.
238 bool zapping(zap_type ztype, int power, bolt &pbolt,
239 bool needs_tracer, const char* msg)
241 dprf("zapping: power=%d", power);
243 pbolt.thrower = KILL_YOU_MISSILE;
245 // Check whether tracer goes through friendlies.
246 // NOTE: Whenever zapping() is called with a randomised value for power
247 // (or effect), player_tracer should be called directly with the highest
248 // power possible respecting current skill, experience level, etc.
249 if (needs_tracer && !player_tracer(ztype, power, pbolt))
250 return (false);
252 // Fill in the bolt structure.
253 _zappy(ztype, power, pbolt);
255 if (msg)
256 mpr(msg);
258 if (ztype == ZAP_LIGHTNING)
260 noisy(25, you.pos(), "You hear a mighty clap of thunder!");
261 pbolt.heard = true;
264 if (ztype == ZAP_DIGGING)
265 pbolt.aimed_at_spot = false;
267 pbolt.fire();
269 return (true);
272 // Returns true if the path is considered "safe", and false if there are
273 // monsters in the way the player doesn't want to hit.
274 // NOTE: Doesn't check for the player being hit by a rebounding lightning bolt.
275 bool player_tracer(zap_type ztype, int power, bolt &pbolt, int range)
277 // Non-controlleable during confusion.
278 // (We'll shoot in a different direction anyway.)
279 if (you.confused())
280 return (true);
282 _zappy(ztype, power, pbolt);
283 pbolt.name = "unimportant";
285 pbolt.is_tracer = true;
286 pbolt.source = you.pos();
287 pbolt.can_see_invis = you.can_see_invisible();
288 pbolt.smart_monster = true;
289 pbolt.attitude = ATT_FRIENDLY;
290 pbolt.thrower = KILL_YOU_MISSILE;
292 // Init tracer variables.
293 pbolt.friend_info.reset();
294 pbolt.foe_info.reset();
296 pbolt.foe_ratio = 100;
297 pbolt.beam_cancelled = false;
298 pbolt.dont_stop_player = false;
300 // Clear misc
301 pbolt.seen = false;
302 pbolt.heard = false;
303 pbolt.reflections = 0;
304 pbolt.bounces = 0;
306 // Save range before overriding it
307 const int old_range = pbolt.range;
308 if (range)
309 pbolt.range = range;
311 pbolt.fire();
313 if (range)
314 pbolt.range = old_range;
316 // Should only happen if the player answered 'n' to one of those
317 // "Fire through friendly?" prompts.
318 if (pbolt.beam_cancelled)
320 dprf("%s", "Beam cancelled.");
321 canned_msg(MSG_OK);
322 you.turn_is_over = false;
323 return (false);
326 // Set to non-tracing for actual firing.
327 pbolt.is_tracer = false;
328 return (true);
331 template<typename T>
332 class power_deducer
334 public:
335 virtual T operator()(int pow) const = 0;
336 virtual ~power_deducer() {}
339 typedef power_deducer<int> tohit_deducer;
341 template<int adder, int mult_num = 0, int mult_denom = 1>
342 class tohit_calculator : public tohit_deducer
344 public:
345 int operator()(int pow) const
347 return adder + (pow * mult_num) / mult_denom;
351 typedef power_deducer<dice_def> dam_deducer;
353 template<int numdice, int adder, int mult_num, int mult_denom>
354 class dicedef_calculator : public dam_deducer
356 public:
357 dice_def operator()(int pow) const
359 return dice_def(numdice, adder + (pow * mult_num) / mult_denom);
363 template<int numdice, int adder, int mult_num, int mult_denom>
364 class calcdice_calculator : public dam_deducer
366 public:
367 dice_def operator()(int pow) const
369 return calc_dice(numdice, adder + (pow * mult_num) / mult_denom);
373 struct zap_info
375 zap_type ztype;
376 const char* name; // NULL means handled specially
377 int power_cap;
378 dam_deducer* damage;
379 tohit_deducer* tohit; // Enchantments have power modifier here
380 int colour;
381 bool is_enchantment;
382 beam_type flavour;
383 dungeon_char_type glyph;
384 bool always_obvious;
385 bool can_beam;
386 bool is_explosion;
387 int hit_loudness;
390 const zap_info zap_data[] = {
392 #include "zap-data.h"
396 #define ZAPDATASIZE (sizeof(zap_data)/sizeof(zap_info))
398 static int zap_index[NUM_ZAPS];
400 void init_zap_index()
402 for (int i = 0; i < NUM_ZAPS; ++i)
403 zap_index[i] = -1;
405 for (unsigned int i = 0; i < ZAPDATASIZE; ++i)
406 zap_index[zap_data[i].ztype] = i;
409 static const zap_info* _seek_zap(zap_type z_type)
411 if (zap_index[z_type] == -1)
412 return (NULL);
413 else
414 return (&zap_data[zap_index[z_type]]);
417 int zap_power_cap(zap_type z_type)
419 const zap_info* zinfo = _seek_zap(z_type);
421 return (zinfo ? zinfo->power_cap : 0);
424 static void _zappy(zap_type z_type, int power, bolt &pbolt)
426 const zap_info* zinfo = _seek_zap(z_type);
428 // None found?
429 if (zinfo == NULL)
431 #ifdef DEBUG_DIAGNOSTICS
432 mprf(MSGCH_ERROR, "Couldn't find zap type %d", z_type);
433 #endif
434 return;
437 // Fill
438 pbolt.name = zinfo->name;
439 pbolt.flavour = zinfo->flavour;
440 pbolt.real_flavour = zinfo->flavour;
441 pbolt.colour = zinfo->colour;
442 pbolt.glyph = dchar_glyph(zinfo->glyph);
443 pbolt.obvious_effect = zinfo->always_obvious;
444 pbolt.is_beam = zinfo->can_beam;
445 pbolt.is_explosion = zinfo->is_explosion;
447 if (zinfo->power_cap > 0)
448 power = std::min(zinfo->power_cap, power);
450 ASSERT(zinfo->is_enchantment == pbolt.is_enchantment());
452 if (zinfo->is_enchantment)
454 pbolt.ench_power = (zinfo->tohit ? (*zinfo->tohit)(power) : power);
455 pbolt.hit = AUTOMATIC_HIT;
457 else
459 pbolt.hit = (*zinfo->tohit)(power);
460 if (wearing_amulet(AMU_INACCURACY))
461 pbolt.hit = std::max(0, pbolt.hit - 5);
464 if (zinfo->damage)
465 pbolt.damage = (*zinfo->damage)(power);
467 // One special case
468 if (z_type == ZAP_ICE_STORM)
469 pbolt.ench_power = power; // used for radius
471 if (pbolt.loudness == 0)
472 pbolt.loudness = zinfo->hit_loudness;
475 bool bolt::can_affect_actor(const actor *act) const
477 // If there's a function that checks whether an actor is affected,
478 // bypass any generic beam-affects-X logic:
479 if (affect_func)
480 return (*affect_func)(*this, act);
482 return !act->submerged();
485 bool bolt::actor_wall_shielded(const actor *act) const
487 return (act->atype() == ACT_PLAYER? false :
488 mons_wall_shielded(act->as_monster()));
491 // Affect actor in wall unless it can shield itself using the wall.
492 // The wall will always shield the actor if the beam bounces off the
493 // wall, and a monster can't use a metal wall to shield itself from
494 // electricity.
495 bool bolt::can_affect_wall_actor(const actor *act) const
497 if (!can_affect_actor(act))
498 return (false);
500 if (is_enchantment())
501 return (true);
503 const bool superconductor = (grd(act->pos()) == DNGN_METAL_WALL
504 && flavour == BEAM_ELECTRICITY);
505 if (actor_wall_shielded(act) && !superconductor)
506 return (false);
508 if (!is_explosion && !is_big_cloud)
509 return (true);
511 if (is_bouncy(grd(act->pos())))
512 return (false);
514 return (false);
517 static beam_type _chaos_beam_flavour()
519 const beam_type flavour = static_cast<beam_type>(
520 random_choose_weighted(
521 10, BEAM_FIRE,
522 10, BEAM_COLD,
523 10, BEAM_ELECTRICITY,
524 10, BEAM_POISON,
525 10, BEAM_NEG,
526 10, BEAM_ACID,
527 10, BEAM_HELLFIRE,
528 10, BEAM_NAPALM,
529 10, BEAM_SLOW,
530 10, BEAM_HASTE,
531 10, BEAM_MIGHT,
532 10, BEAM_BERSERK,
533 10, BEAM_HEALING,
534 10, BEAM_PARALYSIS,
535 10, BEAM_CONFUSION,
536 10, BEAM_INVISIBILITY,
537 10, BEAM_POLYMORPH,
538 10, BEAM_BANISH,
539 10, BEAM_DISINTEGRATION,
540 0));
542 return (flavour);
545 static void _munge_bounced_bolt(bolt &old_bolt, bolt &new_bolt,
546 ray_def &old_ray, ray_def &new_ray)
548 if (new_bolt.real_flavour != BEAM_CHAOS)
549 return;
551 double old_deg = old_ray.get_degrees();
552 double new_deg = new_ray.get_degrees();
553 double angle = fabs(old_deg - new_deg);
555 if (angle >= 180.0)
556 angle -= 180.0;
558 double max = 90.0 + (angle / 2.0);
559 double min = -90.0 + (angle / 2.0);
561 double shift;
563 ray_def temp_ray = new_ray;
564 for (int tries = 0; tries < 20; tries++)
566 shift = random_range(static_cast<int>(min * 10000),
567 static_cast<int>(max * 10000)) / 10000.0;
569 if (new_deg < old_deg)
570 shift = -shift;
571 temp_ray.set_degrees(new_deg + shift);
573 // Don't bounce straight into another wall. Can happen if the beam
574 // is shot into an inside corner.
575 ray_def test_ray = temp_ray;
576 test_ray.advance();
577 if (in_bounds(test_ray.pos()) && !cell_is_solid(test_ray.pos()))
578 break;
580 shift = 0.0;
581 temp_ray = new_ray;
584 new_ray = temp_ray;
585 #if defined(DEBUG_DIAGNOSTICS) || defined(DEBUG_BEAM) || defined(DEBUG_CHAOS_BOUNCE)
586 mprf(MSGCH_DIAGNOSTICS,
587 "chaos beam: old_deg = %5.2f, new_deg = %5.2f, shift = %5.2f",
588 static_cast<float>(old_deg), static_cast<float>(new_deg),
589 static_cast<float>(shift));
590 #endif
592 // Don't use up range in bouncing off walls, so that chaos beams have
593 // as many chances as possible to bounce. They're like demented
594 // ping-pong balls on caffeine.
595 int range_spent = new_bolt.range_used() - old_bolt.range_used();
596 new_bolt.range += range_spent;
599 bool bolt::visible() const
601 return (glyph != 0 && !is_enchantment());
604 void bolt::initialise_fire()
606 // Fix some things which the tracer might have set.
607 extra_range_used = 0;
608 in_explosion_phase = false;
609 use_target_as_pos = false;
611 if (special_explosion != NULL)
613 ASSERT(!is_explosion);
614 ASSERT(special_explosion->is_explosion);
615 ASSERT(special_explosion->special_explosion == NULL);
616 special_explosion->in_explosion_phase = false;
617 special_explosion->use_target_as_pos = false;
620 if (chose_ray)
622 ASSERT(in_bounds(ray.pos()));
624 if (source == coord_def())
625 source = ray.pos();
628 if (target == source)
630 range = 0;
631 aimed_at_feet = true;
632 auto_hit = true;
633 aimed_at_spot = true;
634 use_target_as_pos = true;
637 if (range == -1)
639 #ifdef DEBUG
640 if (is_tracer)
642 mpr("Tracer with range == -1, skipping.", MSGCH_ERROR);
643 return;
646 std::string item_name = item ? item->name(DESC_PLAIN, false, true)
647 : "none";
649 std::string dbg_source_name = "unknown";
650 if (beam_source == NON_MONSTER && source == you.pos())
651 dbg_source_name = "player";
652 else if (!invalid_monster_index(beam_source))
653 dbg_source_name = menv[beam_source].name(DESC_PLAIN, true);
655 mprf(MSGCH_ERROR, "beam '%s' (source '%s', item '%s') has range -1; "
656 "setting to LOS_RADIUS",
657 name.c_str(), dbg_source_name.c_str(), item_name.c_str());
658 #endif
659 range = LOS_RADIUS;
662 ASSERT(in_bounds(source));
663 ASSERT(flavour > BEAM_NONE && flavour < BEAM_FIRST_PSEUDO);
664 ASSERT(!drop_item || item && item->defined());
665 ASSERT(range >= 0);
666 ASSERT(!aimed_at_feet || source == target);
668 real_flavour = flavour;
670 message_cache.clear();
672 // seen might be set by caller to supress this.
673 if (!seen && you.see_cell(source) && range > 0 && visible())
675 seen = true;
676 const monster* mon = monster_at(source);
678 if (flavour != BEAM_VISUAL
679 && !is_tracer
680 && !YOU_KILL(thrower)
681 && !crawl_state.is_god_acting()
682 && (!mon || !mon->observable()))
684 mprf("%s appears from out of thin air!",
685 article_a(name, false).c_str());
689 // Visible self-targeted beams are always seen, even though they don't
690 // leave a path.
691 if (you.see_cell(source) && target == source && visible())
692 seen = true;
694 // Scale draw_delay to match change in arena_delay.
695 if (crawl_state.game_is_arena() && !is_tracer)
697 draw_delay *= Options.arena_delay;
698 draw_delay /= 600;
701 #ifdef DEBUG_DIAGNOSTICS
702 mprf(MSGCH_DIAGNOSTICS, "%s%s%s [%s] (%d,%d) to (%d,%d): "
703 "gl=%d col=%d flav=%d hit=%d dam=%dd%d range=%d",
704 (is_beam) ? "beam" : "missile",
705 (is_explosion) ? "*" :
706 (is_big_cloud) ? "+" : "",
707 (is_tracer) ? " tracer" : "",
708 name.c_str(),
709 source.x, source.y,
710 target.x, target.y,
711 glyph, colour, flavour,
712 hit, damage.num, damage.size,
713 range);
714 #endif
717 void bolt::apply_beam_conducts()
719 if (!is_tracer && YOU_KILL(thrower))
721 switch (flavour)
723 case BEAM_HELLFIRE:
724 did_god_conduct(DID_UNHOLY, 2 + random2(3), effect_known);
725 break;
726 default:
727 break;
732 void bolt::choose_ray()
734 if (!chose_ray || reflections > 0)
736 if (!find_ray(source, target, ray))
737 fallback_ray(source, target, ray);
741 // Draw the bolt at p if needed.
742 void bolt::draw(const coord_def& p)
744 if (is_tracer || is_enchantment() || !you.see_cell(p))
745 return;
747 // We don't clean up the old position.
748 // First, most people like to see the full path,
749 // and second, it is hard to do it right with
750 // respect to killed monsters, cloud trails, etc.
752 const coord_def drawpos = grid2view(p);
754 if (!in_los_bounds_v(drawpos))
755 return;
757 #ifdef USE_TILE
758 if (tile_beam == -1)
759 tile_beam = tileidx_bolt(*this);
761 if (tile_beam != -1)
762 tiles.add_overlay(p, tile_beam);
763 #else
764 cgotoxy(drawpos.x, drawpos.y, GOTO_DNGN);
765 put_colour_ch(colour == BLACK ? random_colour()
766 : element_colour(colour),
767 glyph);
769 // Get curses to update the screen so we can see the beam.
770 update_screen();
771 #endif
772 delay(draw_delay);
775 // Bounce a bolt off a solid feature.
776 // The ray is assumed to have just been advanced into
777 // the feature.
778 void bolt::bounce()
780 ray_def old_ray = ray;
781 bolt old_bolt = *this;
784 ray.regress();
785 while (feat_is_solid(grd(ray.pos())));
787 extra_range_used += range_used(true);
788 bounce_pos = ray.pos();
789 bounces++;
790 reflect_grid rg;
791 for (adjacent_iterator ai(ray.pos(), false); ai; ++ai)
792 rg(*ai - ray.pos()) = feat_is_solid(grd(*ai));
793 ray.bounce(rg);
794 extra_range_used += 2;
796 ASSERT(!feat_is_solid(grd(ray.pos())));
797 _munge_bounced_bolt(old_bolt, *this, old_ray, ray);
800 void bolt::fake_flavour()
802 if (real_flavour == BEAM_RANDOM)
803 flavour = static_cast<beam_type>(random_range(BEAM_FIRE, BEAM_ACID));
804 else if (real_flavour == BEAM_CHAOS)
805 flavour = _chaos_beam_flavour();
808 void bolt::digging_wall_effect()
810 const dungeon_feature_type feat = grd(pos());
811 if (feat == DNGN_ROCK_WALL || feat == DNGN_CLEAR_ROCK_WALL
812 || feat == DNGN_SLIMY_WALL || feat == DNGN_GRATE)
814 nuke_wall(pos());
816 if (!msg_generated)
818 obvious_effect = true;
819 msg_generated = true;
821 std::string wall;
822 if (feat == DNGN_GRATE)
824 mprf("The damaged grate falls apart into pieces.");
825 return;
827 else if (feat == DNGN_SLIMY_WALL)
828 wall = "slime";
829 else if (you.level_type == LEVEL_PANDEMONIUM)
830 wall = "weird stuff";
831 else
832 wall = "rock";
833 mprf("The %s liquefies and sinks out of sight.", wall.c_str());
834 // This is silent.
837 else if (feat_is_wall(feat))
838 finish_beam();
841 void bolt::fire_wall_effect()
843 dungeon_feature_type feat;
844 // Fire only affects wax walls and trees.
845 if ((feat = grd(pos())) != DNGN_WAX_WALL && !feat_is_tree(feat)
846 || env.markers.property_at(pos(), MAT_ANY, "veto_fire") == "veto")
848 finish_beam();
849 return;
852 if (feat == DNGN_WAX_WALL)
854 if (!is_superhot())
856 // No actual effect.
857 if (flavour != BEAM_HELLFIRE && feat == DNGN_WAX_WALL)
859 if (you.see_cell(pos()))
861 emit_message(MSGCH_PLAIN,
862 "The wax appears to soften slightly.");
864 else if (you.can_smell())
865 emit_message(MSGCH_PLAIN, "You smell warm wax.");
868 else
870 // Destroy the wall.
871 nuke_wall(pos());
872 if (you.see_cell(pos()))
873 emit_message(MSGCH_PLAIN, "The wax bubbles and burns!");
874 else if (you.can_smell())
875 emit_message(MSGCH_PLAIN, "You smell burning wax.");
876 ASSERT(agent()); // if this is wrong, please preserve friendliness of kc
877 place_cloud(CLOUD_FIRE, pos(), random2(10)+15, agent());
878 obvious_effect = true;
881 else
883 if (is_superhot())
885 // Destroy the wall.
886 nuke_wall(pos());
887 if (you.see_cell(pos()))
888 emit_message(MSGCH_PLAIN, "The tree burns like a torch!");
889 else if (you.can_smell())
890 emit_message(MSGCH_PLAIN, "You smell burning wood.");
891 if (whose_kill() == KC_YOU)
892 did_god_conduct(DID_KILL_PLANT, 1, effect_known);
893 else if (whose_kill() == KC_FRIENDLY)
894 did_god_conduct(DID_PLANT_KILLED_BY_SERVANT, 1, effect_known);
895 ASSERT(agent());
896 place_cloud(CLOUD_FOREST_FIRE, pos(), random2(30)+25, agent());
897 obvious_effect = true;
900 finish_beam();
903 void bolt::elec_wall_effect()
905 const dungeon_feature_type feat = grd(pos());
906 if (feat_is_tree(feat)
907 && env.markers.property_at(pos(), MAT_ANY, "veto_fire") != "veto")
909 fire_wall_effect();
910 return;
912 finish_beam();
915 static bool _nuke_wall_msg(dungeon_feature_type feat, const coord_def& p)
917 std::string msg;
918 msg_channel_type chan = MSGCH_PLAIN;
919 bool hear = player_can_hear(p);
920 bool see = you.see_cell(p);
922 switch (feat)
924 case DNGN_ROCK_WALL:
925 case DNGN_SLIMY_WALL:
926 case DNGN_WAX_WALL:
927 case DNGN_CLEAR_ROCK_WALL:
928 case DNGN_GRANITE_STATUE:
929 case DNGN_CLOSED_DOOR:
930 case DNGN_DETECTED_SECRET_DOOR:
931 case DNGN_SECRET_DOOR:
932 // XXX: When silenced, features disappear without message.
933 // XXX: For doors, we only issue a sound where the beam hit.
934 // If someone wants to improve on the door messaging,
935 // probably best to merge _nuke_wall_msg back into
936 // nuke_wall_effect. [rob]
937 if (hear)
939 msg = "You hear a grinding noise.";
940 chan = MSGCH_SOUND;
942 break;
944 case DNGN_GRATE:
945 if (hear)
947 msg = "You hear the screech of bent metal.";
948 chan = MSGCH_SOUND;
950 break;
952 case DNGN_ORCISH_IDOL:
953 if (hear)
955 if (see)
956 msg = "You hear a hideous screaming!";
957 else
958 msg = "The idol screams as its substance crumbles away!";
959 chan = MSGCH_SOUND;
961 else if (see)
962 msg = "The idol twists and shakes as its substance crumbles away!";
963 break;
965 case DNGN_TREE:
966 case DNGN_SWAMP_TREE:
967 if (see)
968 msg = "The tree breaks and falls down!";
969 else if (hear)
971 msg = "You hear timber falling.";
972 chan = MSGCH_SOUND;
974 break;
976 default:
977 break;
980 if (!msg.empty())
982 mpr(msg, chan);
983 return (true);
985 else
986 return (false);
989 void bolt::nuke_wall_effect()
991 if (env.markers.property_at(pos(), MAT_ANY, "veto_disintegrate") == "veto")
993 finish_beam();
994 return;
997 const dungeon_feature_type feat = grd(pos());
999 switch (feat)
1001 case DNGN_ROCK_WALL:
1002 case DNGN_SLIMY_WALL:
1003 case DNGN_WAX_WALL:
1004 case DNGN_CLEAR_ROCK_WALL:
1005 case DNGN_GRATE:
1006 case DNGN_GRANITE_STATUE:
1007 case DNGN_ORCISH_IDOL:
1008 case DNGN_TREE:
1009 case DNGN_SWAMP_TREE:
1010 nuke_wall(pos());
1011 break;
1013 case DNGN_CLOSED_DOOR:
1014 case DNGN_DETECTED_SECRET_DOOR:
1015 case DNGN_SECRET_DOOR:
1017 std::set<coord_def> doors = connected_doors(pos());
1018 std::set<coord_def>::iterator it;
1019 for (it = doors.begin(); it != doors.end(); ++it)
1020 nuke_wall(*it);
1021 break;
1024 default:
1025 finish_beam();
1026 return;
1029 obvious_effect = _nuke_wall_msg(feat, pos());
1031 if (feat == DNGN_ORCISH_IDOL)
1033 if (beam_source == NON_MONSTER)
1034 did_god_conduct(DID_DESTROY_ORCISH_IDOL, 8);
1036 else if (feat == DNGN_TREE || feat == DNGN_SWAMP_TREE)
1038 if (whose_kill() == KC_YOU)
1039 did_god_conduct(DID_KILL_PLANT, 1);
1040 else if (whose_kill() == KC_FRIENDLY)
1041 did_god_conduct(DID_PLANT_KILLED_BY_SERVANT, 1, effect_known, 0);
1044 finish_beam();
1047 // integer square root, such that _length((8,1)) == 8.
1048 static int _length(const coord_def& c)
1050 if (c.origin())
1051 return (0);
1052 return (int)(ceil(sqrt(c.abs()-1)));
1055 int bolt::range_used(bool leg_only) const
1057 const int leg_length = _length(pos() - leg_source());
1058 return (leg_only ? leg_length : leg_length + extra_range_used);
1061 void bolt::finish_beam()
1063 extra_range_used = BEAM_STOP;
1066 void bolt::affect_wall()
1068 if (is_tracer)
1070 if (affects_wall(grd(pos())) != B_TRUE)
1071 finish_beam();
1072 return;
1075 if (flavour == BEAM_DIGGING)
1076 digging_wall_effect();
1077 else if (is_fiery())
1078 fire_wall_effect();
1079 else if (flavour == BEAM_ELECTRICITY)
1080 elec_wall_effect();
1081 else if (flavour == BEAM_DISINTEGRATION || flavour == BEAM_NUKE)
1082 nuke_wall_effect();
1084 if (cell_is_solid(pos()))
1085 finish_beam();
1088 coord_def bolt::pos() const
1090 if (in_explosion_phase || use_target_as_pos)
1091 return target;
1092 else
1093 return ray.pos();
1096 bool bolt::need_regress() const
1098 // XXX: The affects_wall check probably makes some of the
1099 // others obsolete.
1100 return ((is_explosion && !in_explosion_phase)
1101 || drop_item
1102 || feat_is_solid(grd(pos())) && !affects_wall(grd(pos()))
1103 || origin_spell == SPELL_PRIMAL_WAVE);
1106 // Returns true if the beam ended due to hitting the wall.
1107 bool bolt::hit_wall()
1109 const dungeon_feature_type feat = grd(pos());
1110 ASSERT(feat_is_solid(feat));
1112 if (is_tracer && YOU_KILL(thrower) && in_bounds(target) && !passed_target
1113 && pos() != target && 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)))
1118 // Okay, with all those tests passed, this is probably an instance
1119 // of the player manually targeting 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 std::string prompt = "Your line of fire to ";
1123 const monster* mon = monster_at(target);
1125 if (mon && mon->observable())
1126 prompt += mon->name(DESC_NOCAP_THE);
1127 else
1129 prompt += "the targeted "
1130 + feature_description(target, false, DESC_PLAIN, false);
1133 prompt += " is blocked by "
1134 + feature_description(pos(), false, DESC_NOCAP_A, false);
1136 prompt += ". Continue anyway?";
1138 if (!yesno(prompt.c_str(), false, 'n'))
1140 beam_cancelled = true;
1141 finish_beam();
1142 return (false);
1145 // Well, we warned them.
1148 // Press trigger/switch/button in wall if hit by something solid
1149 // or solid-ish.
1150 if (in_bounds(pos()) && !is_explosion && !is_tracer && !monster_at(pos())
1151 && (flavour == BEAM_MISSILE || flavour == BEAM_MMISSILE))
1153 dgn_event event(DET_WALL_HIT, pos());;
1154 event.arg1 = beam_source;
1156 dungeon_events.fire_vetoable_position_event(event, target);
1159 if (in_bounds(pos()) && affects_wall(feat))
1160 affect_wall();
1161 else if (is_bouncy(feat) && !in_explosion_phase)
1162 bounce();
1163 else
1165 // Regress for explosions: blow up in an open grid (if regressing
1166 // makes any sense). Also regress when dropping items.
1167 if (pos() != source && need_regress())
1170 ray.regress();
1171 while (ray.pos() != source && cell_is_solid(ray.pos()));
1173 // target is where the explosion is centered, so update it.
1174 if (is_explosion && !is_tracer)
1175 target = ray.pos();
1177 finish_beam();
1179 return (true);
1182 return (false);
1185 void bolt::affect_cell()
1187 // Shooting through clouds affects accuracy.
1188 if (env.cgrid(pos()) != EMPTY_CLOUD)
1189 hit = std::max(hit - 2, 0);
1191 fake_flavour();
1193 const coord_def old_pos = pos();
1194 const bool was_solid = feat_is_solid(grd(pos()));
1196 if (was_solid)
1198 // Some special casing.
1199 if (actor *act = actor_at(pos()))
1201 if (can_affect_wall_actor(act))
1202 affect_actor(act);
1203 else
1205 mprf("The %s protects %s from harm.",
1206 raw_feature_description(grd(act->pos())).c_str(),
1207 act->name(DESC_NOCAP_THE).c_str());
1211 // Note that this can change the ray position and the solidity
1212 // of the wall.
1213 if (hit_wall())
1214 // Beam ended due to hitting wall, so don't hit the player
1215 // or monster with the regressed beam.
1216 return;
1219 // If the player can ever walk through walls, this will need
1220 // special-casing too.
1221 bool hit_player = found_player();
1222 if (hit_player && can_affect_actor(&you))
1223 affect_player();
1225 // We don't want to hit a monster in a wall square twice. Also,
1226 // stop single target beams from affecting a monster if they already
1227 // affected the player on this square. -cao
1228 const bool still_wall = (was_solid && old_pos == pos());
1229 if ((!hit_player || is_beam || is_explosion) && !still_wall)
1230 if (monster* m = monster_at(pos()))
1231 if (can_affect_actor(m))
1232 affect_monster(m);
1234 if (!feat_is_solid(grd(pos())))
1235 affect_ground();
1238 bool bolt::apply_hit_funcs(actor* victim, int dmg, int corpse)
1240 bool affected = false;
1241 for (unsigned int i = 0; i < hit_funcs.size(); ++i)
1242 affected = (*hit_funcs[i])(*this, victim, dmg, corpse) || affected;
1244 return (affected);
1247 bool bolt::apply_dmg_funcs(actor* victim, int &dmg,
1248 std::vector<std::string> &messages)
1250 for (unsigned int i = 0; i < damage_funcs.size(); ++i)
1252 std::string dmg_msg;
1254 if ((*damage_funcs[i])(*this, victim, dmg, dmg_msg))
1255 return (false);
1256 if (!dmg_msg.empty())
1257 messages.push_back(dmg_msg);
1259 return (true);
1262 static void _undo_tracer(bolt &orig, bolt &copy)
1264 // FIXME: we should have a better idea of what gets changed!
1265 orig.target = copy.target;
1266 orig.source = copy.source;
1267 orig.aimed_at_spot = copy.aimed_at_spot;
1268 orig.extra_range_used = copy.extra_range_used;
1269 orig.auto_hit = copy.auto_hit;
1270 orig.ray = copy.ray;
1271 orig.colour = copy.colour;
1272 orig.flavour = copy.flavour;
1273 orig.real_flavour = copy.real_flavour;
1274 orig.bounces = copy.bounces;
1275 orig.bounce_pos = copy.bounce_pos;
1278 // This saves some important things before calling fire().
1279 void bolt::fire()
1281 path_taken.clear();
1283 if (special_explosion)
1284 special_explosion->is_tracer = is_tracer;
1286 if (is_tracer)
1288 bolt boltcopy = *this;
1289 if (special_explosion != NULL)
1290 boltcopy.special_explosion = new bolt(*special_explosion);
1292 do_fire();
1294 if (special_explosion != NULL)
1296 _undo_tracer(*special_explosion, *boltcopy.special_explosion);
1297 delete boltcopy.special_explosion;
1300 _undo_tracer(*this, boltcopy);
1302 else
1303 do_fire();
1305 if (special_explosion != NULL)
1307 seen = seen || special_explosion->seen;
1308 heard = heard || special_explosion->heard;
1312 void bolt::do_fire()
1314 initialise_fire();
1316 if (range < extra_range_used && range > 0)
1318 #ifdef DEBUG
1319 mprf(MSGCH_DIAGNOSTICS, "fire_beam() called on already done beam "
1320 "'%s' (item = '%s')", name.c_str(),
1321 item ? item->name(DESC_PLAIN).c_str() : "none");
1322 #endif
1323 return;
1326 apply_beam_conducts();
1327 cursor_control coff(false);
1329 #ifdef USE_TILE
1330 tile_beam = -1;
1332 if (item && !is_tracer && flavour == BEAM_MISSILE)
1334 const coord_def diff = target - source;
1335 tile_beam = tileidx_item_throw(*item, diff.x, diff.y);
1337 #endif
1339 msg_generated = false;
1340 if (!aimed_at_feet)
1342 choose_ray();
1343 // Take *one* step, so as not to hurt the source.
1344 ray.advance();
1347 #if defined(TARGET_OS_WINDOWS) && !defined(USE_TILE)
1348 // Before we start drawing the beam, turn buffering off.
1349 bool oldValue = true;
1350 if (!is_tracer)
1351 oldValue = set_buffering(false);
1352 #endif
1354 while (map_bounds(pos()))
1356 if (range_used() > range)
1358 ray.regress();
1359 extra_range_used++;
1360 ASSERT(range_used() >= range);
1361 break;
1364 path_taken.push_back(pos());
1366 if (!affects_nothing)
1367 affect_cell();
1369 if (range_used() > range)
1370 break;
1372 if (beam_cancelled)
1373 return;
1375 if (pos() == target)
1377 passed_target = true;
1378 if (stop_at_target())
1379 break;
1382 // Weapons of returning should find an inverse ray
1383 // through find_ray and setup_retrace, but they didn't
1384 // always in the past, and we don't want to crash
1385 // if they accidentally pass through a corner.
1386 ASSERT(!feat_is_solid(grd(pos()))
1387 || is_tracer && affects_wall(grd(pos()))
1388 || affects_nothing); // returning weapons
1390 const bool was_seen = seen;
1391 if (!was_seen && range > 0 && visible() && you.see_cell(pos()))
1392 seen = true;
1394 if (flavour != BEAM_VISUAL && !was_seen && seen && !is_tracer)
1396 mprf("%s appears from out of your range of vision.",
1397 article_a(name, false).c_str());
1400 // Reset chaos beams so that it won't be considered an invisible
1401 // enchantment beam for the purposes of animation.
1402 if (real_flavour == BEAM_CHAOS)
1403 flavour = real_flavour;
1405 // Actually draw the beam/missile/whatever, if the player can see
1406 // the cell.
1407 draw(pos());
1409 noise_generated = false;
1410 ray.advance();
1413 if (!map_bounds(pos()))
1415 ASSERT(!aimed_at_spot);
1417 int tries = std::max(GXM, GYM);
1418 while (!map_bounds(ray.pos()) && tries-- > 0)
1419 ray.regress();
1421 // Something bizarre happening if we can't get back onto the map.
1422 ASSERT(map_bounds(pos()));
1425 // The beam has terminated.
1426 if (!affects_nothing)
1427 affect_endpoint();
1429 // Tracers need nothing further.
1430 if (is_tracer || affects_nothing)
1431 return;
1433 // Canned msg for enchantments that affected no-one, but only if the
1434 // enchantment is yours (and it wasn't a chaos beam, since with chaos
1435 // enchantments are entirely random, and if it randomly attempts
1436 // something which ends up having no obvious effect then the player
1437 // isn't going to realise it).
1438 if (!msg_generated && !obvious_effect && is_enchantment()
1439 && real_flavour != BEAM_CHAOS && YOU_KILL(thrower))
1441 canned_msg(MSG_NOTHING_HAPPENS);
1444 // Reactions if a monster zapped the beam.
1445 if (!invalid_monster_index(beam_source))
1447 if (foe_info.hurt == 0 && friend_info.hurt > 0)
1448 xom_is_stimulated(128);
1449 else if (foe_info.helped > 0 && friend_info.helped == 0)
1450 xom_is_stimulated(128);
1452 // Allow friendlies to react to projectiles, except when in
1453 // sanctuary when pet_target can only be explicitly changed by
1454 // the player.
1455 const monster* mon = &menv[beam_source];
1456 if (foe_info.hurt > 0 && !mon->wont_attack() && !crawl_state.game_is_arena()
1457 && you.pet_target == MHITNOT && env.sanctuary_time <= 0)
1459 you.pet_target = beam_source;
1463 // That's it!
1464 #if defined(TARGET_OS_WINDOWS) && !defined(USE_TILE)
1465 set_buffering(oldValue);
1466 #endif
1469 // Returns damage taken by a monster from a "flavoured" (fire, ice, etc.)
1470 // attack -- damage from clouds and branded weapons handled elsewhere.
1471 int mons_adjust_flavoured(monster* mons, bolt &pbolt, int hurted,
1472 bool doFlavouredEffects)
1474 // If we're not doing flavoured effects, must be preliminary
1475 // damage check only.
1476 // Do not print messages or apply any side effects!
1477 int resist = 0;
1478 int original = hurted;
1480 switch (pbolt.flavour)
1482 case BEAM_FIRE:
1483 case BEAM_STEAM:
1484 hurted = resist_adjust_damage(
1485 mons,
1486 pbolt.flavour,
1487 (pbolt.flavour == BEAM_FIRE) ? mons->res_fire()
1488 : mons->res_steam(),
1489 hurted, true);
1491 if (!hurted)
1493 if (doFlavouredEffects)
1495 simple_monster_message(mons,
1496 (original > 0) ? " completely resists."
1497 : " appears unharmed.");
1500 else if (original > hurted)
1502 if (doFlavouredEffects)
1503 simple_monster_message(mons, " resists.");
1505 else if (original < hurted && doFlavouredEffects)
1507 if (mons->is_icy())
1508 simple_monster_message(mons, " melts!");
1509 else if (mons->type == MONS_BUSH)
1510 simple_monster_message(mons, " is on fire!");
1511 else if (pbolt.flavour == BEAM_FIRE)
1512 simple_monster_message(mons, " is burned terribly!");
1513 else
1514 simple_monster_message(mons, " is scalded terribly!");
1516 break;
1518 case BEAM_WATER:
1519 hurted = resist_adjust_damage(mons, pbolt.flavour,
1520 mons->res_asphyx(),
1521 hurted, true);
1522 if (doFlavouredEffects)
1524 if (!hurted)
1525 simple_monster_message(mons, " shrugs off the wave.");
1527 break;
1529 case BEAM_COLD:
1530 hurted = resist_adjust_damage(mons, pbolt.flavour,
1531 mons->res_cold(),
1532 hurted, true);
1533 if (!hurted)
1535 if (doFlavouredEffects)
1537 simple_monster_message(mons,
1538 (original > 0) ? " completely resists."
1539 : " appears unharmed.");
1542 else if (original > hurted)
1544 if (doFlavouredEffects)
1545 simple_monster_message(mons, " resists.");
1547 else if (original < hurted)
1549 if (doFlavouredEffects)
1550 simple_monster_message(mons, " is frozen!");
1552 break;
1554 case BEAM_ELECTRICITY:
1555 hurted = resist_adjust_damage(mons, pbolt.flavour,
1556 mons->res_elec(),
1557 hurted, true);
1558 if (!hurted)
1560 if (doFlavouredEffects)
1562 simple_monster_message(mons,
1563 (original > 0) ? " completely resists."
1564 : " appears unharmed.");
1567 break;
1569 case BEAM_ACID:
1571 const int res = mons->res_acid();
1572 hurted = resist_adjust_damage(mons, pbolt.flavour,
1573 res, hurted, true);
1574 if (!hurted)
1576 if (doFlavouredEffects)
1578 simple_monster_message(mons,
1579 (original > 0) ? " completely resists."
1580 : " appears unharmed.");
1583 else if (res <= 0 && doFlavouredEffects)
1585 corrode_monster(mons);
1587 break;
1590 case BEAM_POISON:
1592 int res = mons->res_poison();
1593 hurted = resist_adjust_damage(mons, pbolt.flavour, res,
1594 hurted, true);
1595 if (!hurted && res > 0)
1597 if (doFlavouredEffects)
1599 simple_monster_message(mons,
1600 (original > 0) ? " completely resists."
1601 : " appears unharmed.");
1604 else if (res <= 0 && doFlavouredEffects && !one_chance_in(3))
1605 poison_monster(mons, pbolt.whose_kill());
1607 break;
1610 case BEAM_POISON_ARROW:
1611 hurted = resist_adjust_damage(mons, pbolt.flavour,
1612 mons->res_poison(),
1613 hurted);
1614 if (hurted < original)
1616 if (doFlavouredEffects)
1618 simple_monster_message(mons, " partially resists.");
1620 // Poison arrow can poison any living thing regardless of
1621 // poison resistance. - bwr
1622 if (mons->has_lifeforce())
1623 poison_monster(mons, pbolt.whose_kill(), 2, true);
1626 else if (doFlavouredEffects)
1627 poison_monster(mons, pbolt.whose_kill(), 4);
1629 break;
1631 case BEAM_NEG:
1632 if (mons->res_negative_energy() == 3)
1634 if (doFlavouredEffects)
1635 simple_monster_message(mons, " completely resists.");
1637 hurted = 0;
1639 else
1641 // Early out if no side effects.
1642 if (!doFlavouredEffects)
1643 return (hurted);
1645 if (mons->observable())
1646 pbolt.obvious_effect = true;
1648 mons->drain_exp(pbolt.agent());
1650 if (YOU_KILL(pbolt.thrower))
1651 did_god_conduct(DID_NECROMANCY, 2, pbolt.effect_known);
1653 break;
1655 case BEAM_MIASMA:
1656 if (mons->res_rotting())
1658 if (doFlavouredEffects)
1659 simple_monster_message(mons, " completely resists.");
1661 hurted = 0;
1663 else
1665 // Early out for tracer/no side effects.
1666 if (!doFlavouredEffects)
1667 return (hurted);
1669 miasma_monster(mons, pbolt.whose_kill());
1671 if (YOU_KILL(pbolt.thrower))
1672 did_god_conduct(DID_UNCLEAN, 2, pbolt.effect_known);
1674 break;
1676 case BEAM_HOLY:
1678 // Cleansing flame.
1679 const int rhe = mons->res_holy_energy(pbolt.agent());
1680 if (rhe > 0)
1681 hurted = 0;
1682 else if (rhe == 0)
1683 hurted /= 2;
1684 else if (rhe < -1)
1685 hurted = (hurted * 3) / 2;
1687 if (doFlavouredEffects)
1689 simple_monster_message(mons,
1690 hurted == 0 ? " appears unharmed."
1691 : " writhes in agony!");
1693 break;
1696 case BEAM_ICE:
1697 // ice - about 50% of damage is cold, other 50% is impact and
1698 // can't be resisted (except by AC, of course)
1699 hurted = resist_adjust_damage(mons, pbolt.flavour,
1700 mons->res_cold(), hurted,
1701 true);
1702 if (hurted < original)
1704 if (doFlavouredEffects)
1705 simple_monster_message(mons, " partially resists.");
1707 else if (hurted > original)
1709 if (doFlavouredEffects)
1710 simple_monster_message(mons, " is frozen!");
1712 break;
1714 case BEAM_LAVA:
1715 hurted = resist_adjust_damage(mons, pbolt.flavour,
1716 mons->res_fire(), hurted, true);
1718 if (hurted < original)
1720 if (doFlavouredEffects)
1721 simple_monster_message(mons, " partially resists.");
1723 else if (hurted > original)
1725 if (mons->is_icy())
1727 if (doFlavouredEffects)
1728 simple_monster_message(mons, " melts!");
1730 else
1732 if (doFlavouredEffects)
1733 simple_monster_message(mons, " is burned terribly!");
1736 break;
1738 case BEAM_HELLFIRE:
1739 resist = mons->res_hellfire();
1740 if (resist > 0)
1742 if (doFlavouredEffects)
1744 simple_monster_message(mons,
1745 (original > 0) ? " completely resists."
1746 : " appears unharmed.");
1749 hurted = 0;
1751 else if (resist < 0)
1753 if (mons->is_icy())
1755 if (doFlavouredEffects)
1756 simple_monster_message(mons, " melts!");
1758 else
1760 if (doFlavouredEffects)
1761 simple_monster_message(mons, " is burned terribly!");
1764 hurted *= 12; // hellfire
1765 hurted /= 10;
1767 break;
1769 case BEAM_LIGHT:
1770 if (mons->invisible())
1771 hurted = 0;
1772 else if (mons_genus(mons->type) == MONS_VAMPIRE)
1773 hurted += hurted / 2;
1774 if (!hurted)
1776 if (doFlavouredEffects)
1778 if (original > 0)
1779 simple_monster_message(mons, " appears unharmed.");
1780 else if (mons->observable())
1781 mprf("The beam of light passes harmlessly through %s.",
1782 mons->name(DESC_NOCAP_THE, true).c_str());
1785 else if (original < hurted)
1787 if (doFlavouredEffects)
1788 simple_monster_message(mons, " is burned terribly!");
1790 break;
1792 case BEAM_SPORE:
1793 if (mons->type == MONS_BALLISTOMYCETE)
1794 hurted = 0;
1795 break;
1797 case BEAM_AIR:
1798 if (mons->res_wind() > 0)
1799 hurted = 0;
1800 else if (mons->flight_mode())
1801 hurted += hurted / 2;
1802 if (!hurted)
1804 if (doFlavouredEffects)
1805 simple_monster_message(mons, " is harmlessly tossed around.");
1807 else if (original < hurted)
1809 if (doFlavouredEffects)
1810 simple_monster_message(mons, " gets badly buffeted.");
1812 break;
1814 default:
1815 break;
1818 return (hurted);
1821 static bool _monster_resists_mass_enchantment(monster* mons,
1822 enchant_type wh_enchant,
1823 int pow,
1824 bool* did_msg)
1826 // Assuming that the only mass charm is control undead.
1827 if (wh_enchant == ENCH_CHARM)
1829 if (mons->friendly())
1830 return (true);
1832 if (mons->holiness() != MH_UNDEAD)
1833 return (true);
1835 int res_margin = mons->check_res_magic(pow);
1836 if (res_margin > 0)
1838 if (simple_monster_message(mons,
1839 mons_resist_string(mons, res_margin).c_str()))
1841 *did_msg = true;
1843 return (true);
1846 else if (wh_enchant == ENCH_CONFUSION
1847 || mons->holiness() == MH_NATURAL)
1849 if (wh_enchant == ENCH_CONFUSION
1850 && !mons_class_is_confusable(mons->type))
1852 return (true);
1855 int res_margin = mons->check_res_magic(pow);
1856 if (res_margin > 0)
1858 if (simple_monster_message(mons,
1859 mons_resist_string(mons, res_margin).c_str()))
1861 *did_msg = true;
1863 return (true);
1866 // Mass enchantments around lots of plants/fungi shouldn't cause a flood
1867 // of "is unaffected" messages. --Eino
1868 else if (mons_is_firewood(mons))
1870 return (true);
1872 else // trying to enchant an unnatural creature doesn't work
1874 if (simple_monster_message(mons, " is unaffected."))
1875 *did_msg = true;
1876 return (true);
1879 return (false);
1882 // Enchants all monsters in player's sight.
1883 // If m_succumbed is non-NULL, will be set to the number of monsters that
1884 // were enchanted. If m_attempted is non-NULL, will be set to the number of
1885 // monsters that we tried to enchant.
1886 void mass_enchantment(enchant_type wh_enchant, int pow, int origin,
1887 int *m_succumbed, int *m_attempted)
1889 bool did_msg = false;
1891 if (m_succumbed)
1892 *m_succumbed = 0;
1893 if (m_attempted)
1894 *m_attempted = 0;
1896 pow = std::min(pow, 200);
1898 const kill_category kc = (origin == MHITYOU ? KC_YOU : KC_OTHER);
1900 for (monster_iterator mi(you.get_los()); mi; ++mi)
1902 if (mi->has_ench(wh_enchant))
1903 continue;
1905 if (m_attempted)
1906 ++*m_attempted;
1908 if (_monster_resists_mass_enchantment(*mi, wh_enchant, pow, &did_msg))
1909 continue;
1911 if (mi->add_ench(mon_enchant(wh_enchant, 0, kc)))
1913 if (m_succumbed)
1914 ++*m_succumbed;
1916 // Do messaging.
1917 const char* msg;
1918 switch (wh_enchant)
1920 case ENCH_FEAR: msg = " looks frightened!"; break;
1921 case ENCH_CONFUSION: msg = " looks rather confused."; break;
1922 case ENCH_CHARM: msg = " submits to your will."; break;
1923 default: msg = NULL; break;
1925 if (msg && simple_monster_message(*mi, msg))
1926 did_msg = true;
1928 // Extra check for fear (monster needs to reevaluate behaviour).
1929 if (wh_enchant == ENCH_FEAR)
1930 behaviour_event(*mi, ME_SCARE, origin);
1934 if (!did_msg)
1935 canned_msg(MSG_NOTHING_HAPPENS);
1938 void bolt::apply_bolt_paralysis(monster* mons)
1940 if (!mons->paralysed()
1941 && mons->add_ench(ENCH_PARALYSIS)
1942 && (!mons->petrified()
1943 || mons->has_ench(ENCH_PETRIFYING)))
1945 if (simple_monster_message(mons, " suddenly stops moving!"))
1946 obvious_effect = true;
1948 mons_check_pool(mons, mons->pos(), killer(), beam_source);
1952 // Petrification works in two stages. First the monster is slowed down in
1953 // all of its actions and cannot move away (petrifying), and when that times
1954 // out it remains properly petrified (no movement or actions). The second
1955 // part is similar to paralysis, except that insubstantial monsters can't be
1956 // affected and that stabbing damage is drastically reduced.
1957 void bolt::apply_bolt_petrify(monster* mons)
1959 int petrifying = mons->has_ench(ENCH_PETRIFYING);
1960 if (mons->petrified())
1962 // If the petrifying is not yet finished, we can force it to happen
1963 // right away by casting again. Otherwise, the spell has no further
1964 // effect.
1965 if (petrifying > 0)
1967 mons->del_ench(ENCH_PETRIFYING, true);
1968 if (!mons->has_ench(ENCH_PARALYSIS)
1969 && simple_monster_message(mons, " stops moving altogether!"))
1971 obvious_effect = true;
1975 else if (mons->add_ench(ENCH_PETRIFIED)
1976 && !mons->has_ench(ENCH_PARALYSIS))
1978 // Add both the petrifying and the petrified enchantment. The former
1979 // will run out sooner and result in plain petrification behaviour.
1980 mons->add_ench(ENCH_PETRIFYING);
1981 if (simple_monster_message(mons, " is moving more slowly."))
1982 obvious_effect = true;
1984 mons_check_pool(mons, mons->pos(), killer(), beam_source);
1988 static bool _curare_hits_monster(actor *agent, monster* mons,
1989 kill_category who, int levels)
1991 if (!mons->alive())
1992 return (false);
1994 if (mons->res_poison() > 0)
1995 return (false);
1997 poison_monster(mons, who, levels, false);
1999 int hurted = 0;
2001 if (!mons->res_asphyx())
2003 hurted = roll_dice(2, 6);
2005 if (hurted)
2007 simple_monster_message(mons, " convulses.");
2008 mons->hurt(agent, hurted, BEAM_POISON);
2012 if (mons->alive())
2013 enchant_monster_with_flavour(mons, agent, BEAM_SLOW);
2015 // Deities take notice.
2016 if (who == KC_YOU)
2017 did_god_conduct(DID_POISON, 5 + random2(3));
2019 return (hurted > 0);
2022 // Actually poisons a monster (with message).
2023 bool poison_monster(monster* mons, kill_category who, int levels,
2024 bool force, bool verbose)
2026 if (!mons->alive())
2027 return (false);
2029 if ((!force && mons->res_poison() > 0) || levels <= 0)
2030 return (false);
2032 const mon_enchant old_pois = mons->get_ench(ENCH_POISON);
2033 mons->add_ench(mon_enchant(ENCH_POISON, levels, who));
2034 const mon_enchant new_pois = mons->get_ench(ENCH_POISON);
2036 // Actually do the poisoning. The order is important here.
2037 if (new_pois.degree > old_pois.degree)
2039 if (verbose)
2041 simple_monster_message(mons,
2042 old_pois.degree > 0 ? " looks even sicker."
2043 : " is poisoned.");
2045 behaviour_event(mons, ME_ANNOY, (who == KC_YOU) ? MHITYOU : MHITNOT);
2048 // Finally, take care of deity preferences.
2049 if (who == KC_YOU)
2050 did_god_conduct(DID_POISON, 5 + random2(3));
2052 return (new_pois.degree > old_pois.degree);
2055 // Actually poisons, rots, and/or slows a monster with miasma (with
2056 // message).
2057 bool miasma_monster(monster* mons, kill_category who)
2059 if (!mons->alive())
2060 return (false);
2062 if (mons->res_rotting())
2063 return (false);
2065 bool success = poison_monster(mons, who);
2067 if (mons->max_hit_points > 4 && coinflip())
2069 mons->max_hit_points--;
2070 mons->hit_points = std::min(mons->max_hit_points,
2071 mons->hit_points);
2072 success = true;
2075 if (one_chance_in(3))
2077 bolt beam;
2078 beam.flavour = BEAM_SLOW;
2079 beam.apply_enchantment_to_monster(mons);
2080 success = true;
2083 return (success);
2086 // Actually napalms a monster (with message).
2087 bool napalm_monster(monster* mons, kill_category who, int levels,
2088 bool verbose)
2090 if (!mons->alive())
2091 return (false);
2093 if (mons->res_sticky_flame() || levels <= 0)
2094 return (false);
2096 const mon_enchant old_flame = mons->get_ench(ENCH_STICKY_FLAME);
2097 mons->add_ench(mon_enchant(ENCH_STICKY_FLAME, levels, who));
2098 const mon_enchant new_flame = mons->get_ench(ENCH_STICKY_FLAME);
2100 // Actually do the napalming. The order is important here.
2101 if (new_flame.degree > old_flame.degree)
2103 if (verbose)
2104 simple_monster_message(mons, " is covered in liquid flames!");
2105 behaviour_event(mons, ME_WHACK, who == KC_YOU ? MHITYOU : MHITNOT);
2108 return (new_flame.degree > old_flame.degree);
2111 // Used by monsters in "planning" which spell to cast. Fires off a "tracer"
2112 // which tells the monster what it'll hit if it breathes/casts etc.
2114 // The output from this tracer function is written into the
2115 // tracer_info variables (friend_info and foe_info.)
2117 // Note that beam properties must be set, as the tracer will take them
2118 // into account, as well as the monster's intelligence.
2119 void fire_tracer(const monster* mons, bolt &pbolt, bool explode_only)
2121 // Don't fiddle with any input parameters other than tracer stuff!
2122 pbolt.is_tracer = true;
2123 pbolt.source = mons->pos();
2124 pbolt.beam_source = mons->mindex();
2125 pbolt.can_see_invis = mons->can_see_invisible();
2126 pbolt.smart_monster = (mons_intel(mons) >= I_NORMAL);
2127 pbolt.attitude = mons_attitude(mons);
2129 // Init tracer variables.
2130 pbolt.foe_info.reset();
2131 pbolt.friend_info.reset();
2133 // Clear misc
2134 pbolt.reflections = 0;
2135 pbolt.bounces = 0;
2137 // If there's a specifically requested foe_ratio, honour it.
2138 if (!pbolt.foe_ratio)
2140 pbolt.foe_ratio = 80; // default - see mons_should_fire()
2142 // Foe ratio for summoning greater demons & undead -- they may be
2143 // summoned, but they're hostile and would love nothing better
2144 // than to nuke the player and his minions.
2145 if (mons_att_wont_attack(pbolt.attitude)
2146 && !mons_att_wont_attack(mons->attitude))
2148 pbolt.foe_ratio = 25;
2152 pbolt.in_explosion_phase = false;
2154 // Fire!
2155 if (explode_only)
2156 pbolt.explode(false);
2157 else
2158 pbolt.fire();
2160 // Unset tracer flag (convenience).
2161 pbolt.is_tracer = false;
2164 // When a mimic is hit by a ranged attack, it teleports away (the slow
2165 // way) and changes its appearance - the appearance change is in
2166 // monster_teleport() in mon-stuff.cc.
2167 void mimic_alert(monster* mimic)
2169 if (!mimic->alive())
2170 return;
2172 bool should_id = !testbits(mimic->flags, MF_KNOWN_MIMIC)
2173 && mimic->observable();
2175 // If we got here, we at least got a resists message, if not
2176 // a full wounds printing. Thus, might as well id the mimic.
2177 if (mimic->has_ench(ENCH_TP) || mons_is_feat_mimic(mimic->type))
2179 if (should_id)
2180 discover_mimic(mimic);
2182 return;
2185 const bool instant_tele = !one_chance_in(3);
2186 monster_teleport(mimic, instant_tele);
2188 // At least for this short while, we know it's a mimic.
2189 if (!instant_tele && should_id)
2190 discover_mimic(mimic);
2193 static void _create_feat_at(coord_def center,
2194 dungeon_feature_type overwriteable,
2195 dungeon_feature_type newfeat)
2197 if (grd(center) == overwriteable)
2198 dungeon_terrain_changed(center, newfeat, true, false, true);
2201 static void _create_feat_splash(coord_def center,
2202 dungeon_feature_type overwriteable,
2203 dungeon_feature_type newfeat,
2204 int radius,
2205 int nattempts)
2207 // Always affect center.
2208 _create_feat_at(center, overwriteable, newfeat);
2209 for (int i = 0; i < nattempts; ++i)
2211 const coord_def newp(dgn_random_point_visible_from(center, radius));
2212 if (newp.origin() || grd(newp) != overwriteable)
2213 continue;
2214 _create_feat_at(newp, overwriteable, newfeat);
2218 bool bolt::is_bouncy(dungeon_feature_type feat) const
2220 if (real_flavour == BEAM_CHAOS && feat_is_solid(feat))
2221 return (true);
2223 if (is_enchantment())
2224 return (false);
2226 if (flavour == BEAM_ELECTRICITY && feat != DNGN_METAL_WALL
2227 && feat != DNGN_TREE && feat != DNGN_SWAMP_TREE)
2229 return (true);
2232 if ((flavour == BEAM_FIRE || flavour == BEAM_COLD)
2233 && feat == DNGN_GREEN_CRYSTAL_WALL)
2235 return (true);
2238 return (false);
2241 static int _potion_beam_flavour_to_colour(beam_type flavour)
2243 switch (flavour)
2245 case BEAM_POTION_STINKING_CLOUD:
2246 return (GREEN);
2248 case BEAM_POTION_POISON:
2249 return (coinflip() ? GREEN : LIGHTGREEN);
2251 case BEAM_POTION_MIASMA:
2252 case BEAM_POTION_BLACK_SMOKE:
2253 return (DARKGREY);
2255 case BEAM_POTION_STEAM:
2256 case BEAM_POTION_GREY_SMOKE:
2257 return (LIGHTGREY);
2259 case BEAM_POTION_FIRE:
2260 return (coinflip() ? RED : LIGHTRED);
2262 case BEAM_POTION_COLD:
2263 return (coinflip() ? BLUE : LIGHTBLUE);
2265 case BEAM_POTION_BLUE_SMOKE:
2266 return (LIGHTBLUE);
2268 case BEAM_POTION_PURPLE_SMOKE:
2269 return (MAGENTA);
2271 case BEAM_POTION_RANDOM:
2272 default:
2273 // Leave it the colour of the potion, the clouds will colour
2274 // themselves on the next refresh. -- bwr
2275 return (-1);
2277 return (-1);
2280 void bolt::affect_endpoint()
2282 if (special_explosion)
2284 special_explosion->refine_for_explosion();
2285 special_explosion->target = pos();
2286 special_explosion->explode();
2288 // XXX: we're significantly overcounting here.
2289 foe_info += special_explosion->foe_info;
2290 friend_info += special_explosion->friend_info;
2291 beam_cancelled = beam_cancelled || special_explosion->beam_cancelled;
2294 // Leave an object, if applicable.
2295 if (drop_item && item)
2296 drop_object();
2298 if (is_explosion)
2300 refine_for_explosion();
2301 target = pos();
2302 explode();
2303 return;
2306 if (is_tracer)
2307 return;
2309 if (!is_explosion && !noise_generated)
2311 noisy(loudness, pos(), beam_source);
2312 noise_generated = true;
2315 if (origin_spell == SPELL_PRIMAL_WAVE) // &&coinflip()
2317 if (you.see_cell(pos()))
2319 mprf("The wave splashes down.");
2320 noisy(25, pos());
2322 else
2324 noisy(25, pos(), "You hear a splash.");
2326 _create_feat_splash(pos(),
2327 DNGN_FLOOR,
2328 DNGN_SHALLOW_WATER,
2330 random_range(1, 9, 2));
2333 // FIXME: why don't these just have is_explosion set?
2334 // They don't explode in tracers: why not?
2335 if (name == "orb of electricity"
2336 || name == "metal orb"
2337 || name == "great blast of cold")
2339 target = pos();
2340 refine_for_explosion();
2341 explode();
2344 if (name == "noxious blast")
2345 big_cloud(CLOUD_STINK, agent(), pos(), 0, 7 + random2(5));
2347 if (name == "blast of poison")
2348 big_cloud(CLOUD_POISON, agent(), pos(), 0, 7 + random2(5));
2350 if (origin_spell == SPELL_HOLY_BREATH)
2351 big_cloud(CLOUD_HOLY_FLAMES, agent(), pos(), 0, 7 + random2(5));
2353 if (origin_spell == SPELL_FIRE_BREATH && is_big_cloud)
2354 big_cloud(CLOUD_FIRE, agent(), pos(), 0, 7 + random2(5));
2356 if (name == "foul vapour")
2358 // death drake; swamp drakes handled earlier
2359 ASSERT(flavour == BEAM_MIASMA);
2360 big_cloud(CLOUD_MIASMA, agent(), pos(), 0, 9);
2363 if (name == "freezing blast")
2364 big_cloud(CLOUD_COLD, agent(), pos(), random_range(10, 15), 9);
2366 if ((name == "fiery breath" && you.species == SP_RED_DRACONIAN)
2367 || name == "searing blast") // monster and player red draconian breath abilities
2369 place_cloud(CLOUD_FIRE, pos(), 5 + random2(5), agent());
2373 bool bolt::stop_at_target() const
2375 return (is_explosion || is_big_cloud || aimed_at_spot);
2378 void bolt::drop_object()
2380 ASSERT(item != NULL && item->defined());
2382 // Conditions: beam is missile and not tracer.
2383 if (is_tracer || !was_missile)
2384 return;
2386 // Summoned creatures' thrown items disappear.
2387 if (item->flags & ISFLAG_SUMMONED)
2389 if (you.see_cell(pos()))
2391 mprf("%s %s!",
2392 item->name(DESC_CAP_THE).c_str(),
2393 summoned_poof_msg(beam_source, *item).c_str());
2395 item_was_destroyed(*item, beam_source);
2396 return;
2399 if (!thrown_object_destroyed(item, pos()))
2401 if (item->sub_type == MI_THROWING_NET)
2403 monster* m = monster_at(pos());
2404 // Player or monster at position is caught in net.
2405 if (you.pos() == pos() && you.attribute[ATTR_HELD]
2406 || m && m->caught())
2408 // If no trapping net found mark this one.
2409 if (get_trapping_net(pos(), true) == NON_ITEM)
2410 set_item_stationary(*item);
2414 copy_item_to_grid(*item, pos(), 1);
2416 else if (item->sub_type == MI_LARGE_ROCK)
2418 // Large rocks mulch to stone.
2419 bool in_view = you.see_cell(pos());
2420 if (in_view)
2421 mprf("%s shatters into pieces!", item->name(DESC_CAP_THE).c_str());
2422 noisy(12, pos(), in_view ? NULL : "You hear a cracking sound!");
2424 item->sub_type = MI_STONE;
2425 item->quantity = 10 + random2(41);
2426 // Remove thrown flag: we might not want to pick up the stones.
2427 item->flags &= ~ISFLAG_THROWN;
2429 copy_item_to_grid(*item, pos(), item->quantity);
2431 else
2433 item_was_destroyed(*item, NON_MONSTER);
2437 // Returns true if the beam hits the player, fuzzing the beam if necessary
2438 // for monsters without see invis firing tracers at the player.
2439 bool bolt::found_player() const
2441 const bool needs_fuzz = (is_tracer && !can_see_invis
2442 && you.invisible() && !YOU_KILL(thrower));
2443 const int dist = needs_fuzz? 2 : 0;
2445 return (grid_distance(pos(), you.pos()) <= dist);
2448 void bolt::affect_ground()
2450 // Explosions only have an effect during their explosion phase.
2451 // Special cases can be handled here.
2452 if (is_explosion && !in_explosion_phase)
2453 return;
2455 if (is_tracer)
2456 return;
2458 // Spore explosions might spawn a fungus. The spore explosion
2459 // covers 21 tiles in open space, so the expected number of spores
2460 // produced is the x in x_chance_in_y() in the conditional below.
2461 if (is_explosion && flavour == BEAM_SPORE
2462 && this->agent() && !this->agent()->is_summoned())
2464 if (env.grid(pos()) >= DNGN_FLOOR_MIN
2465 && env.grid(pos())<= DNGN_FLOOR_MAX)
2467 env.pgrid(pos()) |= FPROP_MOLD;
2470 if (x_chance_in_y(2, 21)
2471 && !crawl_state.game_is_zotdef() // Turn off in Zotdef
2472 && mons_class_can_pass(MONS_BALLISTOMYCETE, env.grid(pos()))
2473 && !actor_at(pos()))
2475 beh_type beh = attitude_creation_behavior(this->attitude);
2477 if (crawl_state.game_is_arena())
2479 beh = coinflip() ? BEH_FRIENDLY : BEH_HOSTILE;
2482 int rc = create_monster(mgen_data(MONS_BALLISTOMYCETE,
2483 beh,
2484 NULL,
2487 pos(),
2488 MHITNOT,
2489 MG_FORCE_PLACE));
2491 if (rc != -1)
2493 remove_mold(pos());
2494 if (you.see_cell(pos()))
2495 mpr("A fungus suddenly grows.");
2501 if (affects_items)
2503 const int burn_power = is_explosion ? 5 :
2504 is_beam ? 3
2505 : 2;
2506 expose_items_to_element(flavour, pos(), burn_power);
2507 affect_place_clouds();
2511 bool bolt::is_fiery() const
2513 return (flavour == BEAM_FIRE
2514 || flavour == BEAM_HELLFIRE
2515 || flavour == BEAM_LAVA);
2518 bool bolt::is_superhot() const
2520 if (!is_fiery() && flavour != BEAM_ELECTRICITY)
2521 return (false);
2523 return (name == "bolt of fire"
2524 || name == "bolt of magma"
2525 || name == "fireball"
2526 || name == "bolt of lightning"
2527 || name.find("hellfire") != std::string::npos
2528 && in_explosion_phase);
2531 maybe_bool bolt::affects_wall(dungeon_feature_type wall) const
2533 // digging
2534 if (flavour == BEAM_DIGGING
2535 && (wall == DNGN_ROCK_WALL || wall == DNGN_CLEAR_ROCK_WALL
2536 || wall == DNGN_SLIMY_WALL || wall == DNGN_GRATE))
2538 return (B_TRUE);
2541 if (is_fiery() && (wall == DNGN_WAX_WALL || feat_is_tree(wall)))
2542 return (is_superhot() ? B_TRUE : B_MAYBE);
2544 if (flavour == BEAM_ELECTRICITY && feat_is_tree(wall))
2545 return (is_superhot() ? B_TRUE : B_MAYBE);
2547 if (flavour == BEAM_DISINTEGRATION && damage.num >= 3
2548 || flavour == BEAM_NUKE)
2550 if (wall == DNGN_ROCK_WALL
2551 || wall == DNGN_SLIMY_WALL
2552 || wall == DNGN_WAX_WALL
2553 || wall == DNGN_CLEAR_ROCK_WALL
2554 || wall == DNGN_GRATE
2555 || wall == DNGN_GRANITE_STATUE
2556 || wall == DNGN_ORCISH_IDOL
2557 || wall == DNGN_TREE
2558 || wall == DNGN_SWAMP_TREE
2559 || wall == DNGN_CLOSED_DOOR
2560 || wall == DNGN_DETECTED_SECRET_DOOR
2561 || wall == DNGN_SECRET_DOOR)
2562 return (B_TRUE);
2565 // Lee's Rapid Deconstruction
2566 if (flavour == BEAM_FRAG)
2567 return (B_TRUE); // smite targetting, we don't care
2569 return (B_FALSE);
2572 void bolt::affect_place_clouds()
2574 if (in_explosion_phase)
2575 affect_place_explosion_clouds();
2577 const coord_def p = pos();
2579 // Is there already a cloud here?
2580 const int cloudidx = env.cgrid(p);
2581 if (cloudidx != EMPTY_CLOUD)
2583 cloud_type& ctype = env.cloud[cloudidx].type;
2585 // fire cancelling cold & vice versa
2586 if ((ctype == CLOUD_COLD
2587 && (flavour == BEAM_FIRE || flavour == BEAM_LAVA))
2588 || (ctype == CLOUD_FIRE && flavour == BEAM_COLD))
2590 if (player_can_hear(p))
2591 mpr("You hear a sizzling sound!", MSGCH_SOUND);
2593 delete_cloud(cloudidx);
2594 extra_range_used += 5;
2596 return;
2599 // No clouds here, free to make new ones.
2600 const dungeon_feature_type feat = grd(p);
2602 if (name == "blast of poison")
2603 place_cloud(CLOUD_POISON, p, random2(4) + 2, agent());
2605 if (origin_spell == SPELL_HOLY_BREATH)
2606 place_cloud(CLOUD_HOLY_FLAMES, p, random2(4) + 2, agent());
2608 if (origin_spell == SPELL_FIRE_BREATH && is_big_cloud)
2609 place_cloud(CLOUD_FIRE, p,random2(4) + 2, agent());
2611 // Fire/cold over water/lava
2612 if (feat == DNGN_LAVA && flavour == BEAM_COLD
2613 || feat_is_watery(feat) && is_fiery())
2615 place_cloud(CLOUD_STEAM, p, 2 + random2(5), agent());
2618 if (feat_is_watery(feat) && flavour == BEAM_COLD
2619 && damage.num * damage.size > 35)
2621 place_cloud(CLOUD_COLD, p, damage.num * damage.size / 30 + 1, agent());
2624 if (name == "great blast of cold")
2625 place_cloud(CLOUD_COLD, p, random2(5) + 3, agent());
2627 if (name == "ball of steam")
2628 place_cloud(CLOUD_STEAM, p, random2(5) + 2, agent());
2630 if (flavour == BEAM_MIASMA)
2631 place_cloud(CLOUD_MIASMA, p, random2(5) + 2, agent());
2633 if (name == "poison gas")
2634 place_cloud(CLOUD_POISON, p, random2(4) + 3, agent());
2636 if (name == "blast of choking fumes")
2637 place_cloud(CLOUD_STINK, p, random2(4) + 3, agent());
2640 void bolt::affect_place_explosion_clouds()
2642 const coord_def p = pos();
2644 // First check: fire/cold over water/lava.
2645 if (grd(p) == DNGN_LAVA && flavour == BEAM_COLD
2646 || feat_is_watery(grd(p)) && is_fiery())
2648 place_cloud(CLOUD_STEAM, p, 2 + random2(5), agent());
2649 return;
2652 if (flavour >= BEAM_POTION_STINKING_CLOUD && flavour <= BEAM_POTION_RANDOM)
2654 const int duration = roll_dice(2, 3 + ench_power / 20);
2655 cloud_type cl_type;
2657 switch (flavour)
2659 case BEAM_POTION_STINKING_CLOUD:
2660 case BEAM_POTION_POISON:
2661 case BEAM_POTION_MIASMA:
2662 case BEAM_POTION_STEAM:
2663 case BEAM_POTION_FIRE:
2664 case BEAM_POTION_COLD:
2665 case BEAM_POTION_BLACK_SMOKE:
2666 case BEAM_POTION_GREY_SMOKE:
2667 case BEAM_POTION_BLUE_SMOKE:
2668 case BEAM_POTION_PURPLE_SMOKE:
2669 case BEAM_POTION_RAIN:
2670 case BEAM_POTION_MUTAGENIC:
2671 cl_type = beam2cloud(flavour);
2672 break;
2674 case BEAM_POTION_RANDOM:
2675 switch (random2(10))
2677 case 0: cl_type = CLOUD_FIRE; break;
2678 case 1: cl_type = CLOUD_STINK; break;
2679 case 2: cl_type = CLOUD_COLD; break;
2680 case 3: cl_type = CLOUD_POISON; break;
2681 case 4: cl_type = CLOUD_BLACK_SMOKE; break;
2682 case 5: cl_type = CLOUD_GREY_SMOKE; break;
2683 case 6: cl_type = CLOUD_BLUE_SMOKE; break;
2684 case 7: cl_type = CLOUD_PURPLE_SMOKE; break;
2685 default: cl_type = CLOUD_STEAM; break;
2687 break;
2689 default:
2690 cl_type = CLOUD_STEAM;
2691 break;
2694 place_cloud(cl_type, p, duration, agent());
2697 // then check for more specific explosion cloud types.
2698 if (name == "ice storm")
2699 place_cloud(CLOUD_COLD, p, 2 + random2avg(5,2), agent());
2701 if (name == "stinking cloud")
2703 const int duration = 1 + random2(4) + random2((ench_power / 50) + 1);
2704 place_cloud(CLOUD_STINK, p, duration, agent());
2707 if (name == "great blast of fire")
2709 int duration = 1 + random2(5) + roll_dice(2, ench_power / 5);
2711 if (duration > 20)
2712 duration = 20 + random2(4);
2714 place_cloud(CLOUD_FIRE, p, duration, agent());
2716 if (grd(p) == DNGN_FLOOR && !monster_at(p) && one_chance_in(4))
2718 const god_type god =
2719 (crawl_state.is_god_acting()) ? crawl_state.which_god_acting()
2720 : GOD_NO_GOD;
2721 const beh_type att =
2722 (whose_kill() == KC_OTHER ? BEH_HOSTILE : BEH_FRIENDLY);
2724 actor* summ = agent();
2725 mgen_data mg(MONS_FIRE_VORTEX, att, summ, 2, SPELL_FIRE_STORM,
2726 p, MHITNOT, 0, god);
2728 // Spell-summoned monsters need to have a live summoner.
2729 if (summ == NULL || !summ->alive())
2731 if (!source_name.empty())
2732 mg.non_actor_summoner = source_name;
2733 else if (god != GOD_NO_GOD)
2734 mg.non_actor_summoner = god_name(god);
2737 mons_place(mg);
2742 // A little helper function to handle the calling of ouch()...
2743 void bolt::internal_ouch(int dam)
2745 monster* monst = NULL;
2746 if (!invalid_monster_index(beam_source) && menv[beam_source].type != -1)
2747 monst = &menv[beam_source];
2749 const char *what = aux_source.empty() ? name.c_str() : aux_source.c_str();
2751 if (YOU_KILL(thrower) && you.duration[DUR_QUAD_DAMAGE])
2752 dam *= 4;
2754 // The order of this is important.
2755 if (monst && (monst->type == MONS_GIANT_SPORE
2756 || monst->type == MONS_BALL_LIGHTNING
2757 || monst->type == MONS_HYPERACTIVE_BALLISTOMYCETE))
2759 ouch(dam, beam_source, KILLED_BY_SPORE, aux_source.c_str(), true,
2760 source_name.empty() ? NULL : source_name.c_str());
2762 else if (flavour == BEAM_DISINTEGRATION || flavour == BEAM_NUKE)
2764 ouch(dam, beam_source, KILLED_BY_DISINT, what, true,
2765 source_name.empty() ? NULL : source_name.c_str());
2767 else if (YOU_KILL(thrower) && aux_source.empty())
2769 if (reflections > 0)
2770 ouch(dam, reflector, KILLED_BY_REFLECTION, name.c_str());
2771 else if (bounces > 0)
2772 ouch(dam, NON_MONSTER, KILLED_BY_BOUNCE, name.c_str());
2773 else
2775 if (aimed_at_feet && effect_known)
2776 ouch(dam, NON_MONSTER, KILLED_BY_SELF_AIMED, name.c_str());
2777 else
2778 ouch(dam, NON_MONSTER, KILLED_BY_TARGETING);
2781 else if (MON_KILL(thrower))
2782 ouch(dam, beam_source, KILLED_BY_BEAM, aux_source.c_str(), true,
2783 source_name.empty() ? NULL : source_name.c_str());
2784 else // KILL_MISC || (YOU_KILL && aux_source)
2785 ouch(dam, beam_source, KILLED_BY_WILD_MAGIC, aux_source.c_str());
2788 // [ds] Apply a fuzz if the monster lacks see invisible and is trying to target
2789 // an invisible player. This makes invisibility slightly more powerful.
2790 bool bolt::fuzz_invis_tracer()
2792 // Did the monster have a rough idea of where you are?
2793 int dist = grid_distance(target, you.pos());
2795 // No, ditch this.
2796 if (dist > 2)
2797 return (false);
2799 const int beam_src = beam_source_as_target();
2800 if (beam_src != MHITNOT && beam_src != MHITYOU)
2802 // Monsters that can sense invisible
2803 const monster* mon = &menv[beam_src];
2804 if (mons_sense_invis(mon))
2805 return (dist == 0);
2808 // Apply fuzz now.
2809 coord_def fuzz(random_range(-2, 2), random_range(-2, 2));
2810 coord_def newtarget = target + fuzz;
2812 if (in_bounds(newtarget))
2813 target = newtarget;
2815 // Fire away!
2816 return (true);
2819 // A first step towards to-hit sanity for beams. We're still being
2820 // very kind to the player, but it should be fairer to monsters than
2821 // 4.0.
2822 static bool _test_beam_hit(int attack, int defence, bool is_beam,
2823 bool deflect, bool repel, defer_rand &r)
2825 if (attack == AUTOMATIC_HIT)
2826 return (true);
2828 if (is_beam && deflect)
2830 attack = r[0].random2(attack * 2) / 3;
2832 else if (is_beam && repel)
2834 if (attack >= 2)
2835 attack = r[0].random_range((attack + 1) / 2 + 1, attack);
2837 else if (deflect)
2839 attack = r[0].random2(attack / 2);
2841 else if (repel)
2843 attack = r[0].random2(attack);
2846 dprf("Beam attack: %d, defence: %d", attack, defence);
2848 attack = r[1].random2(attack);
2849 defence = r[2].random2avg(defence, 2);
2851 dprf("Beam new attack: %d, defence: %d", attack, defence);
2853 return (attack >= defence);
2856 std::string bolt::zapper() const
2858 const int beam_src = beam_source_as_target();
2859 if (beam_src == MHITYOU)
2860 return ("self");
2861 else if (beam_src == MHITNOT)
2862 return ("");
2863 else
2864 return menv[beam_src].name(DESC_PLAIN);
2867 bool bolt::is_harmless(const monster* mon) const
2869 // For enchantments, this is already handled in nasty_to().
2870 if (is_enchantment())
2871 return (!nasty_to(mon));
2873 // The others are handled here.
2874 switch (flavour)
2876 case BEAM_VISUAL:
2877 case BEAM_DIGGING:
2878 return (true);
2880 case BEAM_HOLY:
2881 return (mon->res_holy_energy(agent()) > 0);
2883 case BEAM_STEAM:
2884 return (mon->res_steam() >= 3);
2886 case BEAM_FIRE:
2887 return (mon->res_fire() >= 3);
2889 case BEAM_COLD:
2890 return (mon->res_cold() >= 3);
2892 case BEAM_MIASMA:
2893 return (mon->res_rotting());
2895 case BEAM_NEG:
2896 return (mon->res_negative_energy() == 3);
2898 case BEAM_ELECTRICITY:
2899 return (mon->res_elec() >= 3);
2901 case BEAM_POISON:
2902 return (mon->res_poison() >= 3);
2904 case BEAM_ACID:
2905 return (mon->res_acid() >= 3);
2907 default:
2908 return (false);
2912 bool bolt::harmless_to_player() const
2914 dprf("beam flavour: %d", flavour);
2916 switch (flavour)
2918 case BEAM_VISUAL:
2919 case BEAM_DIGGING:
2920 return (true);
2922 // Positive enchantments.
2923 case BEAM_HASTE:
2924 case BEAM_HEALING:
2925 case BEAM_INVISIBILITY:
2926 return (true);
2928 case BEAM_HOLY:
2929 return (is_good_god(you.religion));
2931 case BEAM_STEAM:
2932 return (player_res_steam(false) >= 3);
2934 case BEAM_MIASMA:
2935 return (you.res_rotting());
2937 case BEAM_NEG:
2938 return (player_prot_life(false) >= 3);
2940 case BEAM_POISON:
2941 return (player_res_poison(false));
2943 case BEAM_POTION_STINKING_CLOUD:
2944 return (player_res_poison(false) || player_mental_clarity(false)
2945 || you.is_unbreathing());
2947 case BEAM_ELECTRICITY:
2948 return (player_res_electricity(false));
2950 case BEAM_FIRE:
2951 case BEAM_COLD:
2952 case BEAM_ACID:
2953 // Fire and ice can destroy inventory items, acid damage equipment.
2954 return (false);
2956 default:
2957 return (false);
2961 bool bolt::is_reflectable(const item_def *it) const
2963 if (range_used() > range)
2964 return (false);
2966 return (it && is_shield(*it) && shield_reflects(*it));
2969 coord_def bolt::leg_source() const
2971 if (bounces > 0 && map_bounds(bounce_pos))
2972 return (bounce_pos);
2973 else
2974 return (source);
2977 // Reflect a beam back the direction it came. This is used
2978 // by shields of reflection.
2979 void bolt::reflect()
2981 reflections++;
2983 target = leg_source();
2984 extra_range_used += range_used(true);
2985 source = pos();
2987 // Reset bounce_pos, so that if we somehow reflect again before reaching
2988 // the wall that we won't keep heading towards the wall.
2989 bounce_pos.reset();
2991 if (pos() == you.pos())
2992 reflector = NON_MONSTER;
2993 else if (monster* m = monster_at(pos()))
2994 reflector = m->mindex();
2995 else
2997 reflector = -1;
2998 #ifdef DEBUG
2999 mprf(MSGCH_DIAGNOSTICS, "Bolt reflected by neither player nor "
3000 "monster (bolt = %s, item = %s)", name.c_str(),
3001 item ? item->name(DESC_PLAIN).c_str() : "none");
3002 #endif
3005 flavour = real_flavour;
3006 choose_ray();
3009 void bolt::tracer_affect_player()
3011 // Check whether thrower can see player, unless thrower == player.
3012 if (YOU_KILL(thrower))
3014 // Don't ask if we're aiming at ourselves.
3015 if (!dont_stop_player && !harmless_to_player())
3017 if (yesno("That beam is likely to hit you. Continue anyway?",
3018 false, 'n'))
3020 friend_info.count++;
3021 friend_info.power += you.experience_level;
3022 dont_stop_player = true;
3024 else
3026 beam_cancelled = true;
3027 finish_beam();
3031 else if (can_see_invis || !you.invisible() || fuzz_invis_tracer())
3033 if (mons_att_wont_attack(attitude))
3035 friend_info.count++;
3036 friend_info.power += you.experience_level;
3038 else
3040 foe_info.count++;
3041 foe_info.power += you.experience_level;
3045 std::vector<std::string> messages;
3046 int dummy = 0;
3048 apply_dmg_funcs(&you, dummy, messages);
3050 for (unsigned int i = 0; i < messages.size(); ++i)
3051 mpr(messages[i].c_str(), MSGCH_WARN);
3053 apply_hit_funcs(&you, 0);
3054 extra_range_used += range_used_on_hit(&you);
3057 bool bolt::misses_player()
3059 if (is_explosion || aimed_at_feet || auto_hit || is_enchantment())
3060 return (false);
3062 const int dodge = player_evasion();
3063 const int dodge_less = player_evasion(EV_IGNORE_PHASESHIFT);
3064 int real_tohit = hit;
3066 if (real_tohit != AUTOMATIC_HIT)
3068 // Monsters shooting at an invisible player are very inaccurate.
3069 if (you.invisible() && !can_see_invis)
3070 real_tohit /= 2;
3072 if (you.backlit(true, false))
3073 real_tohit += 2 + random2(8);
3076 bool train_shields_more = false;
3078 if (is_blockable()
3079 && (you.shield() || player_mutation_level(MUT_LARGE_BONE_PLATES) > 0)
3080 && !aimed_at_feet
3081 && player_shield_class() > 0)
3083 // We use the original to-hit here.
3084 const int testhit = random2(hit * 130 / 100
3085 + you.shield_block_penalty());
3087 const int block = you.shield_bonus();
3089 dprf("Beamshield: hit: %d, block %d", testhit, block);
3090 if (testhit < block)
3092 if (is_reflectable(you.shield()))
3094 mprf("Your %s reflects the %s!",
3095 you.shield()->name(DESC_PLAIN).c_str(),
3096 name.c_str());
3097 ident_reflector(you.shield());
3098 reflect();
3100 else
3102 mprf("You block the %s.", name.c_str());
3103 finish_beam();
3105 you.shield_block_succeeded(agent());
3106 return (true);
3109 // Some training just for the "attempt".
3110 train_shields_more = true;
3113 if (!aimed_at_feet)
3114 practise(EX_BEAM_MAY_HIT);
3116 defer_rand r;
3117 bool miss = true;
3119 bool dmsl = you.duration[DUR_DEFLECT_MISSILES];
3120 bool rmsl = dmsl || you.duration[DUR_REPEL_MISSILES]
3121 || player_mutation_level(MUT_DISTORTION_FIELD) == 3;
3122 if (flavour == BEAM_LIGHT)
3123 dmsl = rmsl = false;
3125 if (!_test_beam_hit(real_tohit, dodge_less, is_beam, false, false, r))
3127 mprf("The %s misses you.", name.c_str());
3129 else if (!_test_beam_hit(real_tohit, dodge_less, is_beam, false, rmsl, r))
3131 mprf("The %s is repelled.", name.c_str());
3133 else if (!_test_beam_hit(real_tohit, dodge_less, is_beam, dmsl, rmsl, r))
3135 // active voice to imply stronger effect
3136 mprf("You deflect the %s!", name.c_str());
3138 else if (!_test_beam_hit(real_tohit, dodge, is_beam, dmsl, rmsl, r))
3140 mprf("You momentarily phase out as the %s "
3141 "passes through you.", name.c_str());
3143 else
3145 const bool engulfs = is_explosion || is_big_cloud;
3146 int dodge_more = player_evasion(EV_IGNORE_HELPLESS);
3148 if (hit_verb.empty())
3149 hit_verb = engulfs ? "engulfs" : "hits";
3151 if (_test_beam_hit(real_tohit, dodge_more, is_beam, dmsl, rmsl, r))
3153 mprf("The %s %s you!", name.c_str(), hit_verb.c_str());
3155 else
3157 mprf("Helpless, you fail to dodge the %s.", name.c_str());
3160 miss = false;
3163 if (train_shields_more)
3164 practise(EX_SHIELD_BEAM_FAIL);
3166 return (miss);
3169 void bolt::affect_player_enchantment()
3171 // boost paralysis odds a bit, since chain paralysis from beams was removed
3172 if (flavour == BEAM_PARALYSIS)
3173 ench_power = ench_power * 6 / 5;
3175 if (flavour != BEAM_POLYMORPH && has_saving_throw()
3176 && you.check_res_magic(ench_power) > 0)
3178 // You resisted it.
3180 // Give a message.
3181 bool need_msg = true;
3182 if (thrower != KILL_YOU_MISSILE && !invalid_monster_index(beam_source))
3184 monster* mon = &menv[beam_source];
3185 if (!mon->observable())
3187 mpr("Something tries to affect you, but you resist.");
3188 need_msg = false;
3191 if (need_msg)
3192 canned_msg(MSG_YOU_RESIST);
3194 // You *could* have gotten a free teleportation in the Abyss,
3195 // but no, you resisted.
3196 if (flavour == BEAM_TELEPORT && you.level_type == LEVEL_ABYSS)
3197 xom_is_stimulated(255);
3199 extra_range_used += range_used_on_hit(&you);
3200 return;
3203 // You didn't resist it.
3204 if (effect_known)
3205 _ench_animation(real_flavour);
3206 else
3207 _zap_animation(-1);
3209 bool nasty = true, nice = false;
3211 switch (flavour)
3213 case BEAM_HIBERNATION:
3214 you.hibernate(ench_power);
3215 break;
3217 case BEAM_SLEEP:
3218 you.put_to_sleep(NULL, ench_power);
3219 break;
3221 case BEAM_CORONA:
3222 you.backlight();
3223 obvious_effect = true;
3224 break;
3226 case BEAM_POLYMORPH:
3227 if (MON_KILL(thrower))
3229 mpr("Strange energies course through your body.");
3230 you.mutate();
3231 obvious_effect = true;
3233 else if (get_ident_type(OBJ_WANDS, WAND_POLYMORPH_OTHER)
3234 == ID_KNOWN_TYPE)
3236 mpr("This is polymorph other only!");
3238 else
3239 canned_msg(MSG_NOTHING_HAPPENS);
3240 break;
3242 case BEAM_SLOW:
3243 potion_effect(POT_SLOWING, ench_power);
3244 obvious_effect = true;
3245 break;
3247 case BEAM_HASTE:
3248 potion_effect(POT_SPEED, ench_power, false, effect_known);
3249 contaminate_player(1, effect_known);
3250 obvious_effect = true;
3251 nasty = false;
3252 nice = true;
3253 break;
3255 case BEAM_HEALING:
3256 potion_effect(POT_HEAL_WOUNDS, ench_power);
3257 obvious_effect = true;
3258 nasty = false;
3259 nice = true;
3260 break;
3262 case BEAM_PARALYSIS:
3263 potion_effect(POT_PARALYSIS, ench_power);
3264 obvious_effect = true;
3265 break;
3267 case BEAM_PETRIFY:
3268 you.petrify(agent(), ench_power);
3269 obvious_effect = true;
3270 break;
3272 case BEAM_CONFUSION:
3273 potion_effect(POT_CONFUSION, ench_power);
3274 obvious_effect = true;
3275 break;
3277 case BEAM_INVISIBILITY:
3278 you.attribute[ATTR_INVIS_UNCANCELLABLE] = 1;
3279 potion_effect(POT_INVISIBILITY, ench_power);
3280 contaminate_player(1 + random2(2), effect_known);
3281 obvious_effect = true;
3282 nasty = false;
3283 nice = true;
3284 break;
3286 case BEAM_TELEPORT:
3287 you_teleport();
3289 // An enemy helping you escape while in the Abyss, or an
3290 // enemy stabilizing a teleport that was about to happen.
3291 if (!mons_att_wont_attack(attitude)
3292 && you.level_type == LEVEL_ABYSS)
3294 xom_is_stimulated(255);
3297 obvious_effect = true;
3298 break;
3300 case BEAM_BLINK:
3301 random_blink(false);
3302 obvious_effect = true;
3303 break;
3305 case BEAM_BLINK_CLOSE:
3306 blink_other_close(&you, source);
3307 obvious_effect = true;
3308 break;
3310 case BEAM_CHARM:
3311 potion_effect(POT_CONFUSION, ench_power);
3312 obvious_effect = true;
3313 break; // enslavement - confusion?
3315 case BEAM_BANISH:
3316 if (YOU_KILL(thrower))
3318 mpr("This spell isn't strong enough to banish yourself.");
3319 break;
3321 if (you.level_type == LEVEL_ABYSS)
3323 mpr("You feel trapped.");
3324 break;
3326 you.banished = true;
3327 you.banished_by = zapper();
3328 obvious_effect = true;
3329 break;
3331 case BEAM_PAIN:
3332 if (player_res_torment())
3334 canned_msg(MSG_YOU_UNAFFECTED);
3335 break;
3338 if (aux_source.empty())
3339 aux_source = "by nerve-wracking pain";
3341 if (name.find("agony") != std::string::npos)
3343 if (you.res_negative_energy()) // Agony has no effect with rN.
3345 canned_msg(MSG_YOU_UNAFFECTED);
3346 break;
3349 mpr("Your body is wracked with pain!");
3351 // On the player, Agony acts like single-target torment.
3352 internal_ouch(std::max(0, you.hp / 2 - 1));
3354 else
3356 mpr("Pain shoots through your body!");
3358 internal_ouch(damage.roll());
3360 obvious_effect = true;
3361 break;
3363 case BEAM_DISPEL_UNDEAD:
3364 if (!you.is_undead)
3366 canned_msg(MSG_YOU_UNAFFECTED);
3367 break;
3370 mpr("You convulse!");
3372 if (aux_source.empty())
3373 aux_source = "by dispel undead";
3375 if (you.is_undead == US_SEMI_UNDEAD)
3377 if (you.hunger_state == HS_ENGORGED)
3378 damage.size /= 2;
3379 else if (you.hunger_state > HS_SATIATED)
3381 damage.size *= 2;
3382 damage.size /= 3;
3385 internal_ouch(damage.roll());
3386 obvious_effect = true;
3387 break;
3389 case BEAM_DISINTEGRATION:
3390 mpr("You are blasted!");
3392 if (aux_source.empty())
3393 aux_source = "disintegration bolt";
3396 int amt = damage.roll();
3397 internal_ouch(amt);
3399 if (you.can_bleed())
3400 blood_spray(you.pos(), MONS_PLAYER, amt / 5);
3403 obvious_effect = true;
3404 break;
3406 case BEAM_PORKALATOR:
3407 if (!transform(ench_power, TRAN_PIG, true))
3409 mpr("You feel like a pig.");
3410 break;
3412 obvious_effect = true;
3413 break;
3415 case BEAM_BERSERK:
3416 potion_effect(POT_BERSERK_RAGE, ench_power);
3417 obvious_effect = true;
3418 break;
3420 case BEAM_MIGHT:
3421 potion_effect(POT_MIGHT, ench_power);
3422 obvious_effect = true;
3423 break;
3425 default:
3426 // _All_ enchantments should be enumerated here!
3427 mpr("Software bugs nibble your toes!");
3428 break;
3431 if (nasty)
3433 if (mons_att_wont_attack(attitude))
3435 friend_info.hurt++;
3436 if (beam_source == NON_MONSTER)
3438 // Beam from player rebounded and hit player.
3439 if (!aimed_at_feet)
3440 xom_is_stimulated(255);
3442 else
3444 // Beam from an ally or neutral.
3445 xom_is_stimulated(128);
3448 else
3449 foe_info.hurt++;
3451 else if (nice)
3453 if (mons_att_wont_attack(attitude))
3454 friend_info.helped++;
3455 else
3457 foe_info.helped++;
3458 xom_is_stimulated(128);
3462 apply_hit_funcs(&you, 0);
3464 // Regardless of effect, we need to know if this is a stopper
3465 // or not - it seems all of the above are.
3466 extra_range_used += range_used_on_hit(&you);
3469 void bolt::affect_actor(actor *act)
3471 if (act->atype() == ACT_MONSTER)
3472 affect_monster(act->as_monster());
3473 else
3474 affect_player();
3477 void bolt::affect_player()
3479 // Explosions only have an effect during their explosion phase.
3480 // Special cases can be handled here.
3481 if (is_explosion && !in_explosion_phase)
3483 // Trigger the explosion.
3484 finish_beam();
3485 return;
3488 // Digging -- don't care.
3489 if (flavour == BEAM_DIGGING)
3490 return;
3492 if (is_tracer)
3494 tracer_affect_player();
3495 return;
3498 // Trigger an interrupt, so travel will stop on misses which
3499 // generate smoke.
3500 if (!YOU_KILL(thrower))
3501 interrupt_activity(AI_MONSTER_ATTACKS);
3503 if (is_enchantment())
3505 affect_player_enchantment();
3506 return;
3509 msg_generated = true;
3511 if (misses_player())
3512 return;
3514 const bool engulfs = is_explosion || is_big_cloud;
3516 // FIXME: Lots of duplicated code here (compare handling of
3517 // monsters)
3518 int hurted = 0;
3519 int burn_power = (is_explosion) ? 5 : (is_beam) ? 3 : 2;
3521 // Roll the damage.
3522 hurted += damage.roll();
3524 #ifdef DEBUG_DIAGNOSTICS
3525 int roll = hurted;
3526 #endif
3528 std::vector<std::string> messages;
3529 apply_dmg_funcs(&you, hurted, messages);
3531 int armour_damage_reduction = random2(1 + you.armour_class());
3532 if (flavour == BEAM_ELECTRICITY)
3533 armour_damage_reduction /= 2;
3534 else if (flavour == BEAM_HELLFIRE || name == "chilling blast")
3535 armour_damage_reduction = 0;
3536 hurted -= armour_damage_reduction;
3538 // shrapnel has triple AC reduction
3539 if (flavour == BEAM_FRAG)
3541 hurted -= random2(1 + you.armour_class());
3542 hurted -= random2(1 + you.armour_class());
3545 #ifdef DEBUG_DIAGNOSTICS
3546 mprf(MSGCH_DIAGNOSTICS,
3547 "Player damage: rolled=%d; after AC=%d", roll, hurted);
3548 #endif
3550 practise(EX_BEAM_WILL_HIT);
3552 bool was_affected = false;
3553 int old_hp = you.hp;
3555 hurted = std::max(0, hurted);
3557 // If the beam is an actual missile or of the MMISSILE type (Earth magic)
3558 // we might bleed on the floor.
3559 if (!engulfs
3560 && (flavour == BEAM_MISSILE || flavour == BEAM_MMISSILE))
3562 // assumes DVORP_PIERCING, factor: 0.5
3563 int blood = std::min(you.hp, hurted / 2);
3564 bleed_onto_floor(you.pos(), MONS_PLAYER, blood, true);
3567 hurted = check_your_resists(hurted, flavour, "", this);
3569 if (flavour == BEAM_MIASMA && hurted > 0)
3570 was_affected = miasma_player(get_source_name(), name);
3572 if (flavour == BEAM_NUKE) // DISINTEGRATION already handled
3573 blood_spray(you.pos(), MONS_PLAYER, hurted / 5);
3575 // Confusion effect for spore explosions
3576 if (flavour == BEAM_SPORE && hurted && you.holiness() != MH_UNDEAD)
3577 potion_effect(POT_CONFUSION, 1);
3579 // handling of missiles
3580 if (item && item->base_type == OBJ_MISSILES)
3582 // SPMSL_POISONED is handled via callback _poison_hit_victim()
3583 // in item_use.cc.
3584 if (item->sub_type == MI_THROWING_NET)
3586 if (player_caught_in_net())
3588 if (beam_source != NON_MONSTER)
3589 xom_is_stimulated(64);
3590 was_affected = true;
3593 else if (item->special == SPMSL_CURARE)
3595 if (x_chance_in_y(90 - 3 * you.armour_class(), 100))
3597 curare_hits_player(actor_to_death_source(agent()),
3598 1 + random2(3), *this);
3599 was_affected = true;
3604 // Sticky flame.
3605 if (name == "sticky flame")
3607 if (!player_res_sticky_flame())
3609 napalm_player(random2avg(7, 3) + 1);
3610 was_affected = true;
3614 // Acid.
3615 if (flavour == BEAM_ACID)
3616 splash_with_acid(5, affects_items);
3618 if (affects_items)
3620 // Simple cases for scroll burns.
3621 if (flavour == BEAM_LAVA || name.find("hellfire") != std::string::npos)
3622 expose_player_to_element(BEAM_LAVA, burn_power);
3624 // More complex (geez..)
3625 if (flavour == BEAM_FIRE && name != "ball of steam")
3626 expose_player_to_element(BEAM_FIRE, burn_power);
3628 // Potions exploding.
3629 if (flavour == BEAM_COLD)
3630 expose_player_to_element(BEAM_COLD, burn_power);
3632 // Spore pops.
3633 if (in_explosion_phase && flavour == BEAM_SPORE)
3634 expose_player_to_element(BEAM_SPORE, burn_power);
3637 if (origin_spell == SPELL_QUICKSILVER_BOLT)
3638 antimagic();
3640 dprf("Damage: %d", hurted);
3642 was_affected = apply_hit_funcs(&you, hurted) || was_affected;
3644 if (hurted > 0 || old_hp < you.hp || was_affected)
3646 if (mons_att_wont_attack(attitude))
3648 friend_info.hurt++;
3650 // Beam from player rebounded and hit player.
3651 // Xom's amusement at the player's being damaged is handled
3652 // elsewhere.
3653 if (beam_source == NON_MONSTER)
3655 if (!aimed_at_feet)
3656 xom_is_stimulated(255);
3658 else if (was_affected)
3659 xom_is_stimulated(128);
3661 else
3662 foe_info.hurt++;
3665 if (hurted > 0)
3667 for (unsigned int i = 0; i < messages.size(); ++i)
3668 mpr(messages[i].c_str(), MSGCH_WARN);
3671 internal_ouch(hurted);
3673 extra_range_used += range_used_on_hit(&you);
3675 if ((flavour == BEAM_WATER && origin_spell == SPELL_PRIMAL_WAVE)
3676 || (name == "chilling blast" && you.airborne()))
3677 beam_hits_actor(&you);
3680 int bolt::beam_source_as_target() const
3682 return (MON_KILL(thrower) ? beam_source :
3683 thrower == KILL_MISCAST ? beam_source :
3684 thrower == KILL_MISC ? MHITNOT
3685 : MHITYOU);
3688 void bolt::update_hurt_or_helped(monster* mon)
3690 if (!mons_atts_aligned(attitude, mons_attitude(mon)))
3692 if (nasty_to(mon))
3693 foe_info.hurt++;
3694 else if (nice_to(mon))
3696 foe_info.helped++;
3697 // Accidentally helped a foe.
3698 if (!is_tracer && !effect_known)
3700 int interest = 128;
3701 if (flavour == BEAM_INVISIBILITY && can_see_invis)
3702 interest = 32;
3704 xom_is_stimulated(interest);
3708 else
3710 if (nasty_to(mon))
3712 friend_info.hurt++;
3714 // Harmful beam from this monster rebounded and hit the monster.
3715 if (!is_tracer && mon->mindex() == beam_source)
3716 xom_is_stimulated(128);
3718 else if (nice_to(mon))
3719 friend_info.helped++;
3723 void bolt::tracer_enchantment_affect_monster(monster* mon)
3725 // Update friend or foe encountered.
3726 if (!mons_atts_aligned(attitude, mons_attitude(mon)))
3728 foe_info.count++;
3729 foe_info.power += mons_power(mon->type);
3731 else
3733 friend_info.count++;
3734 friend_info.power += mons_power(mon->type);
3737 handle_stop_attack_prompt(mon);
3738 if (!beam_cancelled)
3740 apply_hit_funcs(mon, 0);
3741 extra_range_used += range_used_on_hit(mon);
3745 bool bolt::damage_ignores_armour() const
3747 // [ds] FIXME: replace beam name checks with flavour or origin spell checks
3748 return (flavour == BEAM_HELLFIRE
3749 || name == "freezing breath"
3750 || name == "chilling blast");
3753 // Return false if we should skip handling this monster.
3754 bool bolt::determine_damage(monster* mon, int& preac, int& postac, int& final,
3755 std::vector<std::string>& messages)
3757 preac = postac = final = 0;
3759 // [ds] Changed how tracers determined damage: the old tracer
3760 // model took the average damage potential, subtracted the average
3761 // AC damage reduction and called that the average damage output.
3762 // This could easily predict an average damage output of 0 for
3763 // high AC monsters, with the result that monsters often didn't
3764 // bother using ranged attacks at high AC targets.
3766 // The new model rounds up average damage at every stage, so it
3767 // will predict a damage output of 1 even if the average damage
3768 // expected is much closer to 0. This will allow monsters to use
3769 // ranged attacks vs high AC targets.
3771 // This is not an entirely beneficial change; the old tracer
3772 // damage system would make monsters with weak ranged attacks
3773 // close in to their foes, while the new system will make it more
3774 // likely that such monsters will hang back and make ineffective
3775 // ranged attacks. Thus the new tracer damage calculation will
3776 // hurt monsters with low-damage ranged attacks and high-damage
3777 // melee attacks. I judge this an acceptable compromise (for now).
3779 const int preac_min_damage = damage.size? damage.num : 0;
3780 const int preac_max_damage = damage.num * damage.size;
3782 // preac: damage before AC modifier
3783 // postac: damage after AC modifier
3784 // final: damage after AC and resists
3785 // All these are invalid if we return false.
3787 if (is_tracer)
3788 preac = div_round_up(preac_max_damage + preac_max_damage, 2);
3789 else
3790 preac = damage.roll();
3792 if (!apply_dmg_funcs(mon, preac, messages))
3793 return (false);
3795 postac = preac;
3797 int tracer_postac_min = preac_min_damage;
3798 int tracer_postac_max = preac_max_damage;
3800 // Hellfire and white draconian breath ignores AC.
3801 if (!damage_ignores_armour())
3803 if (is_tracer && preac_max_damage > 0)
3805 tracer_postac_min = std::max(0, preac_min_damage - mon->ac);
3806 tracer_postac_max = preac_max_damage;
3807 postac = div_round_up(tracer_postac_min + tracer_postac_max, 2);
3809 else
3811 postac -= maybe_random2(1 + mon->ac, !is_tracer);
3813 // Fragmentation has triple AC reduction.
3814 if (flavour == BEAM_FRAG)
3816 postac -= maybe_random2(1 + mon->ac, !is_tracer);
3817 postac -= maybe_random2(1 + mon->ac, !is_tracer);
3822 postac = std::max(postac, 0);
3824 if (is_tracer)
3826 const int adjusted_postac_max =
3827 mons_adjust_flavoured(mon, *this, tracer_postac_max, false);
3828 const int adjusted_postac_min =
3829 !tracer_postac_min? 0 :
3830 mons_adjust_flavoured(mon, *this, tracer_postac_min, false);
3831 final = div_round_up(adjusted_postac_max + adjusted_postac_min, 2);
3833 else
3835 // Don't do side effects (beam might miss or be a tracer).
3836 final = mons_adjust_flavoured(mon, *this, postac, false);
3839 // Sanity check. Importantly for
3840 // tracer_nonenchantment_affect_monster, final > 0
3841 // implies preac > 0.
3842 ASSERT(0 <= postac && postac <= preac && 0 <= final
3843 && (preac > 0 || final == 0));
3845 return (true);
3848 void bolt::handle_stop_attack_prompt(monster* mon)
3850 if ((thrower == KILL_YOU_MISSILE || thrower == KILL_YOU)
3851 && !is_harmless(mon))
3853 if (friend_info.count == 1 && !friend_info.dont_stop
3854 || foe_info.count == 1 && !foe_info.dont_stop)
3856 const bool autohit_first = (hit == AUTOMATIC_HIT);
3857 if (stop_attack_prompt(mon, true, target, autohit_first))
3859 beam_cancelled = true;
3860 finish_beam();
3862 else
3864 if (friend_info.count == 1)
3865 friend_info.dont_stop = true;
3866 else if (foe_info.count == 1)
3867 foe_info.dont_stop = true;
3873 void bolt::tracer_nonenchantment_affect_monster(monster* mon)
3875 std::vector<std::string> messages;
3876 int preac, post, final;
3878 if (!determine_damage(mon, preac, post, final, messages))
3879 return;
3881 // Check only if actual damage.
3882 if (final > 0)
3884 ASSERT(preac > 0);
3886 // Monster could be hurt somewhat, but only apply the
3887 // monster's power based on how badly it is affected.
3888 // For example, if a fire giant (power 16) threw a
3889 // fireball at another fire giant, and it only took
3890 // 1/3 damage, then power of 5 would be applied.
3892 // Counting foes is only important for monster tracers.
3893 if (!mons_atts_aligned(attitude, mons_attitude(mon)))
3895 foe_info.power += 2 * final * mons_power(mon->type) / preac;
3896 foe_info.count++;
3898 else
3900 friend_info.power += 2 * final * mons_power(mon->type) / preac;
3901 friend_info.count++;
3905 // Maybe the user wants to cancel at this point.
3906 handle_stop_attack_prompt(mon);
3907 if (beam_cancelled)
3908 return;
3910 // Check only if actual damage.
3911 if (!is_tracer && final > 0)
3913 for (unsigned int i = 0; i < messages.size(); ++i)
3914 mpr(messages[i].c_str(), MSGCH_MONSTER_DAMAGE);
3917 apply_hit_funcs(mon, final);
3919 // Either way, we could hit this monster, so update range used.
3920 extra_range_used += range_used_on_hit(mon);
3923 void bolt::tracer_affect_monster(monster* mon)
3925 // Ignore unseen monsters.
3926 if (!agent() || !mon->visible_to(agent()))
3927 return;
3929 // Trigger explosion on exploding beams.
3930 if (is_explosion && !in_explosion_phase)
3932 finish_beam();
3933 return;
3936 // Special explosions (current exploding missiles) aren't
3937 // auto-hit, so we need to explode them at every possible
3938 // end-point?
3939 if (special_explosion)
3941 bolt orig = *special_explosion;
3942 affect_endpoint();
3943 *special_explosion = orig;
3946 if (is_enchantment())
3947 tracer_enchantment_affect_monster(mon);
3948 else
3949 tracer_nonenchantment_affect_monster(mon);
3952 void bolt::enchantment_affect_monster(monster* mon)
3954 god_conduct_trigger conducts[3];
3955 disable_attack_conducts(conducts);
3957 bool hit_woke_orc = false;
3959 // Nasty enchantments will annoy the monster, and are considered
3960 // naughty (even if a monster might resist).
3961 if (nasty_to(mon))
3963 if (YOU_KILL(thrower))
3965 if (is_sanctuary(mon->pos()) || is_sanctuary(you.pos()))
3966 remove_sanctuary(true);
3968 set_attack_conducts(conducts, mon, you.can_see(mon));
3970 if (you.religion == GOD_BEOGH
3971 && mons_genus(mon->type) == MONS_ORC
3972 && mon->asleep() && !player_under_penance()
3973 && you.piety >= piety_breakpoint(2) && mons_near(mon))
3975 hit_woke_orc = true;
3978 behaviour_event(mon, ME_ANNOY, beam_source_as_target());
3980 else
3981 behaviour_event(mon, ME_ALERT, beam_source_as_target());
3983 enable_attack_conducts(conducts);
3985 // Doing this here so that the player gets to see monsters
3986 // "flicker and vanish" when turning invisible....
3987 if (effect_known)
3988 _ench_animation(real_flavour, mon);
3989 else
3990 _zap_animation(-1, mon, false);
3992 // Try to hit the monster with the enchantment.
3993 int res_margin = 0;
3994 const mon_resist_type ench_result = try_enchant_monster(mon, res_margin);
3996 if (mon->alive()) // Aftereffects.
3998 // Mimics become known.
3999 if (mons_is_mimic(mon->type))
4000 mimic_alert(mon);
4002 // Message or record the success/failure.
4003 switch (ench_result)
4005 case MON_RESIST:
4006 if (simple_monster_message(mon,
4007 resist_margin_phrase(res_margin).c_str()))
4009 msg_generated = true;
4011 break;
4012 case MON_UNAFFECTED:
4013 if (simple_monster_message(mon, " is unaffected."))
4014 msg_generated = true;
4015 break;
4016 case MON_AFFECTED:
4017 case MON_OTHER: // Should this really be here?
4018 update_hurt_or_helped(mon);
4019 break;
4022 if (hit_woke_orc)
4023 beogh_follower_convert(mon, true);
4026 apply_hit_funcs(mon, 0);
4027 extra_range_used += range_used_on_hit(mon);
4030 void bolt::monster_post_hit(monster* mon, int dmg)
4032 if (YOU_KILL(thrower) && mons_near(mon))
4033 print_wounds(mon);
4035 // Don't annoy friendlies or good neutrals if the player's beam
4036 // did no damage. Hostiles will still take umbrage.
4037 if (dmg > 0 || !mon->wont_attack() || !YOU_KILL(thrower))
4038 behaviour_event(mon, ME_ANNOY, beam_source_as_target());
4040 // Sticky flame.
4041 if (name == "sticky flame" || name == "splash of liquid fire")
4043 const int levels = std::min(4, 1 + random2(mon->hit_dice) / 2);
4044 napalm_monster(mon, whose_kill(), levels);
4046 if (name == "splash of liquid fire")
4048 // the breath weapon can splash to adjacent monsters
4049 for (monster_iterator mi(you.get_los()); mi; ++mi)
4051 if (grid_distance(you.pos(), mi->pos()) == 1 &&
4052 grid_distance(mon->pos(), mi->pos()) == 1)
4054 mprf("The sticky flame splashes onto %s!",
4055 mi->name(DESC_NOCAP_THE).c_str());
4056 napalm_monster(*mi, whose_kill(), levels);
4062 bool wake_mimic = true;
4064 // Handle missile effects.
4065 if (item && item->base_type == OBJ_MISSILES)
4067 // SPMSL_POISONED handled via callback _poison_hit_victim() in
4068 // item_use.cc
4069 if (item->special == SPMSL_CURARE)
4071 if (ench_power == AUTOMATIC_HIT
4072 && _curare_hits_monster(agent(), mon, whose_kill(), 2)
4073 && !mon->alive())
4075 wake_mimic = false;
4080 if (name == "bolt of energy"
4081 || origin_spell == SPELL_QUICKSILVER_BOLT) // purple draconian breath
4082 debuff_monster(mon);
4084 if (wake_mimic && mons_is_mimic(mon->type))
4085 mimic_alert(mon);
4086 else if (dmg)
4087 beogh_follower_convert(mon, true);
4089 if ((flavour == BEAM_WATER && origin_spell == SPELL_PRIMAL_WAVE) ||
4090 (name == "freezing breath" && mon->flight_mode() == FL_FLY))
4091 beam_hits_actor(mon);
4094 void bolt::beam_hits_actor(actor *act)
4096 const coord_def oldpos(act->pos());
4098 const bool drac_breath = (name == "freezing breath" || name == "chilling blast");
4100 if (knockback_actor(act))
4102 if (you.can_see(act))
4104 if (drac_breath)
4106 mprf("%s %s blown backwards by the freezing wind.",
4107 act->name(DESC_CAP_THE).c_str(),
4108 act->conj_verb("are").c_str());
4109 knockback_actor(act);
4111 else
4113 mprf("%s %s knocked back by the %s.",
4114 act->name(DESC_CAP_THE).c_str(),
4115 act->conj_verb("are").c_str(),
4116 this->name.c_str());
4119 act->apply_location_effects(oldpos, killer(), beam_source);
4123 // Return true if the block succeeded (including reflections.)
4124 bool bolt::attempt_block(monster* mon)
4126 const int shield_block = mon->shield_bonus();
4127 bool rc = false;
4128 if (shield_block > 0)
4130 const int ht = random2(hit * 130 / 100 + mon->shield_block_penalty());
4131 if (ht < shield_block)
4133 rc = true;
4134 item_def *shield = mon->mslot_item(MSLOT_SHIELD);
4135 if (is_reflectable(shield))
4137 if (mon->observable())
4139 mprf("%s reflects the %s off %s %s!",
4140 mon->name(DESC_CAP_THE).c_str(),
4141 name.c_str(),
4142 mon->pronoun(PRONOUN_NOCAP_POSSESSIVE).c_str(),
4143 shield->name(DESC_PLAIN).c_str());
4144 ident_reflector(shield);
4146 else if (you.see_cell(pos()))
4147 mprf("The %s bounces off of thin air!", name.c_str());
4149 reflect();
4151 else if (you.see_cell(pos()))
4153 mprf("%s blocks the %s.",
4154 mon->name(DESC_CAP_THE).c_str(), name.c_str());
4155 finish_beam();
4158 mon->shield_block_succeeded(agent());
4162 return (rc);
4165 bool bolt::handle_statue_disintegration(monster* mon)
4167 bool rc = false;
4168 if ((flavour == BEAM_DISINTEGRATION || flavour == BEAM_NUKE)
4169 && mons_is_statue(mon->type, true))
4171 rc = true;
4172 // Disintegrate the statue.
4173 if (!silenced(you.pos()))
4175 if (!you.see_cell(mon->pos()))
4176 mpr("You hear a hideous screaming!", MSGCH_SOUND);
4177 else
4179 mpr("The statue screams as its substance crumbles away!",
4180 MSGCH_SOUND);
4183 else if (you.see_cell(mon->pos()))
4185 mpr("The statue twists and shakes as its substance "
4186 "crumbles away!");
4188 obvious_effect = true;
4189 update_hurt_or_helped(mon);
4190 mon->hurt(agent(), INSTANT_DEATH);
4191 apply_hit_funcs(mon, INSTANT_DEATH);
4192 // Stop here.
4193 finish_beam();
4195 return (rc);
4198 void bolt::affect_monster(monster* mon)
4200 // Don't hit dead monsters.
4201 if (!mon->alive())
4203 apply_hit_funcs(mon, 0);
4204 return;
4207 // First some special cases.
4209 // Digging doesn't affect monsters (should it harm earth elementals?)
4210 if (flavour == BEAM_DIGGING)
4212 apply_hit_funcs(mon, 0);
4213 return;
4216 // All kinds of beams go past orbs of destruction.
4217 // We don't check mons_is_projectile() since that probably won't be the
4218 // case for rolling boulders.
4219 if (mon->type == MONS_ORB_OF_DESTRUCTION)
4221 apply_hit_funcs(mon, 0);
4222 return;
4225 // Missiles go past bushes.
4226 if (mon->type == MONS_BUSH && !is_beam && !is_explosion)
4228 apply_hit_funcs(mon, 0);
4229 return;
4232 if (fedhas_shoot_through(*this, mon))
4234 apply_hit_funcs(mon, 0);
4235 if (!is_tracer)
4237 // FIXME: Could use a better message, something about
4238 // dodging that doesn't sound excessively weird would be
4239 // nice.
4240 if (you.see_cell(mon->pos()))
4242 mprf(MSGCH_GOD, "Fedhas protects %s plant from harm.",
4243 attitude == ATT_FRIENDLY ? "your" : "a");
4246 return;
4249 // Fire storm creates these, so we'll avoid affecting them
4250 if (name == "great blast of fire" && mon->type == MONS_FIRE_VORTEX)
4252 apply_hit_funcs(mon, 0);
4253 return;
4256 // Handle tracers separately.
4257 if (is_tracer)
4259 tracer_affect_monster(mon);
4260 return;
4263 // Visual - wake monsters.
4264 if (flavour == BEAM_VISUAL)
4266 behaviour_event(mon, ME_DISTURB, beam_source, source);
4267 apply_hit_funcs(mon, 0);
4268 return;
4271 // Special case: disintegrate (or Shatter) a statue.
4272 // Since disintegration is an enchantment, it has to be handled
4273 // here.
4274 if (handle_statue_disintegration(mon))
4275 return;
4277 if (is_enchantment())
4279 // no to-hit check
4280 enchantment_affect_monster(mon);
4281 return;
4284 if (is_explosion && !in_explosion_phase)
4286 // It hit a monster, so the beam should terminate.
4287 // Don't actually affect the monster; the explosion
4288 // will take care of that.
4289 finish_beam();
4290 return;
4293 // We need to know how much the monster _would_ be hurt by this,
4294 // before we decide if it actually hits.
4295 std::vector<std::string> messages;
4296 int preac, postac, final;
4297 if (!determine_damage(mon, preac, postac, final, messages))
4298 return;
4300 #ifdef DEBUG_DIAGNOSTICS
4301 mprf(MSGCH_DIAGNOSTICS,
4302 "Monster: %s; Damage: pre-AC: %d; post-AC: %d; post-resist: %d",
4303 mon->name(DESC_PLAIN).c_str(), preac, postac, final);
4304 #endif
4306 // Player beams which hit friendlies or good neutrals will annoy
4307 // them and be considered naughty if they do damage (this is so as
4308 // not to penalise players that fling fireballs into a melee with
4309 // fire elementals on their side - the elementals won't give a sh*t,
4310 // after all).
4312 god_conduct_trigger conducts[3];
4313 disable_attack_conducts(conducts);
4315 if (nasty_to(mon))
4317 if (YOU_KILL(thrower) && final > 0)
4319 // It's not the player's fault if he didn't see the monster
4320 // or the monster was caught in an unexpected blast of
4321 // ?immolation.
4322 const bool okay =
4323 (!you.can_see(mon)
4324 || aux_source == "scroll of immolation" && !effect_known);
4326 if (is_sanctuary(mon->pos()) || is_sanctuary(you.pos()))
4327 remove_sanctuary(true);
4329 set_attack_conducts(conducts, mon, !okay);
4333 // Explosions always 'hit'.
4334 const bool engulfs = (is_explosion || is_big_cloud);
4336 if (engulfs && flavour == BEAM_SPORE
4337 && mon->holiness() == MH_NATURAL)
4339 apply_enchantment_to_monster(mon);
4342 // Make a copy of the to-hit before we modify it.
4343 int beam_hit = hit;
4345 if (beam_hit != AUTOMATIC_HIT)
4347 if (mon->invisible() && !can_see_invis)
4348 beam_hit /= 2;
4350 if (mon->backlit(true, false))
4351 beam_hit += 2 + random2(8);
4354 defer_rand r;
4355 int rand_ev = random2(mon->ev);
4356 bool dmsl = mons_class_flag(mon->type, M_DEFLECT_MISSILES);
4358 // FIXME: We're randomising mon->evasion, which is further
4359 // randomised inside test_beam_hit. This is so we stay close to the
4360 // 4.0 to-hit system (which had very little love for monsters).
4361 if (!engulfs && !_test_beam_hit(beam_hit, rand_ev, is_beam, dmsl, false, r))
4363 // If the PLAYER cannot see the monster, don't tell them anything!
4364 if (mon->observable())
4366 // if it would have hit otherwise...
4367 if (_test_beam_hit(beam_hit, rand_ev, is_beam, false, false, r))
4369 msg::stream << mon->name(DESC_CAP_THE) << " deflects the "
4370 << name << '!' << std::endl;
4372 else if (mons_class_flag(mon->type, M_PHASE_SHIFT)
4373 && _test_beam_hit(beam_hit, rand_ev - random2(8),
4374 is_beam, false, false, r))
4376 msg::stream << mon->name(DESC_CAP_THE) << " momentarily phases "
4377 << "out as the " << name << " passes through "
4378 << mon->pronoun(PRONOUN_OBJECTIVE) << ".\n";
4380 else
4382 msg::stream << "The " << name << " misses "
4383 << mon->name(DESC_NOCAP_THE) << '.' << std::endl;
4386 return;
4389 // The monster may block the beam.
4390 if (!engulfs && is_blockable() && attempt_block(mon))
4391 return;
4393 update_hurt_or_helped(mon);
4394 enable_attack_conducts(conducts);
4396 // We'll say giant spore explosions don't trigger the ally attack conduct
4397 // for Fedhas worshipers. Mostly because you can accidentally blow up a
4398 // group of 8 plants and get placed under penance until the end of time
4399 // otherwise. I'd prefer to do this elsewhere but the beam information
4400 // goes out of scope.
4402 // Also exempting miscast explosions from this conduct -cao
4403 if (you.religion == GOD_FEDHAS
4404 && (flavour == BEAM_SPORE
4405 || beam_source == NON_MONSTER
4406 && aux_source.find("your miscasting") != std::string::npos))
4408 conducts[0].enabled = false;
4411 if (!is_explosion && !noise_generated)
4413 heard = noisy(loudness, pos(), beam_source) || heard;
4414 noise_generated = true;
4417 if (!mon->alive())
4418 return;
4420 // The beam hit.
4421 if (mons_near(mon))
4423 // Monsters are never currently helpless in ranged combat.
4424 if (hit_verb.empty())
4425 hit_verb = engulfs ? "engulfs" : "hits";
4427 mprf("The %s %s %s.",
4428 name.c_str(),
4429 hit_verb.c_str(),
4430 mon->observable() ?
4431 mon->name(DESC_NOCAP_THE).c_str() : "something");
4434 else if (heard && !noise_msg.empty())
4435 mprf(MSGCH_SOUND, "%s", noise_msg.c_str());
4436 // The player might hear something, if _they_ fired a missile
4437 // (not magic beam).
4438 else if (!silenced(you.pos()) && flavour == BEAM_MISSILE
4439 && YOU_KILL(thrower))
4441 mprf(MSGCH_SOUND, "The %s hits something.", name.c_str());
4444 // handling of missiles
4445 if (item
4446 && item->base_type == OBJ_MISSILES
4447 && item->sub_type == MI_THROWING_NET)
4449 monster_caught_in_net(mon, *this);
4452 if (final > 0)
4454 for (unsigned int i = 0; i < messages.size(); ++i)
4455 mpr(messages[i].c_str(), MSGCH_MONSTER_DAMAGE);
4458 // Apply flavoured specials.
4459 mons_adjust_flavoured(mon, *this, postac, true);
4461 // mons_adjust_flavoured may kill the monster directly.
4462 if (mon->alive())
4464 // If the beam is an actual missile or of the MMISSILE type
4465 // (Earth magic) we might bleed on the floor.
4466 if (!engulfs
4467 && (flavour == BEAM_MISSILE || flavour == BEAM_MMISSILE)
4468 && !mon->is_summoned())
4470 // Using raw_damage instead of the flavoured one!
4471 // assumes DVORP_PIERCING, factor: 0.5
4472 const int blood = std::min(postac/2, mon->hit_points);
4473 bleed_onto_floor(mon->pos(), mon->type, blood, true);
4475 // Now hurt monster.
4476 mon->hurt(agent(), final, flavour, false);
4479 int corpse = -1;
4480 monster orig = *mon;
4482 if (mon->alive())
4483 monster_post_hit(mon, final);
4484 else
4486 // Preserve name of the source monster if it winds up killing
4487 // itself.
4488 if (mon->mindex() == beam_source && source_name.empty())
4489 source_name = orig.name(DESC_NOCAP_A, true);
4491 // Prevent spore explosions killing plants from being registered
4492 // as a Fedhas misconduct. Deaths can trigger the ally dying or
4493 // plant dying conducts, but spore explosions shouldn't count
4494 // for either of those.
4496 // FIXME: Should be a better way of doing this. For now, we are
4497 // just falsifying the death report... -cao
4498 if (you.religion == GOD_FEDHAS && flavour == BEAM_SPORE
4499 && fedhas_protects(mon))
4501 if (mon->attitude == ATT_FRIENDLY)
4502 mon->attitude = ATT_HOSTILE;
4503 corpse = monster_die(mon, KILL_MON, beam_source_as_target());
4505 else
4507 killer_type ref_killer = thrower;
4508 if (!YOU_KILL(thrower) && reflector == NON_MONSTER)
4509 ref_killer = KILL_YOU_MISSILE;
4510 corpse = monster_die(mon, ref_killer, beam_source_as_target());
4514 // Give the callbacks a dead-but-valid monster object.
4515 if (mon->type == MONS_NO_MONSTER)
4517 orig.hit_points = -1;
4518 mon = &orig;
4521 apply_hit_funcs(mon, final, corpse);
4522 extra_range_used += range_used_on_hit(mon);
4525 bool bolt::has_saving_throw() const
4527 if (aimed_at_feet)
4528 return (false);
4530 switch (flavour)
4532 case BEAM_HASTE:
4533 case BEAM_MIGHT:
4534 case BEAM_BERSERK:
4535 case BEAM_HEALING:
4536 case BEAM_INVISIBILITY:
4537 case BEAM_DISPEL_UNDEAD:
4538 case BEAM_ENSLAVE_SOUL: // has a different saving throw
4539 case BEAM_ENSLAVE_DEMON: // ditto
4540 return (false);
4541 default:
4542 return (true);
4546 static bool _ench_flavour_affects_monster(beam_type flavour, const monster* mon)
4548 bool rc = true;
4549 switch (flavour)
4551 case BEAM_POLYMORPH:
4552 rc = mon->can_mutate();
4553 break;
4555 case BEAM_DEGENERATE:
4556 rc = (mon->holiness() == MH_NATURAL
4557 && mon->type != MONS_PULSATING_LUMP);
4558 break;
4560 case BEAM_ENSLAVE_SOUL:
4561 rc = (mon->holiness() == MH_NATURAL && mon->attitude != ATT_FRIENDLY);
4562 break;
4564 case BEAM_DISPEL_UNDEAD:
4565 rc = (mon->holiness() == MH_UNDEAD);
4566 break;
4568 case BEAM_ENSLAVE_DEMON:
4569 rc = (mon->holiness() == MH_DEMONIC && !mon->friendly());
4570 break;
4572 case BEAM_PAIN:
4573 rc = !mon->res_negative_energy();
4574 break;
4576 case BEAM_HIBERNATION:
4577 rc = mon->can_hibernate();
4578 break;
4580 case BEAM_PORKALATOR:
4581 rc = (mon->holiness() == MH_DEMONIC && mon->type != MONS_HELL_HOG)
4582 || (mon->holiness() == MH_NATURAL && mon->type != MONS_HOG);
4583 break;
4585 default:
4586 break;
4589 return rc;
4592 bool enchant_monster_with_flavour(monster* mon, actor *foe,
4593 beam_type flavour, int powc)
4595 bolt dummy;
4596 dummy.flavour = flavour;
4597 dummy.ench_power = powc;
4598 dummy.set_agent(foe);
4599 dummy.apply_enchantment_to_monster(mon);
4600 return dummy.obvious_effect;
4603 bool enchant_monster_invisible(monster* mon, const std::string how)
4605 // Store the monster name before it becomes an "it". - bwr
4606 const std::string monster_name = mon->name(DESC_CAP_THE);
4608 if (!mon->has_ench(ENCH_INVIS) && mon->add_ench(ENCH_INVIS))
4610 // A casting of invisibility erases corona.
4611 mon->del_ench(ENCH_CORONA);
4613 if (mons_near(mon))
4615 const bool is_visible = mon->visible_to(&you);
4617 // Can't use simple_monster_message() here, since it checks
4618 // for visibility of the monster (and it's now invisible).
4619 // - bwr
4620 mprf("%s %s%s",
4621 monster_name.c_str(),
4622 how.c_str(),
4623 is_visible ? " for a moment."
4624 : "!");
4626 if (!is_visible)
4627 autotoggle_autopickup(true);
4630 return (true);
4633 return (false);
4636 mon_resist_type bolt::try_enchant_monster(monster* mon, int &res_margin)
4638 // Early out if the enchantment is meaningless.
4639 if (!_ench_flavour_affects_monster(flavour, mon))
4640 return (MON_UNAFFECTED);
4642 // Check magic resistance.
4643 if (has_saving_throw())
4645 if (mons_immune_magic(mon))
4646 return (MON_UNAFFECTED);
4648 // (Very) ugly things and shapeshifters will never resist
4649 // polymorph beams.
4650 if (flavour == BEAM_POLYMORPH
4651 && (mon->type == MONS_UGLY_THING
4652 || mon->type == MONS_VERY_UGLY_THING
4653 || mon->is_shapeshifter()))
4657 else
4659 res_margin = mon->check_res_magic(ench_power);
4660 if (res_margin > 0)
4661 return (MON_RESIST);
4665 return (apply_enchantment_to_monster(mon));
4668 mon_resist_type bolt::apply_enchantment_to_monster(monster* mon)
4670 // Gigantic-switches-R-Us
4671 switch (flavour)
4673 case BEAM_TELEPORT:
4674 if (mon->observable())
4675 obvious_effect = true;
4676 monster_teleport(mon, false);
4677 return (MON_AFFECTED);
4679 case BEAM_BLINK:
4680 if (mon->observable())
4681 obvious_effect = true;
4682 monster_blink(mon);
4683 return (MON_AFFECTED);
4685 case BEAM_BLINK_CLOSE:
4686 if (mon->observable())
4687 obvious_effect = true;
4688 blink_other_close(mon, source);
4689 return (MON_AFFECTED);
4691 case BEAM_POLYMORPH:
4692 if (mon->mutate())
4693 obvious_effect = true;
4694 if (YOU_KILL(thrower))
4696 did_god_conduct(DID_DELIBERATE_MUTATING, 2 + random2(3),
4697 effect_known);
4699 return (MON_AFFECTED);
4701 case BEAM_BANISH:
4702 if (you.level_type == LEVEL_ABYSS)
4703 simple_monster_message(mon, " wobbles for a moment.");
4704 else
4705 mon->banish();
4706 obvious_effect = true;
4707 return (MON_AFFECTED);
4709 case BEAM_DEGENERATE:
4710 if (monster_polymorph(mon, MONS_PULSATING_LUMP))
4711 obvious_effect = true;
4712 return (MON_AFFECTED);
4714 case BEAM_DISPEL_UNDEAD:
4715 if (simple_monster_message(mon, " convulses!"))
4716 obvious_effect = true;
4717 mon->hurt(agent(), damage.roll());
4718 return (MON_AFFECTED);
4720 case BEAM_ENSLAVE_SOUL:
4722 dprf("HD: %d; pow: %d", mon->hit_dice, ench_power);
4724 if (!mons_can_be_zombified(mon) || mons_intel(mon) < I_NORMAL)
4726 simple_monster_message(mon, " is unaffected.");
4727 return (MON_OTHER);
4730 // The monster can be no more than lightly wounded/damaged,
4731 // using the formula from mon-stuff.cc:mons_get_damage_level().
4732 if (mon->hit_points <= mon->max_hit_points * 3 / 4)
4734 simple_monster_message(mon, "'s soul is too badly injured.");
4735 return (MON_OTHER);
4738 obvious_effect = true;
4739 const int duration = you.skills[SK_INVOCATIONS] * 3 / 4 + 2;
4740 mon->add_ench(mon_enchant(ENCH_SOUL_RIPE, 0, KC_YOU, duration * 10));
4741 simple_monster_message(mon, "'s soul is now ripe for the taking.");
4742 return (MON_AFFECTED);
4745 case BEAM_ENSLAVE_DEMON:
4746 dprf("HD: %d; pow: %d", mon->hit_dice, ench_power);
4748 if (mon->hit_dice * 11 / 2 >= random2(ench_power)
4749 || mons_is_unique(mon->type))
4751 return (MON_RESIST);
4754 obvious_effect = true;
4755 if (player_will_anger_monster(mon))
4757 simple_monster_message(mon, " is repulsed!");
4758 return (MON_OTHER);
4761 simple_monster_message(mon, " is enslaved.");
4763 // Wow, permanent enslaving! (sometimes)
4764 if (one_chance_in(2 + mon->hit_dice / 4))
4765 mon->attitude = ATT_FRIENDLY;
4766 else
4767 mon->add_ench(ENCH_CHARM);
4768 behaviour_event(mon, ME_ALERT, MHITNOT);
4769 mons_att_changed(mon);
4770 return (MON_AFFECTED);
4772 case BEAM_PAIN: // pain/agony
4773 if (simple_monster_message(mon, " convulses in agony!"))
4774 obvious_effect = true;
4776 if (name.find("agony") != std::string::npos) // agony
4777 mon->hit_points = std::max(mon->hit_points/2, 1);
4778 else // pain
4779 mon->hurt(agent(), damage.roll(), flavour);
4780 return (MON_AFFECTED);
4782 case BEAM_DISINTEGRATION: // disrupt/disintegrate
4783 if (simple_monster_message(mon, " is blasted."))
4784 obvious_effect = true;
4785 mon->hurt(agent(), damage.roll(), flavour);
4786 return (MON_AFFECTED);
4788 case BEAM_HIBERNATION:
4789 if (mon->can_hibernate())
4791 if (simple_monster_message(mon, " looks drowsy..."))
4792 obvious_effect = true;
4793 mon->hibernate();
4794 return (MON_AFFECTED);
4796 return (MON_UNAFFECTED);
4798 case BEAM_CORONA:
4799 if (backlight_monsters(mon->pos(), hit, 0))
4801 obvious_effect = true;
4802 return (MON_AFFECTED);
4804 return (MON_UNAFFECTED);
4806 case BEAM_SLOW:
4807 obvious_effect = do_slow_monster(mon, whose_kill());
4808 return (MON_AFFECTED);
4810 case BEAM_HASTE:
4811 if (YOU_KILL(thrower))
4812 did_god_conduct(DID_HASTY, 6, effect_known);
4813 if (mon->del_ench(ENCH_SLOW, true))
4815 if (simple_monster_message(mon, " is no longer moving slowly."))
4816 obvious_effect = true;
4818 return (MON_AFFECTED);
4821 // Not slowed, haste it.
4822 if (!mon->has_ench(ENCH_HASTE)
4823 && !mons_is_stationary(mon)
4824 && mon->add_ench(ENCH_HASTE))
4826 if (!mon->paralysed() && !mon->petrified()
4827 && simple_monster_message(mon, " seems to speed up."))
4829 obvious_effect = true;
4832 return (MON_AFFECTED);
4834 case BEAM_MIGHT:
4835 if (!mon->has_ench(ENCH_MIGHT)
4836 && !mons_is_stationary(mon)
4837 && mon->add_ench(ENCH_MIGHT))
4839 if (simple_monster_message(mon, " seems to grow stronger."))
4840 obvious_effect = true;
4842 return (MON_AFFECTED);
4844 case BEAM_BERSERK:
4845 if (!mon->berserk())
4847 // currently from potion, hence voluntary
4848 mon->go_berserk(true);
4849 // can't return this from go_berserk, unfortunately
4850 obvious_effect = mons_near(mon);
4852 return (MON_AFFECTED);
4854 case BEAM_HEALING:
4855 if (thrower == KILL_YOU || thrower == KILL_YOU_MISSILE)
4857 // No KILL_YOU_CONF, or we get "You heal ..."
4858 if (cast_healing(5 + damage.roll(), false, mon->pos()) > 0)
4859 obvious_effect = true;
4860 msg_generated = true; // to avoid duplicate "nothing happens"
4862 else if (mon->heal(5 + damage.roll()))
4864 if (mon->hit_points == mon->max_hit_points)
4866 if (simple_monster_message(mon, "'s wounds heal themselves!"))
4867 obvious_effect = true;
4869 else if (simple_monster_message(mon, " is healed somewhat."))
4870 obvious_effect = true;
4872 return (MON_AFFECTED);
4874 case BEAM_PARALYSIS:
4875 apply_bolt_paralysis(mon);
4876 return (MON_AFFECTED);
4878 case BEAM_PETRIFY:
4879 apply_bolt_petrify(mon);
4880 return (MON_AFFECTED);
4882 case BEAM_SPORE:
4883 case BEAM_CONFUSION:
4884 if (!mons_class_is_confusable(mon->type))
4885 return (MON_UNAFFECTED);
4887 if (mon->add_ench(mon_enchant(ENCH_CONFUSION, 0, whose_kill())))
4889 // FIXME: Put in an exception for things you won't notice
4890 // becoming confused.
4891 if (simple_monster_message(mon, " appears confused."))
4892 obvious_effect = true;
4894 return (MON_AFFECTED);
4896 case BEAM_SLEEP:
4897 if (mon->has_ench(ENCH_SLEEPY))
4898 return (MON_UNAFFECTED);
4900 mon->put_to_sleep(agent(), 0);
4901 if (simple_monster_message(mon, " falls asleep!"))
4902 obvious_effect = true;
4904 return (MON_AFFECTED);
4906 case BEAM_INVISIBILITY:
4908 // Mimic or already glowing.
4909 if (mons_is_mimic(mon->type) || mon->glows_naturally())
4910 return (MON_UNAFFECTED);
4912 if (enchant_monster_invisible(mon, "flickers and vanishes"))
4913 obvious_effect = true;
4915 return (MON_AFFECTED);
4918 case BEAM_CHARM:
4919 if (player_will_anger_monster(mon))
4921 simple_monster_message(mon, " is repulsed!");
4922 return (MON_OTHER);
4925 // Being a puppet on magic strings is a nasty thing.
4926 // Mindless creatures shouldn't probably mind, but because of complex
4927 // behaviour of enslaved neutrals, let's disallow that for now.
4928 mon->attitude = ATT_HOSTILE;
4930 // XXX: Another hackish thing for Pikel's band neutrality.
4931 if (mons_is_pikel(mon))
4932 pikel_band_neutralise();
4934 if (simple_monster_message(mon, " is charmed."))
4935 obvious_effect = true;
4936 mon->add_ench(ENCH_CHARM);
4937 return (MON_AFFECTED);
4939 case BEAM_PORKALATOR:
4941 // Monsters which use the ghost structure can't be properly
4942 // restored from hog form.
4943 if (mons_is_ghost_demon(mon->type))
4944 return (MON_UNAFFECTED);
4946 monster orig_mon(*mon);
4947 if (monster_polymorph(mon, (mon->holiness() == MH_DEMONIC ?
4948 MONS_HELL_HOG : MONS_HOG)))
4950 obvious_effect = true;
4952 // Don't restore items to monster if it reverts.
4953 orig_mon.inv = mon->inv;
4955 // monsters can't cast spells in hog form either -doy
4956 mon->spells.clear();
4958 // For monster reverting to original form.
4959 mon->props[ORIG_MONSTER_KEY] = orig_mon;
4963 return (MON_AFFECTED);
4966 default:
4967 break;
4970 return (MON_AFFECTED);
4974 // Extra range used on hit.
4975 int bolt::range_used_on_hit(const actor* victim) const
4977 int used = 0;
4979 // Non-beams can only affect one thing (player/monster).
4980 if (!is_beam)
4981 used = BEAM_STOP;
4982 else if (is_enchantment())
4983 used = (flavour == BEAM_DIGGING ? 0 : BEAM_STOP);
4984 // Hellfire stops for nobody!
4985 else if (name.find("hellfire") != std::string::npos)
4986 used = 0;
4987 // Generic explosion.
4988 else if (is_explosion || is_big_cloud)
4989 used = BEAM_STOP;
4990 // Plant spit.
4991 else if (flavour == BEAM_ACID)
4992 used = BEAM_STOP;
4993 // Lightning goes through things.
4994 else if (flavour == BEAM_ELECTRICITY)
4995 used = 0;
4996 else
4997 used = 1;
4999 // Assume we didn't hit, after all.
5000 if (is_tracer && beam_source == NON_MONSTER && used == BEAM_STOP
5001 && hit < AUTOMATIC_HIT)
5003 return 1;
5006 if (in_explosion_phase)
5007 return (used);
5009 for (unsigned int i = 0; i < range_funcs.size(); ++i)
5010 if ((*range_funcs[i])(*this, victim, used))
5011 break;
5013 return (used);
5016 // Checks whether the beam knocks back the supplied actor. The actor
5017 // should have already failed their EV check, so the save is entirely
5018 // body-mass-based.
5019 bool bolt::knockback_actor(actor *act)
5021 ASSERT(ray.pos() == act->pos());
5023 const coord_def oldpos(ray.pos());
5024 const ray_def ray_copy(ray);
5025 ray.advance();
5027 const coord_def newpos(ray.pos());
5028 if (newpos == oldpos
5029 || actor_at(newpos)
5030 || (act->atype() == ACT_MONSTER
5031 && mons_is_stationary(act->as_monster()))
5032 || feat_is_solid(grd(newpos))
5033 || !act->can_pass_through(newpos)
5034 || !act->is_habitable(newpos)
5035 // Save is based on target's body weight.
5036 || random2(2500) < act->body_weight())
5038 ray = ray_copy;
5039 return false;
5042 act->move_to_pos(newpos);
5044 // Knockback cannot ever kill the actor directly - caller must do
5045 // apply_location_effects after messaging.
5046 return true;
5049 // Takes a bolt and refines it for use in the explosion function.
5050 // Explosions which do not follow from beams (e.g., scrolls of
5051 // immolation) bypass this function.
5052 void bolt::refine_for_explosion()
5054 ASSERT(!special_explosion);
5056 const char *seeMsg = NULL;
5057 const char *hearMsg = NULL;
5059 if (ex_size == 0)
5060 ex_size = 1;
5062 // Assume that the player can see/hear the explosion, or
5063 // gets burned by it anyway. :)
5064 msg_generated = true;
5066 // tmp needed so that what c_str() points to doesn't go out of scope
5067 // before the function ends.
5068 std::string tmp;
5069 if (item != NULL)
5071 tmp = "The " + item->name(DESC_PLAIN, false, false, false)
5072 + " explodes!";
5074 seeMsg = tmp.c_str();
5075 hearMsg = "You hear an explosion!";
5077 glyph = dchar_glyph(DCHAR_FIRED_BURST);
5080 if (name.find("hellfire") != std::string::npos)
5082 seeMsg = "The hellfire explodes!";
5083 hearMsg = "You hear a strangely unpleasant explosion!";
5085 glyph = dchar_glyph(DCHAR_FIRED_BURST);
5086 flavour = BEAM_HELLFIRE;
5089 if (name == "fireball")
5091 seeMsg = "The fireball explodes!";
5092 hearMsg = "You hear an explosion!";
5094 glyph = dchar_glyph(DCHAR_FIRED_BURST);
5095 flavour = BEAM_FIRE;
5096 ex_size = 1;
5099 if (name == "orb of electricity")
5101 seeMsg = "The orb of electricity explodes!";
5102 hearMsg = "You hear a clap of thunder!";
5104 glyph = dchar_glyph(DCHAR_FIRED_BURST);
5105 flavour = BEAM_ELECTRICITY;
5106 colour = LIGHTCYAN;
5107 damage.num = 1;
5108 ex_size = 2;
5111 if (name == "orb of energy")
5113 seeMsg = "The orb of energy explodes!";
5114 hearMsg = "You hear an explosion!";
5117 if (name == "metal orb")
5119 seeMsg = "The orb explodes into a blast of deadly shrapnel!";
5120 hearMsg = "You hear an explosion!";
5122 name = "blast of shrapnel";
5123 glyph = dchar_glyph(DCHAR_FIRED_ZAP);
5124 flavour = BEAM_FRAG; // Sets it from pure damage to shrapnel
5125 // (which is absorbed extra by armour).
5128 if (name == "great blast of cold")
5130 seeMsg = "The blast explodes into a great storm of ice!";
5131 hearMsg = "You hear a raging storm!";
5133 name = "ice storm";
5134 glyph = dchar_glyph(DCHAR_FIRED_ZAP);
5135 colour = WHITE;
5136 ex_size = is_tracer ? 3 : (2 + (random2(ench_power) > 75));
5139 if (name == "stinking cloud")
5141 seeMsg = "The beam explodes into a vile cloud!";
5142 hearMsg = "You hear a loud \'bang\'!";
5145 if (name == "foul vapour")
5147 seeMsg = "The ball explodes into a vile cloud!";
5148 hearMsg = "You hear a loud \'bang\'!";
5149 if (!is_tracer)
5150 name = "stinking cloud";
5153 if (name == "potion")
5155 seeMsg = "The potion explodes!";
5156 hearMsg = "You hear an explosion!";
5157 if (!is_tracer)
5160 name = "cloud";
5161 ASSERT(flavour >= BEAM_POTION_STINKING_CLOUD
5162 && flavour <= BEAM_POTION_RANDOM);
5163 const int newcolour = _potion_beam_flavour_to_colour(flavour);
5164 if (newcolour >= 0)
5165 colour = newcolour;
5169 if (seeMsg == NULL)
5171 seeMsg = "The beam explodes into a cloud of software bugs!";
5172 hearMsg = "You hear the sound of one hand!";
5176 if (!is_tracer && *seeMsg && *hearMsg)
5178 heard = player_can_hear(target);
5179 // Check for see/hear/no msg.
5180 if (you.see_cell(target) || target == you.pos())
5181 mpr(seeMsg);
5182 else
5184 if (!heard)
5185 msg_generated = false;
5186 else
5187 mpr(hearMsg, MSGCH_SOUND);
5192 typedef std::vector< std::vector<coord_def> > sweep_type;
5194 static sweep_type _radial_sweep(int r)
5196 sweep_type result;
5197 sweep_type::value_type work;
5199 // Center first.
5200 work.push_back(coord_def(0,0));
5201 result.push_back(work);
5203 for (int rad = 1; rad <= r; ++rad)
5205 work.clear();
5207 for (int d = -rad; d <= rad; ++d)
5209 // Don't put the corners in twice!
5210 if (d != rad && d != -rad)
5212 work.push_back(coord_def(-rad, d));
5213 work.push_back(coord_def(+rad, d));
5216 work.push_back(coord_def(d, -rad));
5217 work.push_back(coord_def(d, +rad));
5219 result.push_back(work);
5221 return result;
5224 #define MAX_EXPLOSION_RADIUS 9
5225 // Returns true if we saw something happening.
5226 bool bolt::explode(bool show_more, bool hole_in_the_middle)
5228 ASSERT(!special_explosion);
5229 ASSERT(!in_explosion_phase);
5230 ASSERT(ex_size > 0);
5232 // explode() can be called manually without setting real_flavour.
5233 // FIXME: The entire flavour/real_flavour thing needs some
5234 // rewriting!
5235 if (real_flavour == BEAM_CHAOS || real_flavour == BEAM_RANDOM)
5236 flavour = real_flavour;
5237 else
5238 real_flavour = flavour;
5240 const int r = std::min(ex_size, MAX_EXPLOSION_RADIUS);
5241 in_explosion_phase = true;
5243 if (is_sanctuary(pos()))
5245 if (!is_tracer && you.see_cell(pos()) && !name.empty())
5247 mprf(MSGCH_GOD, "By Zin's power, the %s is contained.",
5248 name.c_str());
5249 return (true);
5251 return (false);
5254 #ifdef DEBUG_DIAGNOSTICS
5255 mprf(MSGCH_DIAGNOSTICS,
5256 "explosion at (%d, %d) : g=%d c=%d f=%d hit=%d dam=%dd%d r=%d",
5257 pos().x, pos().y, glyph, colour, flavour, hit, damage.num, damage.size, r);
5258 #endif
5260 if (!is_tracer)
5262 loudness = 10 + 5 * r;
5264 bool heard_expl = noisy(loudness, pos(), beam_source);
5265 heard = heard || heard_expl;
5267 if (heard_expl && !noise_msg.empty() && !you.see_cell(pos()))
5268 mprf(MSGCH_SOUND, "%s", noise_msg.c_str());
5271 // Run DFS to determine which cells are influenced
5272 explosion_map exp_map;
5273 exp_map.init(INT_MAX);
5274 determine_affected_cells(exp_map, coord_def(), 0, r, true, true);
5276 #if defined(TARGET_OS_WINDOWS) && !defined(USE_TILE)
5277 // turn buffering off
5278 bool oldValue = true;
5279 if (!is_tracer)
5280 oldValue = set_buffering(false);
5281 #endif
5283 // We get a bit fancy, drawing all radius 0 effects, then radius
5284 // 1, radius 2, etc. It looks a bit better that way.
5285 const std::vector< std::vector<coord_def> > sweep = _radial_sweep(r);
5286 const coord_def centre(9,9);
5288 typedef sweep_type::const_iterator siter;
5289 typedef sweep_type::value_type::const_iterator viter;
5291 // Draw pass.
5292 if (!is_tracer)
5294 for (siter ci = sweep.begin(); ci != sweep.end(); ++ci)
5296 for (viter cci = ci->begin(); cci != ci->end(); ++cci)
5298 const coord_def delta = *cci;
5300 if (delta.origin() && hole_in_the_middle)
5301 continue;
5303 if (exp_map(delta + centre) < INT_MAX)
5304 explosion_draw_cell(delta + pos());
5306 update_screen();
5308 int explode_delay = 50;
5309 // Scale delay to match change in arena_delay.
5310 if (crawl_state.game_is_arena())
5312 explode_delay *= Options.arena_delay;
5313 explode_delay /= 600;
5316 delay(explode_delay);
5320 // Affect pass.
5321 int cells_seen = 0;
5322 for (siter ci = sweep.begin(); ci != sweep.end(); ++ci)
5324 for (viter cci = ci->begin(); cci != ci->end(); ++cci)
5326 const coord_def delta = *cci;
5328 if (delta.origin() && hole_in_the_middle)
5329 continue;
5331 if (exp_map(delta + centre) < INT_MAX)
5333 if (you.see_cell(delta + pos()))
5334 ++cells_seen;
5336 explosion_affect_cell(delta + pos());
5341 #if defined(TARGET_OS_WINDOWS) && !defined(USE_TILE)
5342 if (!is_tracer)
5343 set_buffering(oldValue);
5344 #endif
5346 // Delay after entire explosion has been drawn.
5347 if (!is_tracer && cells_seen > 0 && show_more)
5349 int explode_delay = 150;
5350 // Scale delay to match change in arena_delay.
5351 if (crawl_state.game_is_arena())
5353 explode_delay *= Options.arena_delay;
5354 explode_delay /= 600;
5357 delay(explode_delay);
5360 return (cells_seen > 0);
5363 void bolt::explosion_draw_cell(const coord_def& p)
5365 if (you.see_cell(p))
5367 const coord_def drawpos = grid2view(p);
5368 #ifdef USE_TILE
5369 if (in_los_bounds_v(drawpos))
5370 tiles.add_overlay(p, tileidx_bolt(*this));
5371 #else
5372 // bounds check
5373 if (in_los_bounds_v(drawpos))
5375 cgotoxy(drawpos.x, drawpos.y, GOTO_DNGN);
5376 put_colour_ch(colour == BLACK ? random_colour() : colour,
5377 dchar_glyph(DCHAR_EXPLOSION));
5379 #endif
5383 void bolt::explosion_affect_cell(const coord_def& p)
5385 // pos() = target during an explosion, so restore it after affecting
5386 // the cell.
5387 const coord_def orig_pos = target;
5389 fake_flavour();
5390 target = p;
5391 affect_cell();
5392 flavour = real_flavour;
5394 target = orig_pos;
5397 // Uses DFS
5398 void bolt::determine_affected_cells(explosion_map& m, const coord_def& delta,
5399 int count, int r,
5400 bool stop_at_statues, bool stop_at_walls)
5402 const coord_def centre(9,9);
5403 const coord_def loc = pos() + delta;
5405 // A bunch of tests for edge cases.
5406 if (delta.rdist() > centre.rdist()
5407 || (delta.abs() > r*(r+1))
5408 || (count > 10*r)
5409 || !map_bounds(loc)
5410 || is_sanctuary(loc))
5412 return;
5415 const dungeon_feature_type dngn_feat = grd(loc);
5417 // Check to see if we're blocked by a wall.
5418 if (feat_is_wall(dngn_feat)
5419 || dngn_feat == DNGN_SECRET_DOOR
5420 || feat_is_closed_door(dngn_feat))
5422 // Special case: explosion originates from rock/statue
5423 // (e.g. Lee's Rapid Deconstruction) - in this case, ignore
5424 // solid cells at the center of the explosion.
5425 if (stop_at_walls && !(delta.origin() && affects_wall(dngn_feat)))
5426 return;
5429 if (feat_is_solid(dngn_feat) && !feat_is_wall(dngn_feat) && stop_at_statues)
5430 return;
5432 // Check if it passes the callback functions.
5433 bool hits = true;
5434 for (unsigned int i = 0; i < aoe_funcs.size(); ++i)
5435 hits = (*aoe_funcs[i])(*this, loc) && hits;
5437 if (hits)
5439 // Hmm, I think we're OK.
5440 m(delta + centre) = std::min(count, m(delta + centre));
5443 // Now recurse in every direction.
5444 for (int i = 0; i < 8; ++i)
5446 const coord_def new_delta = delta + Compass[i];
5448 if (new_delta.rdist() > centre.rdist())
5449 continue;
5451 // Is that cell already covered?
5452 if (m(new_delta + centre) <= count)
5453 continue;
5455 int cadd = 5;
5456 // Changing direction (e.g. looking around a wall) costs more.
5457 if (delta.x * Compass[i].x < 0 || delta.y * Compass[i].y < 0)
5458 cadd = 17;
5460 determine_affected_cells(m, new_delta, count + cadd, r,
5461 stop_at_statues, stop_at_walls);
5465 // Returns true if the beam is harmful (ignoring monster
5466 // resists) -- mon is given for 'special' cases where,
5467 // for example, "Heal" might actually hurt undead, or
5468 // "Holy Word" being ignored by holy monsters, etc.
5470 // Only enchantments should need the actual monster type
5471 // to determine this; non-enchantments are pretty
5472 // straightforward.
5473 bool bolt::nasty_to(const monster* mon) const
5475 // Cleansing flame.
5476 if (flavour == BEAM_HOLY)
5477 return (mon->res_holy_energy(agent()) <= 0);
5479 // The orbs are made of pure disintegration energy. This also has the side
5480 // effect of not stopping us from firing further orbs when the previous one
5481 // is still flying.
5482 if (flavour == BEAM_DISINTEGRATION || flavour == BEAM_NUKE)
5483 return (mon->type != MONS_ORB_OF_DESTRUCTION);
5485 // Take care of other non-enchantments.
5486 if (!is_enchantment())
5487 return (true);
5489 // Now for some non-hurtful enchantments.
5490 if (flavour == BEAM_DIGGING)
5491 return (false);
5493 // Positive effects.
5494 if (nice_to(mon))
5495 return (false);
5497 // Friendly and good neutral monsters don't mind being teleported.
5498 if (flavour == BEAM_TELEPORT)
5499 return (!mon->wont_attack());
5501 // degeneration / sleep / enslave soul
5502 if (flavour == BEAM_DEGENERATE
5503 || flavour == BEAM_HIBERNATION
5504 || flavour == BEAM_ENSLAVE_SOUL)
5506 return (mon->holiness() == MH_NATURAL);
5509 // dispel undead
5510 if (flavour == BEAM_DISPEL_UNDEAD)
5511 return (mon->holiness() == MH_UNDEAD);
5513 // pain / agony
5514 if (flavour == BEAM_PAIN)
5515 return (!mon->res_negative_energy());
5517 // control demon
5518 if (flavour == BEAM_ENSLAVE_DEMON)
5519 return (mon->holiness() == MH_DEMONIC);
5521 // everything else is considered nasty by everyone
5522 return (true);
5525 // Return true if the bolt is considered nice by mon.
5526 // This is not the inverse of nasty_to(): the bolt needs to be
5527 // actively positive.
5528 bool bolt::nice_to(const monster* mon) const
5530 // Polymorphing a (very) ugly thing will mutate it into a different
5531 // (very) ugly thing.
5532 if (flavour == BEAM_POLYMORPH)
5534 return (mon->type == MONS_UGLY_THING
5535 || mon->type == MONS_VERY_UGLY_THING);
5538 if (flavour == BEAM_HASTE
5539 || flavour == BEAM_HEALING
5540 || flavour == BEAM_INVISIBILITY)
5542 return (true);
5545 return (false);
5548 ////////////////////////////////////////////////////////////////////////////
5549 // bolt
5551 // A constructor for bolt to help guarantee that we start clean (this has
5552 // caused way too many bugs). Putting it here since there's no good place to
5553 // put it, and it doesn't do anything other than initialise its members.
5555 // TODO: Eventually it'd be nice to have a proper factory for these things
5556 // (extended from setup_mons_cast() and zapping() which act as limited ones).
5557 bolt::bolt() : origin_spell(SPELL_NO_SPELL),
5558 range(-2), glyph('*'), colour(BLACK), flavour(BEAM_MAGIC),
5559 real_flavour(BEAM_MAGIC), drop_item(false), item(NULL),
5560 source(), target(), damage(0, 0), ench_power(0), hit(0),
5561 thrower(KILL_MISC), ex_size(0), beam_source(MHITNOT),
5562 source_name(), name(), short_name(), hit_verb(),
5563 loudness(0), noise_msg(), is_beam(false), is_explosion(false),
5564 is_big_cloud(false), aimed_at_spot(false), aux_source(),
5565 affects_nothing(false), affects_items(true), effect_known(true),
5566 draw_delay(15), special_explosion(NULL), range_funcs(),
5567 damage_funcs(), hit_funcs(), aoe_funcs(), affect_func(NULL),
5568 obvious_effect(false), seen(false), heard(false),
5569 path_taken(), extra_range_used(0), is_tracer(false),
5570 aimed_at_feet(false), msg_generated(false),
5571 noise_generated(false), passed_target(false),
5572 in_explosion_phase(false), smart_monster(false),
5573 can_see_invis(false), attitude(ATT_HOSTILE), foe_ratio(0),
5574 chose_ray(false), beam_cancelled(false),
5575 dont_stop_player(false), bounces(false), bounce_pos(),
5576 reflections(0), reflector(-1), auto_hit(false)
5580 killer_type bolt::killer() const
5582 if (flavour == BEAM_BANISH)
5583 return (KILL_BANISHED);
5585 switch (thrower)
5587 case KILL_YOU:
5588 case KILL_YOU_MISSILE:
5589 return (flavour == BEAM_PARALYSIS
5590 || flavour == BEAM_PETRIFY) ? KILL_YOU : KILL_YOU_MISSILE;
5592 case KILL_MON:
5593 case KILL_MON_MISSILE:
5594 return (KILL_MON_MISSILE);
5596 case KILL_YOU_CONF:
5597 return (KILL_YOU_CONF);
5599 default:
5600 return (KILL_MON_MISSILE);
5604 void bolt::set_target(const dist &d)
5606 if (!d.isValid)
5607 return;
5609 target = d.target;
5611 chose_ray = d.choseRay;
5612 if (d.choseRay)
5613 ray = d.ray;
5615 if (d.isEndpoint)
5616 aimed_at_spot = true;
5619 void bolt::setup_retrace()
5621 if (pos().x && pos().y)
5622 target = pos();
5624 std::swap(source, target);
5625 chose_ray = false;
5626 affects_nothing = true;
5627 aimed_at_spot = true;
5628 extra_range_used = 0;
5631 void bolt::set_agent(actor *actor)
5633 // NULL actor is fine by us.
5634 if (!actor)
5635 return;
5637 if (actor->atype() == ACT_PLAYER)
5639 thrower = KILL_YOU_MISSILE;
5641 else
5643 thrower = KILL_MON_MISSILE;
5644 beam_source = actor->mindex();
5648 actor* bolt::agent() const
5650 killer_type nominal_ktype = thrower;
5651 int nominal_source = beam_source;
5653 // If the beam was reflected report a different point of origin
5654 if (reflections > 0)
5656 if (reflector == NON_MONSTER)
5657 nominal_ktype = KILL_YOU_MISSILE;
5658 nominal_source = reflector;
5660 if (YOU_KILL(nominal_ktype))
5661 return (&you);
5662 else if (!invalid_monster_index(nominal_source))
5663 return (&menv[nominal_source]);
5664 else
5665 return (NULL);
5668 bool bolt::is_enchantment() const
5670 return (flavour >= BEAM_FIRST_ENCHANTMENT
5671 && flavour <= BEAM_LAST_ENCHANTMENT);
5674 std::string bolt::get_short_name() const
5676 if (!short_name.empty())
5677 return (short_name);
5679 if (item != NULL && item->defined())
5680 return item->name(DESC_NOCAP_A, false, false, false, false,
5681 ISFLAG_IDENT_MASK | ISFLAG_COSMETIC_MASK
5682 | ISFLAG_RACIAL_MASK);
5684 if (real_flavour == BEAM_RANDOM || real_flavour == BEAM_CHAOS)
5685 return _beam_type_name(real_flavour);
5687 if (flavour == BEAM_FIRE && name == "sticky fire")
5688 return ("sticky fire");
5690 if (flavour == BEAM_ELECTRICITY && is_beam)
5691 return ("lightning");
5693 if (flavour == BEAM_NONE || flavour == BEAM_MISSILE
5694 || flavour == BEAM_MMISSILE)
5696 return (name);
5699 return _beam_type_name(flavour);
5702 static std::string _beam_type_name(beam_type type)
5704 switch (type)
5706 case BEAM_NONE: return ("none");
5707 case BEAM_MISSILE: return ("missile");
5708 case BEAM_MMISSILE: return ("magic missile");
5710 case BEAM_POTION_FIRE: // fall through
5711 case BEAM_FIRE: return ("fire");
5713 case BEAM_POTION_COLD: // fall through
5714 case BEAM_COLD: return ("cold");
5715 case BEAM_WATER: return ("water");
5717 case BEAM_MAGIC: return ("magic");
5718 case BEAM_ELECTRICITY: return ("electricity");
5720 case BEAM_POTION_STINKING_CLOUD: return ("noxious fumes");
5722 case BEAM_POTION_POISON: // fall through
5723 case BEAM_POISON: return ("poison");
5725 case BEAM_NEG: return ("negative energy");
5726 case BEAM_ACID: return ("acid");
5728 case BEAM_MIASMA: // fall through
5729 case BEAM_POTION_MIASMA: return ("miasma");
5731 case BEAM_SPORE: return ("spores");
5732 case BEAM_POISON_ARROW: return ("poison arrow");
5733 case BEAM_HELLFIRE: return ("hellfire");
5734 case BEAM_NAPALM: return ("sticky fire");
5736 case BEAM_POTION_STEAM: // fall through
5737 case BEAM_STEAM: return ("steam");
5739 case BEAM_ENERGY: return ("energy");
5740 case BEAM_HOLY: return ("holy energy");
5741 case BEAM_FRAG: return ("fragments");
5742 case BEAM_LAVA: return ("magma");
5743 case BEAM_ICE: return ("ice");
5744 case BEAM_NUKE: return ("nuke");
5745 case BEAM_LIGHT: return ("light");
5746 case BEAM_RANDOM: return ("random");
5747 case BEAM_CHAOS: return ("chaos");
5748 case BEAM_SLOW: return ("slow");
5749 case BEAM_HASTE: return ("haste");
5750 case BEAM_MIGHT: return ("might");
5751 case BEAM_HEALING: return ("healing");
5752 case BEAM_PARALYSIS: return ("paralysis");
5753 case BEAM_CONFUSION: return ("confusion");
5754 case BEAM_INVISIBILITY: return ("invisibility");
5755 case BEAM_DIGGING: return ("digging");
5756 case BEAM_TELEPORT: return ("teleportation");
5757 case BEAM_POLYMORPH: return ("polymorph");
5758 case BEAM_CHARM: return ("enslave");
5759 case BEAM_BANISH: return ("banishment");
5760 case BEAM_DEGENERATE: return ("degeneration");
5761 #if TAG_MAJOR_VERSION == 32
5762 case BEAM_ENSLAVE_UNDEAD: return ("enslave undead");
5763 #endif
5764 case BEAM_ENSLAVE_SOUL: return ("enslave soul");
5765 case BEAM_PAIN: return ("pain");
5766 case BEAM_DISPEL_UNDEAD: return ("dispel undead");
5767 case BEAM_DISINTEGRATION: return ("disintegration");
5768 case BEAM_ENSLAVE_DEMON: return ("enslave demon");
5769 case BEAM_BLINK: return ("blink");
5770 case BEAM_BLINK_CLOSE: return ("blink close");
5771 case BEAM_PETRIFY: return ("petrify");
5772 case BEAM_CORONA: return ("backlight");
5773 case BEAM_PORKALATOR: return ("porkalator");
5774 case BEAM_HIBERNATION: return ("hibernation");
5775 case BEAM_SLEEP: return ("sleep");
5776 case BEAM_BERSERK: return ("berserk");
5777 case BEAM_POTION_BLACK_SMOKE: return ("black smoke");
5778 case BEAM_POTION_GREY_SMOKE: return ("grey smoke");
5779 case BEAM_POTION_BLUE_SMOKE: return ("blue smoke");
5780 case BEAM_POTION_PURPLE_SMOKE: return ("purple smoke");
5781 case BEAM_POTION_RAIN: return ("rain");
5782 case BEAM_POTION_RANDOM: return ("random potion");
5783 case BEAM_POTION_MUTAGENIC: return ("mutagenic fog");
5784 case BEAM_VISUAL: return ("visual effects");
5785 case BEAM_TORMENT_DAMAGE: return ("torment damage");
5786 case BEAM_DEVOUR_FOOD: return ("devour food");
5787 case BEAM_GLOOM: return ("gloom");
5788 case BEAM_INK: return ("ink");
5789 case BEAM_HOLY_FLAME: return ("cleansing flame");
5790 case BEAM_HOLY_LIGHT: return ("holy light");
5791 case BEAM_AIR: return ("air");
5793 case NUM_BEAMS: die("invalid beam type");
5795 die("unknown beam type");
5798 std::string bolt::get_source_name() const
5800 if (!source_name.empty())
5801 return source_name;
5802 const actor *a = agent();
5803 if (a)
5804 return a->name(DESC_NOCAP_A, true);
5805 return "";
5808 void clear_zap_info_on_exit()
5810 const unsigned int zap_size = sizeof(zap_data) / sizeof(zap_info);
5811 for (unsigned int i = 0; i < zap_size; ++i)
5813 delete zap_data[i].damage;
5814 delete zap_data[i].tohit;