I've tweaked my monster pathfinding A* algorithm to only work for non-monsters, so I've been using that.
Because of the mapping, labyrinths are now actually easier to solve. (It's a bit hard to tell, though, since I got really good at solving labyrinths over the 40 something trial runs. :) ) At the same time they are now much more interesting and flavourful because of the map rot, and the shifting provides some wonderful excitement. So, whoever came up with that idea, thanks! :D
Still to do:
The numbers may need tweaking. Currently, they are: shift rate : 10% chance, every 20 turns map rot rate: 45% of the map, every 20 turns size of shifted area: 12x12 to 24x24 number of shifted walls per round: 12-45
Shifting will only ever take place between unexplored (or forgotten) grids. Thus, the chance of changes taking place is greater in areas away from the player.
I also increased the starting distance from the centre from 2020 to 2828.
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@7387 c06c8d41-db1a-0410-9941-cceddc491573
WP5VP57D5BWKDAS7AA224OV2RX4O4BPTI2BLY7TS3T2O2PLUGXCQC
7WMB2JHJFNJNPUEXGVMBUCZE7EP2OODESOC6E6K4N7Y4GTYVTODAC
PF6QKUU7AKP3X7NBR34O73PQ4BS2MWBMPD5MH5HOV2HLQFMA3PZQC
SXUKGEXMGRRN6UO2GJW4HSVU5TSUNUHIHBMMF7CAG7QMLKZYWTKQC
SIDH2P7NBIG5KEOE27XHD3ZT2NQ2OJZFN6VZXWNWYFFY5YVXSSVQC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
LH4OYDEWEON5QNUCM74R7MNBJDP7O4XRETY74ZMYSXUXYQV427PAC
EMOBSWJHHB4V6WVMZL7JCF2V3KZN454Z6NS346OKFPMBNO24EJDQC
Y4NA3JSN63RLATF4NNBPSR5CWF5Z7UEMWCGVX4B6NOAR47CGM4GQC
32PXX2XJVV7YSLLYNAVS7RYKYRAOQ565TZMTITSEPSSXOYPB5M2AC
NPTVMSNYWIALN2GSKESU6NKX7XNG7QDH2TNIWUB6R6NHCLK32NFAC
5FMXUX2ZFIF6NQZCS54W7ZOCVSH7XR6UIMQ5FW2UZLEN4EWP5PSAC
D7SLVLRNCYCBDYYRANHDG3JYEF25CFCSUY5FMF5KXVD5D4UZSDDAC
OONYLF4DAPLIYLBNNRW74IVT5BBTWI4XHQBXSNSPVRX3FTKJBTRAC
GPEJOT73KMACP33IPAKFR5ROGHCOIP22VXZMQNYTGLEA2OSZUM2AC
HIRKGUMNJPWKSVTR6TVBPD3MWNA63CEHCLCIPWEMGDFHVB3NPLDQC
RYCGST674RRH2TASRIXJSBRVAC4K2VLWICKLKIPJJRUQZMRYLGBQC
ED62QWGKBPORWVKDFOQRKJXEIWZVNGR3O4KWQBDSRNPT36AYOQYAC
SQDS2YBPOYDDDCW3GGARBZ2HQIUHCQKL7SSHKFQWDENOL5YNNVNQC
ZGUJWUFJ4NFFJ6PGXLFGQWCWBCZHPWGWI44NJHJEVPRG5L36PADQC
PJ7HBIWAV3H23LXGZAAD2QYJ7HMOFOIR5ZJ4U2UTHI766LOTRRWQC
B7MSPF6X2RLGWN4M6ZZF3WSOPKGYPTTD7LIJVST7DXN27DG6JHNAC
DMG73XDQHY2X2PHKWIY56XKD3O4NPGZKKIO6GX3IV2LLRVXPGKYQC
Y5RFQ6KNJCBQUSV2T6WDR7TPZLZYLOAWBVMUTHDXGOZQDZ2U423AC
BINKDWGFGUPTOA7IE5KK4ZIELGU5WC3X47MYXOWU4X43EGAC5DUAC
LTX72QGIPNUGWQN5ULPOMFCOPZTK7472DQY4AYX5WM3WHSUVXI5QC
FSD7GIK3YLZXWLEH37BU6KV3IUCFGXPQL6IZ7H65YWNRBEKDBX5AC
bool start_pathfind(monsters *mon, coord_def dest, bool msg = false);
bool init_pathfind(monsters *mon, coord_def dest,
bool diag = true, bool msg = false);
bool init_pathfind(coord_def src, coord_def dest,
bool diag = true, bool msg = false);
bool start_pathfind(bool msg = false);
return start_pathfind(msg);
}
bool monster_pathfind::init_pathfind(coord_def src, coord_def dest, bool diag,
bool msg)
{
start = src;
target = dest;
pos = start;
allow_diagonals = diag;
// Easy enough. :P
if (start == target)
return (true);
return start_pathfind(msg);
}
bool monster_pathfind::start_pathfind(bool msg)
{
}
}
// This function checks whether we can turn a wall into a floor space and
// still keep a corridor-like environment. The wall in position x is a
// a candidate for switching if it's flanked by floor grids to two sides
// and by walls (any type) to the remaining cardinal directions.
//
// . # 2
// #x# or .x. -> 0x1
// . # 3
static bool _grid_is_flanked_by_walls(const coord_def &p)
{
const coord_def adjs[] = { coord_def(p.x-1,p.y),
coord_def(p.x+1,p.y),
coord_def(p.x ,p.y-1),
coord_def(p.x ,p.y+1) };
// paranoia!
for (unsigned int i = 0; i < ARRAYSZ(adjs); ++i)
if (!in_bounds(adjs[i]))
return (false);
return (grid_is_wall(grd(adjs[0])) && grid_is_wall(grd(adjs[1]))
&& grd(adjs[2]) == DNGN_FLOOR && grd(adjs[3]) == DNGN_FLOOR
|| grd(adjs[0]) == DNGN_FLOOR && grd(adjs[1]) == DNGN_FLOOR
&& grid_is_wall(grd(adjs[2])) && grid_is_wall(grd(adjs[3])));
}
// Sometimes if a floor is turned into a wall, a dead-end will be created.
// If this is the case, we need to make sure that it is at least two grids
// deep.
//
// Example: If a wall is built at X (A), two deadends are created, a short and
// a long one. The latter is perfectly fine, but the former looks
// a bit odd. If Y is chosen, this looks much better (B).
//
// ####### (A) ####### (B) #######
// ...XY.. ...#... ....#..
// #.##### #.##### #.#####
//
// What this function does is check whether the neighbouring floor grids
// are flanked by walls on both sides, and if so, the grids following that
// also have to be floor flanked by walls.
//
// c.d
// a.b -> if (a, b == walls) then (c, d == walls) or return (false)
// #X#
// .
static bool _deadend_check(const coord_def &p)
{
if (grid_is_wall(grd[p.x-1][p.y]))
{
for (int i = -1; i <= 1; i++)
{
if (i == 0)
continue;
coord_def a(p.x-1, p.y+i);
coord_def b(p.x+1, p.y+i);
coord_def c(p.x-1, p.y+2*i);
coord_def d(p.x+1, p.y+2*i);
if (in_bounds(a) && in_bounds(b)
&& grid_is_wall(grd(a)) && grid_is_wall(grd(b))
&& (!in_bounds(c) || !in_bounds(d)
|| !grid_is_wall(grd(c)) || !grid_is_wall(grd(d))))
{
return (false);
}
}
}
else
{
for (int i = -1; i <= 1; i++)
{
if (i == 0)
continue;
coord_def a(p.x+i , p.y-1);
coord_def b(p.x+i , p.y+1);
coord_def c(p.x+2*i, p.y-1);
coord_def d(p.x+2*i, p.y+1);
if (in_bounds(a) && in_bounds(b)
&& grid_is_wall(grd(a)) && grid_is_wall(grd(b))
&& (!in_bounds(c) || !in_bounds(d)
|| !grid_is_wall(grd(c)) || !grid_is_wall(grd(d))))
{
return (false);
}
}
}
return (true);
}
void change_labyrinth(bool msg)
{
int size = random_range(12, 24);
coord_def c1, c2;
for (int tries = 10; tries > 0; tries--)
{
int x = random_range(LABYRINTH_BORDER, GXM - LABYRINTH_BORDER - size);
int y = random_range(LABYRINTH_BORDER, GYM - LABYRINTH_BORDER - size);
c1 = coord_def(x,y);
c2 = coord_def(x + size, y + size);
int count_known = 0;
for (int xi = c1.x; xi <= c2.x; xi++)
for (int yi = c1.y; yi <= c2.y; yi++)
{
if (is_terrain_seen(xi, yi))
count_known++;
}
if (count_known < size*size/6)
break;
}
if (msg)
{
mprf(MSGCH_DIAGNOSTICS, "Changing labyrinth from (%d, %d) to (%d, %d)",
c1.x, c1.y, c2.x, c2.y);
}
std::vector<coord_def> targets;
for (int xi = c1.x; xi <= c2.x; xi++)
for (int yi = c1.y; yi <= c2.y; yi++)
{
const coord_def c(xi, yi);
if (is_terrain_seen(xi, yi) || !grid_is_wall(grd(c)))
continue;
if (_grid_is_flanked_by_walls(c))
targets.push_back(c);
}
if (targets.empty())
{
if (msg)
mpr("No unexplored wall grids found!");
return;
}
if (msg)
{
std::string path_str = "";
mpr("Here's the list of targets: ", MSGCH_DIAGNOSTICS);
for (unsigned int i = 0; i < targets.size(); i++)
{
snprintf(info, INFO_SIZE, "(%d, %d) ", targets[i].x, targets[i].y);
path_str += info;
}
mpr(path_str.c_str(), MSGCH_DIAGNOSTICS);
mprf(MSGCH_DIAGNOSTICS, "-> #targets = %d", targets.size());
int max_targets = random_range(std::min((int) targets.size(), 12),
std::min((int) targets.size(), 45));
std::random_shuffle(targets.begin(), targets.end(), random2);
std::vector<coord_def> dirs;
dirs.push_back(coord_def(-1,-1));
dirs.push_back(coord_def( 0,-1));
dirs.push_back(coord_def( 1,-1));
dirs.push_back(coord_def(-1, 0));
// dirs.push_back(coord_def( 0, 0));
dirs.push_back(coord_def( 1, 0));
dirs.push_back(coord_def(-1, 1));
dirs.push_back(coord_def( 0, 1));
dirs.push_back(coord_def( 1, 1));
for (int count = 0; count < max_targets; count++)
{
const coord_def c(targets[count]);
// Maybe not valid anymore...
if (!_grid_is_flanked_by_walls(c))
continue;
coord_def src(c.x-1,c.y);
coord_def dst(c.x+1,c.y);
if (grd(src) != DNGN_FLOOR || grd(dst) != DNGN_FLOOR)
{
src = coord_def(c.x, c.y-1);
dst = coord_def(c.x, c.y+1);
}
// Pathfinding from src to dst...
monster_pathfind mp;
bool success = mp.init_pathfind(src, dst, false, msg);
if (!success)
{
if (msg)
{
mpr("Something went badly wrong - no path found!",
MSGCH_DIAGNOSTICS);
}
continue;
}
const std::vector<coord_def> path = mp.backtrack();
std::vector<coord_def> points;
dungeon_feature_type old_grid = grd(c);
grd(c) = DNGN_FLOOR;
for (unsigned int i = 0; i < path.size(); i++)
{
const coord_def p(path[i]);
if (p.x < c1.x || p.x > c2.x || p.y < c1.y || p.y > c2.y)
continue;
if (is_terrain_seen(p.x, p.y))
continue;
// We don't want to deal with monsters being shifted around.
if (mgrd(p) != NON_MONSTER)
continue;
// Do not pick a grid right next to the original wall.
if (std::abs(p.x-c.x) + std::abs(p.y-c.y) <= 1)
continue;
if (_grid_is_flanked_by_walls(p) && _deadend_check(p))
points.push_back(p);
}
if (points.empty())
{
grd(c) = old_grid;
continue;
}
const int pick = random_range(0, (int) points.size() - 1);
if (msg)
{
mprf(MSGCH_DIAGNOSTICS, "Switch %d (%d, %d) with %d (%d, %d).",
(int) grd(c), c.x, c.y,
(int) grd(points[pick]), points[pick].x, points[pick].y);
}
grd(points[pick]) = old_grid;
}
for (int xi = c1.x; xi <= c2.x; xi++)
for (int yi = c1.y; yi <= c2.y; yi++)
{
const coord_def c(xi, yi);
if (!grid_is_wall(grd(c)) || igrd(c) == NON_ITEM)
continue;
if (msg)
{
mprf(MSGCH_DIAGNOSTICS,
"Need to move around some items at pos (%d, %d)...",
xi, yi);
}
std::random_shuffle(dirs.begin(), dirs.end(), random2);
for (unsigned int i = 0; i < dirs.size(); i++)
{
const coord_def p = c + dirs[i];
if (grd(p) == DNGN_FLOOR)
{
int it = igrd(c);
while (it != NON_ITEM)
{
mitm[it].pos.x = p.x;
mitm[it].pos.y = p.y;
it = mitm[it].link;
}
mitm[it].link = igrd(p);
igrd(p) = igrd(c);
igrd(c) = NON_ITEM;
}
#ifdef DEBUG_CHANGE
if (msg)
{
mprf(MSGCH_DIAGNOSTICS, "Moved items over to (%d, %d)",
p.x, p.y);
}
#endif
break;
}
}
// Just to be on the safe side.
fix_item_coordinates();
// Finally, give the player a clue about what just happened.
const int which = (silenced(you.pos()) ? 2 + random2(2)
: random2(4));
switch (which)
{
case 0: mpr("You hear an odd grinding sound!"); break;
case 1: mpr("You hear the creaking of ancient gears!"); break;
case 2: mpr("The floor suddenly vibrates beneath you!"); break;
case 3: mpr("You feel a sudden draft!"); break;
}
// if (you.level_type == LEVEL_LABYRINTH)
// forget_map(you.species == SP_MINOTAUR ? 12 : 25);
if (you.level_type == LEVEL_LABYRINTH)
{
// Now that the labyrinth can be automapped, apply map rot as
// a counter-measure. (Those mazes sure are easy to forget.)
forget_map(you.species == SP_MINOTAUR ? 25 : 45);
// Labyrinths are *only* mappable for minotaurs.
if (you.level_type != LEVEL_LABYRINTH || you.species != SP_MINOTAUR)
// Labyrinths are now mappable, but come with heavy map rot. (jpeg)
if (you.level_type == LEVEL_ABYSS)