summaryrefslogtreecommitdiffstats
path: root/crawl-ref/source/effects.cc
diff options
context:
space:
mode:
Diffstat (limited to 'crawl-ref/source/effects.cc')
-rw-r--r--crawl-ref/source/effects.cc310
1 files changed, 308 insertions, 2 deletions
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();
}