summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorzelgadis <zelgadis@c06c8d41-db1a-0410-9941-cceddc491573>2008-12-01 12:29:07 +0000
committerzelgadis <zelgadis@c06c8d41-db1a-0410-9941-cceddc491573>2008-12-01 12:29:07 +0000
commitf5e97453a4ac3af452849dd8b3b429ecc8c7389d (patch)
treeae4ebfa3f583fde4b04738b120e0575f65b45353
parent04f5058cac8e12d3b85834bda4589239932f371a (diff)
downloadcrawl-ref-f5e97453a4ac3af452849dd8b3b429ecc8c7389d.tar.gz
crawl-ref-f5e97453a4ac3af452849dd8b3b429ecc8c7389d.zip
Added shields of reflection, though they're not randomly generated and have no
valuation is shopping.cc. It currently only reflects missiles that are blocked with the shield. They could also reflect the brand effects of blocked melee weapons, but I'm not sure if that's a good idea. drop_item and the item to be dropped have been moved from fire_beam()'s parameters into the bolt structure, along with the beam's remaining range. Seems to work fine, but it might still lead to some buggyness. The game now distinguishes between killing yourself by hitting yourself with a beam bounced off a wall versus other forms of self targeting. git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@7705 c06c8d41-db1a-0410-9941-cceddc491573
-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
};