summaryrefslogtreecommitdiffstats
path: root/crawl-ref
diff options
context:
space:
mode:
Diffstat (limited to 'crawl-ref')
-rw-r--r--crawl-ref/source/acr.cc7
-rw-r--r--crawl-ref/source/command.cc1
-rw-r--r--crawl-ref/source/debug.cc12
-rw-r--r--crawl-ref/source/dungeon.cc12
-rw-r--r--crawl-ref/source/effects.cc310
-rw-r--r--crawl-ref/source/effects.h3
-rw-r--r--crawl-ref/source/monplace.cc51
-rw-r--r--crawl-ref/source/monplace.h11
-rw-r--r--crawl-ref/source/monstuff.cc6
9 files changed, 396 insertions, 17 deletions
diff --git a/crawl-ref/source/acr.cc b/crawl-ref/source/acr.cc
index c4c77e7025..b71d92209a 100644
--- a/crawl-ref/source/acr.cc
+++ b/crawl-ref/source/acr.cc
@@ -792,6 +792,13 @@ static void _do_wizard_command(int wiz_command, bool silent_fail)
grd(you.pos()) = DNGN_ENTER_LABYRINTH;
break;
+ case 'k':
+ if (you.level_type == LEVEL_LABYRINTH)
+ change_labyrinth(true);
+ else
+ mpr("This only makes sense in a labyrinth!");
+ break;
+
case 'L':
debug_place_map();
break;
diff --git a/crawl-ref/source/command.cc b/crawl-ref/source/command.cc
index 2ab0f6e507..3cd4e34c5f 100644
--- a/crawl-ref/source/command.cc
+++ b/crawl-ref/source/command.cc
@@ -2249,6 +2249,7 @@ int list_wizard_commands(bool do_redraw_screen)
"<w>Ctrl-A</w> : generate new Abyss area\n"
"<w>b</w> : controlled blink\n"
"<w>B</w> : banish yourself to the Abyss\n"
+ "<w>k</w> : shift section of a labyrinth\n"
"<w>u</w>/<w>d</w> : shift up/down one level\n"
"<w>~</w> : go to a specific level\n"
"<w>:</w> : find branches in the dungeon\n"
diff --git a/crawl-ref/source/debug.cc b/crawl-ref/source/debug.cc
index 56042924fc..9982ca961e 100644
--- a/crawl-ref/source/debug.cc
+++ b/crawl-ref/source/debug.cc
@@ -4297,7 +4297,7 @@ void debug_pathfind(int mid)
mprf("Attempting to calculate a path from (%d, %d) to (%d, %d)...",
mon.pos().x, mon.pos().y, dest.x, dest.y);
monster_pathfind mp;
- bool success = mp.start_pathfind(&mon, dest, true);
+ bool success = mp.init_pathfind(&mon, dest, true);
if (success)
{
std::vector<coord_def> path = mp.backtrack();
@@ -4326,6 +4326,16 @@ void debug_pathfind(int mid)
}
}
+void debug_shift_labyrinth()
+{
+ if (you.level_type != LEVEL_LABYRINTH)
+ {
+ mpr("This only makes sense in a labyrinth!");
+ return;
+ }
+ change_labyrinth(true);
+}
+
static void _miscast_screen_update()
{
viewwindow(true, false);
diff --git a/crawl-ref/source/dungeon.cc b/crawl-ref/source/dungeon.cc
index 6f4f28af82..298e999613 100644
--- a/crawl-ref/source/dungeon.cc
+++ b/crawl-ref/source/dungeon.cc
@@ -896,8 +896,8 @@ static void _reset_level()
{
env.level_flags = LFLAG_NO_TELE_CONTROL | LFLAG_NO_MAGIC_MAP;
- // 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)
env.level_flags |= LFLAG_NOT_MAPPABLE;
}
else
@@ -6738,13 +6738,13 @@ static bool _has_vault_in_radius(const coord_def &pos, int radius,
}
// Find an entry point that's:
-// * At least 25 squares away from the exit.
-// * At least 4 squares away from the nearest vault.
+// * At least 28 squares away from the exit.
+// * At least 6 squares away from the nearest vault.
// * Floor (well, obviously).
static coord_def _labyrinth_find_entry_point(const dgn_region &reg,
const coord_def &end)
{
- const int min_distance = 20 * 20;
+ const int min_distance = 28 * 28;
// Try many times.
for (int i = 0; i < 2000; ++i)
{
@@ -6755,7 +6755,7 @@ static coord_def _labyrinth_find_entry_point(const dgn_region &reg,
if ((place - end).abs() < min_distance)
continue;
- if (_has_vault_in_radius(place, 4, MMT_VAULT))
+ if (_has_vault_in_radius(place, 6, MMT_VAULT))
continue;
return (place);
diff --git a/crawl-ref/source/effects.cc b/crawl-ref/source/effects.cc
index 37f96f4646..f8dbc9667f 100644
--- a/crawl-ref/source/effects.cc
+++ b/crawl-ref/source/effects.cc
@@ -2225,6 +2225,304 @@ static void _hell_effects()
}
}
+// 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;
+ }
+}
+
static bool _food_item_needs_time_check(item_def &item)
{
if (!is_valid_item(item))
@@ -2631,9 +2929,17 @@ void handle_time(long time_delta)
exercise(SK_STEALTH, 1);
}
}
-// 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);
+
+ // From time to time change a section of the labyrinth.
+ if (one_chance_in(10))
+ change_labyrinth();
+ }
spawn_random_monsters();
}
diff --git a/crawl-ref/source/effects.h b/crawl-ref/source/effects.h
index d954ca749f..9f784ebf47 100644
--- a/crawl-ref/source/effects.h
+++ b/crawl-ref/source/effects.h
@@ -112,6 +112,9 @@ int torment(int caster, const coord_def& where);
int torment_player(int pow, int caster);
int torment_monsters(coord_def where, int pow, int caster);
+// called from: debug
+void change_labyrinth(bool msg = false);
+
bool forget_inventory(bool quiet = false);
bool vitrify_area(int radius);
void update_corpses(double elapsedTime);
diff --git a/crawl-ref/source/monplace.cc b/crawl-ref/source/monplace.cc
index 6f440d0036..48f43a7b76 100644
--- a/crawl-ref/source/monplace.cc
+++ b/crawl-ref/source/monplace.cc
@@ -2386,7 +2386,8 @@ void monster_pathfind::set_range(int r)
// The main method in the monster_pathfind class.
// Returns true if a path was found, else false.
-bool monster_pathfind::start_pathfind(monsters *mon, coord_def dest, bool msg)
+bool monster_pathfind::init_pathfind(monsters *mon, coord_def dest, bool diag,
+ bool msg)
{
mons = mon;
@@ -2394,6 +2395,7 @@ bool monster_pathfind::start_pathfind(monsters *mon, coord_def dest, bool msg)
start = dest;
target = mon->pos();
pos = start;
+ allow_diagonals = diag;
// Easy enough. :P
if (start == target)
@@ -2403,6 +2405,27 @@ bool monster_pathfind::start_pathfind(monsters *mon, coord_def dest, bool msg)
return (true);
}
+
+ 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)
+{
// NOTE: We never do any traversable() check for the starting square
// (target). This means that even if the target cannot be reached
// we may still find a path leading adjacent to this position, which
@@ -2453,7 +2476,7 @@ bool monster_pathfind::calc_path_to_neighbours()
// For each point, we look at all neighbour points. Check the orthogonals
// last, so that, should an orthogonal and a diagonal direction have the
// same total travel cost, the orthogonal will be picked first, and thus
- // zigzagging can be significantly reduced.
+ // zigzagging will be significantly reduced.
//
// 1 0 3 This means directions are looked at, in order,
// \ | / 1, 3, 5, 7 (diagonals) followed by 0, 2, 4, 6
@@ -2463,6 +2486,10 @@ bool monster_pathfind::calc_path_to_neighbours()
//
for (int dir = 1; dir < 8; (dir += 2) == 9 && (dir = 0))
{
+ // Skip diagonal movement.
+ if (!allow_diagonals && (dir % 2))
+ continue;
+
npos = pos + Compass[dir];
#ifdef DEBUG_PATHFIND
@@ -2653,9 +2680,17 @@ std::vector<coord_def> monster_pathfind::calc_waypoints()
return (waypoints);
}
+bool monster_pathfind::traversable(const coord_def p)
+{
+ if (mons)
+ return mons_traversable(p);
+
+ return (!grid_is_solid(grd(p)) && !grid_destroys_items(grd(p)));
+}
+
// Checks whether a given monster can pass over a certain position, respecting
// its preferred habit and capability of flight or opening doors.
-bool monster_pathfind::traversable(coord_def p)
+bool monster_pathfind::mons_traversable(const coord_def p)
{
const int montype = mons_is_zombified(mons) ? mons_zombie_base(mons)
: mons->type;
@@ -2705,9 +2740,17 @@ bool monster_pathfind::traversable(coord_def p)
return (true);
}
+int monster_pathfind::travel_cost(coord_def npos)
+{
+ if (mons)
+ return mons_travel_cost(npos);
+
+ return (1);
+}
+
// Assumes that grids that really cannot be entered don't even get here.
// (Checked by traversable().)
-int monster_pathfind::travel_cost(coord_def npos)
+int monster_pathfind::mons_travel_cost(coord_def npos)
{
ASSERT(grid_distance(pos, npos) <= 1);
diff --git a/crawl-ref/source/monplace.h b/crawl-ref/source/monplace.h
index d17c6938a8..ac87a555f3 100644
--- a/crawl-ref/source/monplace.h
+++ b/crawl-ref/source/monplace.h
@@ -315,7 +315,11 @@ public:
// public methods
void set_range(int r);
- 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);
std::vector<coord_def> backtrack(void);
std::vector<coord_def> calc_waypoints(void);
@@ -324,6 +328,8 @@ protected:
bool calc_path_to_neighbours(void);
bool traversable(coord_def p);
int travel_cost(coord_def npos);
+ bool mons_traversable(coord_def p);
+ int mons_travel_cost(coord_def npos);
int estimated_cost(coord_def npos);
void add_new_pos(coord_def pos, int total);
void update_pos(coord_def pos, int total);
@@ -336,6 +342,9 @@ protected:
// Our destination, and the current position we're looking at.
coord_def start, target, pos;
+ // Do not move diagonally along the path.
+ bool allow_diagonals;
+
// Maximum range to search between start and target. None, if zero.
int range;
diff --git a/crawl-ref/source/monstuff.cc b/crawl-ref/source/monstuff.cc
index 41428506dc..cf967eca5b 100644
--- a/crawl-ref/source/monstuff.cc
+++ b/crawl-ref/source/monstuff.cc
@@ -2977,7 +2977,7 @@ static void _handle_behaviour(monsters *mon)
if (range > 0)
mp.set_range(range);
- if (mp.start_pathfind(mon, you.pos()))
+ if (mp.init_pathfind(mon, you.pos()))
{
mon->travel_path = mp.calc_waypoints();
if (!mon->travel_path.empty())
@@ -3203,7 +3203,7 @@ static void _handle_behaviour(monsters *mon)
// The last coordinate in the path vector is our
// destination.
int len = mon->travel_path.size();
- if (mp.start_pathfind(mon, mon->travel_path[len-1]))
+ if (mp.init_pathfind(mon, mon->travel_path[len-1]))
{
mon->travel_path = mp.calc_waypoints();
if (!mon->travel_path.empty())
@@ -3284,7 +3284,7 @@ static void _handle_behaviour(monsters *mon)
// Hive entrance after some time, and that is what
// matters.
monster_pathfind mp;
- if (mp.start_pathfind(mon, mon->patrol_point))
+ if (mp.init_pathfind(mon, mon->patrol_point))
{
mon->travel_path = mp.calc_waypoints();
if (!mon->travel_path.empty())