summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--crawl-ref/source/beam.cc94
1 files changed, 56 insertions, 38 deletions
diff --git a/crawl-ref/source/beam.cc b/crawl-ref/source/beam.cc
index 2f0bda82d7..f28bde46e5 100644
--- a/crawl-ref/source/beam.cc
+++ b/crawl-ref/source/beam.cc
@@ -1414,13 +1414,30 @@ static void _munge_bounced_bolt(bolt &old_bolt, bolt &new_bolt,
double max = 90.0 + (angle / 2.0);
double min = -90.0 + (angle / 2.0);
- double shift = (double) random_range(min * 10000, max * 10000) / 10000.0;
+ double shift;
- if (new_deg < old_deg)
- shift = -shift;
- new_ray.set_degrees(new_deg + shift);
+ ray_def temp_ray = new_ray;
+ for (int tries = 0; tries < 20; tries++)
+ {
+ shift = (double) random_range(min * 10000, max * 10000) / 10000.0;
+
+ if (new_deg < old_deg)
+ shift = -shift;
+ temp_ray.set_degrees(new_deg + shift);
+
+ // Don't bounce straight into another wall. Can happen if the beam
+ // is shot into an inside corner.
+ ray_def test_ray = temp_ray;
+ test_ray.advance(true);
+ if (in_bounds(test_ray.pos()) && !grid_is_solid(test_ray.pos()))
+ break;
+
+ shift = 0.0;
+ temp_ray = new_ray;
+ }
-#if DEBUG_DIAGNOSTICS || DEBUG_CHAOS_BOUNCE
+ new_ray = temp_ray;
+#if DEBUG_DIAGNOSTICS || DEBUG_BEAM || DEBUG_CHAOS_BOUNCE
mprf(MSGCH_DIAGNOSTICS,
"chaos beam: old_deg = %5.2f, new_deg = %5.2f, shift = %5.2f",
(float) old_deg, (float) new_deg, (float) shift);
@@ -1431,32 +1448,6 @@ static void _munge_bounced_bolt(bolt &old_bolt, bolt &new_bolt,
// ping-pong balls on caffeine.
int range_spent = new_bolt.range_used - old_bolt.range_used;
new_bolt.range += range_spent;
-
- // XXX HACK: This is to avoid a problem which seems to be caused by
- // ray.advance(true) taking the shortest path but ray.regress() using raw
- // advancement to backtrack: say that the user fires a chaos beam
- // south-west into the corner, the bouncing code regresses it to point X,
- // then it randomly changes direction to slightly south of
- // due east:
- //
- // #####
- // # @ #
- // #X #
- // Y#####
- //
- // The ray advances via shortest path into the wall grid two squares
- // directly south of the player, and then tries to regress for another
- // bounce. However, it uses raw advancement to regress, which updates the
- // internal floating point representation of the position, and since it's
- // now travelling almost due west as it "regresses" it stays inside the
- // wall until it reaches point Y. (At least, I think that's what's
- // happening. The result is definitely the chaos beam passing through
- // the corner and ending up outside the room)
- //
- // Everything else I've tried to fix this causes an assertion somewhere
- // else.
- new_ray.advance(true);
- new_ray.regress();
}
bool bolt::invisible() const
@@ -1469,6 +1460,8 @@ void bolt::initialize_fire()
ASSERT(flavour > BEAM_NONE && flavour < BEAM_FIRST_PSEUDO);
ASSERT(!drop_item || item);
ASSERT(range >= 0);
+ ASSERT(chose_ray && in_bounds(ray.pos())
+ || !chose_ray && in_bounds(source));
real_flavour = flavour;
@@ -1763,14 +1756,16 @@ void bolt::hit_wall()
if (!invalid_monster_index(mgrd(pos())))
affect_monster( &menv[mgrd(pos())] );
- // Regress for explosions: blow up one step earlier.
- if (is_explosion && !in_explosion_phase)
+ // Regress for explosions: blow up in an open grid (if regressing
+ // makes any sense). Also regress when dropping items.
+ if (!aimed_at_spot
+ && ((is_explosion && !in_explosion_phase) || drop_item))
{
do {
ray.regress();
- } while (grid_is_solid(ray.pos()));
- finish_beam();
+ } while (ray.pos() != source && grid_is_solid(ray.pos()));
}
+ finish_beam();
}
}
@@ -1874,8 +1869,8 @@ void bolt::do_fire()
msg_generated = false;
if (target == source)
{
- auto_hit = true;
- aimed_at_spot = true;
+ auto_hit = true;
+ aimed_at_spot = true;
use_target_as_pos = true;
}
else
@@ -1892,7 +1887,7 @@ void bolt::do_fire()
oldValue = set_buffering(false);
#endif
- while (true)
+ while (in_bounds(pos()))
{
affect_cell();
@@ -1906,6 +1901,8 @@ void bolt::do_fire()
if (stop_at_target() && pos() == target)
break;
+ ASSERT(!grid_is_solid(grd(pos())));
+
const bool was_seen = seen;
if (!was_seen && range > 0 && !invisible() && see_grid(pos()))
seen = true;
@@ -1932,6 +1929,24 @@ void bolt::do_fire()
ray.advance(true);
}
+ if (!in_bounds(pos()))
+ {
+ ASSERT(!aimed_at_spot);
+#ifdef DEBUG
+ mprf(MSGCH_DIAGNOSTICS, "fire_beam(): beam '%s' passed off edge of "
+ "map (item = '%s')", name.c_str(),
+ item ? item->name(DESC_PLAIN).c_str() : "none");
+#endif
+
+ int tries = std::max(GXM, GYM);
+
+ while (!in_bounds(ray.pos()) && tries-- > 0)
+ ray.regress();
+
+ // Something bizarre happening if we can't get back onto the map.
+ ASSERT(in_bounds(pos()));
+ }
+
// The beam has terminated.
affect_endpoint();
@@ -5069,6 +5084,9 @@ static sweep_type _radial_sweep(int r)
bool bolt::explode(bool show_more, bool hole_in_the_middle)
{
+ ASSERT(!in_explosion_phase);
+ ASSERT(ex_size > 0);
+
real_flavour = flavour;
const int r = std::min(ex_size, MAX_EXPLOSION_RADIUS);
in_explosion_phase = true;