3 * Summary: Functions related to ranged attacks.
4 * Written by: Linley Henzell
24 #include "attitude-change.h"
38 #include "map_knowledge.h"
49 #include "mgen_data.h"
52 #include "mon-death.h"
54 #include "mon-place.h"
55 #include "mon-stuff.h"
61 #include "godconduct.h"
63 #include "spl-clouds.h"
64 #include "spl-goditem.h"
65 #include "spl-monench.h"
66 #include "spl-transloc.h"
74 #include "transform.h"
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
,
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()
100 void tracer_info::reset()
102 count
= power
= hurt
= helped
= 0;
106 const tracer_info
& tracer_info::operator+=(const tracer_info
&other
)
108 count
+= other
.count
;
109 power
+= other
.power
;
111 helped
+= other
.helped
;
113 dont_stop
= dont_stop
|| other
.dont_stop
;
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())
132 message_cache
.insert(message
);
135 kill_category
bolt::whose_kill() const
137 if (YOU_KILL(thrower
))
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
];
147 return (KC_FRIENDLY
);
153 // A simple animated flash from Rupert Smith (expanded to be more
155 static void _zap_animation(int colour
, const monster
* mon
= NULL
,
158 coord_def p
= you
.pos();
162 if (!force
&& !mon
->visible_to(&you
))
168 if (!you
.see_cell(p
))
171 const coord_def drawp
= grid2view(p
);
173 if (in_los_bounds_v(drawp
))
175 // Default to whatever colour magic is today.
180 tiles
.add_overlay(p
, tileidx_zap(colour
));
183 cgotoxy(drawp
.x
, drawp
.y
, GOTO_DNGN
);
184 put_colour_ch(colour
, dchar_glyph(DCHAR_FIRED_ZAP
));
190 // Scale delay to match change in arena_delay.
191 if (crawl_state
.game_is_arena())
193 zap_delay
*= Options
.arena_delay
;
201 // Special front function for zap_animation to interpret enchantment flavours.
202 static void _ench_animation(int flavour
, const monster
* mon
, bool force
)
213 case BEAM_DISPEL_UNDEAD
:
217 elem
= ETC_MUTAGENIC
;
225 case BEAM_BLINK_CLOSE
:
233 _zap_animation(element_colour(elem
), mon
, force
);
236 // If needs_tracer is true, we need to check the beam path for friendly
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
))
252 // Fill in the bolt structure.
253 _zappy(ztype
, power
, pbolt
);
258 if (ztype
== ZAP_LIGHTNING
)
260 noisy(25, you
.pos(), "You hear a mighty clap of thunder!");
264 if (ztype
== ZAP_DIGGING
)
265 pbolt
.aimed_at_spot
= false;
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.)
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;
303 pbolt
.reflections
= 0;
306 // Save range before overriding it
307 const int old_range
= pbolt
.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.");
322 you
.turn_is_over
= false;
326 // Set to non-tracing for actual firing.
327 pbolt
.is_tracer
= false;
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
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
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
367 dice_def
operator()(int pow
) const
369 return calc_dice(numdice
, adder
+ (pow
* mult_num
) / mult_denom
);
376 const char* name
; // NULL means handled specially
379 tohit_deducer
* tohit
; // Enchantments have power modifier here
383 dungeon_char_type glyph
;
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
)
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)
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
);
431 #ifdef DEBUG_DIAGNOSTICS
432 mprf(MSGCH_ERROR
, "Couldn't find zap type %d", z_type
);
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
;
459 pbolt
.hit
= (*zinfo
->tohit
)(power
);
460 if (wearing_amulet(AMU_INACCURACY
))
461 pbolt
.hit
= std::max(0, pbolt
.hit
- 5);
465 pbolt
.damage
= (*zinfo
->damage
)(power
);
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:
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
495 bool bolt::can_affect_wall_actor(const actor
*act
) const
497 if (!can_affect_actor(act
))
500 if (is_enchantment())
503 const bool superconductor
= (grd(act
->pos()) == DNGN_METAL_WALL
504 && flavour
== BEAM_ELECTRICITY
);
505 if (actor_wall_shielded(act
) && !superconductor
)
508 if (!is_explosion
&& !is_big_cloud
)
511 if (is_bouncy(grd(act
->pos())))
517 static beam_type
_chaos_beam_flavour()
519 const beam_type flavour
= static_cast<beam_type
>(
520 random_choose_weighted(
523 10, BEAM_ELECTRICITY
,
536 10, BEAM_INVISIBILITY
,
539 10, BEAM_DISINTEGRATION
,
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
)
551 double old_deg
= old_ray
.get_degrees();
552 double new_deg
= new_ray
.get_degrees();
553 double angle
= fabs(old_deg
- new_deg
);
558 double max
= 90.0 + (angle
/ 2.0);
559 double min
= -90.0 + (angle
/ 2.0);
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
)
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
;
577 if (in_bounds(test_ray
.pos()) && !cell_is_solid(test_ray
.pos()))
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
));
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;
622 ASSERT(in_bounds(ray
.pos()));
624 if (source
== coord_def())
628 if (target
== source
)
631 aimed_at_feet
= true;
633 aimed_at_spot
= true;
634 use_target_as_pos
= true;
642 mpr("Tracer with range == -1, skipping.", MSGCH_ERROR
);
646 std::string item_name
= item
? item
->name(DESC_PLAIN
, false, true)
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());
662 ASSERT(in_bounds(source
));
663 ASSERT(flavour
> BEAM_NONE
&& flavour
< BEAM_FIRST_PSEUDO
);
664 ASSERT(!drop_item
|| item
&& item
->defined());
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())
676 const monster
* mon
= monster_at(source
);
678 if (flavour
!= BEAM_VISUAL
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
691 if (you
.see_cell(source
) && target
== source
&& visible())
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
;
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" : "",
711 glyph
, colour
, flavour
,
712 hit
, damage
.num
, damage
.size
,
717 void bolt::apply_beam_conducts()
719 if (!is_tracer
&& YOU_KILL(thrower
))
724 did_god_conduct(DID_UNHOLY
, 2 + random2(3), effect_known
);
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
))
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
))
759 tile_beam
= tileidx_bolt(*this);
762 tiles
.add_overlay(p
, tile_beam
);
764 cgotoxy(drawpos
.x
, drawpos
.y
, GOTO_DNGN
);
765 put_colour_ch(colour
== BLACK
? random_colour()
766 : element_colour(colour
),
769 // Get curses to update the screen so we can see the beam.
775 // Bounce a bolt off a solid feature.
776 // The ray is assumed to have just been advanced into
780 ray_def old_ray
= ray
;
781 bolt old_bolt
= *this;
785 while (feat_is_solid(grd(ray
.pos())));
787 extra_range_used
+= range_used(true);
788 bounce_pos
= ray
.pos();
791 for (adjacent_iterator
ai(ray
.pos(), false); ai
; ++ai
)
792 rg(*ai
- ray
.pos()) = feat_is_solid(grd(*ai
));
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
)
818 obvious_effect
= true;
819 msg_generated
= true;
822 if (feat
== DNGN_GRATE
)
824 mprf("The damaged grate falls apart into pieces.");
827 else if (feat
== DNGN_SLIMY_WALL
)
829 else if (you
.level_type
== LEVEL_PANDEMONIUM
)
830 wall
= "weird stuff";
833 mprf("The %s liquefies and sinks out of sight.", wall
.c_str());
837 else if (feat_is_wall(feat
))
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")
852 if (feat
== DNGN_WAX_WALL
)
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.");
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;
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
);
896 place_cloud(CLOUD_FOREST_FIRE
, pos(), random2(30)+25, agent());
897 obvious_effect
= true;
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")
915 static bool _nuke_wall_msg(dungeon_feature_type feat
, const coord_def
& p
)
918 msg_channel_type chan
= MSGCH_PLAIN
;
919 bool hear
= player_can_hear(p
);
920 bool see
= you
.see_cell(p
);
925 case DNGN_SLIMY_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]
939 msg
= "You hear a grinding noise.";
947 msg
= "You hear the screech of bent metal.";
952 case DNGN_ORCISH_IDOL
:
956 msg
= "You hear a hideous screaming!";
958 msg
= "The idol screams as its substance crumbles away!";
962 msg
= "The idol twists and shakes as its substance crumbles away!";
966 case DNGN_SWAMP_TREE
:
968 msg
= "The tree breaks and falls down!";
971 msg
= "You hear timber falling.";
989 void bolt::nuke_wall_effect()
991 if (env
.markers
.property_at(pos(), MAT_ANY
, "veto_disintegrate") == "veto")
997 const dungeon_feature_type feat
= grd(pos());
1001 case DNGN_ROCK_WALL
:
1002 case DNGN_SLIMY_WALL
:
1004 case DNGN_CLEAR_ROCK_WALL
:
1006 case DNGN_GRANITE_STATUE
:
1007 case DNGN_ORCISH_IDOL
:
1009 case DNGN_SWAMP_TREE
:
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
)
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);
1047 // integer square root, such that _length((8,1)) == 8.
1048 static int _length(const coord_def
& c
)
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()
1070 if (affects_wall(grd(pos())) != B_TRUE
)
1075 if (flavour
== BEAM_DIGGING
)
1076 digging_wall_effect();
1077 else if (is_fiery())
1079 else if (flavour
== BEAM_ELECTRICITY
)
1081 else if (flavour
== BEAM_DISINTEGRATION
|| flavour
== BEAM_NUKE
)
1084 if (cell_is_solid(pos()))
1088 coord_def
bolt::pos() const
1090 if (in_explosion_phase
|| use_target_as_pos
)
1096 bool bolt::need_regress() const
1098 // XXX: The affects_wall check probably makes some of the
1100 return ((is_explosion
&& !in_explosion_phase
)
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
);
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;
1145 // Well, we warned them.
1148 // Press trigger/switch/button in wall if hit by something solid
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
))
1161 else if (is_bouncy(feat
) && !in_explosion_phase
)
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())
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
)
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);
1193 const coord_def old_pos
= pos();
1194 const bool was_solid
= feat_is_solid(grd(pos()));
1198 // Some special casing.
1199 if (actor
*act
= actor_at(pos()))
1201 if (can_affect_wall_actor(act
))
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
1214 // Beam ended due to hitting wall, so don't hit the player
1215 // or monster with the regressed beam.
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
))
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
))
1234 if (!feat_is_solid(grd(pos())))
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
;
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
))
1256 if (!dmg_msg
.empty())
1257 messages
.push_back(dmg_msg
);
1262 static void _undo_tracer(bolt
&orig
, bolt
©
)
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().
1283 if (special_explosion
)
1284 special_explosion
->is_tracer
= is_tracer
;
1288 bolt boltcopy
= *this;
1289 if (special_explosion
!= NULL
)
1290 boltcopy
.special_explosion
= new bolt(*special_explosion
);
1294 if (special_explosion
!= NULL
)
1296 _undo_tracer(*special_explosion
, *boltcopy
.special_explosion
);
1297 delete boltcopy
.special_explosion
;
1300 _undo_tracer(*this, boltcopy
);
1305 if (special_explosion
!= NULL
)
1307 seen
= seen
|| special_explosion
->seen
;
1308 heard
= heard
|| special_explosion
->heard
;
1312 void bolt::do_fire()
1316 if (range
< extra_range_used
&& range
> 0)
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");
1326 apply_beam_conducts();
1327 cursor_control
coff(false);
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
);
1339 msg_generated
= false;
1343 // Take *one* step, so as not to hurt the source.
1347 #if defined(TARGET_OS_WINDOWS) && !defined(USE_TILE)
1348 // Before we start drawing the beam, turn buffering off.
1349 bool oldValue
= true;
1351 oldValue
= set_buffering(false);
1354 while (map_bounds(pos()))
1356 if (range_used() > range
)
1360 ASSERT(range_used() >= range
);
1364 path_taken
.push_back(pos());
1366 if (!affects_nothing
)
1369 if (range_used() > range
)
1375 if (pos() == target
)
1377 passed_target
= true;
1378 if (stop_at_target())
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()))
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
1409 noise_generated
= false;
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)
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
)
1429 // Tracers need nothing further.
1430 if (is_tracer
|| affects_nothing
)
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
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
;
1464 #if defined(TARGET_OS_WINDOWS) && !defined(USE_TILE)
1465 set_buffering(oldValue
);
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!
1478 int original
= hurted
;
1480 switch (pbolt
.flavour
)
1484 hurted
= resist_adjust_damage(
1487 (pbolt
.flavour
== BEAM_FIRE
) ? mons
->res_fire()
1488 : mons
->res_steam(),
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
)
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!");
1514 simple_monster_message(mons
, " is scalded terribly!");
1519 hurted
= resist_adjust_damage(mons
, pbolt
.flavour
,
1522 if (doFlavouredEffects
)
1525 simple_monster_message(mons
, " shrugs off the wave.");
1530 hurted
= resist_adjust_damage(mons
, pbolt
.flavour
,
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!");
1554 case BEAM_ELECTRICITY
:
1555 hurted
= resist_adjust_damage(mons
, pbolt
.flavour
,
1560 if (doFlavouredEffects
)
1562 simple_monster_message(mons
,
1563 (original
> 0) ? " completely resists."
1564 : " appears unharmed.");
1571 const int res
= mons
->res_acid();
1572 hurted
= resist_adjust_damage(mons
, pbolt
.flavour
,
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
);
1592 int res
= mons
->res_poison();
1593 hurted
= resist_adjust_damage(mons
, pbolt
.flavour
, res
,
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());
1610 case BEAM_POISON_ARROW
:
1611 hurted
= resist_adjust_damage(mons
, pbolt
.flavour
,
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);
1632 if (mons
->res_negative_energy() == 3)
1634 if (doFlavouredEffects
)
1635 simple_monster_message(mons
, " completely resists.");
1641 // Early out if no side effects.
1642 if (!doFlavouredEffects
)
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
);
1656 if (mons
->res_rotting())
1658 if (doFlavouredEffects
)
1659 simple_monster_message(mons
, " completely resists.");
1665 // Early out for tracer/no side effects.
1666 if (!doFlavouredEffects
)
1669 miasma_monster(mons
, pbolt
.whose_kill());
1671 if (YOU_KILL(pbolt
.thrower
))
1672 did_god_conduct(DID_UNCLEAN
, 2, pbolt
.effect_known
);
1679 const int rhe
= mons
->res_holy_energy(pbolt
.agent());
1685 hurted
= (hurted
* 3) / 2;
1687 if (doFlavouredEffects
)
1689 simple_monster_message(mons
,
1690 hurted
== 0 ? " appears unharmed."
1691 : " writhes in agony!");
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
,
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!");
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
)
1727 if (doFlavouredEffects
)
1728 simple_monster_message(mons
, " melts!");
1732 if (doFlavouredEffects
)
1733 simple_monster_message(mons
, " is burned terribly!");
1739 resist
= mons
->res_hellfire();
1742 if (doFlavouredEffects
)
1744 simple_monster_message(mons
,
1745 (original
> 0) ? " completely resists."
1746 : " appears unharmed.");
1751 else if (resist
< 0)
1755 if (doFlavouredEffects
)
1756 simple_monster_message(mons
, " melts!");
1760 if (doFlavouredEffects
)
1761 simple_monster_message(mons
, " is burned terribly!");
1764 hurted
*= 12; // hellfire
1770 if (mons
->invisible())
1772 else if (mons_genus(mons
->type
) == MONS_VAMPIRE
)
1773 hurted
+= hurted
/ 2;
1776 if (doFlavouredEffects
)
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!");
1793 if (mons
->type
== MONS_BALLISTOMYCETE
)
1798 if (mons
->res_wind() > 0)
1800 else if (mons
->flight_mode())
1801 hurted
+= hurted
/ 2;
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.");
1821 static bool _monster_resists_mass_enchantment(monster
* mons
,
1822 enchant_type wh_enchant
,
1826 // Assuming that the only mass charm is control undead.
1827 if (wh_enchant
== ENCH_CHARM
)
1829 if (mons
->friendly())
1832 if (mons
->holiness() != MH_UNDEAD
)
1835 int res_margin
= mons
->check_res_magic(pow
);
1838 if (simple_monster_message(mons
,
1839 mons_resist_string(mons
, res_margin
).c_str()))
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
))
1855 int res_margin
= mons
->check_res_magic(pow
);
1858 if (simple_monster_message(mons
,
1859 mons_resist_string(mons
, res_margin
).c_str()))
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
))
1872 else // trying to enchant an unnatural creature doesn't work
1874 if (simple_monster_message(mons
, " is unaffected."))
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;
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
))
1908 if (_monster_resists_mass_enchantment(*mi
, wh_enchant
, pow
, &did_msg
))
1911 if (mi
->add_ench(mon_enchant(wh_enchant
, 0, kc
)))
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
))
1928 // Extra check for fear (monster needs to reevaluate behaviour).
1929 if (wh_enchant
== ENCH_FEAR
)
1930 behaviour_event(*mi
, ME_SCARE
, origin
);
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
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
)
1994 if (mons
->res_poison() > 0)
1997 poison_monster(mons
, who
, levels
, false);
2001 if (!mons
->res_asphyx())
2003 hurted
= roll_dice(2, 6);
2007 simple_monster_message(mons
, " convulses.");
2008 mons
->hurt(agent
, hurted
, BEAM_POISON
);
2013 enchant_monster_with_flavour(mons
, agent
, BEAM_SLOW
);
2015 // Deities take notice.
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
)
2029 if ((!force
&& mons
->res_poison() > 0) || levels
<= 0)
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
)
2041 simple_monster_message(mons
,
2042 old_pois
.degree
> 0 ? " looks even sicker."
2045 behaviour_event(mons
, ME_ANNOY
, (who
== KC_YOU
) ? MHITYOU
: MHITNOT
);
2048 // Finally, take care of deity preferences.
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
2057 bool miasma_monster(monster
* mons
, kill_category who
)
2062 if (mons
->res_rotting())
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
,
2075 if (one_chance_in(3))
2078 beam
.flavour
= BEAM_SLOW
;
2079 beam
.apply_enchantment_to_monster(mons
);
2086 // Actually napalms a monster (with message).
2087 bool napalm_monster(monster
* mons
, kill_category who
, int levels
,
2093 if (mons
->res_sticky_flame() || levels
<= 0)
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
)
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();
2134 pbolt
.reflections
= 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;
2156 pbolt
.explode(false);
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())
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
))
2180 discover_mimic(mimic
);
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
,
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
)
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
))
2223 if (is_enchantment())
2226 if (flavour
== BEAM_ELECTRICITY
&& feat
!= DNGN_METAL_WALL
2227 && feat
!= DNGN_TREE
&& feat
!= DNGN_SWAMP_TREE
)
2232 if ((flavour
== BEAM_FIRE
|| flavour
== BEAM_COLD
)
2233 && feat
== DNGN_GREEN_CRYSTAL_WALL
)
2241 static int _potion_beam_flavour_to_colour(beam_type flavour
)
2245 case BEAM_POTION_STINKING_CLOUD
:
2248 case BEAM_POTION_POISON
:
2249 return (coinflip() ? GREEN
: LIGHTGREEN
);
2251 case BEAM_POTION_MIASMA
:
2252 case BEAM_POTION_BLACK_SMOKE
:
2255 case BEAM_POTION_STEAM
:
2256 case BEAM_POTION_GREY_SMOKE
:
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
:
2268 case BEAM_POTION_PURPLE_SMOKE
:
2271 case BEAM_POTION_RANDOM
:
2273 // Leave it the colour of the potion, the clouds will colour
2274 // themselves on the next refresh. -- bwr
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
)
2300 refine_for_explosion();
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.");
2324 noisy(25, pos(), "You hear a splash.");
2326 _create_feat_splash(pos(),
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")
2340 refine_for_explosion();
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
)
2386 // Summoned creatures' thrown items disappear.
2387 if (item
->flags
& ISFLAG_SUMMONED
)
2389 if (you
.see_cell(pos()))
2392 item
->name(DESC_CAP_THE
).c_str(),
2393 summoned_poof_msg(beam_source
, *item
).c_str());
2395 item_was_destroyed(*item
, beam_source
);
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());
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
);
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
)
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
,
2494 if (you
.see_cell(pos()))
2495 mpr("A fungus suddenly grows.");
2503 const int burn_power
= is_explosion
? 5 :
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
)
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
2534 if (flavour
== BEAM_DIGGING
2535 && (wall
== DNGN_ROCK_WALL
|| wall
== DNGN_CLEAR_ROCK_WALL
2536 || wall
== DNGN_SLIMY_WALL
|| wall
== DNGN_GRATE
))
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
)
2565 // Lee's Rapid Deconstruction
2566 if (flavour
== BEAM_FRAG
)
2567 return (B_TRUE
); // smite targetting, we don't care
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;
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());
2652 if (flavour
>= BEAM_POTION_STINKING_CLOUD
&& flavour
<= BEAM_POTION_RANDOM
)
2654 const int duration
= roll_dice(2, 3 + ench_power
/ 20);
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
);
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;
2690 cl_type
= CLOUD_STEAM
;
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);
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()
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
);
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
])
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());
2775 if (aimed_at_feet
&& effect_known
)
2776 ouch(dam
, NON_MONSTER
, KILLED_BY_SELF_AIMED
, name
.c_str());
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());
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
))
2809 coord_def
fuzz(random_range(-2, 2), random_range(-2, 2));
2810 coord_def newtarget
= target
+ fuzz
;
2812 if (in_bounds(newtarget
))
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
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
)
2828 if (is_beam
&& deflect
)
2830 attack
= r
[0].random2(attack
* 2) / 3;
2832 else if (is_beam
&& repel
)
2835 attack
= r
[0].random_range((attack
+ 1) / 2 + 1, attack
);
2839 attack
= r
[0].random2(attack
/ 2);
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
)
2861 else if (beam_src
== MHITNOT
)
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.
2881 return (mon
->res_holy_energy(agent()) > 0);
2884 return (mon
->res_steam() >= 3);
2887 return (mon
->res_fire() >= 3);
2890 return (mon
->res_cold() >= 3);
2893 return (mon
->res_rotting());
2896 return (mon
->res_negative_energy() == 3);
2898 case BEAM_ELECTRICITY
:
2899 return (mon
->res_elec() >= 3);
2902 return (mon
->res_poison() >= 3);
2905 return (mon
->res_acid() >= 3);
2912 bool bolt::harmless_to_player() const
2914 dprf("beam flavour: %d", flavour
);
2922 // Positive enchantments.
2925 case BEAM_INVISIBILITY
:
2929 return (is_good_god(you
.religion
));
2932 return (player_res_steam(false) >= 3);
2935 return (you
.res_rotting());
2938 return (player_prot_life(false) >= 3);
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));
2953 // Fire and ice can destroy inventory items, acid damage equipment.
2961 bool bolt::is_reflectable(const item_def
*it
) const
2963 if (range_used() > range
)
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
);
2977 // Reflect a beam back the direction it came. This is used
2978 // by shields of reflection.
2979 void bolt::reflect()
2983 target
= leg_source();
2984 extra_range_used
+= range_used(true);
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.
2991 if (pos() == you
.pos())
2992 reflector
= NON_MONSTER
;
2993 else if (monster
* m
= monster_at(pos()))
2994 reflector
= m
->mindex();
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");
3005 flavour
= real_flavour
;
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?",
3020 friend_info
.count
++;
3021 friend_info
.power
+= you
.experience_level
;
3022 dont_stop_player
= true;
3026 beam_cancelled
= true;
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
;
3041 foe_info
.power
+= you
.experience_level
;
3045 std::vector
<std::string
> messages
;
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())
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
)
3072 if (you
.backlit(true, false))
3073 real_tohit
+= 2 + random2(8);
3076 bool train_shields_more
= false;
3079 && (you
.shield() || player_mutation_level(MUT_LARGE_BONE_PLATES
) > 0)
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(),
3097 ident_reflector(you
.shield());
3102 mprf("You block the %s.", name
.c_str());
3105 you
.shield_block_succeeded(agent());
3109 // Some training just for the "attempt".
3110 train_shields_more
= true;
3114 practise(EX_BEAM_MAY_HIT
);
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());
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());
3157 mprf("Helpless, you fail to dodge the %s.", name
.c_str());
3163 if (train_shields_more
)
3164 practise(EX_SHIELD_BEAM_FAIL
);
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)
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.");
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
);
3203 // You didn't resist it.
3205 _ench_animation(real_flavour
);
3209 bool nasty
= true, nice
= false;
3213 case BEAM_HIBERNATION
:
3214 you
.hibernate(ench_power
);
3218 you
.put_to_sleep(NULL
, ench_power
);
3223 obvious_effect
= true;
3226 case BEAM_POLYMORPH
:
3227 if (MON_KILL(thrower
))
3229 mpr("Strange energies course through your body.");
3231 obvious_effect
= true;
3233 else if (get_ident_type(OBJ_WANDS
, WAND_POLYMORPH_OTHER
)
3236 mpr("This is polymorph other only!");
3239 canned_msg(MSG_NOTHING_HAPPENS
);
3243 potion_effect(POT_SLOWING
, ench_power
);
3244 obvious_effect
= true;
3248 potion_effect(POT_SPEED
, ench_power
, false, effect_known
);
3249 contaminate_player(1, effect_known
);
3250 obvious_effect
= true;
3256 potion_effect(POT_HEAL_WOUNDS
, ench_power
);
3257 obvious_effect
= true;
3262 case BEAM_PARALYSIS
:
3263 potion_effect(POT_PARALYSIS
, ench_power
);
3264 obvious_effect
= true;
3268 you
.petrify(agent(), ench_power
);
3269 obvious_effect
= true;
3272 case BEAM_CONFUSION
:
3273 potion_effect(POT_CONFUSION
, ench_power
);
3274 obvious_effect
= true;
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;
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;
3301 random_blink(false);
3302 obvious_effect
= true;
3305 case BEAM_BLINK_CLOSE
:
3306 blink_other_close(&you
, source
);
3307 obvious_effect
= true;
3311 potion_effect(POT_CONFUSION
, ench_power
);
3312 obvious_effect
= true;
3313 break; // enslavement - confusion?
3316 if (YOU_KILL(thrower
))
3318 mpr("This spell isn't strong enough to banish yourself.");
3321 if (you
.level_type
== LEVEL_ABYSS
)
3323 mpr("You feel trapped.");
3326 you
.banished
= true;
3327 you
.banished_by
= zapper();
3328 obvious_effect
= true;
3332 if (player_res_torment())
3334 canned_msg(MSG_YOU_UNAFFECTED
);
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
);
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));
3356 mpr("Pain shoots through your body!");
3358 internal_ouch(damage
.roll());
3360 obvious_effect
= true;
3363 case BEAM_DISPEL_UNDEAD
:
3366 canned_msg(MSG_YOU_UNAFFECTED
);
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
)
3379 else if (you
.hunger_state
> HS_SATIATED
)
3385 internal_ouch(damage
.roll());
3386 obvious_effect
= true;
3389 case BEAM_DISINTEGRATION
:
3390 mpr("You are blasted!");
3392 if (aux_source
.empty())
3393 aux_source
= "disintegration bolt";
3396 int amt
= damage
.roll();
3399 if (you
.can_bleed())
3400 blood_spray(you
.pos(), MONS_PLAYER
, amt
/ 5);
3403 obvious_effect
= true;
3406 case BEAM_PORKALATOR
:
3407 if (!transform(ench_power
, TRAN_PIG
, true))
3409 mpr("You feel like a pig.");
3412 obvious_effect
= true;
3416 potion_effect(POT_BERSERK_RAGE
, ench_power
);
3417 obvious_effect
= true;
3421 potion_effect(POT_MIGHT
, ench_power
);
3422 obvious_effect
= true;
3426 // _All_ enchantments should be enumerated here!
3427 mpr("Software bugs nibble your toes!");
3433 if (mons_att_wont_attack(attitude
))
3436 if (beam_source
== NON_MONSTER
)
3438 // Beam from player rebounded and hit player.
3440 xom_is_stimulated(255);
3444 // Beam from an ally or neutral.
3445 xom_is_stimulated(128);
3453 if (mons_att_wont_attack(attitude
))
3454 friend_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());
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.
3488 // Digging -- don't care.
3489 if (flavour
== BEAM_DIGGING
)
3494 tracer_affect_player();
3498 // Trigger an interrupt, so travel will stop on misses which
3500 if (!YOU_KILL(thrower
))
3501 interrupt_activity(AI_MONSTER_ATTACKS
);
3503 if (is_enchantment())
3505 affect_player_enchantment();
3509 msg_generated
= true;
3511 if (misses_player())
3514 const bool engulfs
= is_explosion
|| is_big_cloud
;
3516 // FIXME: Lots of duplicated code here (compare handling of
3519 int burn_power
= (is_explosion
) ? 5 : (is_beam
) ? 3 : 2;
3522 hurted
+= damage
.roll();
3524 #ifdef DEBUG_DIAGNOSTICS
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
);
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.
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()
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;
3605 if (name
== "sticky flame")
3607 if (!player_res_sticky_flame())
3609 napalm_player(random2avg(7, 3) + 1);
3610 was_affected
= true;
3615 if (flavour
== BEAM_ACID
)
3616 splash_with_acid(5, 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
);
3633 if (in_explosion_phase
&& flavour
== BEAM_SPORE
)
3634 expose_player_to_element(BEAM_SPORE
, burn_power
);
3637 if (origin_spell
== SPELL_QUICKSILVER_BOLT
)
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
))
3650 // Beam from player rebounded and hit player.
3651 // Xom's amusement at the player's being damaged is handled
3653 if (beam_source
== NON_MONSTER
)
3656 xom_is_stimulated(255);
3658 else if (was_affected
)
3659 xom_is_stimulated(128);
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
3688 void bolt::update_hurt_or_helped(monster
* mon
)
3690 if (!mons_atts_aligned(attitude
, mons_attitude(mon
)))
3694 else if (nice_to(mon
))
3697 // Accidentally helped a foe.
3698 if (!is_tracer
&& !effect_known
)
3701 if (flavour
== BEAM_INVISIBILITY
&& can_see_invis
)
3704 xom_is_stimulated(interest
);
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
)))
3729 foe_info
.power
+= mons_power(mon
->type
);
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.
3788 preac
= div_round_up(preac_max_damage
+ preac_max_damage
, 2);
3790 preac
= damage
.roll();
3792 if (!apply_dmg_funcs(mon
, preac
, messages
))
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);
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);
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);
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));
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;
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
))
3881 // Check only if actual damage.
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
;
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
);
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()))
3929 // Trigger explosion on exploding beams.
3930 if (is_explosion
&& !in_explosion_phase
)
3936 // Special explosions (current exploding missiles) aren't
3937 // auto-hit, so we need to explode them at every possible
3939 if (special_explosion
)
3941 bolt orig
= *special_explosion
;
3943 *special_explosion
= orig
;
3946 if (is_enchantment())
3947 tracer_enchantment_affect_monster(mon
);
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).
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());
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....
3988 _ench_animation(real_flavour
, mon
);
3990 _zap_animation(-1, mon
, false);
3992 // Try to hit the monster with the enchantment.
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
))
4002 // Message or record the success/failure.
4003 switch (ench_result
)
4006 if (simple_monster_message(mon
,
4007 resist_margin_phrase(res_margin
).c_str()))
4009 msg_generated
= true;
4012 case MON_UNAFFECTED
:
4013 if (simple_monster_message(mon
, " is unaffected."))
4014 msg_generated
= true;
4017 case MON_OTHER
: // Should this really be here?
4018 update_hurt_or_helped(mon
);
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
))
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());
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
4069 if (item
->special
== SPMSL_CURARE
)
4071 if (ench_power
== AUTOMATIC_HIT
4072 && _curare_hits_monster(agent(), mon
, whose_kill(), 2)
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
))
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
))
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
);
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();
4128 if (shield_block
> 0)
4130 const int ht
= random2(hit
* 130 / 100 + mon
->shield_block_penalty());
4131 if (ht
< shield_block
)
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(),
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());
4151 else if (you
.see_cell(pos()))
4153 mprf("%s blocks the %s.",
4154 mon
->name(DESC_CAP_THE
).c_str(), name
.c_str());
4158 mon
->shield_block_succeeded(agent());
4165 bool bolt::handle_statue_disintegration(monster
* mon
)
4168 if ((flavour
== BEAM_DISINTEGRATION
|| flavour
== BEAM_NUKE
)
4169 && mons_is_statue(mon
->type
, 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
);
4179 mpr("The statue screams as its substance crumbles away!",
4183 else if (you
.see_cell(mon
->pos()))
4185 mpr("The statue twists and shakes as its substance "
4188 obvious_effect
= true;
4189 update_hurt_or_helped(mon
);
4190 mon
->hurt(agent(), INSTANT_DEATH
);
4191 apply_hit_funcs(mon
, INSTANT_DEATH
);
4198 void bolt::affect_monster(monster
* mon
)
4200 // Don't hit dead monsters.
4203 apply_hit_funcs(mon
, 0);
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);
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);
4225 // Missiles go past bushes.
4226 if (mon
->type
== MONS_BUSH
&& !is_beam
&& !is_explosion
)
4228 apply_hit_funcs(mon
, 0);
4232 if (fedhas_shoot_through(*this, mon
))
4234 apply_hit_funcs(mon
, 0);
4237 // FIXME: Could use a better message, something about
4238 // dodging that doesn't sound excessively weird would be
4240 if (you
.see_cell(mon
->pos()))
4242 mprf(MSGCH_GOD
, "Fedhas protects %s plant from harm.",
4243 attitude
== ATT_FRIENDLY
? "your" : "a");
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);
4256 // Handle tracers separately.
4259 tracer_affect_monster(mon
);
4263 // Visual - wake monsters.
4264 if (flavour
== BEAM_VISUAL
)
4266 behaviour_event(mon
, ME_DISTURB
, beam_source
, source
);
4267 apply_hit_funcs(mon
, 0);
4271 // Special case: disintegrate (or Shatter) a statue.
4272 // Since disintegration is an enchantment, it has to be handled
4274 if (handle_statue_disintegration(mon
))
4277 if (is_enchantment())
4280 enchantment_affect_monster(mon
);
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.
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
))
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
);
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,
4312 god_conduct_trigger conducts
[3];
4313 disable_attack_conducts(conducts
);
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
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.
4345 if (beam_hit
!= AUTOMATIC_HIT
)
4347 if (mon
->invisible() && !can_see_invis
)
4350 if (mon
->backlit(true, false))
4351 beam_hit
+= 2 + random2(8);
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";
4382 msg::stream
<< "The " << name
<< " misses "
4383 << mon
->name(DESC_NOCAP_THE
) << '.' << std::endl
;
4389 // The monster may block the beam.
4390 if (!engulfs
&& is_blockable() && attempt_block(mon
))
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;
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.",
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
4446 && item
->base_type
== OBJ_MISSILES
4447 && item
->sub_type
== MI_THROWING_NET
)
4449 monster_caught_in_net(mon
, *this);
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.
4464 // If the beam is an actual missile or of the MMISSILE type
4465 // (Earth magic) we might bleed on the floor.
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);
4480 monster orig
= *mon
;
4483 monster_post_hit(mon
, final
);
4486 // Preserve name of the source monster if it winds up killing
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());
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;
4521 apply_hit_funcs(mon
, final
, corpse
);
4522 extra_range_used
+= range_used_on_hit(mon
);
4525 bool bolt::has_saving_throw() const
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
4546 static bool _ench_flavour_affects_monster(beam_type flavour
, const monster
* mon
)
4551 case BEAM_POLYMORPH
:
4552 rc
= mon
->can_mutate();
4555 case BEAM_DEGENERATE
:
4556 rc
= (mon
->holiness() == MH_NATURAL
4557 && mon
->type
!= MONS_PULSATING_LUMP
);
4560 case BEAM_ENSLAVE_SOUL
:
4561 rc
= (mon
->holiness() == MH_NATURAL
&& mon
->attitude
!= ATT_FRIENDLY
);
4564 case BEAM_DISPEL_UNDEAD
:
4565 rc
= (mon
->holiness() == MH_UNDEAD
);
4568 case BEAM_ENSLAVE_DEMON
:
4569 rc
= (mon
->holiness() == MH_DEMONIC
&& !mon
->friendly());
4573 rc
= !mon
->res_negative_energy();
4576 case BEAM_HIBERNATION
:
4577 rc
= mon
->can_hibernate();
4580 case BEAM_PORKALATOR
:
4581 rc
= (mon
->holiness() == MH_DEMONIC
&& mon
->type
!= MONS_HELL_HOG
)
4582 || (mon
->holiness() == MH_NATURAL
&& mon
->type
!= MONS_HOG
);
4592 bool enchant_monster_with_flavour(monster
* mon
, actor
*foe
,
4593 beam_type flavour
, int powc
)
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
);
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).
4621 monster_name
.c_str(),
4623 is_visible
? " for a moment."
4627 autotoggle_autopickup(true);
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
4650 if (flavour
== BEAM_POLYMORPH
4651 && (mon
->type
== MONS_UGLY_THING
4652 || mon
->type
== MONS_VERY_UGLY_THING
4653 || mon
->is_shapeshifter()))
4659 res_margin
= mon
->check_res_magic(ench_power
);
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
4674 if (mon
->observable())
4675 obvious_effect
= true;
4676 monster_teleport(mon
, false);
4677 return (MON_AFFECTED
);
4680 if (mon
->observable())
4681 obvious_effect
= true;
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
:
4693 obvious_effect
= true;
4694 if (YOU_KILL(thrower
))
4696 did_god_conduct(DID_DELIBERATE_MUTATING
, 2 + random2(3),
4699 return (MON_AFFECTED
);
4702 if (you
.level_type
== LEVEL_ABYSS
)
4703 simple_monster_message(mon
, " wobbles for a moment.");
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.");
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.");
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!");
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
;
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);
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;
4794 return (MON_AFFECTED
);
4796 return (MON_UNAFFECTED
);
4799 if (backlight_monsters(mon
->pos(), hit
, 0))
4801 obvious_effect
= true;
4802 return (MON_AFFECTED
);
4804 return (MON_UNAFFECTED
);
4807 obvious_effect
= do_slow_monster(mon
, whose_kill());
4808 return (MON_AFFECTED
);
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
);
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
);
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
);
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
);
4879 apply_bolt_petrify(mon
);
4880 return (MON_AFFECTED
);
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
);
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
);
4919 if (player_will_anger_monster(mon
))
4921 simple_monster_message(mon
, " is repulsed!");
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
);
4970 return (MON_AFFECTED
);
4974 // Extra range used on hit.
4975 int bolt::range_used_on_hit(const actor
* victim
) const
4979 // Non-beams can only affect one thing (player/monster).
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
)
4987 // Generic explosion.
4988 else if (is_explosion
|| is_big_cloud
)
4991 else if (flavour
== BEAM_ACID
)
4993 // Lightning goes through things.
4994 else if (flavour
== BEAM_ELECTRICITY
)
4999 // Assume we didn't hit, after all.
5000 if (is_tracer
&& beam_source
== NON_MONSTER
&& used
== BEAM_STOP
5001 && hit
< AUTOMATIC_HIT
)
5006 if (in_explosion_phase
)
5009 for (unsigned int i
= 0; i
< range_funcs
.size(); ++i
)
5010 if ((*range_funcs
[i
])(*this, victim
, 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
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
);
5027 const coord_def
newpos(ray
.pos());
5028 if (newpos
== oldpos
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())
5042 act
->move_to_pos(newpos
);
5044 // Knockback cannot ever kill the actor directly - caller must do
5045 // apply_location_effects after messaging.
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
;
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.
5071 tmp
= "The " + item
->name(DESC_PLAIN
, false, false, false)
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
;
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
;
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!";
5134 glyph
= dchar_glyph(DCHAR_FIRED_ZAP
);
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\'!";
5150 name
= "stinking cloud";
5153 if (name
== "potion")
5155 seeMsg
= "The potion explodes!";
5156 hearMsg
= "You hear an explosion!";
5161 ASSERT(flavour
>= BEAM_POTION_STINKING_CLOUD
5162 && flavour
<= BEAM_POTION_RANDOM
);
5163 const int newcolour
= _potion_beam_flavour_to_colour(flavour
);
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())
5185 msg_generated
= false;
5187 mpr(hearMsg
, MSGCH_SOUND
);
5192 typedef std::vector
< std::vector
<coord_def
> > sweep_type
;
5194 static sweep_type
_radial_sweep(int r
)
5197 sweep_type::value_type work
;
5200 work
.push_back(coord_def(0,0));
5201 result
.push_back(work
);
5203 for (int rad
= 1; rad
<= r
; ++rad
)
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
);
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
5235 if (real_flavour
== BEAM_CHAOS
|| real_flavour
== BEAM_RANDOM
)
5236 flavour
= real_flavour
;
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.",
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
);
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;
5280 oldValue
= set_buffering(false);
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
;
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
)
5303 if (exp_map(delta
+ centre
) < INT_MAX
)
5304 explosion_draw_cell(delta
+ pos());
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
);
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
)
5331 if (exp_map(delta
+ centre
) < INT_MAX
)
5333 if (you
.see_cell(delta
+ pos()))
5336 explosion_affect_cell(delta
+ pos());
5341 #if defined(TARGET_OS_WINDOWS) && !defined(USE_TILE)
5343 set_buffering(oldValue
);
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
);
5369 if (in_los_bounds_v(drawpos
))
5370 tiles
.add_overlay(p
, tileidx_bolt(*this));
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
));
5383 void bolt::explosion_affect_cell(const coord_def
& p
)
5385 // pos() = target during an explosion, so restore it after affecting
5387 const coord_def orig_pos
= target
;
5392 flavour
= real_flavour
;
5398 void bolt::determine_affected_cells(explosion_map
& m
, const coord_def
& delta
,
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))
5410 || is_sanctuary(loc
))
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
)))
5429 if (feat_is_solid(dngn_feat
) && !feat_is_wall(dngn_feat
) && stop_at_statues
)
5432 // Check if it passes the callback functions.
5434 for (unsigned int i
= 0; i
< aoe_funcs
.size(); ++i
)
5435 hits
= (*aoe_funcs
[i
])(*this, loc
) && 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())
5451 // Is that cell already covered?
5452 if (m(new_delta
+ centre
) <= count
)
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)
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
5473 bool bolt::nasty_to(const monster
* mon
) const
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
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())
5489 // Now for some non-hurtful enchantments.
5490 if (flavour
== BEAM_DIGGING
)
5493 // Positive effects.
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
);
5510 if (flavour
== BEAM_DISPEL_UNDEAD
)
5511 return (mon
->holiness() == MH_UNDEAD
);
5514 if (flavour
== BEAM_PAIN
)
5515 return (!mon
->res_negative_energy());
5518 if (flavour
== BEAM_ENSLAVE_DEMON
)
5519 return (mon
->holiness() == MH_DEMONIC
);
5521 // everything else is considered nasty by everyone
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
)
5548 ////////////////////////////////////////////////////////////////////////////
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
);
5588 case KILL_YOU_MISSILE
:
5589 return (flavour
== BEAM_PARALYSIS
5590 || flavour
== BEAM_PETRIFY
) ? KILL_YOU
: KILL_YOU_MISSILE
;
5593 case KILL_MON_MISSILE
:
5594 return (KILL_MON_MISSILE
);
5597 return (KILL_YOU_CONF
);
5600 return (KILL_MON_MISSILE
);
5604 void bolt::set_target(const dist
&d
)
5611 chose_ray
= d
.choseRay
;
5616 aimed_at_spot
= true;
5619 void bolt::setup_retrace()
5621 if (pos().x
&& pos().y
)
5624 std::swap(source
, target
);
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.
5637 if (actor
->atype() == ACT_PLAYER
)
5639 thrower
= KILL_YOU_MISSILE
;
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
))
5662 else if (!invalid_monster_index(nominal_source
))
5663 return (&menv
[nominal_source
]);
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
)
5699 return _beam_type_name(flavour
);
5702 static std::string
_beam_type_name(beam_type 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");
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())
5802 const actor
*a
= agent();
5804 return a
->name(DESC_NOCAP_A
, true);
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
;