New specialization of "paths" class to implement vision.

Consequentially, shift edge-finding from clear_shroud_loc() to find_routes().

(Allows more streamlining, to come later.)
This commit is contained in:
J. Tyne 2012-04-13 19:49:11 +00:00
parent dcd73d3ca3
commit af04480128
3 changed files with 171 additions and 53 deletions

View file

@ -2271,46 +2271,57 @@ int combat_modifier(const map_location &loc,
namespace {
/**
* Clears shroud from a single location.
*
* In a few cases, this will also clear corner hexes that otherwise would
* not normally get cleared.
* @param tm The team whose fog/shroud is affected.
* @param loc The location to clear.
* @param cleared If loc is cleared, it gets added to this vector.
* @return true if the specified location was fogged or shrouded.
*/
bool clear_shroud_loc(team &tm,
const map_location& loc,
std::vector<map_location>* cleared)
{
gamemap &map = *resources::game_map;
bool result = false;
map_location adj[7];
get_adjacent_tiles(loc,adj);
adj[6] = loc;
bool on_board_loc = map.on_board(loc);
for(int i = 0; i != 7; ++i) {
// We clear one past the edge of the board, so that the half-hexes
// at the edge can also be cleared of fog/shroud.
if (on_board_loc || map.on_board_with_border(adj[i])) {
// Both functions should be executed so don't use || which
// uses short-cut evaluation.
const bool res = tm.clear_shroud(adj[i]) | tm.clear_fog(adj[i]);
// We clear one past the edge of the board, so that the half-hexes
// at the edge can also be cleared of fog/shroud.
if ( map.on_board_with_border(loc)) {
// Both functions should be executed so don't use || which
// uses short-cut evaluation.
result = tm.clear_shroud(loc) | tm.clear_fog(loc);
if(res) {
result = true;
// If we're near the corner it might be the corner also needs to be cleared
// this always happens at the lower left corner and depending on the with
// at the upper or lower right corner.
if(adj[i].x == 0 && adj[i].y == map.h() - 1) { // Lower left corner
const map_location corner(-1 , map.h());
tm.clear_shroud(corner);
tm.clear_fog(corner);
} else if(map.w() % 2 && adj[i].x == map.w() - 1 && adj[i].y == map.h() - 1) { // Lower right corner
const map_location corner(map.w() , map.h());
tm.clear_shroud(corner);
tm.clear_fog(corner);
} else if(!(map.w() % 2) && adj[i].x == map.w() - 1 && adj[i].y == 0) { // Upper right corner
const map_location corner(map.w() , -1);
tm.clear_shroud(corner);
tm.clear_fog(corner);
}
if(cleared) {
cleared->push_back(adj[i]);
}
if ( result ) {
// If we are near a corner, the corner might also need to be cleared.
// This happens at the lower-left corner and at either the upper- or
// lower- right corner (depending on the width).
// Lower-left corner:
if ( loc.x == 0 && loc.y == map.h()-1 ) {
const map_location corner(-1, map.h());
tm.clear_shroud(corner);
tm.clear_fog(corner);
}
// Lower-right corner, odd width:
else if ( is_odd(map.w()) && loc.x == map.w()-1 && loc.y == map.h()-1 ) {
const map_location corner(map.w(), map.h());
tm.clear_shroud(corner);
tm.clear_fog(corner);
}
// Upper-right corner, even width:
else if ( is_even(map.w()) && loc.x == map.w()-1 && loc.y == 0) {
const map_location corner(map.w(), -1);
tm.clear_shroud(corner);
tm.clear_fog(corner);
}
// Add the specified location to the feedback vector.
if(cleared) {
cleared->push_back(loc);
}
}
}
@ -2336,10 +2347,14 @@ namespace {
return false;
}
pathfind::paths p(*resources::game_map, *resources::units, *u, *resources::teams, true, false, tm, 0, false, true);
foreach (const pathfind::paths::step &dest, p.destinations) {
// Clear the fog.
pathfind::vision_path sight(*resources::game_map, *u, loc);
foreach (const pathfind::paths::step &dest, sight.destinations) {
clear_shroud_loc(tm, dest.curr, &cleared_locations);
}
foreach (const map_location &dest, sight.edges) {
clear_shroud_loc(tm, dest, &cleared_locations);
}
// clear_shroud_loc is supposed not introduce repetition in cleared_locations
for(std::vector<map_location>::const_iterator it =

View file

@ -177,14 +177,38 @@ struct comp {
};
}
static void find_routes(const gamemap& map, const unit& u,
/**
* Creates a list of routes that a unit can traverse from the provided location.
* (This is called when creating pathfind::paths and descendent classes.)
*
* @param map[in] The gamemap to use (for identifying terrain).
* @param units[in] Currently unused.
* @param u[in] The unit whose moves and movement type will be used.
* @param loc[in] The location at which to begin the routes.
* @param move_left[in] The number of movement points left for the current turn.
* @param destinations[out] The traversable routes.
* @param edges[out] The hexes (possibly off-map) adjacent to those in
* destinations. (It is permissible for this to contain
* some hexes that are also in destinations.)
* @param teams[in] The teams of the game (for recognizing enemies).
* @param force_ignore_zoc[in] Set to true to completely ignore zones of control.
* @param allow_teleport[in] Set to true to consider teleportation abilities.
* @param turns_left[in] The number of additional turns of movement to use,
* in addition to the current turn.
* @param viewing_team[in] Usually the current team, except for "show enemy
* moves", etc. Relevant if allowing teleports or
* if not ignoring units.
* @param see_all[in] Set to true to remove unit visibility from consideration.
* @param ignore_units[in] Set to true if units should never obstruct paths
* (implies ignoring ZoC as well).
*/
static void find_routes(const gamemap& map, const unit& u, const map_location& loc,
int move_left, pathfind::paths::dest_vect &destinations,
const team &current_team,
std::set<map_location> *edges, const team &current_team,
bool force_ignore_zocs, bool allow_teleport, int turns_left,
const team &viewing_team,
bool see_all, bool ignore_units)
{
const map_location loc = u.get_location();
pathfind::teleport_map teleports;
if (allow_teleport) {
teleports = pathfind::get_teleport_locations(u, viewing_team, see_all, ignore_units);
@ -219,7 +243,11 @@ static void find_routes(const gamemap& map, const unit& u,
std::copy(allowed_teleports.begin(), allowed_teleports.end(), locs.begin() + 6);
get_adjacent_tiles(n.curr, &locs[0]);
for (int i = locs.size(); i-- > 0; ) {
if (!locs[i].valid(map.w(), map.h())) continue;
if (!locs[i].valid(map.w(), map.h())) {
if ( edges != NULL )
edges->insert(locs[i]);
continue;
}
if (locs[i] == n.curr) continue;
@ -245,15 +273,22 @@ static void find_routes(const gamemap& map, const unit& u,
t.turns_left--;
}
if (t.movement_left < move_cost || t.turns_left < 0) continue;
if (t.movement_left < move_cost || t.turns_left < 0) {
if ( edges != NULL )
edges->insert(t.curr);
continue;
}
t.movement_left -= move_cost;
if (!ignore_units) {
const unit *v =
get_visible_unit(locs[i], viewing_team, see_all);
if (v && current_team.is_enemy(v->side()))
if (v && current_team.is_enemy(v->side())) {
if ( edges != NULL )
edges->insert(t.curr);
continue;
}
if (!force_ignore_zocs && t.movement_left > 0
&& pathfind::enemy_zoc(current_team, locs[i], viewing_team, see_all)
@ -353,6 +388,22 @@ bool pathfind::paths::dest_vect::contains(const map_location &loc) const
return find(loc) != end();
}
/**
* Construct a list of paths for the specified unit.
*
* This function is used for several purposes, including showing a unit's
* potential moves and generating currently possible paths.
* @param map The gamemap to use (for identifying terrain).
* @param units The unit_map to use (for identifying units).
* @param u The unit whose moves and movement type will be used.
* @param teams The teams of the game (for recognizing enemies).
* @param force_ignore_zoc Set to true to completely ignore zones of control.
* @param allow_teleport Set to true to consider teleportation abilities.
* @param viewing_team Usually the current team, except for "show enemy moves", etc.
* @param additional_turns The number of turns to account for, in addition to the current.
* @param see_all Set to true to remove unit visibility from consideration.
* @param ignore_units Set to true if units should never obstruct paths (implies ignoring ZoC as well).
*/
pathfind::paths::paths(gamemap const &map, unit_map const &/*units*/,
const unit& u, std::vector<team> const &teams,
bool force_ignore_zoc, bool allow_teleport, const team &viewing_team,
@ -363,9 +414,52 @@ pathfind::paths::paths(gamemap const &map, unit_map const &/*units*/,
return;
}
find_routes(map, u, u.movement_left(), destinations, teams[u.side()-1],
force_ignore_zoc, allow_teleport, additional_turns,
viewing_team, see_all, ignore_units);
find_routes(map, u, u.get_location(), u.movement_left(), destinations, NULL,
teams[u.side()-1], force_ignore_zoc, allow_teleport,
additional_turns, viewing_team, see_all, ignore_units);
}
/**
* Virtual destructor to support child classes.
*/
pathfind::paths::~paths()
{
}
/**
* Constructs a list of vision paths for a unit.
*
* This is used to construct a list of hexes that the indicated unit can see.
* It differs from pathfinding in that it will only ever go out one turn,
* and that it will also collect a set of border hexes (the "one hex beyond"
* movement to which vision extends).
* @param map The gamemap to use (for identifying terrain).
* @param viewer The unit doing the viewing.
* @param loc The location from which the viewing occurs
* (does not have to be the unit's location).
* @param full_move Usually this will be true, but if false, the unit's current
* movement will be used instead of its maximum moves.
*/
pathfind::vision_path::vision_path(gamemap const &map, const unit& viewer,
map_location const &loc, bool full_move)
: paths(), edges()
{
const team & viewer_team = (*resources::teams)[viewer.side()-1];
const int sight_range = full_move ? viewer.total_movement() :
viewer.movement_left();
// Finding routes: ignore ZoC, disallow teleports, zero turns left,
// (viewing team), see all, and ignore units.
// (The "see all" setting does not currently matter since teleports are
// not allowed and units are ignored. If something changes to make it
// significant, I might have incorrectly guessed the appropriate value.)
find_routes(map, viewer, loc, sight_range, destinations, &edges,
viewer_team, true, false, 0, viewer_team, true, true);
}
/// Default destructor
pathfind::vision_path::~vision_path()
{
}
pathfind::marked_route pathfind::mark_route(const plain_route &rt)

View file

@ -76,20 +76,15 @@ struct paths
{
}
// Construct a list of paths for the unit at loc.
// - force_ignore_zocs: find the path ignoring ZOC entirely,
// if false, will use the unit on the loc's ability
// - allow_teleport: indicates whether the paths should include teleportation (false for sight)
// - additional_turns: if 0, paths for how far the unit can move this turn will be calculated.
// If 1, paths for how far the unit can move by the end of next turn
// will be calculated, and so forth.
// viewing_team is usually current team, except for Show Enemy Moves etc.
/// Construct a list of paths for the specified unit.
paths(gamemap const &map,
unit_map const &/*units*/, // Not used
const unit& u, std::vector<team> const &teams,
bool force_ignore_zocs, bool allow_teleport,
const team &viewing_team, int additional_turns = 0,
bool see_all = false, bool ignore_units = false);
const team &viewing_team, int additional_turns = 0,
bool see_all = false, bool ignore_units = false);
/// Virtual destructor (default processing).
virtual ~paths();
struct step
{
@ -108,6 +103,20 @@ struct paths
dest_vect destinations;
};
/**
* A refinement of paths for use when calculating vision.
*/
struct vision_path : public paths
{
/// Construct a list of seen hexes for a unit.
vision_path(gamemap const &map, const unit& viewer,
map_location const &loc, bool full_move=true);
virtual ~vision_path();
/// The edges are the non-destination hexes bordering the destinations.
std::set<map_location> edges;
};
/** Structure which holds a single route between one location and another. */
struct plain_route
{