los_param got a new opacity_monmove to help. The change compiles and the game runs, but I'm unsure how to test monster pathfinding. Please tell me if anything went wrong!
Removes mon-los.{cc,h}, so I'm afraid I may have broken the non-Unix builds again.
RG4JTNTDB35PSDT3N3IBNBCPSSZLWL4K7DRTOD73OEDRB4I5MTCAC
IOYLVGTLPFTKADPHYIWMXXD64HCW7UMR2P44M7K26ZLLEDBVMJQQC
UL7XFKMUX3WIU4O2LZANK4ECJ654UZPDBFGNXUEYZYOLKBYBCG6AC
ACZYEIX7WMPIIODKCATBCUE626AJ4ZGGBOMVC6BGXM27EQU2RECAC
TZNURHCLTLE3A55EPNU66BU6NX7LOBA7EFG4NAJJBUKZLOAEFU4AC
DTLSPUE47XI4YC3QWKTBWJOWBU52GXPGXFEEBG374I4JAYVM7KZAC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
UCEAWJ4I6AEFR64SSUFQSX6Q62JGLMJQ5BOO5MQSV3XIE7KAS6CQC
YKVVFNKT2M5WG2KBURRTOJG23BJVI6WUBP5JOGYPHQBS4RNGFGWQC
22ORIULMB2NHLFZNJFK2AF275Q6XBKOBE4ZRMOADY64VK5FAUUSQC
H7AOW4T4Q7AKOXREMK6ZXN3GK6A4I24ICE6VTROMJ5Y3LX47TSDAC
P5TRGRH7XMQSPCZKM5IEEO34TY6WMLGHHX7BU6Y453JFRXLUR2VQC
LHYTGOCNDWX3CVD2HSQ6LAYC6NLKKI6ZKKNWZ5IQWP6YP5PQEVWQC
ZJMTMMZBCXHCPFHBTI2PL3Q4XTJQUWMMHCCG6JDIEDMJYCC33TFQC
BR42OZ3CHR5F5MKPBUOPRG3EXIEQRRRWD5L54BTZQRDXV6VBLATQC
MQ62OAMLGJVRW2QIL4PAZRAU6PC52ZVGY2FCOBIY6IWGQIHMU5CAC
PVHG5SLN34UM4FRF5YJZOG4EN22Q5LZ5MNUHYZFWAXWIB5K4RZ2AC
AL7EYY4HB7JNEFGDB6NVVHCVVUYYKUJKC4UFH4T7XUT3P5NT4NAAC
2EBEWIV4YHXXAFR4GG2GMZJ2K77NK762HNQ77CZLHI3LDVGX7RJAC
NWWSJFJEA23B64WF4AT7K7KNFUMK3I22L4RCDSIMEI3XJL3KS2CQC
DRH5HH762ZIS2R6NCKQYD3XO2P6RDNS6N4XEULIEQPNMB46HEDFQC
#ifndef MON_LOS_H
/*
* File: mon-los.cc
* Summary: Monster line-of-sight.
*/
#define MON_LOS_H
#define _monster_los_LSIZE (2 * LOS_RADIUS + 1)
// This class can be used to fill the entire surroundings (los_range)
// of a monster or other position with seen/unseen values, so as to be able
// to compare several positions within this range.
class monster_los
{
public:
monster_los();
virtual ~monster_los();
// public methods
void set_monster(monsters *mon);
void set_los_centre(int x, int y);
void set_los_centre(const coord_def& p) { this->set_los_centre(p.x, p.y); }
void set_los_range(int r);
void fill_los_field(void);
bool in_sight(int x, int y);
bool in_sight(const coord_def& p) { return this->in_sight(p.x, p.y); }
protected:
// protected methods
coord_def pos_to_index(coord_def &p);
coord_def index_to_pos(coord_def &i);
void set_los_value(int x, int y, bool blocked, bool override = false);
int get_los_value(int x, int y);
bool is_blocked(int x, int y);
bool is_unknown(int x, int y);
void check_los_beam(int dx, int dy);
// The (fixed) size of the array.
static const int LSIZE;
static const int L_VISIBLE;
static const int L_UNKNOWN;
static const int L_BLOCKED;
// The centre of our los field.
int gridx, gridy;
// Habitat checks etc. should be done for this monster.
// Usually the monster whose LOS we're trying to calculate
// (if mon->x == gridx, mon->y == gridy).
// Else, any monster trying to move around within this los field.
monsters *mons;
// Range may never be greater than LOS_RADIUS!
int range;
// The array to store the LOS values.
int los_field[_monster_los_LSIZE][_monster_los_LSIZE];
};
#endif
/*
* File: mon-los.cc
* Summary: Monster line-of-sight.
*/
#include "AppHdr.h"
REVISION("$Rev$");
#include "mon-los.h"
#include "los.h"
#include "ray.h"
#include "stuff.h"
// Static class members must be initialised outside of the class
// declaration, or gcc won't define them in view.o and we'll get a
// linking error.
const int monster_los::LSIZE = _monster_los_LSIZE;
const int monster_los::L_VISIBLE = 1;
const int monster_los::L_UNKNOWN = 0;
const int monster_los::L_BLOCKED = -1;
/////////////////////////////////////////////////////////////////////////////
// monster_los
monster_los::monster_los()
: gridx(0), gridy(0), mons(), range(LOS_RADIUS), los_field()
{
}
monster_los::~monster_los()
{
}
void monster_los::set_monster(monsters *mon)
{
mons = mon;
set_los_centre(mon->pos());
}
void monster_los::set_los_centre(int x, int y)
{
if (!in_bounds(x, y))
return;
gridx = x;
gridy = y;
}
void monster_los::set_los_range(int r)
{
ASSERT (r >= 1 && r <= LOS_RADIUS);
range = r;
}
coord_def monster_los::pos_to_index(coord_def &p)
{
int ix = LOS_RADIUS + p.x - gridx;
int iy = LOS_RADIUS + p.y - gridy;
ASSERT(ix >= 0 && ix < LSIZE);
ASSERT(iy >= 0 && iy < LSIZE);
return (coord_def(ix, iy));
}
coord_def monster_los::index_to_pos(coord_def &i)
{
int px = i.x + gridx - LOS_RADIUS;
int py = i.y + gridy - LOS_RADIUS;
ASSERT(in_bounds(px, py));
return (coord_def(px, py));
}
void monster_los::set_los_value(int x, int y, bool blocked, bool override)
{
if (!override && !is_unknown(x,y))
return;
coord_def c(x,y);
coord_def lpos = pos_to_index(c);
int value = (blocked ? L_BLOCKED : L_VISIBLE);
if (value != los_field[lpos.x][lpos.y])
los_field[lpos.x][lpos.y] = value;
}
int monster_los::get_los_value(int x, int y)
{
// Too far away -> definitely out of sight!
return (L_BLOCKED);
coord_def c(x,y);
coord_def lpos = pos_to_index(c);
return (los_field[lpos.x][lpos.y]);
}
bool monster_los::in_sight(int x, int y)
{
// Is the path to (x,y) clear?
return (get_los_value(x,y) == L_VISIBLE);
}
bool monster_los::is_blocked(int x, int y)
{
// Is the path to (x,y) blocked?
return (get_los_value(x, y) == L_BLOCKED);
}
bool monster_los::is_unknown(int x, int y)
{
return (get_los_value(x, y) == L_UNKNOWN);
}
static bool _blocks_movement_sight(monsters *mon, dungeon_feature_type feat)
{
if (feat < DNGN_MINMOVE)
return (true);
if (!mon) // No monster defined?
return (false);
if (!mon->can_pass_through_feat(feat))
return (true);
return (false);
}
void monster_los::fill_los_field()
{
int pos_x, pos_y;
for (int k = 1; k <= range; k++)
for (int i = -1; i <= 1; i++)
for (int j = -1; j <= 1; j++)
{
if (i == 0 && j == 0) // Ignore centre grid.
continue;
pos_x = gridx + k*i;
pos_y = gridy + k*j;
if (!in_bounds(pos_x, pos_y))
continue;
if (!_blocks_movement_sight(mons, grd[pos_x][pos_y]))
set_los_value(pos_x, pos_y, false);
else
{
set_los_value(pos_x, pos_y, true);
// Check all beam potentially going through a blocked grid.
check_los_beam(pos_x, pos_y);
}
}
}
// (cx, cy) is the centre point
// (dx, dy) is the target we're aiming *through*
// target1 and target2 are targets we'll be aiming *at* to fire through (dx,dy)
static bool _set_beam_target(int cx, int cy, int dx, int dy,
int &target1_x, int &target1_y,
int &target2_x, int &target2_y,
int range)
{
const int xdist = dx - cx;
const int ydist = dy - cy;
if (xdist == 0 && ydist == 0)
return (false); // Nothing to be done.
if (xdist <= -range || xdist >= range
|| ydist <= -range || ydist >= range)
{
// Grids on the edge of a monster's LOS don't block sight any further.
return (false);
}
/*
* The following code divides the field into eights of different directions.
*
* \ NW | NE /
* \ | /
* WN \ | / EN
* ----------------
* WS / | \ ES
* / | \
* / SW | SE \
*
* target1_x and target1_y mark the base line target, so the base beam ends
* on the diagonal line closest to the target (or on one of the straight
* lines if cx == dx or dx == dy).
*
* target2_x and target2_y then mark the second target our beam finder should
* cycle through. It'll always be target2_x = dx or target2_y = dy, the other
* being on the edge of LOS, which one depending on the quadrant.
*
* The beam finder can then cycle from the nearest corner (target1) to the
* second edge target closest to (dx,dy).
*/
if (xdist == 0)
{
target1_x = cx;
target1_y = (ydist > 0 ? cy + range
: cy - range);
target2_x = target1_x;
target2_y = target1_y;
}
else if (ydist == 0)
{
target1_x = (xdist > 0 ? cx + range
: cx - range);
target1_y = cy;
target2_x = target1_x;
target2_y = target1_y;
}
else if (xdist < 0 && ydist < 0 || xdist > 0 && ydist > 0)
{
if (xdist < 0)
{
target1_x = cx - range;
target1_y = cy - range;
}
else
{
target1_x = cx + range;
target1_y = cy + range;
}
if (xdist == ydist)
{
target2_x = target1_x;
target2_y = target1_y;
}
else
{
if (xdist < 0) // both are negative (upper left corner)
{
if (dx > dy)
{
target2_x = dx;
target2_y = cy - range;
}
else
{
target2_x = cx - range;
target2_y = dy;
}
}
else // both are positive (lower right corner)
{
if (dx > dy)
{
target2_x = cx + range;
target2_y = dy;
}
else
{
target2_x = dx;
target2_y = cy + range;
}
}
}
}
else if (xdist < 0 && ydist > 0 || xdist > 0 && ydist < 0)
{
if (xdist < 0) // lower left corner
{
target1_x = cx - range;
target1_y = cy + range;
}
else // upper right corner
{
target1_x = cx + range;
target1_y = cy - range;
}
if (xdist == -ydist)
{
target2_x = target1_x;
target2_y = target1_y;
}
else
{
if (xdist < 0) // ydist > 0
{
if (-xdist > ydist)
{
target2_x = cx - range;
target2_y = dy;
}
else
{
target2_x = dx;
target2_y = cy + range;
}
}
else // xdist > 0, ydist < 0
{
if (-xdist > ydist)
{
target2_x = dx;
target2_y = cy - range;
}
else
{
target2_x = cx + range;
target2_y = dy;
}
}
}
}
else
{
// Everything should have been handled above.
ASSERT(false);
}
return (true);
}
void monster_los::check_los_beam(int dx, int dy)
{
ray_def ray;
int target1_x = 0, target1_y = 0, target2_x = 0, target2_y = 0;
if (!_set_beam_target(gridx, gridy, dx, dy, target1_x, target1_y,
target2_x, target2_y, range))
{
// Nothing to be done.
return;
}
if (target1_x > target2_x || target1_y > target2_y)
{
// Swap the two targets so our loop will work correctly.
int help = target1_x;
target1_x = target2_x;
target2_x = help;
help = target1_y;
target1_y = target2_y;
target2_y = help;
}
const int max_dist = range;
int dist;
bool blocked = false;
for (int tx = target1_x; tx <= target2_x; tx++)
for (int ty = target1_y; ty <= target2_y; ty++)
{
// If (tx, ty) lies outside the level boundaries, there's nothing
// that shooting a ray into that direction could bring us, esp.
// as earlier grids in the ray will already have been handled, and
// out of bounds grids are simply skipped in any LoS check.
if (!map_bounds(tx, ty))
continue;
// Already calculated a beam to (tx, ty), don't do so again.
if (!is_unknown(tx, ty))
continue;
dist = 0;
if (ray.x() == gridx && ray.y() == gridy)
ray.advance(true);
while (dist++ <= max_dist)
{
// The ray brings us out of bounds of the level map.
// Since we're always shooting outwards there's nothing more
// to look at in that direction, and we can break the loop.
if (!map_bounds(ray.x(), ray.y()))
break;
if (blocked)
{
// Earlier grid blocks this beam, set to blocked if
// unknown, but don't overwrite visible grids.
set_los_value(ray.x(), ray.y(), true);
}
else if (_blocks_movement_sight(mons, grd[ray.x()][ray.y()]))
{
set_los_value(ray.x(), ray.y(), true);
// The rest of the beam will now be blocked.
blocked = true;
}
else
{
// Allow overriding in case another beam has marked this
// field as blocked, because we've found a solution where
// that isn't the case.
set_los_value(ray.x(), ray.y(), false, true);
}
if (ray.x() == tx && ray.y() == ty)
break;
ray.advance(true);
}
}
}
fallback_ray(coord_def(gridx, gridy), coord_def(tx, ty), ray);
if (!find_ray(coord_def(gridx, gridy), coord_def(tx, ty), ray))
if (distance(x, y, gridx, gridy) > get_los_radius_sq())
monster_los patrol;
// Set monster to make monster_los respect habitat restrictions.
patrol.set_monster(mon);
patrol.set_los_centre(mon->patrol_point);
patrol.set_los_range(rad);
patrol.fill_los_field();
env_show_grid patrol;
losight(patrol, mon->patrol_point, opacity_monmove(*mon), bounds_radius(rad));
static bounds_radius_sq bds_deflos = bounds_radius_sq(LOS_RADIUS_SQ);
static bounds_radius_sq bds_maxlos = bounds_radius_sq(LOS_MAX_RADIUS_SQ);
// LOS bounded by fixed radius.
struct bounds_radius : bounds_radius_sq
{
bounds_radius(int r)
: bounds_radius_sq(r * r + 1)
{
}
};