Removed DOS_TERM, PLAIN_TERM special casery - all platforms get PLAIN_TERM.
Better end-of-greedy-explore reporting for items on traps (Erik).
Cleaned up find_travel_pos - moved guts of travel pathfinding to travel_pathfind class.
Miscellaneous other stuff.
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@882 c06c8d41-db1a-0410-9941-cceddc491573
RC6L3CIBLJEH4GWRFD7UQNGI6PZT74FRUVOYHSAN2XCC74NZUASQC
CAGCTYIUYWDHQAJOLVLKOEV5HG6K5ZG7IDHONLIG6BDNCWZJAK4AC
PGTE3JC4J5U536IJTCJFXTUOSRE73JXZJINWAGCANOQOCGC7J6AAC
V6S33CAMTUXXDETG654VX2K4DA25DI6KFBTKPM2EGFDIBMAU4TJAC
LEF6VQNLRIJXXWQFODNMZZZFYJXHYAXD4O5UNBHBS4RHMAVF6IFQC
WREARQBLQW4ZLVXTPQTVPSXJVKPWQGE2JSIZYTJ5AJ55WREI72OAC
LXLUKS5CKXBUSVV3QTZ4SM7NWSY6JFQEBHUBQW2VUEU5DOL3RRLAC
4EWXDZSMYTEINQRUL4OHRUZVWMKCWEPYVJVGENQPBWUYU37SP63QC
GCIZIUXO5TYROKDUYB3HAY7H7MRDTJNM7HR7DGSH7KXDIZC2LCDAC
JM6GKZ6VMX6FNVOZIDXIV22HGX7YESMIFZFE6EEQVCMFJIEA3FNAC
RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
SDLKLUNFGVKDS55DDJZCBAVIB7NL3RRYPTACAY65SCUQKV6APFSAC
AJJ6D6JRV6ZAZOAUHYUM2IQG42V6PBALOD4KEMNKSVVAOJXAUCPQC
Y66ZAXN24E3HLIBOSW4OXUTQ4X4PRGNJII4KVDQH4GQJVA6GO3NAC
547JREUJXTZNYVGHNNAET5F5O5JYYGNTDQB6ABZNT7YX5EY64OHAC
IPXXB4VRVZWOU5DKQ5ZTD37LS3QNK2R6APNZUO672YEEJT6OFAYQC
3YK4G4IQBXW63HPGU5WRTV6L2FCMKAK4DOTCHFK2FNSB5B3Y3PVQC
TJISAZK5RWKXIIC5UTQNY4KT3UX3ASGBUQQNWZ7ZDULPRYFRZXQQC
Q2XXRX5KFUHUQC6VJ7BMLZ6FWEUTXLUUHCIC2GS5DRGZ4VAALUIAC
VIFRP3HZEONFR6PQRYZYM3YUEJOQ7T4F5CZY4MN4YJMB23FMU7XAC
MSQI3TH6T62JAXQGLL52QZCWAMC372TGB6ZNNRDGUGMJKBNNV2VAC
Z262AMUK447BTC7MVOPQQ3NZTQXQOE5JODNHXN3IU7WL4ABSW5YQC
5UVDIVD4NSXA52U4QMQIVST3GSZJ2A2YZK3RUEXKPM43YVQ7LI5AC
MWHMD65QP6UKXO6Q4ZVEAMXY563AJ6KH7J6UEZOB5CRPPSRB762QC
SH6NU4H5TG4O7CRXRHZ7MDHABZLWWXQ77NJDBHI7KCVHXHQYK46QC
5ASC3STDYCNLZFEBN6UTMUCGDETHBR2OCBZCF5VIAZ5RRWLOTDYQC
LAMIVDKY7LO5ONX5Z273ZCCEA5UBENOJD5VWNE4AK2EXGFED6BFQC
NXVPOFYKJFWQWKVPQUMWH2Y2KJEZX44BUOBFJ4JD4KFGPEGYHG4QC
43ZTEB57FU7KE5EVMYWZONNVJBZCGF3JEAJZIY25LC4LGE65PG5QC
BUUPPUTWFL2NTMFJWP7LKDXON74CX47E37WDAZ3QI4N55UOUQTXAC
22RFWMSJGG26Z2MQEEXGKVTFSTLREHQIG46WYOTMDRKI5YVMRNVAC
#if defined(DOS_TERM)
// DOS functions like gettext() and puttext() can only
// work with arrays of characters, not shorts.
typedef unsigned char screen_buffer_t;
#else
// If we've a waypoint on the current square, *and* the square is
// a normal floor square with nothing on it, show the waypoint
// number.
// If we've a waypoint on the current square, *and* the
// square is a normal floor square with nothing on it,
// show the waypoint number.
// Handles travel and explore floodfill pathfinding. Does not do interlevel
// travel pathfinding directly (but is used internally by interlevel travel).
// * All coordinates are grid coords.
// * Do not reuse one travel_pathfind for different runmodes.
class travel_pathfind
{
public:
travel_pathfind();
int level_distance(level_id first, level_id second);
// Finds travel direction or explore target.
const coord_def pathfind(run_mode_type rt);
// For flood-fills (explore), sets starting (seed) square.
void set_floodseed(const coord_def &seed, bool double_flood = false);
// For regular travel, set starting point (usually the character's current
// position) and destination.
void set_src_dst(const coord_def &src, const coord_def &dst);
// Request that the point distance array be annotated with magic numbers for
// excludes and waypoints.
void set_annotate_map(bool annotate);
// Sets the travel_distance_grid_t to use instead of travel_point_distance.
void set_distance_grid(travel_distance_grid_t distgrid);
// Set feature vector to use; if non-NULL, also sets annotate_map to true.
void set_feature_vector(std::vector<coord_def> *features);
// The next square to go to to move towards the travel destination. Return
// value is undefined if pathfind was not called with RMODE_TRAVEL.
const coord_def travel_move() const;
// Square to go to for (greedy) explore. Return value is undefined if
// pathfind was not called with RMODE_EXPLORE or RMODE_EXPLORE_GREEDY.
const coord_def explore_target() const;
// Nearest greed-inducing square. Return value is undefined if
// pathfind was not called with RMODE_EXPLORE_GREEDY.
const coord_def greedy_square() const;
// Nearest unexplored territory. Return value is undefined if
// pathfind was not called with RMODE_EXPLORE or
// RMODE_EXPLORE_GREEDY.
const coord_def unexplored_square() const;
private:
bool is_greed_inducing_square(const coord_def &c) const;
bool path_examine_point(const coord_def &c);
bool path_flood(const coord_def &c, const coord_def &dc);
bool square_slows_movement(const coord_def &c);
void check_square_greed(const coord_def &c);
bool can_travel_to(const level_id &lid);
bool can_travel_interlevel();
bool prompt_stop_explore(int es_why);
private:
static const int UNFOUND_DIST = -10000;
static const int INFINITE_DIST = 10000;
private:
run_mode_type runmode;
// Where pathfinding starts, and the destination. Note that dest is not
// relevant for explore!
coord_def start, dest;
// This is the square adjacent to the starting position to move
// along the shortest path to the destination. Does *not* apply
// for explore!
coord_def next_travel_move;
// True if flooding outwards from start square for explore.
bool floodout, double_flood;
// Set true in the second part of a double floodfill to completely ignore
// hostile squares.
bool ignore_hostile;
// If true, use magic numbers in point distance array which can be
// used to colour the level-map.
bool annotate_map;
// Stashes on this level (needed for greedy explore and to populate the
// feature vector with stashes on the X level-map).
const LevelStashes *ls;
// Are we greedy exploring?
bool need_for_greed;
// Targets for explore and greedy explore.
coord_def unexplored_place, greedy_place;
// How far from player's location unexplored_place and greedy_place are.
int unexplored_dist, greedy_dist;
const int *refdist;
// For double-floods, the points to restart floodfill from at the end of
// the first flood.
std::vector<coord_def> reseed_points;
std::vector<coord_def> *features;
travel_distance_col *point_distance;
// How many points are we currently considering? We start off with just one
// point, and spread outwards like a flood-filler.
int points;
// How many points we'll consider next iteration.
int next_iter_points;
// How far we've traveled from (start_x, start_y), in moves (a diagonal move
// is no longer than an orthogonal move).
int traveled_distance;
}
// Determines if the level is fully explored. Clobbers you.run_x/y.
static bool fully_explored()
{
const int oldrun = you.running;
if (!you.running.is_explore())
you.running = RMODE_EXPLORE;
// Do a second floodfill to check if the level is fully explored.
// Note we're passing in a features vector to force find_travel_pos to
// reseed past traps/deep water/lava. Icky.
std::vector<coord_def> features_dummy;
find_travel_pos(you.x_pos, you.y_pos, NULL, NULL, &features_dummy);
you.running = oldrun;
return !(you.running.x > 0 && you.running.y > 0);
extern short point_distance[GXM][GYM];
// We have to set the point_distance array if the level map is
// to be properly coloured. The caller isn't going to do it because
// we say this square is inaccessible, so in a horrible hack, we
// do it ourselves. Ecch.
point_distance[x][y] = ignore_hostile? -42 : 42;
return false;
return (false);
// Could not pick up interesting items because of hostile terrain. Note
// that this and EST_PARTLY_EXPLORED are not mutually exclusive.
EST_GREED_UNFULFILLED = 2
};
// Determines if the level is fully explored.
static int find_explore_status(const travel_pathfind &tp)
{
int explore_status = 0;
const coord_def greed = tp.greedy_square();
if (greed.x || greed.y)
explore_status |= EST_GREED_UNFULFILLED;
const coord_def unexplored = tp.unexplored_square();
if (unexplored.x || unexplored.y)
explore_status |= EST_PARTLY_EXPLORED;
return (explore_status);
}
static void explore_find_target_square()
{
travel_pathfind tp;
tp.set_floodseed(coord_def(you.x_pos, you.y_pos), true);
coord_def whereto =
tp.pathfind( static_cast<run_mode_type>(you.running.runmode) );
if (whereto.x || whereto.y)
{
// Make sure this is a square that is reachable, since we asked
// travel_pathfind to give us even unreachable squares.
if (travel_point_distance[whereto.x][whereto.y] <= 0)
whereto.reset();
}
if (whereto.x || whereto.y)
{
you.running.x = whereto.x;
you.running.y = whereto.y;
}
else
{
// No place to go? Report to the player.
const int estatus = find_explore_status(tp);
if (!estatus)
mpr("Done exploring.");
else
{
std::vector<std::string> inacc;
if (estatus & EST_GREED_UNFULFILLED)
inacc.push_back("items");
if (estatus & EST_PARTLY_EXPLORED)
inacc.push_back("places");
mprf("Partly explored, can't reach some %s.",
comma_separated_line(
inacc.begin(),
inacc.end()).c_str());
}
stop_running();
}
}
you.running.x = 0;
find_travel_pos(you.x_pos, you.y_pos, NULL, NULL);
// No place to go?
if (!you.running.x)
{
// Do fully_explored() *before* stop_running!
if (fully_explored())
mpr("Done exploring.");
else
mpr("Partly explored, some areas are inaccessible.");
stop_running();
}
explore_find_target_square();
}
/////////////////////////////////////////////////////////////////////////////
// travel_pathfind
FixedVector<coord_def, GXM * GYM> travel_pathfind::circumference[2];
const int travel_pathfind::UNFOUND_DIST;
const int travel_pathfind::INFINITE_DIST;
travel_pathfind::travel_pathfind()
: runmode(RMODE_NOT_RUNNING), start(), dest(), next_travel_move(),
floodout(false), double_flood(false), ignore_hostile(false),
annotate_map(false), ls(NULL), need_for_greed(false),
unexplored_place(), greedy_place(), unexplored_dist(0),
greedy_dist(0), refdist(NULL), reseed_points(),
features(NULL), point_distance(travel_point_distance),
points(0), next_iter_points(0), traveled_distance(0),
circ_index(0)
{
static bool is_greed_inducing_square(const LevelStashes *ls, int x, int y)
bool travel_pathfind::is_greed_inducing_square(const coord_def &c) const
{
return (ls && ls->needs_visit(c.x, c.y));
}
void travel_pathfind::set_src_dst(const coord_def &src, const coord_def &dst)
{
// Yes, this is backwards - for travel, we always start from the destination
// and search outwards for the starting position.
start = dst;
dest = src;
floodout = double_flood = false;
}
void travel_pathfind::set_floodseed(const coord_def &seed, bool dblflood)
{
start = seed;
dest.reset();
floodout = true;
double_flood = dblflood;
}
void travel_pathfind::set_annotate_map(bool annotate)
{
annotate_map = annotate;
}
void travel_pathfind::set_distance_grid(travel_distance_grid_t grid)
{
point_distance = grid;
}
void travel_pathfind::set_feature_vector(std::vector<coord_def> *feats)
{
features = feats;
if (features)
{
double_flood = true;
annotate_map = true;
}
}
const coord_def travel_pathfind::travel_move() const
{
return (next_travel_move);
}
const coord_def travel_pathfind::explore_target() const
{
if (unexplored_dist != UNFOUND_DIST && greedy_dist != UNFOUND_DIST)
return (unexplored_dist < greedy_dist? unexplored_place : greedy_place);
else if (unexplored_dist != UNFOUND_DIST)
return (unexplored_place);
else if (greedy_dist != UNFOUND_DIST)
return (greedy_place);
return coord_def(0, 0);
}
const coord_def travel_pathfind::greedy_square() const
{
return (greedy_place);
}
const coord_def travel_pathfind::unexplored_square() const
init_terrain_check();
int start_x = you.running.x, start_y = you.running.y;
int dest_x = youx, dest_y = youy;
bool floodout = false;
unsigned char feature;
const LevelStashes *lev = stashes.find_current_level();
const bool need_for_greed =
you.running == RMODE_EXPLORE_GREEDY && can_autopickup();
if (rmode == RMODE_INTERLEVEL)
rmode = RMODE_TRAVEL;
runmode = rmode;
// For greedy explore, keep track of the closest unexplored
// territory and the closest greedy square.
int e_x = 0, e_y = 0; // Unexplored
int i_x = 0, i_y = 0; // Square with interesting item.
// Use these weird defaults to handle negative item greeds.
int ex_dist = -10000, ix_dist = -10000;
// Check whether species or levitation permits travel through terrain such
// as deep water.
init_terrain_check();
// Normally we start from the destination and floodfill outwards, looking
// for the character's current position. If we're merely trying to populate
// the point_distance array (or exploring), we'll want to start from the
// character's current position and fill outwards
if (!move_x || !move_y)
{
start_x = youx;
start_y = youy;
need_for_greed =
(rmode == RMODE_EXPLORE_GREEDY && can_autopickup());
floodout = true;
}
next_travel_move.reset();
// For greedy explore, keep track of the closest unexplored territory and
// the closest greedy square. Exploring to the nearest (unexplored / greedy)
// square is easier, but it produces unintuitive explore behaviour where
// grabbing items is not favoured over simple exploring.
//
// Greedy explore instead uses the explore_item_greed option to weight
// greedy explore towards grabbing items over exploring. An
// explore_item_greed set to 10, for instance, forces explore to prefer
// items that are less than 10 squares farther away from the player than the
// nearest unmapped square. Negative explore_item_greed values force greedy
// explore to favour unexplored territory over picking up items. For the
// most natural greedy explore behaviour, explore_item_greed should be set
// to 10 or more.
//
unexplored_place = greedy_place = coord_def(0, 0);
unexplored_dist = greedy_dist = UNFOUND_DIST;
// Abort run if we're trying to go someplace evil
if (dest_x != -1 && !is_travel_ok(start_x, start_y, false) &&
!is_trap(start_x, start_y))
refdist = Options.explore_item_greed > 0? &unexplored_dist: &greedy_dist;
// Abort run if we're trying to go someplace evil. Travel to traps is
// specifically allowed here if the player insists on it.
if (!floodout
&& !is_travel_ok(start.x, start.y, false)
&& !is_trap(start.x, start.y)) // The player likes pain
// Abort run if we're going nowhere.
if (start_x == dest_x && start_y == dest_y)
{
you.running = RMODE_NOT_RUNNING;
return ;
}
// Nothing to do?
if (!floodout && start == dest)
return (start);
// The circumference points of the floodfilled area, for this iteration
// and the next (this iteration's points being circ_index amd the next one's
// being !circ_index).
static FixedVector<coord_def, GXM * GYM> circumference[2];
// Coordinates of all discovered traps. If we're exploring instead of
// travelling, we'll reseed from these points after we've explored the map
std::vector<coord_def> trap_seeds;
// When set to true, the travel code ignores features, traps and hostile
// terrain, and simply tries to map contiguous floorspace. Will only be set
// to true if we're exploring, instead of travelling.
bool ignore_hostile = false;
int x = circumference[circ_index][i].x,
y = circumference[circ_index][i].y;
// (x,y) is a known (explored) location - we never put unknown
// points in the circumference vector, so we don't need to examine
// the map array, just the grid array.
feature = grd[x][y];
// If this is a feature that'll take time to travel past, we
// simulate that extra turn by taking this feature next turn,
// thereby artificially increasing traveled_distance.
//
// Note: I don't know how slow walking through shallow water and
// opening closed doors is - right now it's considered to have
// the cost of two normal moves.
int feat_cost = feature_traverse_cost(feature);
if (feat_cost > 1
&& point_distance[x][y] > traveled_distance - feat_cost)
if (path_examine_point(circumference[circ_index][i]))
// For each point, we look at all surrounding points. Take them
// orthogonals first so that the travel path doesn't zigzag all over
// the map. Note the (dir = 1) is intentional assignment.
for (int dir = 0; dir < 8; (dir += 2) == 8 && (dir = 1))
if (next_iter_points == 0)
{
// Don't reseed unless we've found no target for explore, OR
// we're doing map annotation or feature tracking.
if ((runmode == RMODE_EXPLORE || runmode == RMODE_EXPLORE_GREEDY)
&& double_flood
&& !ignore_hostile
&& !features
&& !annotate_map
&& (unexplored_dist != UNFOUND_DIST
|| greedy_dist != UNFOUND_DIST))
if (floodout && you.running.is_explore())
{
if (!is_player_mapped(envf))
{
if (!need_for_greed)
{
you.running.x = x;
you.running.y = y;
return;
}
if (double_flood
&& !ignore_hostile
&& !reseed_points.empty())
{
// Reseed here
for (unsigned i = 0, size = reseed_points.size(); i < size; ++i)
circumference[!circ_index][i] = reseed_points[i];
next_iter_points = reseed_points.size();
ignore_hostile = true;
}
}
} // for ( ; points > 0 ...
if (ex_dist == -10000)
{
e_x = x;
e_y = y;
ex_dist =
traveled_distance + Options.explore_item_greed;
}
}
else if (need_for_greed
&& ix_dist == -10000
&& is_greed_inducing_square(lev, dx, dy)
&& is_travel_ok(dx, dy, ignore_hostile))
{
i_x = dx;
i_y = dy;
ix_dist = traveled_distance + 1;
}
if (features && floodout)
{
for (int i = 0, size = curr_excludes.size(); i < size; ++i)
{
const coord_def &exc = curr_excludes[i];
// An exclude - wherever it is - is always a feature.
if (std::find(features->begin(), features->end(), exc)
== features->end())
features->push_back(exc);
// Short-circuit if we can.
if (need_for_greed)
{
const int refdist =
Options.explore_item_greed > 0? ex_dist: ix_dist;
if (refdist != -10000
&& traveled_distance > refdist)
{
if (Options.explore_item_greed > 0)
ix_dist = 10000;
else
ex_dist = 10000;
}
}
// ex_dist/ix_dist are only ever set in
// greedy-explore so this check implies
// greedy-explore.
if (ex_dist != -10000 && ix_dist != -10000)
{
if (ex_dist < ix_dist)
{
you.running.x = e_x;
you.running.y = e_y;
}
else
{
you.running.x = i_x;
you.running.y = i_y;
}
return;
}
}
fill_exclude_radius(exc);
}
}
if ((dx != dest_x || dy != dest_y)
&& !is_travel_ok(dx, dy, ignore_hostile))
{
// This point is not okay to travel on, but if this is a
// trap, we'll want to put it on the feature vector anyway.
if (is_reseedable(dx, dy)
&& !point_distance[dx][dy]
&& (dx != start_x || dy != start_y))
{
if (features)
{
const coord_def c(dx, dy);
if (is_trap(dx, dy) || is_exclude_root(dx, dy))
features->push_back(c);
trap_seeds.push_back(c);
}
return (rmode == RMODE_TRAVEL? travel_move()
: explore_target());
}
// Appropriate mystic number. Nobody else should check
// this number, since this square is unsafe for travel.
point_distance[dx][dy] =
is_exclude_root(dx, dy)? PD_EXCLUDED :
is_excluded(dx, dy) ? PD_EXCLUDED_RADIUS :
PD_TRAP;
}
continue;
}
bool travel_pathfind::square_slows_movement(const coord_def &c)
{
// c is a known (explored) location - we never put unknown points in the
// circumference vector, so we don't need to examine the map array, just the
// grid array.
const int feature = grd(c);
if (dx == dest_x && dy == dest_y)
{
// Hallelujah, we're home!
if (is_safe_move(x, y) && move_x && move_y)
{
*move_x = sgn(x - dest_x);
*move_y = sgn(y - dest_y);
}
return ;
}
else if (!point_distance[dx][dy])
{
// This point is going to be on the agenda for the next
// iteration
circumference[!circ_index][next_iter_points].x = dx;
circumference[!circ_index][next_iter_points].y = dy;
next_iter_points++;
// If this is a feature that'll take time to travel past, we simulate that
// extra turn by taking this feature next turn, thereby artificially
// increasing traveled_distance.
//
// Walking through shallow water and opening closed doors is considered to
// have the cost of two normal moves for travel purposes.
const int feat_cost = feature_traverse_cost(feature);
if (feat_cost > 1
&& point_distance[c.x][c.y] > traveled_distance - feat_cost)
{
circumference[!circ_index][next_iter_points++] = c;
return (true);
}
// Negative distances here so that show_map can colour
// the map differently for these squares.
if (ignore_hostile)
{
point_distance[dx][dy] = -point_distance[dx][dy];
if (is_exclude_root(dx, dy))
point_distance[dx][dy] = PD_EXCLUDED;
else if (is_excluded(dx, dy))
point_distance[dx][dy] = PD_EXCLUDED_RADIUS;
}
void travel_pathfind::check_square_greed(const coord_def &c)
{
if (greedy_dist == UNFOUND_DIST
&& is_greed_inducing_square(c)
&& is_travel_ok(c.x, c.y, ignore_hostile))
{
greedy_place = c;
greedy_dist = traveled_distance;
}
}
feature = grd[dx][dy];
if (features && !ignore_hostile
&& ((feature != DNGN_FLOOR
&& feature != DNGN_SHALLOW_WATER
&& feature != DNGN_DEEP_WATER
&& feature != DNGN_LAVA)
|| is_waypoint(dx, dy)
|| is_stash(lev, dx, dy))
&& (dx != start_x || dy != start_y))
{
const coord_def c(dx, dy);
features->push_back(c);
}
bool travel_pathfind::path_flood(const coord_def &c, const coord_def &dc)
{
if (!in_bounds(dc))
return (false);
if (features && is_exclude_root(dx, dy) && dx != start_x
&& dy != start_y)
{
const coord_def c(dx, dy);
features->push_back(c);
}
}
} // for (dir = 0; dir < 8 ...
} // for (i = 0; i < points ...
if (floodout
&& (runmode == RMODE_EXPLORE || runmode == RMODE_EXPLORE_GREEDY))
{
if (!is_terrain_seen(dc))
{
if (!need_for_greed)
{
// Found explore target!
unexplored_place = c;
unexplored_dist = traveled_distance;
return (true);
}
if (unexplored_dist == UNFOUND_DIST)
{
unexplored_place = c;
unexplored_dist =
traveled_distance + Options.explore_item_greed;
}
}
if (!next_iter_points && features && !move_x && !ignore_hostile
&& trap_seeds.size())
// Short-circuit if we can. If traveled_distance (the current
// distance from the center of the floodfill) is greater
// than the adjusted distance to the nearest greedy explore
// target, we have a target. Note the adjusted distance is
// the distance with explore_item_greed applied (if
// explore_item_greed > 0, it is added to the distance to
// unexplored terrain, if explore_item_greed < 0, it is
// added to the distance to interesting items.
//
// We never short-circuit if ignore_hostile is true. This is
// important so we don't need to do multiple floods to work out
// whether explore is complete.
if (need_for_greed
&& !ignore_hostile
&& *refdist != UNFOUND_DIST
&& traveled_distance > *refdist)
// Reseed here
for (unsigned i = 0; i < trap_seeds.size(); ++i)
circumference[!circ_index][i] = trap_seeds[i];
next_iter_points = trap_seeds.size();
ignore_hostile = true;
if (Options.explore_item_greed > 0)
greedy_dist = INFINITE_DIST;
else
unexplored_dist = INFINITE_DIST;
for (int i = 0, size = curr_excludes.size(); i < size; ++i)
// This point is not okay to travel on, but if this is a
// trap, we'll want to put it on the feature vector anyway.
if (is_reseedable(dc.x, dc.y)
&& !point_distance[dc.x][dc.y]
&& dc != start)
const coord_def &exc = curr_excludes[i];
// An exclude - wherever it is - is always a feature.
if (std::find(features->begin(), features->end(), exc)
== features->end())
features->push_back(exc);
if (features &&
(is_trap(dc.x, dc.y) || is_exclude_root(dc.x, dc.y)))
{
features->push_back(dc);
}
fill_exclude_radius(exc);
if (double_flood)
reseed_points.push_back(dc);
// Appropriate mystic number. Nobody else should check
// this number, since this square is unsafe for travel.
point_distance[dc.x][dc.y] =
is_exclude_root(dc.x, dc.y)? PD_EXCLUDED :
is_excluded(dc.x, dc.y) ? PD_EXCLUDED_RADIUS :
PD_TRAP;
if (ix_dist != -10000)
// This point is going to be on the agenda for the next
// iteration
circumference[!circ_index][next_iter_points++] = dc;
point_distance[dc.x][dc.y] = traveled_distance;
// Negative distances here so that show_map can colour
// the map differently for these squares.
if (ignore_hostile)
you.running.x = e_x;
you.running.y = e_y;
if (features && !ignore_hostile)
{
const int feature = grd(dc);
if (((feature != DNGN_FLOOR
&& feature != DNGN_SHALLOW_WATER
&& feature != DNGN_DEEP_WATER
&& feature != DNGN_LAVA)
|| is_waypoint(dc.x, dc.y)
|| is_stash(ls, dc.x, dc.y))
&& dc != start)
{
features->push_back(dc);
}
}
if (features && dc != start && is_exclude_root(dc.x, dc.y))
features->push_back(dc);
}
return (false);
}
bool travel_pathfind::path_examine_point(const coord_def &c)
{
if (square_slows_movement(c))
return (false);
// Greedy explore check should happen on (x,y), not (dx,dy) as for
// regular explore.
if (need_for_greed)
check_square_greed(c);
// For each point, we look at all surrounding points. Take them orthogonals
// first so that the travel path doesn't zigzag all over the map. Note the
// (dir = 1) is intentional assignment.
for (int dir = 0; dir < 8; (dir += 2) == 8 && (dir = 1))
{
if (path_flood(c, c + Compass[dir]))
return (true);
}
return (false);
}
/////////////////////////////////////////////////////////////////////////////
void find_travel_pos(int youx, int youy,
char *move_x, char *move_y,
std::vector<coord_def>* features)
{
travel_pathfind tp;
if (move_x && move_y)
tp.set_src_dst(coord_def(youx, youy),
coord_def(you.running.x, you.running.y));
else
tp.set_floodseed(coord_def(youx, youy));
tp.set_feature_vector(features);
run_mode_type rmode = move_x && move_y? RMODE_TRAVEL : RMODE_NOT_RUNNING;
const coord_def dest = tp.pathfind( rmode );
if (dest.x == 0 && dest.y == 0)
{
if (move_x && move_y)
you.running = RMODE_NOT_RUNNING;
* This function relies on the point_distance array being correctly populated
* with a floodout call to find_travel_pos starting from the player's location.
* This function relies on the travel_point_distance array being correctly
* populated with a floodout call to find_travel_pos starting from the player's
* location.
#ifdef USE_SYSTEM_RAND
cf_setseed();
#endif
}
#ifdef USE_SYSTEM_RAND
int random2(int max)
{
#ifdef USE_NEW_RANDOM
//return (int) ((((float) max) * rand()) / RAND_MAX); - this is bad!
// Uses FP, so is horribly slow on computers without coprocessors.
// Taken from comp.lang.c FAQ. May have problems as max approaches
// RAND_MAX, but this is rather unlikely.
// We've used rand() rather than random() for the portability, I think.
if (max < 1 || max >= RAND_MAX)
return 0;
else
return (int) rand() / (RAND_MAX / max + 1);
#else
if (max < 1)
return 0;
return rand() % max;
#endif
// I got to thinking a bit more about how much people talk
// about RNGs and RLs and also about the issue of performance
// when it comes to Crawl's RNG ... turning to *Numerical
// Recipies in C* (Chapter 7-4, page 298), I hit upon what
// struck me as a fine solution.
// You can read all the details about this function (pretty
// much stolen shamelessly from NRinC) elsewhere, but having
// tested it out myself I think it satisfies Crawl's incessant
// need to decide things on a 50-50 flip of the coin. No call
// to random2() required -- along with all that wonderful math
// and type casting -- and only a single variable its pointer,
// and some bitwise operations to randomly generate 1s and 0s!
// No parameter passing, nothing. Too good to be true, but it
// works as long as cfseed is not set to absolute zero when it
// is initialized ... good for 2**n-1 random bits before the
// pattern repeats (n = long's bitlength on your platform).
// It also avoids problems with poor implementations of rand()
// on some platforms in regards to low-order bits ... a big
// problem if one is only looking for a 1 or a 0 with random2()!
// Talk about a hard sell! Anyway, it returns bool, so please
// use appropriately -- I set it to bool to prevent such
// tomfoolery, as I think that pure RNG and quickly grabbing
// either a value of 1 or 0 should be separated where possible
// to lower overhead in Crawl ... at least until it assembles
// itself into something a bit more orderly :P 16jan2000 {dlb}
// NB(1): cfseed is defined atop stuff.cc
// NB(2): IB(foo) and MASK are defined somewhere in defines.h
// NB(3): the function assumes that cf_setseed() has been called
// beforehand - the call is presently made in acr::initialise()
// right after srandom() and srand() are called (note also
// that cf_setseed() requires rand() - random2 returns int
// but a long can't hurt there).
bool coinflip(void)
{
extern unsigned long cfseed; // defined atop stuff.cc
unsigned long *ptr_cfseed = &cfseed;
if (*ptr_cfseed & IB18)
{
*ptr_cfseed = ((*ptr_cfseed ^ MASK) << 1) | IB1;
return true;
}
else
{
*ptr_cfseed <<= 1;
return false;
}
} // end coinflip()
// cf_setseed should only be called but once in all of Crawl!!! {dlb}
void cf_setseed(void)
{
extern unsigned long cfseed; // defined atop stuff.cc
unsigned long *ptr_cfseed = &cfseed;
do
{
// using rand() here makes these predictable -- bwr
*ptr_cfseed = rand();
}
while (*ptr_cfseed == 0);
}
static std::stack<long> rng_states;
void push_rng_state()
{
// XXX: Does this even work? randart.cc uses it, but I can't find anything
// that says this will restore the RNG to its original state. Anyway, we're
// now using MT with a deterministic push/pop.
rng_states.push(rand());
}
void pop_rng_state()
{
if (!rng_states.empty())
{
seed_rng(rng_states.top());
rng_states.pop();
}
}
unsigned long random_int( void )
{
return rand();
}
#else // USE_SYSTEM_RAND
}
// takes rectangle (x1,y1)-(x2,y2) and shifts it somewhere randomly in bounds
void random_place_rectangle( int &x1, int &y1, int &x2, int &y2, bool excl )
{
const unsigned int dx = abs( x2 - x1 );
const unsigned int dy = abs( y2 - y1 );
x1 = X_BOUND_1 + random2( X_WIDTH - dx - 2 * excl ) + excl;
y1 = Y_BOUND_1 + random2( Y_WIDTH - dy - 2 * excl ) + excl;
x2 = x1 + dx;
y2 = y1 + dy;
}
// returns true if point (px,py) is in rectangle (rx1, ry1) - (rx2, ry2)
bool in_rectangle( int px, int py, int rx1, int ry1, int rx2, int ry2,
bool excl )
{
ASSERT( rx1 < rx2 - 1 && ry1 < ry2 - 1 );
if (excl)
{
rx1++;
rx2--;
ry1++;
ry2--;
}
return (px >= rx1 && px <= rx2 && py >= ry1 && py <= ry2);
// XXX: this can be done better
// returns true if rectables a and b overlap
bool rectangles_overlap( int ax1, int ay1, int ax2, int ay2,
int bx1, int by1, int bx2, int by2,
bool excl )
{
ASSERT( ax1 < ax2 - 1 && ay1 < ay2 - 1 );
ASSERT( bx1 < bx2 - 1 && by1 < by2 - 1 );
if (excl)
{
ax1++;
ax2--;
ay1++;
ay2--;
}
return (in_rectangle( ax1, ay1, bx1, by1, bx2, by2, excl )
|| in_rectangle( ax1, ay2, bx1, by1, bx2, by2, excl )
|| in_rectangle( ax2, ay1, bx1, by1, bx2, by2, excl )
|| in_rectangle( ax2, ay2, bx1, by1, bx2, by2, excl ));
}
branches[BRANCH_ECUMENICAL_TEMPLE].startdepth = 3 + random2(4);
branches[BRANCH_ORCISH_MINES].startdepth = 5 + random2(6);
branches[BRANCH_ELVEN_HALLS].startdepth = coinflip() ? 4 : 3;
branches[BRANCH_LAIR].startdepth = 7 + random2(6);
branches[BRANCH_HIVE].startdepth = 10 + random2(6);
branches[BRANCH_SLIME_PITS].startdepth = 3 + random2(4);
branches[BRANCH_SWAMP].startdepth = 2 + random2(6);
branches[BRANCH_SNAKE_PIT].startdepth = coinflip() ? 7 : 6;
branches[BRANCH_VAULTS].startdepth = 13 + random2(6);
branches[BRANCH_CRYPT].startdepth = 2 + random2(3);
branches[BRANCH_HALL_OF_BLADES].startdepth = 4;
branches[BRANCH_TOMB].startdepth = coinflip() ? 3 : 2;
branches[BRANCH_ECUMENICAL_TEMPLE].startdepth = random_range(4, 7);
branches[BRANCH_ORCISH_MINES].startdepth = random_range(6, 11);
branches[BRANCH_ELVEN_HALLS].startdepth = random_range(3, 4);
branches[BRANCH_LAIR].startdepth = random_range(8, 13);
branches[BRANCH_HIVE].startdepth = random_range(11, 16);
branches[BRANCH_SLIME_PITS].startdepth = random_range(3, 9);
branches[BRANCH_SWAMP].startdepth = random_range(2, 7);
branches[BRANCH_SNAKE_PIT].startdepth = random_range(3, 8);
branches[BRANCH_VAULTS].startdepth = random_range(14, 19);
branches[BRANCH_CRYPT].startdepth = random_range(2, 4);
branches[BRANCH_HALL_OF_BLADES].startdepth = random_range(4, 6);
branches[BRANCH_TOMB].startdepth = random_range(2, 3);
New_Message_Count++;
// Prompt lines are presumably shown to / seen by the player accompanied
// by a request for input, which should do the equivalent of a more(); to
// save annoyance, don't bump New_Message_Count for prompts.
if (channel != MSGCH_PROMPT)
New_Message_Count++;
MCHMOD = 2755
INSTALLDIR := /usr/games
# If you have lex and yacc, set DOYACC to y (lowercase y).
DOYACC := y
# Permissions to set on the game executable.
MCHMOD := 2755
# Permissions to set on the save directory.
MCHMOD_SAVEDIR := 775
# The user:group to install the game as.
INSTALL_UGRP := games:games
INSTALLDIR := /usr/games
#endif
#else
#define getch_with_command_macros() getch()
#define getchm(x) getch()
#define flush_input_buffer(XXX) ;
#define macro_buf_add(x)
#define is_userfunction(x) false
#define get_userfunction(x) NULL
#define call_userfunction(x)