summaryrefslogtreecommitdiffstats
path: root/crawl-ref
diff options
context:
space:
mode:
Diffstat (limited to 'crawl-ref')
-rw-r--r--crawl-ref/source/beam.cc23
-rw-r--r--crawl-ref/source/direct.cc22
-rw-r--r--crawl-ref/source/externs.h57
-rw-r--r--crawl-ref/source/view.cc197
-rw-r--r--crawl-ref/source/view.h3
5 files changed, 262 insertions, 40 deletions
diff --git a/crawl-ref/source/beam.cc b/crawl-ref/source/beam.cc
index ef80105aab..8305758b35 100644
--- a/crawl-ref/source/beam.cc
+++ b/crawl-ref/source/beam.cc
@@ -1262,11 +1262,12 @@ static void zappy( zap_type z_type, int power, struct bolt &pbolt )
*/
-void fire_beam( struct bolt &pbolt, item_def *item )
+void fire_beam( bolt &pbolt, item_def *item )
{
bool beamTerminate; // has beam been 'stopped' by something?
int tx = 0, ty = 0; // test(new) x,y - integer
int rangeRemaining;
+ bool did_bounce = false;
cursor_control coff(false);
beam_message_cache.clear();
@@ -1302,11 +1303,12 @@ void fire_beam( struct bolt &pbolt, item_def *item )
{
ray.fullray_idx = -1; // to quiet valgrind
find_ray( pbolt.source_x, pbolt.source_y,
- pbolt.target_x, pbolt.target_y, true, ray);
+ pbolt.target_x, pbolt.target_y, true, ray,
+ 0, true );
}
if ( !pbolt.aimed_at_feet )
- ray.advance();
+ ray.advance_through(pbolt.target());
// give chance for beam to affect one cell even if aimed_at_feet.
beamTerminate = false;
@@ -1351,16 +1353,19 @@ void fire_beam( struct bolt &pbolt, item_def *item )
else
{
// BEGIN bounce case
- if (!isBouncy(pbolt, grd[tx][ty])) {
- ray.regress();
+ if (!isBouncy(pbolt, grd[tx][ty]))
+ {
+ ray.regress(pbolt.target());
tx = ray.x();
ty = ray.y();
break; // breaks from line tracing
}
+ did_bounce = true;
+
// bounce
do {
- ray.regress();
+ ray.regress(pbolt.target());
ray.advance_and_bounce();
--rangeRemaining;
} while ( rangeRemaining > 0 &&
@@ -1458,7 +1463,11 @@ void fire_beam( struct bolt &pbolt, item_def *item )
}
}
- ray.advance();
+
+ if (!did_bounce)
+ ray.advance_through(pbolt.target());
+ else
+ ray.advance(true);
} // end- while !beamTerminate
// the beam has finished, and terminated at tx, ty
diff --git a/crawl-ref/source/direct.cc b/crawl-ref/source/direct.cc
index 77d3a80d16..0b746910d5 100644
--- a/crawl-ref/source/direct.cc
+++ b/crawl-ref/source/direct.cc
@@ -291,7 +291,7 @@ static const char *target_mode_help_text(int mode)
// dx,dy direction delta for DIR_DIR
//
//---------------------------------------------------------------
-void direction(struct dist& moves, targeting_type restricts,
+void direction(dist& moves, targeting_type restricts,
int mode, bool just_looking, const char *prompt)
{
// NOTE: Even if just_looking is set, moves is still interesting,
@@ -466,8 +466,17 @@ void direction(struct dist& moves, targeting_type restricts,
break;
case CMD_TARGET_HIDE_BEAM:
- show_beam = false;
- need_beam_redraw = true;
+ if (show_beam)
+ {
+ show_beam = false;
+ need_beam_redraw = true;
+ }
+ else
+ {
+ show_beam = find_ray(you.x_pos, you.y_pos, moves.tx, moves.ty,
+ true, ray, 0, true);
+ need_beam_redraw = show_beam;
+ }
break;
case CMD_TARGET_FIND_YOU:
@@ -696,7 +705,8 @@ void direction(struct dist& moves, targeting_type restricts,
{
have_moved = true;
show_beam = show_beam &&
- find_ray(you.x_pos, you.y_pos, moves.tx, moves.ty, true, ray);
+ find_ray(you.x_pos, you.y_pos, moves.tx, moves.ty, true, ray,
+ 0, true);
}
if ( force_redraw )
@@ -738,7 +748,7 @@ void direction(struct dist& moves, targeting_type restricts,
grid2viewY(raycopy.y()));
cprintf("*");
}
- raycopy.advance();
+ raycopy.advance_through(moves.target());
}
textcolor(LIGHTGREY);
}
@@ -762,7 +772,7 @@ static void extend_move_to_edge(dist &moves)
int mx = 0, my = 0;
if (moves.dx > 0)
- mx = (GXM - 1) - you.x_pos;
+ mx = (GXM - 1) - you.x_pos;
if (moves.dx < 0)
mx = you.x_pos;
diff --git a/crawl-ref/source/externs.h b/crawl-ref/source/externs.h
index c845e382f5..1608e76b51 100644
--- a/crawl-ref/source/externs.h
+++ b/crawl-ref/source/externs.h
@@ -248,41 +248,53 @@ struct coord_def
int distance_from(const coord_def &b) const;
- bool operator == (const coord_def &other) const {
+ bool operator == (const coord_def &other) const
+ {
return x == other.x && y == other.y;
}
- bool operator != (const coord_def &other) const {
+ bool operator != (const coord_def &other) const
+ {
return !operator == (other);
}
- bool operator < (const coord_def &other) const {
+ bool operator < (const coord_def &other) const
+ {
return (x < other.x) || (x == other.x && y < other.y);
}
- const coord_def &operator += (const coord_def &other) {
+ const coord_def &operator += (const coord_def &other)
+ {
x += other.x;
y += other.y;
return (*this);
}
- const coord_def &operator -= (const coord_def &other) {
+ const coord_def &operator -= (const coord_def &other)
+ {
x -= other.x;
y -= other.y;
return (*this);
}
- coord_def operator + (const coord_def &other) const {
+ coord_def operator + (const coord_def &other) const
+ {
coord_def copy = *this;
copy += other;
return (copy);
}
- coord_def operator - (const coord_def &other) const {
+ coord_def operator - (const coord_def &other) const
+ {
coord_def copy = *this;
copy -= other;
return (copy);
}
+
+ int abs() const
+ {
+ return (x * x + y * y);
+ }
};
struct dice_def
@@ -295,6 +307,7 @@ struct dice_def
struct ray_def
{
+public:
double accx;
double accy;
double slope;
@@ -304,12 +317,25 @@ struct ray_def
// Quadrant 4: up-right
int quadrant;
int fullray_idx; // for cycling: where did we come from?
-
+
+public:
int x() const { return static_cast<int>(accx); }
int y() const { return static_cast<int>(accy); }
- int advance(); // returns the direction taken (0,1,2)
+ coord_def pos() const { return coord_def(accx, accy); }
+
+ // returns the direction taken (0,1,2)
+ int advance(bool shorten = false, const coord_def *p = NULL);
+ int advance_through(const coord_def &point);
void advance_and_bounce();
- void regress();
+ void regress(const coord_def &point);
+
+private:
+ int raw_advance();
+ double reflect(bool x, double oldc, double newc) const;
+ double reflect(double x, double c) const;
+ void set_reflect_point(const double oldx, const double oldy,
+ double *newx, double *newy,
+ bool blocked_x, bool blocked_y);
};
// output from direction() function:
@@ -328,6 +354,12 @@ struct dist
// internal use - ignore
int prev_target; // previous target
+
+ // target - source (source == you.pos())
+ coord_def target() const
+ {
+ return coord_def(tx, ty);
+ }
};
struct bolt
@@ -384,6 +416,11 @@ public:
// Returns YOU_KILL or MON_KILL, depending on the source of the beam.
int killer() const;
+
+ coord_def target() const
+ {
+ return (coord_def(target_x, target_y));
+ }
};
struct run_check_dir
diff --git a/crawl-ref/source/view.cc b/crawl-ref/source/view.cc
index 20ca4468ea..3858f89aba 100644
--- a/crawl-ref/source/view.cc
+++ b/crawl-ref/source/view.cc
@@ -27,6 +27,7 @@
#include "view.h"
#include <string.h>
+#include <cmath>
#ifdef DOS
#include <conio.h>
@@ -1144,6 +1145,50 @@ static int find_next_intercept(double* accx, double* accy, const double slope)
return rc;
}
+double ray_def::reflect(double p, double c) const
+{
+ return (c + c - p);
+}
+
+double ray_def::reflect(bool rx, double oldx, double newx) const
+{
+ if (rx? abs(slope) > 1.0 : abs(slope) < 1.0)
+ return (reflect(oldx, floor(oldx) + 0.5));
+
+ const int flnew = newx;
+ const int flold = oldx;
+ return (reflect(oldx,
+ flnew > flold? flnew :
+ flold > flnew? flold :
+ (newx + oldx) / 2));
+}
+
+void ray_def::set_reflect_point(const double oldx, const double oldy,
+ double *newx, double *newy,
+ bool blocked_x, bool blocked_y)
+{
+ if (blocked_x == blocked_y)
+ {
+ // What to do?
+ *newx = oldx;
+ *newy = oldy;
+ return;
+ }
+
+ if (blocked_x)
+ {
+ ASSERT(int(oldy) != int(*newy));
+ *newy = oldy;
+ *newx = reflect(true, oldx, *newx);
+ }
+ else
+ {
+ ASSERT(int(oldx) != int(*newx));
+ *newx = oldx;
+ *newy = reflect(false, oldy, *newy);
+ }
+}
+
void ray_def::advance_and_bounce()
{
// 0 = down-right, 1 = down-left, 2 = up-left, 3 = up-right
@@ -1152,9 +1197,14 @@ void ray_def::advance_and_bounce()
{ 1, 3, 2 }, { 0, 2, 3 }, { 3, 1, 0 }, { 2, 0, 1 }
};
int oldx = x(), oldy = y();
- int rc = advance();
+ const double oldaccx = accx, oldaccy = accy;
+ int rc = advance(true);
int newx = x(), newy = y();
ASSERT( grid_is_solid(grd[newx][newy]) );
+
+ const bool blocked_x = grid_is_solid(grd[oldx][newy]);
+ const bool blocked_y = grid_is_solid(grd[newx][oldy]);
+
if ( double_is_zero(slope) || slope > 100.0 )
quadrant = bouncequad[quadrant][2];
else if ( rc != 2 )
@@ -1162,8 +1212,6 @@ void ray_def::advance_and_bounce()
else
{
ASSERT( (oldx != newx) && (oldy != newy) );
- bool blocked_x = grid_is_solid(grd[oldx][newy]);
- bool blocked_y = grid_is_solid(grd[newx][oldy]);
if ( blocked_x && blocked_y )
quadrant = bouncequad[quadrant][rc];
else if ( blocked_x )
@@ -1171,18 +1219,54 @@ void ray_def::advance_and_bounce()
else
quadrant = bouncequad[quadrant][0];
}
- advance();
+
+ set_reflect_point(oldaccx, oldaccy, &accx, &accy, blocked_x, blocked_y);
}
-void ray_def::regress()
+void ray_def::regress(const coord_def &point)
{
int opp_quadrant[4] = { 2, 3, 0, 1 };
quadrant = opp_quadrant[quadrant];
- advance();
+ advance(true, &point);
quadrant = opp_quadrant[quadrant];
}
-int ray_def::advance()
+int ray_def::advance_through(const coord_def &target)
+{
+ return (advance(true, &target));
+}
+
+int ray_def::advance(bool shortest_possible, const coord_def *target)
+{
+ if (!shortest_possible)
+ return (raw_advance());
+
+ // If we want to minimize the number of moves on the ray, look one
+ // step ahead and see if we can get a diagonal.
+
+ const coord_def old(accx, accy);
+ const int ret = raw_advance();
+
+ if (ret == 2 || (target && pos() == *target))
+ return (ret);
+
+ const double maccx = accx, maccy = accy;
+ if (raw_advance() != 2)
+ {
+ const coord_def second(accx, accy);
+ // If we can convert to a diagonal, do so.
+ if ((second - old).abs() == 2)
+ return (2);
+ }
+
+ // No diagonal, so roll back.
+ accx = maccx;
+ accy = maccy;
+
+ return (ret);
+}
+
+int ray_def::raw_advance()
{
int rc;
switch ( quadrant )
@@ -1566,9 +1650,12 @@ static int cyclic_offset( unsigned int ui, int cycle_dir, int startpoint,
// If cycle_dir is 0, find the first fitting ray. If it is 1 or -1,
// assume that ray is appropriately filled in, and look for the next
// ray in that cycle direction.
+// If find_shortest is true, examine all rays that hit the target and
+// take the shortest (starting at ray.fullray_idx).
bool find_ray( int sourcex, int sourcey, int targetx, int targety,
- bool allow_fallback, ray_def& ray, int cycle_dir )
+ bool allow_fallback, ray_def& ray, int cycle_dir,
+ bool find_shortest )
{
int cellray, inray;
@@ -1577,6 +1664,10 @@ bool find_ray( int sourcex, int sourcey, int targetx, int targety,
const int absx = signx * (targetx - sourcex);
const int absy = signy * (targety - sourcey);
int cur_offset = 0;
+ int shortest = INFINITE_DISTANCE;
+ int imbalance = INFINITE_DISTANCE;
+ std::vector<coord_def> unaliased_ray;
+
for ( unsigned int fray = 0; fray < fullrays.size(); ++fray )
{
const int fullray = cyclic_offset( fray, cycle_dir, ray.fullray_idx,
@@ -1591,25 +1682,94 @@ bool find_ray( int sourcex, int sourcey, int targetx, int targety,
if ( ray_coord_x[cellray + cur_offset] == absx &&
ray_coord_y[cellray + cur_offset] == absy )
{
+ if (find_shortest)
+ {
+ unaliased_ray.clear();
+ unaliased_ray.push_back(coord_def(0, 0));
+ }
// check if we're blocked so far
bool blocked = false;
+ coord_def c1, c3;
+ int real_length = 0;
for ( inray = 0; inray < cellray; ++inray )
{
- if (grid_is_solid(
- grd[sourcex
- + signx * ray_coord_x[inray + cur_offset]]
- [sourcey
- + signy * ray_coord_y[inray + cur_offset]]))
+ const int xi = signx * ray_coord_x[inray + cur_offset];
+ const int yi = signy * ray_coord_y[inray + cur_offset];
+ if (grid_is_solid(grd[sourcex + xi][sourcey + yi]))
{
- blocked = true;
+ blocked = true;
break;
}
+
+ if (find_shortest)
+ {
+ c3 = coord_def(xi, yi);
+
+ // We've moved at least two steps if inray > 0.
+ if (inray)
+ {
+ // Check for a perpendicular corner on the ray and
+ // pretend that it's a diagonal.
+ if ((c3 - c1).abs() != 2)
+ ++real_length;
+ else
+ {
+ // c2 was a dud move, pop it off
+ unaliased_ray.pop_back();
+ }
+ }
+ else
+ ++real_length;
+
+ unaliased_ray.push_back(c3);
+ c1 = unaliased_ray[real_length - 1];
+ }
+ }
+
+ if (find_shortest)
+ unaliased_ray.push_back(
+ coord_def(signx * absx, signy * absy));
+
+ int cimbalance = 0;
+ // If this ray is a candidate for shortest, calculate
+ // the imbalance. I'm defining 'imbalance' as the
+ // number of consecutive diagonal or orthogonal moves
+ // in the ray. This is a reasonable measure of deviation from
+ // the Bresenham line between our selected source and
+ // destination.
+ if (!blocked && find_shortest && shortest >= real_length)
+ {
+ int diags = 0, straights = 0;
+ for (int i = 1, size = unaliased_ray.size(); i < size;
+ ++i)
+ {
+ const int dist =
+ (unaliased_ray[i] - unaliased_ray[i - 1]).abs();
+ if (dist == 2)
+ {
+ straights = 0;
+ if (++diags > cimbalance)
+ cimbalance = diags;
+ }
+ else
+ {
+ diags = 0;
+ if (++straights > cimbalance)
+ cimbalance = straights;
+ }
+ }
}
- if ( !blocked )
+ if ( !blocked
+ && (!find_shortest
+ || shortest > real_length
+ || (shortest == real_length
+ && imbalance > cimbalance)) )
{
// success!
+ shortest = real_length;
+ imbalance = cimbalance;
ray = fullrays[fullray];
ray.fullray_idx = fullray;
if ( sourcex > targetx )
@@ -1619,11 +1779,16 @@ bool find_ray( int sourcex, int sourcey, int targetx, int targety,
ray.accx += sourcex;
ray.accy += sourcey;
set_ray_quadrant(ray, sourcex, sourcey, targetx, targety);
- return true;
+ if (!find_shortest)
+ return true;
}
}
}
}
+
+ if (find_shortest && shortest != INFINITE_DISTANCE)
+ return (true);
+
if ( allow_fallback )
{
ray.accx = sourcex + 0.5;
diff --git a/crawl-ref/source/view.h b/crawl-ref/source/view.h
index 099f39e48e..841e33c3ab 100644
--- a/crawl-ref/source/view.h
+++ b/crawl-ref/source/view.h
@@ -156,7 +156,8 @@ void viewwindow(bool draw_it, bool do_updates);
void fire_monster_alerts();
bool find_ray( int sourcex, int sourcey, int targetx, int targety,
- bool allow_fallback, ray_def& ray, int cycle_dir = 0 );
+ bool allow_fallback, ray_def& ray, int cycle_dir = 0,
+ bool find_shortest = false );
dungeon_char_type dchar_by_name(const std::string &name);