point on the target level that is connected to at least one stone stair.
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@1893 c06c8d41-db1a-0410-9941-cceddc491573
2WRXQTGYDBLV46WRNVIUKGNA5QS563XZNNW3N2L6PVOCHIP2YGHQC
FMKQARDH6JZ7DUTPPQ6I3LLNQAFGXS36XJCSROUJMEIYQYJDVITQC
F5PJ6H7W2SS5LTUJ7N6JW4SSWTGTDJRXX7L4JBM2NSD2QC26FFLQC
2YSMM7QMFZOPD5NXAD2OAMDJEY5LOZO4NCYBC7UCQVANKINJRNBAC
SDLKLUNFGVKDS55DDJZCBAVIB7NL3RRYPTACAY65SCUQKV6APFSAC
RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC
OYTCBRC7LE44EUVRZVYTOOVKQWJ6P6YE3FXTOGUTNKEMLNWPHKSQC
RC6L3CIBLJEH4GWRFD7UQNGI6PZT74FRUVOYHSAN2XCC74NZUASQC
ILOED4VB4I6VPAUTR75ZWX6MXDYXB5DO2EDK2UH67O3HNKWV23RQC
MSQI3TH6T62JAXQGLL52QZCWAMC372TGB6ZNNRDGUGMJKBNNV2VAC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
SW3RLYFNRT3IJBK6LYKHKP2J2YDU7SXQWAJZX7U6S7ICYW43OMNQC
W5VEC2PBIM5DMU5233HOWAZUEPTGWJRZZIA3H35YYQQW6BTP6XUAC
EOMCPVNQLX3IMLC46EAO67DPBH5KEG2FQTPBLGU62HIRWA3UQ7XQC
C22455VGUQOSUX2OORA32LROFQ7NNYDMD2ZDTTUZSAQLXK4AD6QAC
IVVTHLTTLOP5TSULXJWUSSXHOKYWVU3OWKYVK45A7RIB6V34MYQAC
// If the player has overridden deep water already, we'll respect that.
set_pass_feature(DNGN_DEEP_WATER, water);
// If the player has overridden deep water already, we'll respect that.
set_pass_feature(DNGN_DEEP_WATER, water);
// Permanently levitating players can cross most hostile terrain.
signed char trav = player_is_permalevitating()?
TRAVERSABLE : IMPASSABLE;
// Permanently levitating players can cross most hostile terrain.
signed char trav = player_is_permalevitating()?
TRAVERSABLE : IMPASSABLE;
if (water != TRAVERSABLE)
set_pass_feature(DNGN_DEEP_WATER, trav);
set_pass_feature(DNGN_LAVA, trav);
set_pass_feature(DNGN_TRAP_MECHANICAL, trav);
if (water != TRAVERSABLE)
set_pass_feature(DNGN_DEEP_WATER, trav);
set_pass_feature(DNGN_LAVA, trav);
set_pass_feature(DNGN_TRAP_MECHANICAL, trav);
}
else
{
set_pass_feature(DNGN_DEEP_WATER, IMPASSABLE);
set_pass_feature(DNGN_LAVA, IMPASSABLE);
set_pass_feature(DNGN_TRAP_MECHANICAL, IMPASSABLE);
}
}
bool grid_is_stone_stair(dungeon_feature_type grid)
{
switch (grid)
{
case DNGN_STONE_STAIRS_UP_I:
case DNGN_STONE_STAIRS_UP_II:
case DNGN_STONE_STAIRS_UP_III:
case DNGN_STONE_STAIRS_DOWN_I:
case DNGN_STONE_STAIRS_DOWN_II:
case DNGN_STONE_STAIRS_DOWN_III:
return (true);
default:
return (false);
}
static coord_def find_nearby_stair(int stair_to_find, bool find_closest)
{
// scan around the player's position first
int basex = you.x_pos;
int basey = you.y_pos;
// check for illegal starting point
if ( !in_bounds(basex, basey) )
{
basex = 0;
basey = 0;
}
coord_def result;
int found = 0;
int best_dist = 1 + GXM*GXM + GYM*GYM;
// XXX These passes should be rewritten to use an iterator of STL
// algorithm of some kind.
// First pass: look for an exact match
for (int xcode = 0; xcode < GXM; ++xcode )
{
const int xsign = ((xcode % 2) ? 1 : -1);
const int xdiff = xsign * (xcode + 1)/2;
const int xpos = (basex + xdiff + GXM) % GXM;
for (int ycode = 0; ycode < GYM; ++ycode)
{
const int ysign = ((ycode % 2) ? 1 : -1);
const int ydiff = ysign * (ycode + 1)/2;
const int ypos = (basey + ydiff + GYM) % GYM;
// note that due to the wrapping above, we can't just use
// xdiff*xdiff + ydiff*ydiff
const int dist = (xpos-basex)*(xpos-basex) +
(ypos-basey)*(ypos-basey);
if (grd[xpos][ypos] == stair_to_find)
{
found++;
if (find_closest)
{
if (dist < best_dist)
{
best_dist = dist;
result.x = xpos;
result.y = ypos;
}
}
else if (one_chance_in( found ))
{
result.x = xpos;
result.y = ypos;
}
}
}
}
if ( found )
return result;
best_dist = 1 + GXM*GXM + GYM*GYM;
// Second pass: find a staircase in the proper direction
for (int xcode = 0; xcode < GXM; ++xcode )
{
const int xsign = ((xcode % 2) ? 1 : -1);
const int xdiff = xsign * (xcode + 1)/2;
const int xpos = (basex + xdiff + GXM) % GXM;
for (int ycode = 0; ycode < GYM; ++ycode)
{
const int ysign = ((ycode % 2) ? 1 : -1);
const int ydiff = ysign * (ycode + 1)/2;
const int ypos = (basey + ydiff + GYM) % GYM;
bool good_stair;
const int looking_at = grd[xpos][ypos];
if (stair_to_find <= DNGN_ROCK_STAIRS_DOWN )
good_stair =
(looking_at >= DNGN_STONE_STAIRS_DOWN_I) &&
(looking_at <= DNGN_ROCK_STAIRS_DOWN);
else
good_stair =
(looking_at >= DNGN_STONE_STAIRS_UP_I) &&
(looking_at <= DNGN_ROCK_STAIRS_UP);
const int dist = (xpos-basex)*(xpos-basex) +
(ypos-basey)*(ypos-basey);
if ( good_stair )
{
found++;
if (find_closest && dist < best_dist)
{
best_dist = dist;
result.x = xpos;
result.y = ypos;
}
else if (one_chance_in( found ))
{
result.x = xpos;
result.y = ypos;
}
}
}
}
if ( found )
return result;
// Third pass: look for any clear terrain (shouldn't happen).
// We abandon the idea of looking nearby now.
for (int xpos = 0; xpos < GXM; xpos++)
{
for (int ypos = 0; ypos < GYM; ypos++)
{
if (grd[xpos][ypos] >= DNGN_FLOOR)
{
found++;
if (one_chance_in( found ))
{
result.x = xpos;
result.y = ypos;
}
}
}
}
ASSERT( found );
return result;
}
static void fixup_pandemonium_stairs()
{
for (int i = 0; i < GXM; i++)
{
for (int j = 0; j < GYM; j++)
{
if (grd[i][j] >= DNGN_STONE_STAIRS_UP_I
&& grd[i][j] <= DNGN_ROCK_STAIRS_UP)
{
if (one_chance_in( you.mutation[MUT_PANDEMONIUM] ? 5 : 50 ))
grd[i][j] = DNGN_EXIT_PANDEMONIUM;
else
grd[i][j] = DNGN_FLOOR;
}
if (grd[i][j] >= DNGN_ENTER_LABYRINTH
&& grd[i][j] <= DNGN_ROCK_STAIRS_DOWN)
{
grd[i][j] = DNGN_TRANSIT_PANDEMONIUM;
}
}
}
}
return (grid == DNGN_ROCK_WALL
|| (grid_is_solid(grid) && grid != DNGN_CLOSED_DOOR
&& grid != DNGN_SECRET_DOOR));
return !(grid == DNGN_ROCK_WALL
|| (grid_is_solid(grid) && grid != DNGN_CLOSED_DOOR
&& grid != DNGN_SECRET_DOOR));
}
static inline void dgn_point_record_stub(const coord_def &) { }
template <class point_record>
static void dgn_fill_zone(
const coord_def &c, int zone, point_record &prec,
bool (*passable)(dungeon_feature_type) = dgn_grid_is_passable)
{
// No bounds checks, assuming the level has at least one layer of
// rock border.
travel_point_distance[c.x][c.y] = zone;
for (int yi = -1; yi <= 1; ++yi)
{
for (int xi = -1; xi <= 1; ++xi)
{
if (!xi && !yi)
continue;
const coord_def cp(c.x + xi, c.y + yi);
if (travel_point_distance[cp.x][cp.y]
|| dgn_map_mask(cp)
|| !passable(grd(cp)))
{
continue;
}
prec(cp);
dgn_fill_zone(cp, zone, prec);
}
}
struct nearest_point
{
coord_def target;
coord_def nearest;
int distance;
nearest_point(const coord_def &t) : target(t), nearest(), distance(-1)
{
}
void operator () (const coord_def &c)
{
if (grd(c) == DNGN_FLOOR)
{
const int ndist = (c - target).abs();
if (distance == -1 || ndist < distance)
{
distance = ndist;
nearest = c;
}
}
}
};
// Fill travel_point_distance out from all stone stairs on the level.
static coord_def dgn_find_closest_to_stone_stairs()
{
memset(travel_point_distance, 0, sizeof(travel_distance_grid_t));
init_travel_terrain_check(false);
nearest_point np(you.pos());
for (int y = 0; y < GYM; ++y)
for (int x = 0; x < GXM; ++x)
if (!travel_point_distance[x][y] && grid_is_stone_stair(grd[x][y]))
dgn_fill_zone(coord_def(x, y), 1, np, is_traversable);
return (np.nearest);
}
coord_def dgn_find_nearby_stair(int stair_to_find, bool find_closest)
{
if (stair_to_find == DNGN_ROCK_STAIRS_UP
|| stair_to_find == DNGN_ROCK_STAIRS_DOWN)
{
const coord_def pos(dgn_find_closest_to_stone_stairs());
if (in_bounds(pos))
return (pos);
}
// scan around the player's position first
int basex = you.x_pos;
int basey = you.y_pos;
// check for illegal starting point
if ( !in_bounds(basex, basey) )
{
basex = 0;
basey = 0;
}
coord_def result;
int found = 0;
int best_dist = 1 + GXM*GXM + GYM*GYM;
// XXX These passes should be rewritten to use an iterator of STL
// algorithm of some kind.
// First pass: look for an exact match
for (int xcode = 0; xcode < GXM; ++xcode )
{
const int xsign = ((xcode % 2) ? 1 : -1);
const int xdiff = xsign * (xcode + 1)/2;
const int xpos = (basex + xdiff + GXM) % GXM;
for (int ycode = 0; ycode < GYM; ++ycode)
{
const int ysign = ((ycode % 2) ? 1 : -1);
const int ydiff = ysign * (ycode + 1)/2;
const int ypos = (basey + ydiff + GYM) % GYM;
// note that due to the wrapping above, we can't just use
// xdiff*xdiff + ydiff*ydiff
const int dist = (xpos-basex)*(xpos-basex) +
(ypos-basey)*(ypos-basey);
if (grd[xpos][ypos] == stair_to_find)
{
found++;
if (find_closest)
{
if (dist < best_dist)
{
best_dist = dist;
result.x = xpos;
result.y = ypos;
}
}
else if (one_chance_in( found ))
{
result.x = xpos;
result.y = ypos;
}
}
}
}
if ( found )
return result;
best_dist = 1 + GXM*GXM + GYM*GYM;
// Second pass: find a staircase in the proper direction
for (int xcode = 0; xcode < GXM; ++xcode )
{
const int xsign = ((xcode % 2) ? 1 : -1);
const int xdiff = xsign * (xcode + 1)/2;
const int xpos = (basex + xdiff + GXM) % GXM;
for (int ycode = 0; ycode < GYM; ++ycode)
{
const int ysign = ((ycode % 2) ? 1 : -1);
const int ydiff = ysign * (ycode + 1)/2;
const int ypos = (basey + ydiff + GYM) % GYM;
bool good_stair;
const int looking_at = grd[xpos][ypos];
if (stair_to_find <= DNGN_ROCK_STAIRS_DOWN )
good_stair =
(looking_at >= DNGN_STONE_STAIRS_DOWN_I) &&
(looking_at <= DNGN_ROCK_STAIRS_DOWN);
else
good_stair =
(looking_at >= DNGN_STONE_STAIRS_UP_I) &&
(looking_at <= DNGN_ROCK_STAIRS_UP);
const int dist = (xpos-basex)*(xpos-basex) +
(ypos-basey)*(ypos-basey);
if ( good_stair )
{
found++;
if (find_closest && dist < best_dist)
{
best_dist = dist;
result.x = xpos;
result.y = ypos;
}
else if (one_chance_in( found ))
{
result.x = xpos;
result.y = ypos;
}
}
}
}
if ( found )
return result;
// Third pass: look for any clear terrain and abandon the idea of
// looking nearby now. This is used when taking transit Pandemonium gates,
// or landing in Labyrinths. Never land the PC inside a Pan or Lab vault.
// We can't check vaults for other levels because vault information is
// not saved, and the player can re-enter other levels.
for (int xpos = 0; xpos < GXM; xpos++)
{
for (int ypos = 0; ypos < GYM; ypos++)
{
if (grd[xpos][ypos] >= DNGN_FLOOR
&& (you.level_type == LEVEL_DUNGEON
|| unforbidden(coord_def(xpos, ypos), MMT_VAULT)))
{
found++;
if (one_chance_in( found ))
{
result.x = xpos;
result.y = ypos;
}
}
}
}
ASSERT( found );
return result;
}