summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--crawl-ref/source/beam.cc323
-rw-r--r--crawl-ref/source/beam.h27
-rw-r--r--crawl-ref/source/describe.cc5
-rw-r--r--crawl-ref/source/hiscores.cc40
-rw-r--r--crawl-ref/source/item_use.cc7
-rw-r--r--crawl-ref/source/itemname.cc2
-rw-r--r--crawl-ref/source/itemprop.cc7
-rw-r--r--crawl-ref/source/itemprop.h14
-rw-r--r--crawl-ref/source/monstuff.cc9
-rw-r--r--crawl-ref/source/mstuff2.cc6
-rw-r--r--crawl-ref/source/ouch.cc4
-rw-r--r--crawl-ref/source/ouch.h16
12 files changed, 371 insertions, 89 deletions
diff --git a/crawl-ref/source/beam.cc b/crawl-ref/source/beam.cc
index b6195fb054..90f636d0d0 100644
--- a/crawl-ref/source/beam.cc
+++ b/crawl-ref/source/beam.cc
@@ -206,6 +206,9 @@ static void _beam_set_default_values(bolt &beam, int power)
beam.is_beam = false; // default for all beams.
beam.is_tracer = false; // default for all player beams
beam.thrower = KILL_YOU_MISSILE; // missile from player
+ beam.dropped_item = false; // no item droped yet
+ beam.reflections = 0; // no reflections yet
+ beam.bounces = 0; // no bounces yet
beam.aux_source.clear(); // additional source info, unused
}
@@ -278,6 +281,11 @@ bool player_tracer( zap_type ztype, int power, bolt &pbolt, int range)
pbolt.beam_cancelled = false;
pbolt.dont_stop_foe = pbolt.dont_stop_fr = pbolt.dont_stop_player = false;
+ // Clear misc
+ pbolt.dropped_item = false;
+ pbolt.reflections = 0;
+ pbolt.bounces = 0;
+
fire_beam(pbolt);
// Should only happen if the player answered 'n' to one of those
@@ -292,6 +300,13 @@ bool player_tracer( zap_type ztype, int power, bolt &pbolt, int range)
return (false);
}
+ // Tracers shouldn't drop items.
+ ASSERT(!pbolt.dropped_item);
+
+ // Reset, since these are cumulative over recursive calls to fire_beam.
+ pbolt.reflections = 0;
+ pbolt.bounces = 0;
+
// Set to non-tracing for actual firing.
pbolt.is_tracer = false;
return (true);
@@ -1404,17 +1419,55 @@ static bool _affect_mon_in_wall(bolt &pbolt, item_def *item,
* 3d. If no valid move or bounce is found, break
* 4. Check for beam termination on target
* 5. Affect the cell which the beam just moved into -> affect()
- * 6. Decrease remaining range appropriately
- * 7. Check for early out due to aimed_at_feet
- * 8. Draw the beam
- * 9. Drop an object where the beam 'landed'
- *10. Beams explode where the beam 'landed'
- *11. If no message generated yet, send "nothing happens" (enchantments only)
+ * 6. If the beam was reflected during affect() then return, since
+ * a recursive call to fire_beam() took care of the rest.
+ * 7. Decrease remaining range appropriately
+ * 8. Check for early out due to aimed_at_feet
+ * 9. Draw the beam
+ *10. Drop an object where the beam 'landed'
+ *11. Beams explode where the beam 'landed'
+ *12. If no message generated yet, send "nothing happens" (enchantments only)
*
*/
-void fire_beam(bolt &pbolt, item_def *item, bool drop_item)
+void fire_beam(bolt &pbolt)
{
+ const int reflections = pbolt.reflections;
+ if (reflections == 0)
+ {
+ // We aren't being recursively called.
+ beam_message_cache.clear();
+ pbolt.range_used = 0;
+ }
+
+ ASSERT(pbolt.range >= 0 && pbolt.range_used >=0);
+ ASSERT(pbolt.range_used <= pbolt.range);
+ ASSERT(!pbolt.drop_item || pbolt.item);
+ ASSERT(!pbolt.dropped_item);
+
+ if (pbolt.range == pbolt.range_used && pbolt.range > 0)
+ {
+#ifdef DEBUG
+ mprf(MSGCH_DIAGNOSTICS, "fire_beam() called on already done beam "
+ "'%s' (item = '%s')", pbolt.name.c_str(),
+ pbolt.item ? pbolt.item->name(DESC_PLAIN).c_str() : "none");
+#endif
+ return;
+ }
+
+ if (!pbolt.is_tracer && reflections == 0 && YOU_KILL(pbolt.thrower))
+ {
+ switch(pbolt.flavour)
+ {
+ case BEAM_HELLFIRE:
+ case BEAM_HELLFROST:
+ did_god_conduct(DID_UNHOLY, 2 + random2(3), pbolt.effect_known);
+ break;
+ default:
+ break;
+ }
+ }
+
bool beamTerminate; // Has beam been 'stopped' by something?
coord_def &testpos(pbolt.pos);
bool did_bounce = false;
@@ -1425,15 +1478,13 @@ void fire_beam(bolt &pbolt, item_def *item, bool drop_item)
// This fixes beams being in explosion after use as a tracer.
pbolt.in_explosion_phase = false;
- beam_message_cache.clear();
-
#ifdef USE_TILE
int tile_beam = -1;
- if (item && !pbolt.is_tracer && pbolt.flavour == BEAM_MISSILE)
+ if (pbolt.item && !pbolt.is_tracer && pbolt.flavour == BEAM_MISSILE)
{
const coord_def diff = pbolt.target - pbolt.source;
- tile_beam = tileidx_item_throw(*item, diff.x, diff.y);
+ tile_beam = tileidx_item_throw(*pbolt.item, diff.x, diff.y);
}
#endif
@@ -1475,9 +1526,6 @@ void fire_beam(bolt &pbolt, item_def *item, bool drop_item)
// Give chance for beam to affect one cell even if aimed_at_feet.
beamTerminate = false;
- // Setup range.
- int rangeRemaining = pbolt.range;
-
// Before we start drawing the beam, turn buffering off.
#ifdef WIN32CONSOLE
bool oldValue = true;
@@ -1501,7 +1549,11 @@ void fire_beam(bolt &pbolt, item_def *item, bool drop_item)
// Should we ever get a tracer with a wall-affecting
// beam (possible I suppose), we'll quit tracing now.
if (!pbolt.is_tracer)
- rangeRemaining -= affect(pbolt, testpos, item);
+ {
+ (void) affect(pbolt, testpos);
+ if (pbolt.reflections > reflections)
+ return;
+ }
// If it's still a wall, quit.
if (grid_is_solid(grd(testpos)))
@@ -1514,11 +1566,14 @@ void fire_beam(bolt &pbolt, item_def *item, bool drop_item)
if (!_isBouncy(pbolt, grd(testpos)))
{
// Affect any monster that might be in the wall.
- rangeRemaining -= affect(pbolt, testpos, item);
+ (void) affect(pbolt, testpos);
+ if (pbolt.reflections > reflections)
+ return;
do
{
ray.regress();
+ pbolt.bounce_pos = ray.pos();
}
while (grid_is_solid(grd(ray.pos())));
@@ -1527,20 +1582,25 @@ void fire_beam(bolt &pbolt, item_def *item, bool drop_item)
}
did_bounce = true;
+ pbolt.bounces++;
// bounce
do
{
do
+ {
ray.regress();
+ pbolt.bounce_pos = ray.pos();
+ }
while (grid_is_solid(grd(ray.pos())));
ray.advance_and_bounce();
- rangeRemaining -= 2;
+ pbolt.range_used += 2;
}
- while (rangeRemaining > 0 && grid_is_solid(grd(ray.pos())));
+ while (pbolt.range_used < pbolt.range
+ && grid_is_solid(grd(ray.pos())));
- if (rangeRemaining < 1)
+ if (pbolt.range_used >= pbolt.range)
break;
testpos = ray.pos();
@@ -1578,7 +1638,11 @@ void fire_beam(bolt &pbolt, item_def *item, bool drop_item)
}
if (!pbolt.affects_nothing)
- rangeRemaining -= affect(pbolt, testpos, item);
+ {
+ (void) affect(pbolt, testpos);
+ if (pbolt.reflections > reflections)
+ return;
+ }
if (random_beam)
{
@@ -1587,14 +1651,16 @@ void fire_beam(bolt &pbolt, item_def *item, bool drop_item)
}
}
+ ASSERT(pbolt.reflections == reflections);
+
if (pbolt.beam_cancelled)
return;
// Always decrease range by 1.
- rangeRemaining--;
+ pbolt.range_used++;
// Check for range termination.
- if (rangeRemaining <= 0)
+ if (pbolt.range_used >= pbolt.range)
beamTerminate = true;
// Special case - beam was aimed at feet.
@@ -1656,10 +1722,8 @@ void fire_beam(bolt &pbolt, item_def *item, bool drop_item)
// The beam has finished, and terminated at tx, ty.
// Leave an object, if applicable.
- if (drop_item && item)
- beam_drop_object(pbolt, item, testpos);
-
- ASSERT(!drop_item || item);
+ if (pbolt.drop_item && pbolt.item && !pbolt.dropped_item)
+ beam_drop_object(pbolt);
// Check for explosion. NOTE that for tracers, we have to make a copy
// of target co-ords and then reset after calling this -- tracers should
@@ -1707,23 +1771,6 @@ void fire_beam(bolt &pbolt, item_def *item, bool drop_item)
if (!pbolt.is_tracer)
set_buffering(oldValue);
#endif
-
- if (!pbolt.is_tracer)
- {
- switch(pbolt.flavour)
- {
- case BEAM_HELLFIRE:
- case BEAM_HELLFROST:
- if (YOU_KILL(pbolt.thrower))
- {
- did_god_conduct(DID_UNHOLY, 2 + random2(3),
- pbolt.effect_known);
- }
- break;
- default:
- break;
- }
- }
}
@@ -2533,6 +2580,11 @@ void fire_tracer(const monsters *monster, bolt &pbolt, bool explode_only)
pbolt.fr_helped = pbolt.fr_hurt = 0;
pbolt.foe_helped = pbolt.foe_hurt = 0;
+ // Clear misc
+ pbolt.dropped_item = false;
+ pbolt.reflections = 0;
+ pbolt.bounces = 0;
+
// If there's a specifically requested foe_ratio, honour it.
if (!pbolt.foe_ratio)
{
@@ -2554,8 +2606,15 @@ void fire_tracer(const monsters *monster, bolt &pbolt, bool explode_only)
else
fire_beam(pbolt);
+ // Tracers shouldn't drop items.
+ ASSERT(!pbolt.dropped_item);
+
+ // Reset, since these are cumulative over recursive calls to fire_beam.
+ pbolt.reflections = 0;
+ pbolt.bounces = 0;
+
// Unset tracer flag (convenience).
- pbolt.is_tracer = false;
+ pbolt.is_tracer = false;
}
bool check_line_of_sight( const coord_def& source, const coord_def& target )
@@ -2765,14 +2824,39 @@ static bool _beam_term_on_target(bolt &beam, const coord_def& p)
return (false);
}
-void beam_drop_object( bolt &beam, item_def *item, const coord_def& p )
+void beam_drop_object( bolt &beam, item_def *item, const coord_def& _p )
{
+ if (!item)
+ item = beam.item;
ASSERT( item != NULL );
+ ASSERT( is_valid_item(*item) );
+ ASSERT( item->quantity > 0);
+
+#ifdef DEBUG
+ if (!beam.drop_item)
+ mprf(MSGCH_DIAGNOSTICS, "beam_drop_object() called when beam.drop_item "
+ "is false (beam = %s, item = %s)", beam.name.c_str(),
+ item->name(DESC_PLAIN).c_str());
+#endif
- // Conditions: beam is missile and not tracer.
if (beam.is_tracer || beam.flavour != BEAM_MISSILE)
return;
+ if (beam.dropped_item)
+ {
+#ifdef DEBUG
+ mprf(MSGCH_DIAGNOSTICS, "beam_drop_object() called after object "
+ "already dropped (beam = %s, item = %s)", beam.name.c_str(),
+ item->name(DESC_PLAIN).c_str());
+#endif
+ return;
+ }
+
+ coord_def p = _p;
+ if (!in_bounds(p))
+ p = beam.pos;
+
+ // Conditions: beam is missile and not tracer.
if (YOU_KILL(beam.thrower)
&& !thrown_object_destroyed(item, p, false)
|| MON_KILL(beam.thrower)
@@ -2791,7 +2875,16 @@ void beam_drop_object( bolt &beam, item_def *item, const coord_def& p )
}
}
- copy_item_to_grid( *item, p, 1 );
+ if (copy_item_to_grid( *item, p, 1 ))
+ beam.dropped_item = true;
+ else
+ {
+#ifdef DEBUG
+ mprf(MSGCH_DIAGNOSTICS, "beam_drop_object() unable to drop "
+ "object (beam = %s, item = %s)", beam.name.c_str(),
+ item->name(DESC_PLAIN).c_str());
+#endif
+ }
}
}
@@ -2806,7 +2899,7 @@ static bool _found_player(const bolt &beam, const coord_def& p)
return (grid_distance(p, you.pos()) <= dist);
}
-int affect(bolt &beam, const coord_def& p, item_def *item, bool affect_items)
+int affect(bolt &beam, const coord_def& _p, item_def *item, bool affect_items)
{
// Extra range used by hitting something.
int rangeUsed = 0;
@@ -2815,10 +2908,20 @@ int affect(bolt &beam, const coord_def& p, item_def *item, bool affect_items)
if (beam.flavour == BEAM_LINE_OF_SIGHT)
return (0);
+ coord_def p = _p;
+ if (!in_bounds(_p))
+ p = beam.pos;
+
+ if (!item)
+ item = beam.item;
+
if (grid_is_solid(grd(p)))
{
if (beam.is_tracer) // Tracers always stop on walls.
+ {
+ beam.range_used += BEAM_STOP;
return (BEAM_STOP);
+ }
if (_affects_wall(beam, grd(p)))
rangeUsed += _affect_wall(beam, p);
@@ -2843,6 +2946,7 @@ int affect(bolt &beam, const coord_def& p, item_def *item, bool affect_items)
}
}
+ beam.range_used += rangeUsed;
return (rangeUsed);
}
}
@@ -2876,7 +2980,10 @@ int affect(bolt &beam, const coord_def& p, item_def *item, bool affect_items)
}
if (_beam_term_on_target(beam, p))
+ {
+ beam.range_used += BEAM_STOP;
return (BEAM_STOP);
+ }
}
// If there is a monster at this location, affect it.
@@ -2903,10 +3010,14 @@ int affect(bolt &beam, const coord_def& p, item_def *item, bool affect_items)
}
if (_beam_term_on_target(beam, p))
+ {
+ beam.range_used += BEAM_STOP;
return (BEAM_STOP);
+ }
}
}
+ beam.range_used += rangeUsed;
return (rangeUsed);
}
@@ -3277,7 +3388,16 @@ static void _beam_ouch(int dam, bolt &beam)
beam.aux_source.c_str());
}
else if (YOU_KILL(beam.thrower) && beam.aux_source.empty())
- ouch(dam, NON_MONSTER, KILLED_BY_TARGETTING);
+ {
+ if (beam.reflections > 0)
+ ouch(dam, beam.reflector, KILLED_BY_REFLECTION,
+ beam.name.c_str());
+ else if (beam.bounces > 0)
+ ouch(dam, NON_MONSTER, KILLED_BY_BOUNCE,
+ beam.name.c_str());
+ else
+ ouch(dam, NON_MONSTER, KILLED_BY_TARGETTING);
+ }
else if (MON_KILL(beam.thrower))
{
ouch(dam, beam.beam_source, KILLED_BY_BEAM,
@@ -3437,6 +3557,54 @@ static bool _beam_is_harmless_player(bolt &beam)
}
}
+static bool _beam_is_reflectable( const bolt &beam, const item_def *item )
+{
+ if (beam.range_used >= beam.range)
+ return (false);
+
+ return (item && is_shield(*item) && shield_reflects(*item));
+}
+
+static void _ident_reflector(item_def *item)
+{
+ if (!is_artefact(*item))
+ set_ident_flags(*item, ISFLAG_KNOW_TYPE);
+}
+
+static void _reflect_beam(bolt &beam)
+{
+ beam.reflections++;
+
+ // If it bounced off a wall before being refleced then head back towards
+ // the wall.
+ if (beam.bounces > 0 && in_bounds(beam.bounce_pos))
+ beam.target = beam.bounce_pos;
+ else
+ beam.target = beam.source;
+
+ beam.source = beam.pos;
+
+ // Reset bounce_pos, so that if we somehow reflect again before reaching
+ // the wall that we won't keep heading towards the wall.
+ beam.bounce_pos.set(0, 0);
+
+ if (beam.pos == you.pos())
+ beam.reflector = NON_MONSTER;
+ else if (mgrd(beam.pos) != NON_MONSTER)
+ beam.reflector = mgrd(beam.pos);
+ else
+ {
+ beam.reflector = -1;
+#ifdef DEBUG
+ mprf(MSGCH_DIAGNOSTICS, "Bolt reflected by neither player nor "
+ "monster (bolt = %s, item = %s)", beam.name.c_str(),
+ beam.item ? beam.item->name(DESC_PLAIN).c_str() : "none");
+#endif
+ }
+
+ fire_beam(beam);
+}
+
// Returns amount of extra range used up by affectation of the player.
static int _affect_player( bolt &beam, item_def *item, bool affect_items )
{
@@ -3549,7 +3717,16 @@ static int _affect_player( bolt &beam, item_def *item, bool affect_items )
#endif
if (hit < block)
{
- mprf( "You block the %s.", beam.name.c_str() );
+ if (_beam_is_reflectable(beam, you.shield()))
+ {
+ mprf( "Your %s reflects the %s!",
+ you.shield()->name(DESC_PLAIN).c_str(),
+ beam.name.c_str() );
+ _ident_reflector(you.shield());
+ _reflect_beam(beam);
+ }
+ else
+ mprf( "You block the %s.", beam.name.c_str() );
you.shield_block_succeeded();
return (BEAM_STOP);
}
@@ -4421,12 +4598,30 @@ static int _affect_monster(bolt &beam, monsters *mon, item_def *item)
{
const int hit = random2( beam.hit * 130 / 100
+ mon->shield_block_penalty() );
- if (hit < shield_block && mons_near(mon)
- && player_monster_visible(mon))
+ if (hit < shield_block)
{
- mprf("%s blocks the %s.",
- mon->name(DESC_CAP_THE).c_str(),
- beam.name.c_str());
+ item_def *shield = mon->mslot_item(MSLOT_SHIELD);
+ if (_beam_is_reflectable(beam, shield))
+ {
+ if (you.can_see(mon))
+ {
+ mprf("%s reflects the %s off %s %s!",
+ mon->name(DESC_CAP_THE).c_str(),
+ beam.name.c_str(),
+ mon->pronoun(PRONOUN_NOCAP_POSSESSIVE).c_str(),
+ shield->name(DESC_PLAIN).c_str());
+ _ident_reflector(shield);
+ }
+ else if (see_grid(beam.pos))
+ mprf("The %s bounces off of thin air!",
+ beam.name.c_str());
+
+ _reflect_beam(beam);
+ }
+ else
+ mprf("%s blocks the %s.",
+ mon->name(DESC_CAP_THE).c_str(),
+ beam.name.c_str());
mon->shield_block_succeeded();
return (BEAM_STOP);
@@ -5371,20 +5566,22 @@ static bool _nice_beam(monsters *mon, const bolt &beam)
// (extended from setup_mons_cast() and zapping() which act as limited ones).
bolt::bolt() : range(0), type('*'),
colour(BLACK),
- flavour(BEAM_MAGIC), source(), target(), pos(), damage(0,0),
- ench_power(0), hit(0),
+ flavour(BEAM_MAGIC), drop_item(false), item(NULL), source(),
+ target(), pos(), damage(0,0), ench_power(0), hit(0),
thrower(KILL_MISC), ex_size(0), beam_source(MHITNOT), name(),
is_beam(false), is_explosion(false), is_big_cloud(false),
- aimed_at_spot(false),
- aux_source(), affects_nothing(false), obvious_effect(false),
- effect_known(true), fr_count(0), foe_count(0), fr_power(0),
- foe_power(0), fr_hurt(0), foe_hurt(0), fr_helped(0),
- foe_helped(0), is_tracer(false), aimed_at_feet(false),
+ aimed_at_spot(false), aux_source(), affects_nothing(false),
+ effect_known(true), obvious_effect(false), fr_count(0),
+ foe_count(0), fr_power(0), foe_power(0), fr_hurt(0),
+ foe_hurt(0), fr_helped(0), foe_helped(0),
+ dropped_item(false), item_pos(), item_index(NON_ITEM),
+ range_used(0), is_tracer(false), aimed_at_feet(false),
msg_generated(false), in_explosion_phase(false),
smart_monster(false), can_see_invis(false),
attitude(ATT_HOSTILE), foe_ratio(0), chose_ray(false),
beam_cancelled(false), dont_stop_foe(false),
- dont_stop_fr(false), dont_stop_player(false)
+ dont_stop_fr(false), dont_stop_player(false),
+ bounces(false), bounce_pos(), reflections(false), reflector(-1)
{
}
diff --git a/crawl-ref/source/beam.h b/crawl-ref/source/beam.h
index ab74ec8ce2..69d12c6051 100644
--- a/crawl-ref/source/beam.h
+++ b/crawl-ref/source/beam.h
@@ -40,6 +40,8 @@ struct bolt
unsigned type; // missile gfx
int colour;
beam_type flavour;
+ bool drop_item; // should drop an item when done
+ item_def* item; // item to drop
coord_def source; // beam origin
coord_def target; // intended target
coord_def pos; // actual position
@@ -57,15 +59,22 @@ struct bolt
bool affects_nothing; // should not hit monsters or features
+ bool effect_known; // did we _know_ this would happen?
+
// OUTPUT parameters (tracing, ID)
bool obvious_effect; // did an 'obvious' effect happen?
- bool effect_known; // did we _know_ this would happen?
+
int fr_count, foe_count; // # of times a friend/foe is "hit"
int fr_power, foe_power; // total levels/hit dice affected
int fr_hurt, foe_hurt; // # of friends/foes actually hurt
int fr_helped, foe_helped; // # of friends/foes actually helped
+ bool dropped_item; // item has been dropped
+ coord_def item_pos; // position item was dropped at
+ int item_index; // mitm[index] of item
+
// INTERNAL use - should not usually be set outside of beam.cc
+ int range_used;
bool is_tracer; // is this a tracer?
bool aimed_at_feet; // this was aimed at self!
bool msg_generated; // an appropriate msg was already mpr'd
@@ -81,6 +90,13 @@ struct bolt
// friend
bool dont_stop_player; // player answered self target prompt with 'y'
+ int bounces; // # times beam bounced off walls
+ coord_def bounce_pos; // position of latest wall bounce,
+ // reset if a reflection happens
+
+ int reflections; // # times beam reflected off shields
+ int reflector; // latest thing to reflect beam
+
ray_def ray; // shoot on this specific ray
@@ -104,7 +120,7 @@ dice_def calc_dice( int num_dice, int max_damage );
// Test if the to-hit (attack) beats evasion (defence).
bool test_beam_hit(int attack, int defence);
-void fire_beam(bolt &pbolt, item_def *item = NULL, bool drop_item = false);
+void fire_beam(bolt &pbolt);
int explosion( bolt &pbolt, bool hole_in_the_middle = false,
bool explode_in_wall = false,
@@ -139,8 +155,9 @@ bool zapping( zap_type ztype, int power, struct bolt &pbolt,
bool needs_tracer = false, std::string msg = "" );
bool player_tracer( zap_type ztype, int power, struct bolt &pbolt,
int range = 0 );
-int affect(bolt &beam, const coord_def& p, item_def *item = NULL,
- bool affect_items = true);
-void beam_drop_object( bolt &beam, item_def *item, const coord_def& where );
+int affect(bolt &beam, const coord_def& p = coord_def(),
+ item_def *item = NULL, bool affect_items = true);
+void beam_drop_object( bolt &beam, item_def *item = NULL,
+ const coord_def& where = coord_def() );
#endif
diff --git a/crawl-ref/source/describe.cc b/crawl-ref/source/describe.cc
index 4fd6f9a611..3aa735d7e5 100644
--- a/crawl-ref/source/describe.cc
+++ b/crawl-ref/source/describe.cc
@@ -1343,6 +1343,11 @@ static std::string _describe_armour( const item_def &item, bool verbose )
description += "It protects its wearer's possessions "
"from damage and destruction.";
break;
+
+ case SPARM_REFLECTION:
+ description += "It reflects blocked things back in the "
+ "direction they came from.";
+ break;
}
}
diff --git a/crawl-ref/source/hiscores.cc b/crawl-ref/source/hiscores.cc
index 548e9c1532..9f3576d51e 100644
--- a/crawl-ref/source/hiscores.cc
+++ b/crawl-ref/source/hiscores.cc
@@ -763,7 +763,8 @@ void scorefile_entry::init_death_cause(int dam, int dsrc,
// for death by monster
if ((death_type == KILLED_BY_MONSTER || death_type == KILLED_BY_BEAM
- || death_type == KILLED_BY_SPORE)
+ || death_type == KILLED_BY_SPORE
+ || death_type == KILLED_BY_REFLECTION)
&& !invalid_monster_index(death_source)
&& menv[death_source].type != -1)
{
@@ -1640,6 +1641,43 @@ std::string scorefile_entry::death_description(death_desc_verbosity verbosity)
needs_damage = true;
break;
+ case KILLED_BY_REFLECTION:
+ needs_damage = true;
+ if (terse)
+ desc += "reflected bolt";
+ else
+ {
+ desc += "Killed by a reflected ";
+ if (auxkilldata.empty())
+ desc += "bolt";
+ else
+ desc += auxkilldata;
+
+ if (!death_source_name.empty() && !oneline && !semiverbose)
+ {
+ desc += "\n";
+ desc += " ";
+ desc += "... reflected by ";
+ desc += death_source_name;
+ needs_damage = false;
+ }
+ }
+ break;
+
+ case KILLED_BY_BOUNCE:
+ if (terse)
+ desc += "bounced beam";
+ else
+ {
+ desc += "Killed themselves with a bounced ";
+ if (auxkilldata.empty())
+ desc += "beam";
+ else
+ desc += auxkilldata;
+ }
+ needs_damage = true;
+ break;
+
case KILLED_BY_SPORE:
if (terse)
{
diff --git a/crawl-ref/source/item_use.cc b/crawl-ref/source/item_use.cc
index 6f300fffd2..6c6eaaedf3 100644
--- a/crawl-ref/source/item_use.cc
+++ b/crawl-ref/source/item_use.cc
@@ -2032,6 +2032,8 @@ bool throw_it(bolt &pbolt, int throw_2, bool teleport, int acc_bonus,
item.quantity = 1;
item.slot = index_to_letter(item.link);
+ pbolt.item = &item;
+
// Now start real firing!
origin_set_unknown(item);
std::string ammo_name;
@@ -2712,7 +2714,8 @@ bool throw_it(bolt &pbolt, int throw_2, bool teleport, int acc_bonus,
Options.tut_throw_counter++;
// Dropping item copy, since the launched item might be different.
- fire_beam(pbolt, &item, !did_return);
+ pbolt.drop_item = !did_return;
+ fire_beam(pbolt);
// The item can be destroyed before returning.
if (did_return && thrown_object_destroyed(&item, pbolt.target, true))
@@ -2728,7 +2731,7 @@ bool throw_it(bolt &pbolt, int throw_2, bool teleport, int acc_bonus,
// Fire beam in reverse.
pbolt.setup_retrace();
viewwindow(true, false);
- fire_beam(pbolt, &item, false);
+ fire_beam(pbolt);
msg::stream << item.name(DESC_CAP_THE) << " returns to your pack!"
<< std::endl;
diff --git a/crawl-ref/source/itemname.cc b/crawl-ref/source/itemname.cc
index feb61f56cd..89a6d3c251 100644
--- a/crawl-ref/source/itemname.cc
+++ b/crawl-ref/source/itemname.cc
@@ -338,6 +338,7 @@ static const char* armour_ego_name( special_armour_type sparm, bool terse )
case SPARM_POSITIVE_ENERGY: return "positive energy";
case SPARM_ARCHMAGI: return "the Archmagi";
case SPARM_PRESERVATION: return "preservation";
+ case SPARM_REFLECTION: return "reflection";
default: return "bugginess";
}
}
@@ -363,6 +364,7 @@ static const char* armour_ego_name( special_armour_type sparm, bool terse )
case SPARM_POSITIVE_ENERGY: return " {rN+}";
case SPARM_ARCHMAGI: return " {Archmagi}";
case SPARM_PRESERVATION: return " {rCorr, Cons}";
+ case SPARM_REFLECTION: return " {rflct}";
default: return " {buggy}";
}
}
diff --git a/crawl-ref/source/itemprop.cc b/crawl-ref/source/itemprop.cc
index 915223cf6d..e418f1dc7d 100644
--- a/crawl-ref/source/itemprop.cc
+++ b/crawl-ref/source/itemprop.cc
@@ -2672,6 +2672,13 @@ bool is_shield_incompatible(const item_def &weapon, const item_def *shield)
&& !is_range_weapon(weapon));
}
+bool shield_reflects(const item_def &shield)
+{
+ ASSERT(is_shield(shield));
+
+ return (get_armour_ego_type(shield) == SPARM_REFLECTION);
+}
+
std::string item_base_name(const item_def &item)
{
switch (item.base_type)
diff --git a/crawl-ref/source/itemprop.h b/crawl-ref/source/itemprop.h
index 2723fc4057..7eda4e983e 100644
--- a/crawl-ref/source/itemprop.h
+++ b/crawl-ref/source/itemprop.h
@@ -343,7 +343,8 @@ enum special_armour_type
SPARM_RESISTANCE, // 15
SPARM_POSITIVE_ENERGY,
SPARM_ARCHMAGI,
- SPARM_PRESERVATION // 18
+ SPARM_PRESERVATION,
+ SPARM_REFLECTION // 19
};
enum special_missile_type // to separate from weapons in general {dlb}
@@ -637,6 +638,12 @@ bool item_is_rechargable(const item_def &it, bool known = false);
bool is_enchantable_weapon(const item_def &wpn, bool uncurse);
bool is_enchantable_armour(const item_def &arm, bool uncurse);
+bool is_shield(const item_def &item);
+bool is_shield_incompatible(const item_def &weapon,
+ const item_def *shield = NULL);
+bool shield_reflects(const item_def &shield);
+
+// Only works for armour/weapons/missiles
// weapon functions:
int weapon_rarity( int w_type );
@@ -721,11 +728,6 @@ size_type item_size( const item_def &item );
bool is_colourful_item( const item_def &item );
-bool is_shield(const item_def &item);
-bool is_shield_incompatible(const item_def &weapon,
- const item_def *shield = NULL);
-
-// Only works for armour/weapons/missiles
std::string item_base_name(const item_def &item);
const char* weapon_base_name(unsigned char subtype);
diff --git a/crawl-ref/source/monstuff.cc b/crawl-ref/source/monstuff.cc
index cece516b84..f603049d0e 100644
--- a/crawl-ref/source/monstuff.cc
+++ b/crawl-ref/source/monstuff.cc
@@ -5816,10 +5816,12 @@ static bool _handle_throw(monsters *monster, bolt & beem)
if (_is_player_or_mon_sanct(monster))
return (false);
+ item_def *missile = &mitm[mon_item];
+
// Throwing a net at a target that is already caught would be
// completely useless, so bail out.
- if (mitm[mon_item].base_type == OBJ_MISSILES
- && mitm[mon_item].sub_type == MI_THROWING_NET
+ if (missile->base_type == OBJ_MISSILES
+ && missile->sub_type == MI_THROWING_NET
&& ( beem.target == you.pos() && you.caught()
|| mgrd(beem.target) != NON_MONSTER
&& mons_is_caught(&menv[mgrd(beem.target)])))
@@ -5841,6 +5843,9 @@ static bool _handle_throw(monsters *monster, bolt & beem)
// Set fake damage for the tracer.
beem.damage = dice_def(10, 10);
+ // Set item for tracer, even though it probably won't be used
+ beem.item = missile;
+
// Fire tracer.
fire_tracer( monster, beem );
diff --git a/crawl-ref/source/mstuff2.cc b/crawl-ref/source/mstuff2.cc
index a6e5c5f5fb..76ab701b82 100644
--- a/crawl-ref/source/mstuff2.cc
+++ b/crawl-ref/source/mstuff2.cc
@@ -862,6 +862,7 @@ bool mons_throw(struct monsters *monster, struct bolt &pbolt, int hand_used)
pbolt.colour = item.colour;
pbolt.flavour = BEAM_MISSILE;
pbolt.thrower = KILL_MON_MISSILE;
+ pbolt.item = &item;
pbolt.aux_source.clear();
const launch_retval projected =
@@ -1183,7 +1184,8 @@ bool mons_throw(struct monsters *monster, struct bolt &pbolt, int hand_used)
else
really_returns = false;
- fire_beam( pbolt, &item, !really_returns );
+ pbolt.drop_item = !really_returns;
+ fire_beam( pbolt );
// The item can be destroyed before returning.
if (really_returns && mons_thrown_object_destroyed(&item, pbolt.target,
@@ -1197,7 +1199,7 @@ bool mons_throw(struct monsters *monster, struct bolt &pbolt, int hand_used)
// Fire beam in reverse.
pbolt.setup_retrace();
viewwindow(true, false);
- fire_beam(pbolt, &item, false);
+ fire_beam(pbolt);
msg::stream << "The weapon returns "
<< (player_monster_visible(monster)?
("to " + monster->name(DESC_NOCAP_THE))
diff --git a/crawl-ref/source/ouch.cc b/crawl-ref/source/ouch.cc
index a00a296e8d..6cdfe1c2fb 100644
--- a/crawl-ref/source/ouch.cc
+++ b/crawl-ref/source/ouch.cc
@@ -724,7 +724,9 @@ static void _xom_checks_damage(kill_method_type death_type,
{
if (you.religion == GOD_XOM)
{
- if (death_type == KILLED_BY_TARGETTING)
+ if (death_type == KILLED_BY_TARGETTING
+ || death_type == KILLED_BY_BOUNCE
+ || death_type == KILLED_BY_REFLECTION)
{
// Xom thinks the player hurting him/herself is funny.
xom_is_stimulated(255 * dam / (dam + you.hp));
diff --git a/crawl-ref/source/ouch.h b/crawl-ref/source/ouch.h
index b9f0627aa3..a1addae560 100644
--- a/crawl-ref/source/ouch.h
+++ b/crawl-ref/source/ouch.h
@@ -17,27 +17,27 @@
enum kill_method_type
{
- KILLED_BY_MONSTER, // 0
+ KILLED_BY_MONSTER, // 0
KILLED_BY_POISON,
KILLED_BY_CLOUD,
- KILLED_BY_BEAM, // 3
+ KILLED_BY_BEAM, // 3
KILLED_BY_DEATHS_DOOR, // should be deprecated, but you never know {dlb}
- KILLED_BY_LAVA, // 5
+ KILLED_BY_LAVA, // 5
KILLED_BY_WATER,
KILLED_BY_STUPIDITY,
KILLED_BY_WEAKNESS,
KILLED_BY_CLUMSINESS,
- KILLED_BY_TRAP, // 10
+ KILLED_BY_TRAP, // 10
KILLED_BY_LEAVING,
KILLED_BY_WINNING,
KILLED_BY_QUITTING,
KILLED_BY_DRAINING,
- KILLED_BY_STARVATION, // 15
+ KILLED_BY_STARVATION, // 15
KILLED_BY_FREEZING,
KILLED_BY_BURNING,
KILLED_BY_WILD_MAGIC,
KILLED_BY_XOM,
- KILLED_BY_STATUE, // 20
+ KILLED_BY_STATUE, // 20
KILLED_BY_ROTTING,
KILLED_BY_TARGETTING,
KILLED_BY_SPORE,
@@ -51,7 +51,9 @@ enum kill_method_type
KILLED_BY_MELTING,
KILLED_BY_BLEEDING,
KILLED_BY_BEOGH_SMITING,
- KILLED_BY_DIVINE_WRATH, // 34
+ KILLED_BY_DIVINE_WRATH,
+ KILLED_BY_BOUNCE, // 35
+ KILLED_BY_REFLECTION,
NUM_KILLBY
};