diff options
Diffstat (limited to 'crawl-ref')
-rw-r--r-- | crawl-ref/source/acr.cc | 7 | ||||
-rw-r--r-- | crawl-ref/source/command.cc | 1 | ||||
-rw-r--r-- | crawl-ref/source/debug.cc | 12 | ||||
-rw-r--r-- | crawl-ref/source/dungeon.cc | 12 | ||||
-rw-r--r-- | crawl-ref/source/effects.cc | 310 | ||||
-rw-r--r-- | crawl-ref/source/effects.h | 3 | ||||
-rw-r--r-- | crawl-ref/source/monplace.cc | 51 | ||||
-rw-r--r-- | crawl-ref/source/monplace.h | 11 | ||||
-rw-r--r-- | crawl-ref/source/monstuff.cc | 6 |
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 ®, 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 ®, 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()) |