diff options
Diffstat (limited to 'src/generate.cc')
-rw-r--r-- | src/generate.cc | 8698 |
1 files changed, 8698 insertions, 0 deletions
diff --git a/src/generate.cc b/src/generate.cc new file mode 100644 index 00000000..b5733682 --- /dev/null +++ b/src/generate.cc @@ -0,0 +1,8698 @@ +/* + * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke + * + * This software may be copied and distributed for educational, research, and + * not for profit purposes provided that this copyright and statement are + * included in all such copies. + */ + +#include "generate.hpp" + +#include "artifact_type.hpp" +#include "cave.hpp" +#include "cave_type.hpp" +#include "dungeon_info_type.hpp" +#include "feature_type.hpp" +#include "hook_build_room1_in.hpp" +#include "hooks.hpp" +#include "init1.hpp" +#include "levels.hpp" +#include "loadsave.hpp" +#include "monster2.hpp" +#include "monster_race.hpp" +#include "monster_type.hpp" +#include "object2.hpp" +#include "object_kind.hpp" +#include "options.hpp" +#include "player_type.hpp" +#include "randart.hpp" +#include "spells1.hpp" +#include "tables.hpp" +#include "town_type.hpp" +#include "traps.hpp" +#include "util.hpp" +#include "variable.hpp" +#include "vault_type.hpp" +#include "wild.hpp" +#include "wilderness_map.hpp" +#include "z-rand.hpp" + +#include <cassert> +#include <memory> +#include <vector> + +#define SAFE_MAX_ATTEMPTS 5000 + +/* + * Note that Level generation is *not* an important bottleneck, + * though it can be annoyingly slow on older machines... Thus + * we emphasize "simplicity" and "correctness" over "speed". + * + * This entire file is only needed for generating levels. + * This may allow smart compilers to only load it when needed. + * + * Consider the "v_info.txt" file for vault generation. + * + * In this file, we use the "special" granite and perma-wall sub-types, + * where "basic" is normal, "inner" is inside a room, "outer" is the + * outer wall of a room, and "solid" is the outer wall of the dungeon + * or any walls that may not be pierced by corridors. Thus the only + * wall type that may be pierced by a corridor is the "outer granite" + * type. The "basic granite" type yields the "actual" corridors. + * + * Note that we use the special "solid" granite wall type to prevent + * multiple corridors from piercing a wall in two adjacent locations, + * which would be messy, and we use the special "outer" granite wall + * to indicate which walls "surround" rooms, and may thus be "pierced" + * by corridors entering or leaving the room. + * + * Note that a tunnel which attempts to leave a room near the "edge" + * of the dungeon in a direction toward that edge will cause "silly" + * wall piercings, but will have no permanently incorrect effects, + * as long as the tunnel can *eventually* exit from another side. + * And note that the wall may not come back into the room by the + * hole it left through, so it must bend to the left or right and + * then optionally re-enter the room (at least 2 grids away). This + * is not a problem since every room that is large enough to block + * the passage of tunnels is also large enough to allow the tunnel + * to pierce the room itself several times. + * + * Note that no two corridors may enter a room through adjacent grids, + * they must either share an entryway or else use entryways at least + * two grids apart. This prevents "large" (or "silly") doorways. + * + * To create rooms in the dungeon, we first divide the dungeon up + * into "blocks" of 11x11 grids each, and require that all rooms + * occupy a rectangular group of blocks. As long as each room type + * reserves a sufficient number of blocks, the room building routines + * will not need to check bounds. Note that most of the normal rooms + * actually only use 23x11 grids, and so reserve 33x11 grids. + * + * Note that the use of 11x11 blocks (instead of the old 33x11 blocks) + * allows more variability in the horizontal placement of rooms, and + * at the same time has the disadvantage that some rooms (two thirds + * of the normal rooms) may be "split" by panel boundaries. This can + * induce a situation where a player is in a room and part of the room + * is off the screen. It may be annoying enough to go back to 33x11 + * blocks to prevent this visual situation. + * + * Note that the dungeon generation routines are much different (2.7.5) + * and perhaps "DUN_ROOMS" should be less than 50. + * + * XXX XXX XXX Note that it is possible to create a room which is only + * connected to itself, because the "tunnel generation" code allows a + * tunnel to leave a room, wander around, and then re-enter the room. + * + * XXX XXX XXX Note that it is possible to create a set of rooms which + * are only connected to other rooms in that set, since there is nothing + * explicit in the code to prevent this from happening. But this is less + * likely than the "isolated room" problem, because each room attempts to + * connect to another room, in a giant cycle, thus requiring at least two + * bizarre occurances to create an isolated section of the dungeon. + * + * Note that (2.7.9) monster pits have been split into monster "nests" + * and monster "pits". The "nests" have a collection of monsters of a + * given type strewn randomly around the room (jelly, animal, or undead), + * while the "pits" have a collection of monsters of a given type placed + * around the room in an organized manner (orc, troll, giant, dragon, or + * demon). Note that both "nests" and "pits" are now "level dependant", + * and both make 16 "expensive" calls to the "get_mon_num()" function. + * + * Note that the cave grid flags changed in a rather drastic manner + * for Angband 2.8.0 (and 2.7.9+), in particular, dungeon terrain + * features, such as doors and stairs and traps and rubble and walls, + * are all handled as a set of 64 possible "terrain features", and + * not as "fake" objects (440-479) as in pre-2.8.0 versions. + * + * The 64 new "dungeon features" will also be used for "visual display" + * but we must be careful not to allow, for example, the user to display + * hidden traps in a different way from floors, or secret doors in a way + * different from granite walls, or even permanent granite in a different + * way from granite. XXX XXX XXX + */ + + +/* + * Dungeon generation values + */ +#define DUN_ROOMS 50 /* Number of rooms to attempt */ +#define DUN_UNUSUAL 194 /* Level/chance of unusual room (was 200) */ +#define DUN_DEST 18 /* 1/chance of having a destroyed level */ +#define SMALL_LEVEL 6 /* 1/chance of smaller size (3->6) */ +#define EMPTY_LEVEL 15 /* 1/chance of being 'empty' (15)*/ +#define DARK_EMPTY 5 /* 1/chance of arena level NOT being lit (2)*/ +#define XTRA_MAGIC 10 /* 1/chance of having a level with more magic (10)*/ +#define DUN_WILD_VAULT 50 /* Chance of finding a wilderness vault. */ +#define DUN_WAT_RNG 2 /* Width of rivers */ +#define DUN_WAT_CHG 50 /* 1 in 50 chance of junction in river */ +#define DUN_CAVERN 30 /* 1/chance of having a cavern level */ + +/* + * Dungeon tunnel generation values + */ +#define DUN_TUN_RND 10 /* Chance of random direction */ +#define DUN_TUN_CHG 30 /* Chance of changing direction */ +#define DUN_TUN_CON 15 /* Chance of extra tunneling */ +#define DUN_TUN_PEN 25 /* Chance of doors at room entrances */ +#define DUN_TUN_JCT 90 /* Chance of doors at tunnel junctions */ + +/* + * Dungeon streamer generation values + */ +#define DUN_STR_DEN 5 /* Density of streamers */ +#define DUN_STR_RNG 2 /* Width of streamers */ +#define DUN_STR_MAG 3 /* Number of magma streamers */ +#define DUN_STR_MC 90 /* 1/chance of treasure per magma */ +#define DUN_STR_QUA 2 /* Number of quartz streamers */ +#define DUN_STR_QC 40 /* 1/chance of treasure per quartz */ +#define DUN_STR_SAN 1 /* Number of sand streamers */ +#define DUN_STR_SC 10 /* 1/chance of treasure per sandwall */ +#define DUN_STR_WLW 1 /* Width of lava & water streamers -KMW- */ +#define DUN_STR_DWLW 8 /* Density of water & lava streams -KMW- */ + + +/* + * Dungeon treausre allocation values + */ +#define DUN_AMT_ROOM 9 /* Amount of objects for rooms */ +#define DUN_AMT_ITEM 3 /* Amount of objects for rooms/corridors */ +#define DUN_AMT_GOLD 3 /* Amount of treasure for rooms/corridors */ +#define DUN_AMT_ALTAR 1 /* Amount of altars */ +#define DUN_AMT_BETWEEN 2 /* Amount of between gates */ +#define DUN_AMT_FOUNTAIN 1 /* Amount of fountains */ + +/* + * Hack -- Dungeon allocation "places" + */ +#define ALLOC_SET_CORR 1 /* Hallway */ +#define ALLOC_SET_ROOM 2 /* Room */ +#define ALLOC_SET_BOTH 3 /* Anywhere */ + +/* + * Hack -- Dungeon allocation "types" + */ +#define ALLOC_TYP_RUBBLE 1 /* Rubble */ +#define ALLOC_TYP_TRAP 3 /* Trap */ +#define ALLOC_TYP_GOLD 4 /* Gold */ +#define ALLOC_TYP_OBJECT 5 /* Object */ +#define ALLOC_TYP_ALTAR 6 /* Altar */ +#define ALLOC_TYP_BETWEEN 7 /* Between */ +#define ALLOC_TYP_FOUNTAIN 8 /* Fountain */ + + +/* + * The "size" of a "generation block" in grids + */ +#define BLOCK_HGT 11 +#define BLOCK_WID 11 + +/* + * Maximum numbers of rooms along each axis (currently 6x6) + */ +#define MAX_ROOMS_ROW (MAX_HGT / BLOCK_HGT) +#define MAX_ROOMS_COL (MAX_WID / BLOCK_WID) + + +/* + * Bounds on some arrays used in the "dun_data" structure. + * These bounds are checked, though usually this is a formality. + */ +#define CENT_MAX 100 +#define DOOR_MAX 200 +#define WALL_MAX 500 +#define TUNN_MAX 900 + + +/* + * Maximal number of room types + */ +#define ROOM_MAX 12 + + + +/* + * Simple structure to hold a map location + */ + + +typedef struct coord coord; + +struct coord +{ + byte y; + byte x; +}; + + +/* + * Room type information + */ + +typedef struct room_data room_data; + +struct room_data +{ + /* Required size in blocks */ + s16b dy1, dy2, dx1, dx2; + + /* Hack -- minimum level */ + s16b level; +}; + + +/* + * Structure to hold all "dungeon generation" data + */ + +typedef struct dun_data dun_data; + +struct dun_data +{ + /* Array of centers of rooms */ + int cent_n; + coord cent[CENT_MAX]; + + /* Array of possible door locations */ + int door_n; + coord door[DOOR_MAX]; + + /* Array of wall piercing locations */ + int wall_n; + coord wall[WALL_MAX]; + + /* Array of tunnel grids */ + int tunn_n; + coord tunn[TUNN_MAX]; + + /* Number of blocks along each axis */ + int row_rooms; + int col_rooms; + + /* Array of which blocks are used */ + bool_ room_map[MAX_ROOMS_ROW][MAX_ROOMS_COL]; + + /* Hack -- there is a pit/nest on this level */ + bool_ crowded; +}; + +/* + * Level generator type + */ + +typedef struct level_generator_type level_generator_type; +struct level_generator_type +{ + cptr name; + bool_ (*generator)(); + + struct level_generator_type *next; +}; + +static level_generator_type *level_generators = NULL; + +/* + * Add a new generator + */ +void add_level_generator(cptr name, bool_ (*generator)()) +{ + assert(name != nullptr); + + level_generator_type *g = new level_generator_type; + + g->name = strdup(name); + g->generator = generator; + + g->next = level_generators; + level_generators = g; +} + + +/* + * Dungeon generation data -- see "cave_gen()" + */ +static dun_data *dun; + +/* + * ??? + */ +static int template_race; + + + +/* + * Array of room types depths + */ +static s16b roomdep[] = +{ + 0, /* 0 = Nothing */ + 1, /* 1 = Simple (33x11) */ + 1, /* 2 = Overlapping (33x11) */ + 3, /* 3 = Crossed (33x11) */ + 3, /* 4 = Large (33x11) */ + 5, /* 5 = Monster nest (33x11) */ + 5, /* 6 = Monster pit (33x11) */ + 5, /* 7 = Lesser vault (33x22) */ + 10, /* 8 = Greater vault (66x44) */ + 1, /* 9 = Circular rooms (22x22) */ + 3, /* 10 = Fractal cave (42x24) */ + 10, /* 11 = Random vault (44x22) */ + 10, /* 12 = Crypts (22x22) */ +}; + + +/* + * Always picks a correct direction + */ +static void correct_dir(int *rdir, int *cdir, int y1, int x1, int y2, int x2) +{ + /* Extract vertical and horizontal directions */ + *rdir = (y1 == y2) ? 0 : (y1 < y2) ? 1 : -1; + *cdir = (x1 == x2) ? 0 : (x1 < x2) ? 1 : -1; + + /* Never move diagonally */ + if (*rdir && *cdir) + { + if (rand_int(100) < 50) + { + *rdir = 0; + } + else + { + *cdir = 0; + } + } +} + + +/* + * Pick a random direction + */ +static void rand_dir(int *rdir, int *cdir) +{ + /* Pick a random direction */ + int i = rand_int(4); + + /* Extract the dy/dx components */ + *rdir = ddy_ddd[i]; + *cdir = ddx_ddd[i]; +} + + +/* + * Convert existing terrain type to "up stairs" + */ +static void place_up_stairs(int y, int x) +{ + cave_type *c_ptr = &cave[y][x]; + + /* Create up stairs */ + if ((rand_int(3) != 0) || (dungeon_flags2 & DF2_NO_SHAFT)) + { + cave_set_feat(y, x, FEAT_LESS); + } + else + { + cave_set_feat(y, x, FEAT_SHAFT_UP); + } + + c_ptr->special = 0; +} + + +/* + * Convert existing terrain type to "down stairs" with dungeon changing. + */ +static void place_magical_stairs(int y, int x, byte next) +{ + cave_type *c_ptr = &cave[y][x]; + + /* Create up stairs */ + cave_set_feat(y, x, FEAT_MORE); + c_ptr->special = next; +} + + +/* + * Convert existing terrain type to "down stairs" + */ +static void place_down_stairs(int y, int x) +{ + cave_type *c_ptr = &cave[y][x]; + + /* + * Create down stairs + * All thoses tests are necesary because a shaft can jump up to 4 levels + */ + if ((dun_level + 4 > d_info[dungeon_type].maxdepth) || + (rand_int(3) != 0) || (dungeon_flags2 & DF2_NO_SHAFT)) + { + cave_set_feat(y, x, FEAT_MORE); + } + else + { + cave_set_feat(y, x, FEAT_SHAFT_DOWN); + } + + c_ptr->special = 0; +} + + +/* + * Helper function for place_new_way. Determine if y, x is one of + * floor features of the current dungeon + */ +static bool_ is_safe_floor(int y, int x) +{ + dungeon_info_type *d_ptr = &d_info[dungeon_type]; + byte feat = cave[y][x].feat; + + /* One of the legal floor types */ + if (feat == d_ptr->floor1) return (TRUE); + if (feat == d_ptr->floor2) return (TRUE); + if (feat == d_ptr->floor3) return (TRUE); + + /* Assume non-floor */ + return (FALSE); +} + + +/* + * Place a way to next / previoous level on flat places + */ +void place_new_way(int *y, int *x) +{ + int xx, yy; + int x0, x1, x2; + int y0, y1, y2; + cave_type *c_ptr; + bool_ ok; + int i, way_n; + byte way_x[MAX_WID], way_y[MAX_WID]; + + + /* Find valid location XXX XXX XXX */ + while (TRUE) + { + /* A way on vertical edge */ + if (rand_int(cur_hgt + cur_wid) < cur_hgt) + { + /* Pick a random grid */ + yy = *y = rand_int(cur_hgt - 2) + 1; + xx = *x = 1 + (rand_int(2) * (cur_wid - 3)); + + /* Pick a direction */ + if (xx == 1) + { + /* Left */ + x0 = + 1; + y0 = 0; + + /* Sides */ + x1 = 0; + y1 = -1; + x2 = 0; + y2 = + 1; + } + else + { + /* Right */ + x0 = -1; + y0 = 0; + + /* Sides */ + x1 = 0; + y1 = -1; + x2 = 0; + y2 = + 1; + } + } + + /* A way on horizontal edge */ + else + { + /* Pick a random grid */ + xx = *x = rand_int(cur_wid - 2) + 1; + yy = *y = 1 + (rand_int(2) * (cur_hgt - 3)); + + /* Pick a direction */ + if (yy == 1) + { + /* Down */ + x0 = 0; + y0 = + 1; + + /* Sides */ + x1 = -1; + y1 = 0; + x2 = + 1; + y2 = 0; + } + else + { + /* Up */ + x0 = 0; + y0 = -1; + + /* Sides */ + x1 = -1; + y1 = 0; + x2 = + 1; + y2 = 0; + } + } + + + /* Look at the starting location */ + c_ptr = &cave[yy][xx]; + + /* Reject locations inside vaults */ + if (c_ptr->info & (CAVE_ICKY)) continue; + + /* Reject permanent features */ + if ((f_info[c_ptr->feat].flags1 & (FF1_PERMANENT)) && + (f_info[c_ptr->feat].flags1 & (FF1_FLOOR))) continue; + + /* Reject room walls */ + if ((c_ptr->info & (CAVE_ROOM)) && + (c_ptr->feat == feat_wall_outer)) continue; + + /* Look at a neighbouring edge */ + c_ptr = &cave[yy + y1][xx + x1]; + + /* Reject two adjacent ways */ + if ((c_ptr->feat == FEAT_WAY_MORE) || + (c_ptr->feat == FEAT_WAY_LESS)) continue; + + /* Look at the other neighbouring edge */ + c_ptr = &cave[yy + y2][xx + x2]; + + /* Reject two adjacent ways */ + if ((c_ptr->feat == FEAT_WAY_MORE) || + (c_ptr->feat == FEAT_WAY_LESS)) continue; + + /* Look ahead */ + c_ptr = &cave[yy + y0][xx + x0]; + + /* Reject two adjacent ways -- relatively rare, but this can happen */ + if ((c_ptr->feat == FEAT_WAY_MORE) || + (c_ptr->feat == FEAT_WAY_LESS)) continue; + + + /* Reset counter */ + way_n = 0; + + /* Assume bad location */ + ok = FALSE; + + /* Check if it connects to current dungeon */ + while (in_bounds(yy, xx)) + { + /* Check grids ahead */ + if (is_safe_floor(yy + y0, xx + x0)) ok = TRUE; + + /* Check side grids */ + if (is_safe_floor(yy + y1, xx + x1)) ok = TRUE; + if (is_safe_floor(yy + y2, xx + x2)) ok = TRUE; + + /* Connected */ + if (ok) break; + + /* Access grid (ahead) */ + c_ptr = &cave[yy + y0][xx + x0]; + + /* Avoid opening vaults */ + if (c_ptr->feat == FEAT_PERM_OUTER) + { + /* Comment this out if you find any problems... */ + ok = TRUE; + + break; + } + + /* Paranoia */ + if (c_ptr->feat == FEAT_PERM_SOLID) break; + + /* + * Hack -- Avoid digging room corner + * + * CAVEAT: Can't handle situations like this: + * + * .....######## + * .....######## + * ######.....># + * ############# + * .....# + * .....# + */ + if (c_ptr->info & (CAVE_ROOM)) + { + cave_type *c1_ptr = &cave[yy + y0 + y1][xx + x0 + x1]; + cave_type *c2_ptr = &cave[yy + y0 + y2][xx + x0 + x2]; + + /* Bend the way */ + if ((c1_ptr->info & (CAVE_ROOM)) && + !(c2_ptr->info & (CAVE_ROOM))) + { + way_x[way_n] = xx + x1; + way_y[way_n] = yy + y1; + way_n++; + way_x[way_n] = xx + x1 + x0; + way_y[way_n] = yy + y1 + y0; + way_n++; + } + + /* Bend the way -- the other direction */ + else if ((c2_ptr->info & (CAVE_ROOM)) && + !(c1_ptr->info & (CAVE_ROOM))) + { + way_x[way_n] = xx + x2; + way_y[way_n] = yy + y2; + way_n++; + way_x[way_n] = xx + x2 + x0; + way_y[way_n] = yy + y2 + y0; + way_n++; + } + + else + { + way_x[way_n] = xx + x0; + way_y[way_n] = yy + y0; + way_n++; + } + + ok = TRUE; + break; + } + + /* Remember the location */ + way_x[way_n] = xx + x0; + way_y[way_n] = yy + y0; + way_n++; + + /* Advance */ + xx += x0; + yy += y0; + } + + /* Accept connected corridor */ + if (ok) break; + + /* Try again, until valid location is found */ + } + + + /* Actually dig a corridor connecting the way to dungeon */ + for (i = 0; i < way_n; i++) + { + /* Dig */ + place_floor(way_y[i], way_x[i]); + } +} + + +/* + * Returns random co-ordinates for player/monster/object + */ +bool_ new_player_spot(int branch) +{ + int y, x; + int max_attempts = 5000; + + /* Place the player */ + if (dungeon_flags1 & DF1_FLAT) + { + place_new_way(&y, &x); + } + else + { + while (max_attempts--) + { + /* Pick a legal spot */ + y = rand_range(1, cur_hgt - 2); + x = rand_range(1, cur_wid - 2); + + /* Must be a "naked" floor grid */ + if (!cave_naked_bold(y, x)) continue; + + /* Refuse to start on anti-teleport grids */ + if (cave[y][x].info & (CAVE_ICKY)) continue; + + /* Done */ + break; + + } + } + + /* Should be -1, actually if we failed... */ + if (max_attempts < 1) return (FALSE); + + + /* Save the new player grid */ + p_ptr->py = y; + p_ptr->px = x; + + /* XXX XXX XXX */ + if (dungeon_stair && !(dungeon_flags2 & DF2_NO_STAIR) && dun_level && + (!is_quest(dun_level) || (old_dun_level < dun_level)) && !branch) + { + if (old_dun_level < dun_level) + { + place_up_stairs(p_ptr->py , p_ptr->px); + if (dungeon_flags1 & DF1_FLAT) + { + cave_set_feat(p_ptr->py, p_ptr->px, FEAT_WAY_LESS); + } + } + else + { + place_down_stairs(p_ptr->py , p_ptr->px); + if (dungeon_flags1 & DF1_FLAT) + { + cave_set_feat(p_ptr->py, p_ptr->px, FEAT_WAY_MORE); + } + } + } + + return (TRUE); +} + + + +/* + * Count the number of walls adjacent to the given grid. + * + * Note -- Assumes "in_bounds(y, x)" + * + * We count only granite walls and permanent walls. + */ +static int next_to_walls(int y, int x) +{ + int k = 0; + + if (f_info[cave[y + 1][x].feat].flags1 & FF1_WALL) k++; + if (f_info[cave[y - 1][x].feat].flags1 & FF1_WALL) k++; + if (f_info[cave[y][x + 1].feat].flags1 & FF1_WALL) k++; + if (f_info[cave[y][x - 1].feat].flags1 & FF1_WALL) k++; + + return (k); +} + + + +/* + * Convert existing terrain type to rubble + */ +static void place_rubble(int y, int x) +{ + /* Create rubble */ + cave_set_feat(y, x, FEAT_RUBBLE); +} + + +/* + * Place an altar at the given location + */ +static void place_altar(int y, int x) +{ + if (magik(10)) + cave_set_feat(y, x, 164); +} + + +/* + * Place a fountain at the given location + */ +static void place_fountain(int y, int x) +{ + cave_type *c_ptr = &cave[y][x]; + int svals[SV_POTION_LAST + SV_POTION2_LAST + 1], maxsval = 0, k; + + /* List of usable svals */ + for (k = 1; k < max_k_idx; k++) + { + object_kind *k_ptr = &k_info[k]; + + if (((k_ptr->tval == TV_POTION) || (k_ptr->tval == TV_POTION2)) && + (k_ptr->level <= dun_level) && (k_ptr->flags4 & TR4_FOUNTAIN)) + { + if (k_ptr->tval == TV_POTION2) svals[maxsval] = k_ptr->sval + SV_POTION_LAST; + else svals[maxsval] = k_ptr->sval; + maxsval++; + } + } + + if (maxsval == 0) return; + + /* Place the fountain */ + if (randint(100) < 30) + { + cave_set_feat(y, x, FEAT_EMPTY_FOUNTAIN); + c_ptr->special2 = 0; + } + else + { + cave_set_feat(y, x, FEAT_FOUNTAIN); + c_ptr->special2 = damroll(3, 4); + } + + c_ptr->special = svals[rand_int(maxsval)]; +} + + +/* + * Place a between gate at the given location + */ +static void place_between(int y, int x) +{ + cave_type *c_ptr = &cave[y][x], *c1_ptr; + int gx, gy; + + while (TRUE) + { + /* Location */ + gy = rand_int(cur_hgt); + gx = rand_int(cur_wid); + + /* Require "naked" floor grid */ + if (cave_naked_bold(gy, gx)) break; + } + + /* Access the target grid */ + c1_ptr = &cave[gy][gx]; + + /* Place a pair of between gates */ + cave_set_feat(y, x, FEAT_BETWEEN); + c_ptr->special = gx + (gy << 8); + cave_set_feat(gy, gx, FEAT_BETWEEN); + c1_ptr->special = x + (y << 8); +} + + +/* + * Place an up/down staircase at given location + */ +static void place_random_stairs(int y, int x) +{ + /* Paranoia */ + if (!cave_clean_bold(y, x)) return; + + /* Choose a staircase */ + if (!dun_level) + { + place_down_stairs(y, x); + } + else if (is_quest(dun_level) && (dun_level > 1)) + { + place_up_stairs(y, x); + } + else if (dun_level >= d_info[dungeon_type].maxdepth) + { + if (d_info[dungeon_type].next) + { + place_magical_stairs(y, x, d_info[dungeon_type].next); + } + else + { + place_up_stairs(y, x); + } + } + else if (rand_int(100) < 50) + { + place_down_stairs(y, x); + } + else + { + place_up_stairs(y, x); + } +} + + +/* + * Place a locked door at the given location + */ +static void place_locked_door(int y, int x) +{ + /* Create locked door */ + cave_set_feat(y, x, FEAT_DOOR_HEAD + randint(7)); +} + + +/* + * Place a secret door at the given location + */ +static void place_secret_door(int y, int x) +{ + cave_type *c_ptr = &cave[y][x]; + + /* Vaults */ + if (c_ptr->info & CAVE_ICKY) + { + c_ptr->mimic = FEAT_WALL_INNER; + } + + /* Ordinary room -- use current outer or inner wall */ + else if (c_ptr->info & CAVE_ROOM) + { + /* Determine if it's inner or outer XXX XXX XXX */ + if ((cave[y - 1][x].info & CAVE_ROOM) && + (cave[y + 1][x].info & CAVE_ROOM) && + (cave[y][x - 1].info & CAVE_ROOM) && + (cave[y][x + 1].info & CAVE_ROOM)) + { + c_ptr->mimic = feat_wall_inner; + } + else + { + c_ptr->mimic = feat_wall_outer; + } + } + else + { + c_ptr->mimic = fill_type[rand_int(100)]; + } + + /* Create secret door */ + cave_set_feat(y, x, FEAT_SECRET); +} + + +/* + * Place a random type of door at the given location + */ +static void place_random_door(int y, int x) +{ + int tmp; + + /* Choose an object */ + tmp = rand_int(1000); + + /* Open doors (300/1000) */ + if (tmp < 300) + { + /* Create open door */ + cave_set_feat(y, x, FEAT_OPEN); + } + + /* Broken doors (100/1000) */ + else if (tmp < 400) + { + /* Create broken door */ + cave_set_feat(y, x, FEAT_BROKEN); + } + + /* Secret doors (200/1000) */ + else if (tmp < 600) + { + /* Create secret door */ + place_secret_door(y, x); + } + + /* Closed doors (300/1000) */ + else if (tmp < 900) + { + /* Create closed door */ + cave_set_feat(y, x, FEAT_DOOR_HEAD + 0x00); + } + + /* Locked doors (99/1000) */ + else if (tmp < 999) + { + /* Create locked door */ + cave_set_feat(y, x, FEAT_DOOR_HEAD + randint(7)); + } + + /* Stuck doors (1/1000) */ + else + { + /* Create jammed door */ + cave_set_feat(y, x, FEAT_DOOR_HEAD + 0x08 + rand_int(8)); + } +} + + + +/* + * Places some staircases near walls + */ +static void alloc_stairs(int feat, int num, int walls, int branch) +{ + int y, x, i, j, cnt; + + /* Place "num" stairs */ + for (cnt = 0, i = 0; i < num || (cnt < 1 && num > 1); i++) + { + /* Try several times, then decrease "walls" */ + for (j = 0; j <= SAFE_MAX_ATTEMPTS; j++) + { + if (dungeon_flags1 & DF1_FLAT) + { + place_new_way(&y, &x); + } + else + { + /* Pick a random grid */ + y = rand_int(cur_hgt); + x = rand_int(cur_wid); + + /* Require "naked" floor grid */ + if (!cave_naked_bold(y, x)) continue; + + /* Require a certain number of adjacent walls */ + if (next_to_walls(y, x) < walls) continue; + } + + /* Town -- must go down */ + if (!dun_level) + { + /* Clear previous contents, add down stairs */ + if (dungeon_flags1 & DF1_FLAT) + { + cave_set_feat(y, x, FEAT_WAY_MORE); + } + else if ((rand_int(3) == 0) && (!(dungeon_flags2 & DF2_NO_SHAFT))) + { + cave_set_feat(y, x, FEAT_SHAFT_DOWN); + } + else + { + cave_set_feat(y, x, FEAT_MORE); + } + } + + /* Quest -- must go up */ + else if ((is_quest(dun_level) && (dun_level >= 1)) || + ((dun_level >= d_info[dungeon_type].maxdepth) && + (!(dungeon_flags1 & DF1_FORCE_DOWN)))) + { + /* Clear previous contents, add up stairs */ + if (dungeon_flags1 & DF1_FLAT) + { + cave_set_feat(y, x, FEAT_WAY_LESS); + } + else if ((rand_int(3) == 0) && (!(dungeon_flags2 & DF2_NO_SHAFT))) + { + cave_set_feat(y, x, FEAT_SHAFT_UP); + } + else + { + cave_set_feat(y, x, FEAT_LESS); + } + } + + /* Requested type */ + else + { + /* Clear previous contents, add stairs */ + cave_set_feat(y, x, feat); + } + + cave[y][x].special = branch; + + /* Count the number of stairs we've actually managed to place. */ + cnt++; + + /* All done */ + break; + } + + /* Require fewer walls */ + if (walls) walls--; + } +} + + + + +/* + * Allocates some objects (using "place" and "type") + */ +static void alloc_object(int set, int typ, int num) +{ + int y = 1, x = 1, k; + int dummy = 0; + + /* Place some objects */ + for (k = 0; k < num; k++) + { + /* Pick a "legal" spot */ + while (dummy < SAFE_MAX_ATTEMPTS) + { + bool_ room; + + dummy++; + + /* Location */ + y = rand_int(cur_hgt); + x = rand_int(cur_wid); + + /* Require "naked" floor grid */ + if (!cave_naked_bold(y, x)) continue; + + /* Check for "room" */ + room = (cave[y][x].info & (CAVE_ROOM)) ? TRUE : FALSE; + + /* Require corridor? */ + if ((set == ALLOC_SET_CORR) && room) continue; + + /* Require room? */ + if ((set == ALLOC_SET_ROOM) && !room) continue; + + /* Accept it */ + break; + } + + if (dummy >= SAFE_MAX_ATTEMPTS) + { + if (cheat_room) + { + msg_format("Warning! Could not place object, type : %d!", typ); + } + + return; + } + + + /* Place something */ + switch (typ) + { + case ALLOC_TYP_RUBBLE: + { + place_rubble(y, x); + break; + } + + case ALLOC_TYP_TRAP: + { + place_trap(y, x); + break; + } + + case ALLOC_TYP_GOLD: + { + place_gold(y, x); + break; + } + + case ALLOC_TYP_OBJECT: + { + place_object(y, x, FALSE, FALSE, OBJ_FOUND_FLOOR); + break; + } + + case ALLOC_TYP_ALTAR: + { + place_altar(y, x); + break; + } + + case ALLOC_TYP_BETWEEN: + { + place_between(y, x); + break; + } + + case ALLOC_TYP_FOUNTAIN: + { + place_fountain(y, x); + break; + } + } + } +} + + +/* + * The following functions create a rectangle (e.g. outer wall of rooms) + */ +void build_rectangle(int y1, int x1, int y2, int x2, int feat, int info) +{ + int y, x; + + /* Top and bottom boundaries */ + for (x = x1; x <= x2; x++) + { + cave_set_feat(y1, x, feat); + cave[y1][x].info |= (info); + + cave_set_feat(y2, x, feat); + cave[y2][x].info |= (info); + } + + /* Top and bottom boundaries */ + for (y = y1; y <= y2; y++) + { + cave_set_feat(y, x1, feat); + cave[y][x1].info |= (info); + + cave_set_feat(y, x2, feat); + cave[y][x2].info |= (info); + } +} + + +/* + * Place water through the dungeon using recursive fractal algorithm + * + * Why do those good at math and/or algorithms tend *not* to + * place any spaces around binary operators? I've been always + * wondering. This seems almost a unversal phenomenon... + * Tried to make those conform to the rule, but there may still + * some left untouched... + */ +static void recursive_river(int x1, int y1, int x2, int y2, + int feat1, int feat2, int width) +{ + int dx, dy, length, l, x, y; + int changex, changey; + int ty, tx; + + + length = distance(x1, y1, x2, y2); + + if (length > 4) + { + /* + * Divide path in half and call routine twice. + * There is a small chance of splitting the river + */ + dx = (x2 - x1) / 2; + dy = (y2 - y1) / 2; + + if (dy != 0) + { + /* perturbation perpendicular to path */ + changex = randint(abs(dy)) * 2 - abs(dy); + } + else + { + changex = 0; + } + + if (dx != 0) + { + /* perturbation perpendicular to path */ + changey = randint(abs(dx)) * 2 - abs(dx); + } + else + { + changey = 0; + } + + + + /* construct river out of two smaller ones */ + recursive_river(x1, y1, x1 + dx + changex, y1 + dy + changey, + feat1, feat2, width); + recursive_river(x1 + dx + changex, y1 + dy + changey, x2, y2, + feat1, feat2, width); + + /* Split the river some of the time -junctions look cool */ + if ((width > 0) && (rand_int(DUN_WAT_CHG) == 0)) + { + recursive_river(x1 + dx + changex, y1 + dy + changey, + x1 + 8 * (dx + changex), y1 + 8 * (dy + changey), + feat1, feat2, width - 1); + } + } + + /* Actually build the river */ + else + { + for (l = 0; l < length; l++) + { + x = x1 + l * (x2 - x1) / length; + y = y1 + l * (y2 - y1) / length; + + for (ty = y - width - 1; ty <= y + width + 1; ty++) + { + for (tx = x - width - 1; tx <= x + width + 1; tx++) + { + if (!in_bounds(ty, tx)) continue; + + if (cave[ty][tx].feat == feat1) continue; + if (cave[ty][tx].feat == feat2) continue; + + if (distance(ty, tx, y, x) > rand_spread(width, 1)) continue; + + /* Do not convert permanent features */ + if (cave_perma_bold(ty, tx)) continue; + + /* + * Clear previous contents, add feature + * The border mainly gets feat2, while the center + * gets feat1 + */ + if (distance(ty, tx, y, x) > width) + { + cave_set_feat(ty, tx, feat2); + } + else + { + cave_set_feat(ty, tx, feat1); + } + + /* Lava terrain glows */ + if ((feat1 == FEAT_DEEP_LAVA) || + (feat1 == FEAT_SHAL_LAVA)) + { + cave[ty][tx].info |= CAVE_GLOW; + } + + /* Hack -- don't teleport here */ + cave[ty][tx].info |= CAVE_ICKY; + } + } + } + } +} + + +/* + * Places water through dungeon. + */ +static void add_river(int feat1, int feat2) +{ + int y2, x2; + int y1 = 0, x1 = 0, wid; + + + /* Hack -- Choose starting point */ + y2 = randint(cur_hgt / 2 - 2) + cur_hgt / 2; + x2 = randint(cur_wid / 2 - 2) + cur_wid / 2; + + /* Hack -- Choose ending point somewhere on boundary */ + switch (randint(4)) + { + case 1: + { + /* top boundary */ + x1 = randint(cur_wid - 2) + 1; + y1 = 1; + break; + } + case 2: + { + /* left boundary */ + x1 = 1; + y1 = randint(cur_hgt - 2) + 1; + break; + } + case 3: + { + /* right boundary */ + x1 = cur_wid - 1; + y1 = randint(cur_hgt - 2) + 1; + break; + } + case 4: + { + /* bottom boundary */ + x1 = randint(cur_wid - 2) + 1; + y1 = cur_hgt - 1; + break; + } + } + wid = randint(DUN_WAT_RNG); + recursive_river(x1, y1, x2, y2, feat1, feat2, wid); +} + + +/* + * Places "streamers" of rock through dungeon + * + * Note that their are actually six different terrain features used + * to represent streamers. Three each of magma and quartz, one for + * basic vein, one with hidden gold, and one with known gold. The + * hidden gold types are currently unused. + */ +static void build_streamer(int feat, int chance) +{ + int i, tx, ty; + int y, x, dir; + int dummy = 0; + cave_type *c_ptr; + + + /* Hack -- Choose starting point */ + y = rand_spread(cur_hgt / 2, 10); + x = rand_spread(cur_wid / 2, 15); + + /* Choose a random compass direction */ + dir = ddd[rand_int(8)]; + + /* Place streamer into dungeon */ + while (dummy < SAFE_MAX_ATTEMPTS) + { + dummy++; + + /* One grid per density */ + for (i = 0; i < DUN_STR_DEN; i++) + { + int d = DUN_STR_RNG; + + /* Pick a nearby grid */ + while (1) + { + ty = rand_spread(y, d); + tx = rand_spread(x, d); + if (!in_bounds2(ty, tx)) continue; + break; + } + + /* Access the grid */ + c_ptr = &cave[ty][tx]; + + /* Only convert "granite" walls */ + if ((c_ptr->feat != feat_wall_inner) && + (c_ptr->feat != feat_wall_outer) && + (c_ptr->feat != d_info[dungeon_type].fill_type1) && + (c_ptr->feat != d_info[dungeon_type].fill_type2) && + (c_ptr->feat != d_info[dungeon_type].fill_type3)) continue; + + /* Clear mimic feature to avoid nasty consequences */ + c_ptr->mimic = 0; + + /* Clear previous contents, add proper vein type */ + cave_set_feat(ty, tx, feat); + + /* Hack -- Add some (known) treasure */ + if (rand_int(chance) == 0) + { + cave_set_feat(ty, tx, c_ptr->feat + 0x04); + } + } + + if (dummy >= SAFE_MAX_ATTEMPTS) + { + if (cheat_room) + { + msg_print("Warning! Could not place streamer!"); + } + return; + } + + + /* Advance the streamer */ + y += ddy[dir]; + x += ddx[dir]; + + /* Quit before leaving the dungeon */ + if (!in_bounds(y, x)) break; + } +} + + + +/* + * Place streams of water, lava, & trees -KMW- + * This routine varies the placement based on dungeon level + * otherwise is similar to build_streamer + */ +static void build_streamer2(int feat, int killwall) +{ + int i, j, mid, tx, ty; + int y, x, dir; + int poolchance; + int poolsize; + cave_type *c_ptr; + + poolchance = randint(10); + + /* Hack -- Choose starting point */ + y = rand_spread(cur_hgt / 2, 10); + x = rand_spread(cur_wid / 2, 15); + + /* Choose a random compass direction */ + dir = ddd[rand_int(8)]; + + /* Place streamer into dungeon */ + if (poolchance > 2) + { + while (TRUE) + { + /* One grid per density */ + for (i = 0; i < (DUN_STR_DWLW + 1); i++) + { + int d = DUN_STR_WLW; + + /* Pick a nearby grid */ + while (1) + { + ty = rand_spread(y, d); + tx = rand_spread(x, d); + if (in_bounds(ty, tx)) break; + } + + /* Access grid */ + c_ptr = &cave[ty][tx]; + + /* Never convert vaults */ + if (c_ptr->info & (CAVE_ICKY)) continue; + + /* Reject permanent features */ + if ((f_info[c_ptr->feat].flags1 & (FF1_PERMANENT)) && + (f_info[c_ptr->feat].flags1 & (FF1_FLOOR))) continue; + + /* Avoid converting walls when told so */ + if (killwall == 0) + { + if (f_info[c_ptr->feat].flags1 & FF1_WALL) continue; + } + + /* Clear mimic feature to avoid nasty consequences */ + c_ptr->mimic = 0; + + /* Clear previous contents, add proper vein type */ + cave_set_feat(ty, tx, feat); + } + + /* Advance the streamer */ + y += ddy[dir]; + x += ddx[dir]; + + /* Change direction */ + if (rand_int(20) == 0) dir = ddd[rand_int(8)]; + + /* Stop at dungeon edge */ + if (!in_bounds(y, x)) break; + } + } + + /* Create pool */ + else if ((feat == FEAT_DEEP_WATER) || (feat == FEAT_DEEP_LAVA)) + { + poolsize = 5 + randint(10); + mid = poolsize / 2; + + /* One grid per density */ + for (i = 0; i < poolsize; i++) + { + for (j = 0; j < poolsize; j++) + { + tx = x + j; + ty = y + i; + + if (!in_bounds(ty, tx)) continue; + + if (i < mid) + { + if (j < mid) + { + if ((i + j + 1) < mid) + continue; + } + else if (j > (mid + i)) + continue; + } + else if (j < mid) + { + if (i > (mid + j)) + continue; + } + else if ((i + j) > ((mid * 3)-1)) + continue; + + /* Only convert non-permanent features */ + if (f_info[cave[ty][tx].feat].flags1 & FF1_PERMANENT) continue; + + /* Clear mimic feature to avoid nasty consequences */ + cave[ty][tx].mimic = 0; + + /* Clear previous contents, add proper vein type */ + cave_set_feat(ty, tx, feat); + } + } + } +} + + + +/* + * Build a destroyed level + */ +static void destroy_level(void) +{ + int y1, x1, y, x, k, t, n; + + cave_type *c_ptr; + + /* Note destroyed levels */ + if ((cheat_room) || (p_ptr->precognition)) msg_print("Destroyed Level"); + + /* Drop a few epi-centers (usually about two) */ + for (n = 0; n < randint(5); n++) + { + /* Pick an epi-center */ + x1 = rand_range(5, cur_wid - 1 - 5); + y1 = rand_range(5, cur_hgt - 1 - 5); + + /* Big area of affect */ + for (y = (y1 - 15); y <= (y1 + 15); y++) + { + for (x = (x1 - 15); x <= (x1 + 15); x++) + { + /* Skip illegal grids */ + if (!in_bounds(y, x)) continue; + + /* Extract the distance */ + k = distance(y1, x1, y, x); + + /* Stay in the circle of death */ + if (k >= 16) continue; + + /* Delete the monster (if any) */ + delete_monster(y, x); + + /* Destroy valid grids */ + if (cave_valid_bold(y, x)) + { + /* Delete objects */ + delete_object(y, x); + + /* Access the grid */ + c_ptr = &cave[y][x]; + + /* Wall (or floor) type */ + t = rand_int(200); + + /* Granite */ + if (t < 20) + { + /* Create granite wall */ + cave_set_feat(y, x, FEAT_WALL_EXTRA); + } + + /* Quartz */ + else if (t < 60) + { + /* Create quartz vein */ + cave_set_feat(y, x, FEAT_QUARTZ); + } + + /* Magma */ + else if (t < 90) + { + /* Create magma vein */ + cave_set_feat(y, x, FEAT_MAGMA); + } + + /* Sand */ + else if (t < 110) + { + /* Create sand vein */ + cave_set_feat(y, x, FEAT_SANDWALL); + } + + /* Floor */ + else + { + /* Create floor */ + place_floor(y, x); + } + + /* No longer part of a room or vault */ + c_ptr->info &= ~(CAVE_ROOM | CAVE_ICKY); + + /* No longer illuminated or known */ + c_ptr->info &= ~(CAVE_MARK | CAVE_GLOW); + } + } + } + } +} + + +/* + * Function that sees if a square is a floor (Includes range checking) + */ +static bool_ get_is_floor(int x, int y) +{ + /* Out of bounds */ + if (!in_bounds(y, x)) return (FALSE); + + /* Do the real check: */ + if (f_info[cave[y][x].feat].flags1 & FF1_FLOOR) return (TRUE); + + return (FALSE); +} + + +/* + * Tunnel around a room if it will cut off part of a cave system + */ +static void check_room_boundary(int x1, int y1, int x2, int y2) +{ + int count, x, y; + bool_ old_is_floor, new_is_floor; + + /* Avoid doing this in irrelevant places -- pelpel */ + if (!(dungeon_flags1 & DF1_CAVERN)) return; + + /* Initialize */ + count = 0; + + old_is_floor = get_is_floor(x1 - 1, y1); + + /* + * Count the number of floor-wall boundaries around the room + * Note: diagonal squares are ignored since the player can move diagonally + * to bypass these if needed. + */ + + /* Above the top boundary */ + for (x = x1; x <= x2; x++) + { + new_is_floor = get_is_floor(x, y1 - 1); + + /* increment counter if they are different */ + if (new_is_floor != old_is_floor) count++; + + old_is_floor = new_is_floor; + } + + /* Right boundary */ + for (y = y1; y <= y2; y++) + { + new_is_floor = get_is_floor(x2 + 1, y); + + /* increment counter if they are different */ + if (new_is_floor != old_is_floor) count++; + + old_is_floor = new_is_floor; + } + + /* Bottom boundary*/ + for (x = x2; x >= x1; x--) + { + new_is_floor = get_is_floor(x, y2 + 1); + + /* Increment counter if they are different */ + if (new_is_floor != old_is_floor) count++; + + old_is_floor = new_is_floor; + } + + /* Left boundary */ + for (y = y2; y >= y1; y--) + { + new_is_floor = get_is_floor(x1 - 1, y); + + /* Increment counter if they are different */ + if (new_is_floor != old_is_floor) count++; + + old_is_floor = new_is_floor; + } + + + /* If all the same, or only one connection exit */ + if ((count == 0) || (count == 2)) return; + + + /* Tunnel around the room so to prevent problems with caves */ + for (y = y1; y <= y2; y++) + { + for (x = x1; x <= x2; x++) + { + if (in_bounds(y, x)) place_floor(y, x); + } + } +} + + +/* + * Create up to "num" objects near the given coordinates + * Only really called by some of the "vault" routines. + */ +static void vault_objects(int y, int x, int num) +{ + int dummy = 0; + int i = 0, j = y, k = x; + + + /* Attempt to place 'num' objects */ + for (; num > 0; --num) + { + /* Try up to 11 spots looking for empty space */ + for (i = 0; i < 11; ++i) + { + /* Pick a random location */ + while (dummy < SAFE_MAX_ATTEMPTS) + { + j = rand_spread(y, 2); + k = rand_spread(x, 3); + dummy++; + if (in_bounds(j, k)) break; + } + + + if (dummy >= SAFE_MAX_ATTEMPTS) + { + if (cheat_room) + { + msg_print("Warning! Could not place vault object!"); + } + } + + + /* Require "clean" floor space */ + if (!cave_clean_bold(j, k)) continue; + + /* Place an item */ + if (rand_int(100) < 75) + { + place_object(j, k, FALSE, FALSE, OBJ_FOUND_FLOOR); + } + + /* Place gold */ + else + { + place_gold(j, k); + } + + /* Placement accomplished */ + break; + } + } +} + + +/* + * Place a trap with a given displacement of point + */ +static void vault_trap_aux(int y, int x, int yd, int xd) +{ + int count = 0, y1 = y, x1 = x; + int dummy = 0; + + /* Place traps */ + for (count = 0; count <= 5; count++) + { + /* Get a location */ + while (dummy < SAFE_MAX_ATTEMPTS) + { + y1 = rand_spread(y, yd); + x1 = rand_spread(x, xd); + dummy++; + if (in_bounds(y1, x1)) break; + } + + if (dummy >= SAFE_MAX_ATTEMPTS) + { + if (cheat_room) + { + msg_print("Warning! Could not place vault trap!"); + } + } + + + /* Require "naked" floor grids */ + if (!cave_naked_bold(y1, x1)) continue; + + /* Place the trap */ + place_trap(y1, x1); + + /* Done */ + break; + } +} + + +/* + * Place some traps with a given displacement of given location + */ +static void vault_traps(int y, int x, int yd, int xd, int num) +{ + int i; + + for (i = 0; i < num; i++) + { + vault_trap_aux(y, x, yd, xd); + } +} + + +/* + * Hack -- Place some sleeping monsters near the given location + */ +static void vault_monsters(int y1, int x1, int num) +{ + int k, i, y, x; + + /* Try to summon "num" monsters "near" the given location */ + for (k = 0; k < num; k++) + { + /* Try nine locations */ + for (i = 0; i < 9; i++) + { + int d = 1; + + /* Pick a nearby location */ + scatter(&y, &x, y1, x1, d); + + /* Require "empty" floor grids */ + if (!cave_empty_bold(y, x)) continue; + + /* Place the monster (allow groups) */ + monster_level = dun_level + 2; + (void)place_monster(y, x, TRUE, TRUE); + monster_level = dun_level; + } + } +} + +/* + * Allocate the space needed by a room in the room_map array. + * + * width, height represent the size of the room (0...x-1) by (0...y-1). + * crowded is used to denote a monset nest. + * by0, bx0 are the positions in the room_map array given to the build_type'x' + * function. + * cx, cy are the returned center of the allocated room in coordinates for + * cave.feat and cave.info etc. + */ +bool_ room_alloc(int width, int height, bool_ crowded, int by0, int bx0, int *cx, int *cy) +{ + int temp, eby, ebx, by, bx; + + /* Calculate number of room_map squares to allocate */ + + /* Total number along width */ + temp = ((width - 1) / BLOCK_WID) + 1; + + for (ebx = bx0 + temp; bx0 > 0 && ebx > dun->col_rooms; bx0--, ebx--); + + if (ebx > dun->col_rooms) return (FALSE); + + /* Total number along height */ + temp = ((height - 1) / BLOCK_HGT) + 1; + + for (eby = by0 + temp; by0 > 0 && eby > dun->row_rooms; by0--, eby--); + + /* Never run off the screen */ + if (eby > dun->row_rooms) return (FALSE); + + /* Verify open space */ + for (by = by0; by < eby; by++) + { + for (bx = bx0; bx < ebx; bx++) + { + if (dun->room_map[by][bx]) return (FALSE); + } + } + + /* + * It is *extremely* important that the following calculation + * be *exactly* correct to prevent memory errors XXX XXX XXX + */ + + /* Acquire the location of the room */ + *cy = ((by0 + eby) * BLOCK_HGT) / 2; + *cx = ((bx0 + ebx) * BLOCK_WID) / 2; + + /* Save the room location */ + if (dun->cent_n < CENT_MAX) + { + dun->cent[dun->cent_n].y = *cy; + dun->cent[dun->cent_n].x = *cx; + dun->cent_n++; + } + + /* Reserve some blocks */ + for (by = by0; by < eby; by++) + { + for (bx = bx0; bx < ebx; bx++) + { + dun->room_map[by][bx] = TRUE; + } + } + + /* Count "crowded" rooms */ + if (crowded) dun->crowded = TRUE; + + /* + * Hack -- See if room will cut off a cavern. + * If so, fix by tunneling outside the room in such a way as + * to conect the caves. + */ + check_room_boundary(*cx - width / 2 - 1, *cy - height / 2 - 1, + *cx + width / 2 + 1, *cy + height / 2 + 1); + + /* Success */ + return (TRUE); +} + +/* + * Room building routines. + * + * Room types: + * 1 -- normal + * 2 -- overlapping + * 3 -- cross shaped + * 4 -- large room with features + * 5 -- monster nests + * 6 -- monster pits + * 7 -- simple vaults + * 8 -- greater vaults + * 9 -- circular rooms + */ + +/* + * Type 1 -- normal rectangular rooms + */ +static void build_type1(int by0, int bx0) +{ + u16b info; + int y, x = 1, y2, x2, yval, xval; + int y1, x1, xsize, ysize; + + /* Pick a room size */ + y1 = rand_range(1, 4); + x1 = rand_range(1, 10); + y2 = rand_range(1, 3); + x2 = rand_range(1, 9); + + xsize = x1 + x2; + ysize = y1 + y2; + + /* Try to allocate space for room. If fails, exit */ + if (!room_alloc(xsize + 2, ysize + 2, FALSE, by0, bx0, &xval, &yval)) return; + + /* Get corner values */ + y1 = yval - ysize / 2; + x1 = xval - xsize / 2; + y2 = y1 + ysize - 1; + x2 = x1 + xsize - 1; + + info = (dun_level <= randint(25)) ? (CAVE_ROOM|CAVE_GLOW) : CAVE_ROOM; + + /* Place a full floor under the room */ + for (y = y1; y <= y2; y++) + { + for (x = x1; x <= x2; x++) + { + place_floor(y, x); + cave[y][x].info |= info; + } + } + + /* Walls around the room */ + build_rectangle(y1 - 1, x1 - 1, y2 + 1, x2 + 1, feat_wall_outer, info); + + /* Hack -- Occasional pillar room */ + if (ysize > 2 && xsize > 2) + { + if (rand_int(20) == 0) + { + for (y = y1; y <= y2; y += 2) + { + for (x = x1; x <= x2; x += 2) + { + cave_set_feat(y, x, feat_wall_inner); + } + } + } + + /* Hack -- Occasional ragged-edge room */ + else if (rand_int(50) == 0) + { + for (y = y1 + 2; y <= y2 - 2; y += 2) + { + cave_set_feat(y, x1, feat_wall_inner); + cave_set_feat(y, x2, feat_wall_inner); + } + for (x = x1 + 2; x <= x2 - 2; x += 2) + { + cave_set_feat(y1, x, feat_wall_inner); + cave_set_feat(y2, x, feat_wall_inner); + } + } + } +} + +/* + * Type 2 -- Overlapping rectangular rooms + */ +static void build_type2(int by0, int bx0) +{ + u16b info; + int y, x, yval, xval; + int y1a, x1a, y2a, x2a; + int y1b, x1b, y2b, x2b; + + /* Try to allocate space for room. If fails, exit */ + if (!room_alloc(25, 11, FALSE, by0, bx0, &xval, &yval)) return; + + /* Determine extents of the first room */ + y1a = yval - randint(4); + y2a = yval + randint(3); + x1a = xval - randint(14); + x2a = xval + randint(6); + + /* Determine extents of the second room */ + y1b = yval - randint(3); + y2b = yval + randint(4); + x1b = xval - randint(6); + x2b = xval + randint(14); + + info = (dun_level <= randint(25)) ? (CAVE_ROOM|CAVE_GLOW) : CAVE_ROOM; + + /* Place the walls around room "a" */ + build_rectangle(y1a - 1, x1a - 1, y2a + 1, x2a + 1, feat_wall_outer, info); + + /* Place the walls around room "a" */ + build_rectangle(y1b - 1, x1b - 1, y2b + 1, x2b + 1, feat_wall_outer, info); + + /* Replace the floor for room "a" */ + for (y = y1a; y <= y2a; y++) + { + for (x = x1a; x <= x2a; x++) + { + place_floor(y, x); + cave[y][x].info |= info; + } + } + + /* Replace the floor for room "b" */ + for (y = y1b; y <= y2b; y++) + { + for (x = x1b; x <= x2b; x++) + { + place_floor(y, x); + cave[y][x].info |= info; + } + } +} + +/* + * Type 3 -- Cross shaped rooms + * + * Builds a room at a row, column coordinate + * + * Room "a" runs north/south, and Room "b" runs east/east + * So the "central pillar" runs from x1a,y1b to x2a,y2b. + * + * Note that currently, the "center" is always 3x3, but I think that + * the code below will work (with "bounds checking") for 5x5, or even + * for unsymetric values like 4x3 or 5x3 or 3x4 or 3x5, or even larger. + */ +static void build_type3(int by0, int bx0) +{ + u16b info; + int y, x, dy, dx, wy, wx; + int y1a, x1a, y2a, x2a; + int y1b, x1b, y2b, x2b; + int yval, xval; + + /* Try to allocate space for room. If fails, exit */ + if (!room_alloc(25, 11, FALSE, by0, bx0, &xval, &yval)) return; + + /* For now, always 3x3 */ + wx = wy = 1; + + /* Pick max vertical size (at most 4) */ + dy = rand_range(3, 4); + + /* Pick max horizontal size (at most 11) */ + dx = rand_range(3, 11); + + /* Determine extents of the north/south room */ + y1a = yval - dy; + y2a = yval + dy; + x1a = xval - wx; + x2a = xval + wx; + + /* Determine extents of the east/west room */ + y1b = yval - wy; + y2b = yval + wy; + x1b = xval - dx; + x2b = xval + dx; + + info = (dun_level <= randint(25)) ? (CAVE_ROOM|CAVE_GLOW) : CAVE_ROOM; + + /* Place the walls around room "a" */ + build_rectangle(y1a - 1, x1a - 1, y2a + 1, x2a + 1, feat_wall_outer, info); + + /* Place the walls around room "a" */ + build_rectangle(y1b - 1, x1b - 1, y2b + 1, x2b + 1, feat_wall_outer, info); + + /* Replace the floor for room "a" */ + for (y = y1a; y <= y2a; y++) + { + for (x = x1a; x <= x2a; x++) + { + place_floor(y, x); + cave[y][x].info |= info; + } + } + + /* Replace the floor for room "b" */ + for (y = y1b; y <= y2b; y++) + { + for (x = x1b; x <= x2b; x++) + { + place_floor(y, x); + cave[y][x].info |= info; + } + } + + /* Special features (3/4) */ + switch (rand_int(4)) + { + /* Large solid middle pillar */ + case 1: + { + for (y = y1b; y <= y2b; y++) + { + for (x = x1a; x <= x2a; x++) + { + cave_set_feat(y, x, feat_wall_inner); + } + } + break; + } + + /* Inner treasure vault */ + case 2: + { + /* Build the vault */ + build_rectangle(y1b, x1a, y2b, x2a, feat_wall_inner, info); + + /* Place a secret door on the inner room */ + switch (rand_int(4)) + { + case 0: + place_secret_door(y1b, xval); + break; + case 1: + place_secret_door(y2b, xval); + break; + case 2: + place_secret_door(yval, x1a); + break; + case 3: + place_secret_door(yval, x2a); + break; + } + + /* Place a treasure in the vault */ + place_object(yval, xval, FALSE, FALSE, OBJ_FOUND_FLOOR); + + /* Let's guard the treasure well */ + vault_monsters(yval, xval, rand_int(2) + 3); + + /* Traps naturally */ + vault_traps(yval, xval, 4, 4, rand_int(3) + 2); + + break; + } + + /* Something else */ + case 3: + { + /* Occasionally pinch the center shut */ + if (rand_int(3) == 0) + { + /* Pinch the east/west sides */ + for (y = y1b; y <= y2b; y++) + { + if (y == yval) continue; + cave_set_feat(y, x1a - 1, feat_wall_inner); + cave_set_feat(y, x2a + 1, feat_wall_inner); + } + + /* Pinch the north/south sides */ + for (x = x1a; x <= x2a; x++) + { + if (x == xval) continue; + cave_set_feat(y1b - 1, x, feat_wall_inner); + cave_set_feat(y2b + 1, x, feat_wall_inner); + } + + /* Sometimes shut using secret doors */ + if (rand_int(3) == 0) + { + place_secret_door(yval, x1a - 1); + place_secret_door(yval, x2a + 1); + place_secret_door(y1b - 1, xval); + place_secret_door(y2b + 1, xval); + } + } + + /* Occasionally put a "plus" in the center */ + else if (rand_int(3) == 0) + { + cave_set_feat(yval, xval, feat_wall_inner); + cave_set_feat(y1b, xval, feat_wall_inner); + cave_set_feat(y2b, xval, feat_wall_inner); + cave_set_feat(yval, x1a, feat_wall_inner); + cave_set_feat(yval, x2a, feat_wall_inner); + } + + /* Occasionally put a pillar in the center */ + else if (rand_int(3) == 0) + { + cave_set_feat(yval, xval, feat_wall_inner); + } + + break; + } + } +} + +/* + * Type 4 -- Large room with inner features + * + * Possible sub-types: + * 1 - Just an inner room with one door + * 2 - An inner room within an inner room + * 3 - An inner room with pillar(s) + * 4 - Inner room has a maze + * 5 - A set of four inner rooms + */ +static void build_type4(int by0, int bx0) +{ + u16b info; + int y, x, y1, x1; + int y2, x2, tmp, yval, xval; + + /* Try to allocate space for room. If fails, exit */ + if (!room_alloc(25, 11, FALSE, by0, bx0, &xval, &yval)) return; + + /* Large room */ + y1 = yval - 4; + y2 = yval + 4; + x1 = xval - 11; + x2 = xval + 11; + + info = (dun_level <= randint(25)) ? (CAVE_ROOM|CAVE_GLOW) : CAVE_ROOM; + + /* Place a full floor under the room */ + for (y = y1 - 1; y <= y2 + 1; y++) + { + for (x = x1 - 1; x <= x2 + 1; x++) + { + place_floor(y, x); + cave[y][x].info |= info; + } + } + + /* Outer Walls */ + build_rectangle(y1 - 1, x1 - 1, y2 + 1, x2 + 1, feat_wall_outer, info); + + /* The inner room */ + y1 = y1 + 2; + y2 = y2 - 2; + x1 = x1 + 2; + x2 = x2 - 2; + + /* The inner walls */ + build_rectangle(y1 - 1, x1 - 1, y2 + 1, x2 + 1, feat_wall_inner, info); + + /* Inner room variations */ + switch (randint(5)) + { + /* Just an inner room with a monster */ + case 1: + { + /* Place a secret door */ + switch (randint(4)) + { + case 1: + place_secret_door(y1 - 1, xval); + break; + case 2: + place_secret_door(y2 + 1, xval); + break; + case 3: + place_secret_door(yval, x1 - 1); + break; + case 4: + place_secret_door(yval, x2 + 1); + break; + } + + /* Place a monster in the room */ + vault_monsters(yval, xval, 1); + + break; + } + + /* Treasure Vault (with a door) */ + case 2: + { + /* Place a secret door */ + switch (randint(4)) + { + case 1: + place_secret_door(y1 - 1, xval); + break; + case 2: + place_secret_door(y2 + 1, xval); + break; + case 3: + place_secret_door(yval, x1 - 1); + break; + case 4: + place_secret_door(yval, x2 + 1); + break; + } + + /* Place another inner room */ + build_rectangle(yval - 1, xval - 1, yval + 1, xval + 1, + feat_wall_inner, info); + + /* Place a locked door on the inner room */ + switch (randint(4)) + { + case 1: + place_locked_door(yval - 1, xval); + break; + case 2: + place_locked_door(yval + 1, xval); + break; + case 3: + place_locked_door(yval, xval - 1); + break; + case 4: + place_locked_door(yval, xval + 1); + break; + } + + /* Monsters to guard the "treasure" */ + vault_monsters(yval, xval, randint(3) + 2); + + /* Object (80%) */ + if (rand_int(100) < 80) + { + place_object(yval, xval, FALSE, FALSE, OBJ_FOUND_FLOOR); + } + + /* Stairs (20%) */ + else + { + place_random_stairs(yval, xval); + } + + /* Traps to protect the treasure */ + vault_traps(yval, xval, 4, 10, 2 + randint(3)); + + break; + } + + /* Inner pillar(s). */ + case 3: + { + /* Place a secret door */ + switch (randint(4)) + { + case 1: + place_secret_door(y1 - 1, xval); + break; + case 2: + place_secret_door(y2 + 1, xval); + break; + case 3: + place_secret_door(yval, x1 - 1); + break; + case 4: + place_secret_door(yval, x2 + 1); + break; + } + + /* Large Inner Pillar */ + for (y = yval - 1; y <= yval + 1; y++) + { + for (x = xval - 1; x <= xval + 1; x++) + { + cave_set_feat(y, x, feat_wall_inner); + } + } + + /* Occasionally, two more Large Inner Pillars */ + if (rand_int(2) == 0) + { + tmp = randint(2); + for (y = yval - 1; y <= yval + 1; y++) + { + for (x = xval - 5 - tmp; x <= xval - 3 - tmp; x++) + { + cave_set_feat(y, x, feat_wall_inner); + } + for (x = xval + 3 + tmp; x <= xval + 5 + tmp; x++) + { + cave_set_feat(y, x, feat_wall_inner); + } + } + } + + /* Occasionally, some Inner rooms */ + if (rand_int(3) == 0) + { + /* Long horizontal walls */ + for (x = xval - 5; x <= xval + 5; x++) + { + cave_set_feat(yval - 1, x, feat_wall_inner); + cave_set_feat(yval + 1, x, feat_wall_inner); + } + + /* Close off the left/right edges */ + cave_set_feat(yval, xval - 5, feat_wall_inner); + cave_set_feat(yval, xval + 5, feat_wall_inner); + + /* Secret doors (random top/bottom) */ + place_secret_door(yval - 3 + (randint(2) * 2), xval - 3); + place_secret_door(yval - 3 + (randint(2) * 2), xval + 3); + + /* Monsters */ + vault_monsters(yval, xval - 2, randint(2)); + vault_monsters(yval, xval + 2, randint(2)); + + /* Objects */ + if (rand_int(3) == 0) place_object(yval, xval - 2, FALSE, FALSE, OBJ_FOUND_FLOOR); + if (rand_int(3) == 0) place_object(yval, xval + 2, FALSE, FALSE, OBJ_FOUND_FLOOR); + } + + break; + } + + /* Maze inside. */ + case 4: + { + /* Place a secret door */ + switch (randint(4)) + { + case 1: + place_secret_door(y1 - 1, xval); + break; + case 2: + place_secret_door(y2 + 1, xval); + break; + case 3: + place_secret_door(yval, x1 - 1); + break; + case 4: + place_secret_door(yval, x2 + 1); + break; + } + + /* Maze (really a checkerboard) */ + for (y = y1; y <= y2; y++) + { + for (x = x1; x <= x2; x++) + { + if (0x1 & (x + y)) + { + cave_set_feat(y, x, feat_wall_inner); + } + } + } + + /* Monsters just love mazes. */ + vault_monsters(yval, xval - 5, randint(3)); + vault_monsters(yval, xval + 5, randint(3)); + + /* Traps make them entertaining. */ + vault_traps(yval, xval - 3, 2, 8, randint(3)); + vault_traps(yval, xval + 3, 2, 8, randint(3)); + + /* Mazes should have some treasure too. */ + vault_objects(yval, xval, 3); + + break; + } + + /* Four small rooms. */ + case 5: + { + /* Inner "cross" */ + for (y = y1; y <= y2; y++) + { + cave_set_feat(y, xval, feat_wall_inner); + } + + for (x = x1; x <= x2; x++) + { + cave_set_feat(yval, x, feat_wall_inner); + } + + /* Doors into the rooms */ + if (rand_int(100) < 50) + { + int i = randint(10); + place_secret_door(y1 - 1, xval - i); + place_secret_door(y1 - 1, xval + i); + place_secret_door(y2 + 1, xval - i); + place_secret_door(y2 + 1, xval + i); + } + else + { + int i = randint(3); + place_secret_door(yval + i, x1 - 1); + place_secret_door(yval - i, x1 - 1); + place_secret_door(yval + i, x2 + 1); + place_secret_door(yval - i, x2 + 1); + } + + /* Treasure, centered at the center of the cross */ + vault_objects(yval, xval, 2 + randint(2)); + + /* Gotta have some monsters. */ + vault_monsters(yval + 1, xval - 4, randint(4)); + vault_monsters(yval + 1, xval + 4, randint(4)); + vault_monsters(yval - 1, xval - 4, randint(4)); + vault_monsters(yval - 1, xval + 4, randint(4)); + + break; + } + } +} + + +/* + * Determine if the given monster is appropriate for inclusion in + * a monster nest or monster pit or the given type. + * + * None of the pits/nests are allowed to include "unique" monsters, + * or monsters which can "multiply". + * + * Some of the pits/nests are asked to avoid monsters which can blink + * away or which are invisible. This is probably a hack. + * + * The old method made direct use of monster "names", which is bad. + * + * Note the use of Angband 2.7.9 monster race pictures in various places. + */ + + +/* + * Helper function for "monster nest (jelly)" + */ +static bool_ vault_aux_jelly(int r_idx) +{ + monster_race *r_ptr = &r_info[r_idx]; + + /* Decline unique monsters */ + if (r_ptr->flags1 & (RF1_UNIQUE)) return (FALSE); + + /* Also decline evil jellies (like death molds and shoggoths) */ + if (r_ptr->flags3 & (RF3_EVIL)) return (FALSE); + + /* Require icky thing, jelly, mold, or mushroom */ + if (!strchr("ijm,", r_ptr->d_char)) return (FALSE); + + /* Okay */ + return (TRUE); +} + + +/* + * Helper function for "monster nest (animal)" + */ +static bool_ vault_aux_animal(int r_idx) +{ + monster_race *r_ptr = &r_info[r_idx]; + + /* Decline unique monsters */ + if (r_ptr->flags1 & (RF1_UNIQUE)) return (FALSE); + + /* Require "animal" flag */ + if (!(r_ptr->flags3 & (RF3_ANIMAL))) return (FALSE); + + /* Okay */ + return (TRUE); +} + + +/* + * Helper function for "monster nest (undead)" + */ +static bool_ vault_aux_undead(int r_idx) +{ + monster_race *r_ptr = &r_info[r_idx]; + + /* Decline unique monsters */ + if (r_ptr->flags1 & (RF1_UNIQUE)) return (FALSE); + + /* Require Undead */ + if (!(r_ptr->flags3 & (RF3_UNDEAD))) return (FALSE); + + /* Okay */ + return (TRUE); +} + + +/* + * Helper function for "monster nest (chapel)" + */ +static bool_ vault_aux_chapel(int r_idx) +{ + monster_race *r_ptr = &r_info[r_idx]; + + /* Decline unique monsters */ + if (r_ptr->flags1 & (RF1_UNIQUE)) return (FALSE); + + /* Require "priest" or Angel */ + if (!((r_ptr->d_char == 'A') || strstr(r_ptr->name, "riest"))) + { + return (FALSE); + } + + /* Okay */ + return (TRUE); +} + + +/* + * Helper function for "monster nest (kennel)" + */ +static bool_ vault_aux_kennel(int r_idx) +{ + monster_race *r_ptr = &r_info[r_idx]; + + /* Decline unique monsters */ + if (r_ptr->flags1 & (RF1_UNIQUE)) return (FALSE); + + /* Require a Zephyr Hound or a dog */ + return ((r_ptr->d_char == 'Z') || (r_ptr->d_char == 'C')); + +} + + +/* + * Helper function for "monster nest (treasure)" + */ +static bool_ vault_aux_treasure(int r_idx) +{ + monster_race *r_ptr = &r_info[r_idx]; + + /* Decline unique monsters */ + if (r_ptr->flags1 & (RF1_UNIQUE)) return (FALSE); + + /* Require "priest" or Angel */ + if (!((r_ptr->d_char == '!') || (r_ptr->d_char == '|') || + (r_ptr->d_char == '$') || (r_ptr->d_char == '?') || + (r_ptr->d_char == '='))) + { + return (FALSE); + } + + /* Okay */ + return (TRUE); +} + + +/* + * Helper function for "monster nest (clone)" + */ +static bool_ vault_aux_clone(int r_idx) +{ + return (r_idx == template_race); +} + + +/* + * Helper function for "monster nest (symbol clone)" + */ +static bool_ vault_aux_symbol(int r_idx) +{ + return ((r_info[r_idx].d_char == (r_info[template_race].d_char)) + && !(r_info[r_idx].flags1 & RF1_UNIQUE)); +} + + +/* + * Helper function for "monster pit (orc)" + */ +static bool_ vault_aux_orc(int r_idx) +{ + monster_race *r_ptr = &r_info[r_idx]; + + /* Decline unique monsters */ + if (r_ptr->flags1 & (RF1_UNIQUE)) return (FALSE); + + /* Hack -- Require "o" monsters */ + if (!strchr("o", r_ptr->d_char)) return (FALSE); + + /* Okay */ + return (TRUE); +} + + + +/* + * Helper function for "monster pit (troll)" + */ +static bool_ vault_aux_troll(int r_idx) +{ + monster_race *r_ptr = &r_info[r_idx]; + + /* Decline unique monsters */ + if (r_ptr->flags1 & (RF1_UNIQUE)) return (FALSE); + + /* Hack -- Require "T" monsters */ + if (!strchr("T", r_ptr->d_char)) return (FALSE); + + /* Okay */ + return (TRUE); +} + + +/* + * Helper function for "monster pit (giant)" + */ +static bool_ vault_aux_giant(int r_idx) +{ + monster_race *r_ptr = &r_info[r_idx]; + + /* Decline unique monsters */ + if (r_ptr->flags1 & (RF1_UNIQUE)) return (FALSE); + + /* Hack -- Require "P" monsters */ + if (!strchr("P", r_ptr->d_char)) return (FALSE); + + /* Okay */ + return (TRUE); +} + + +/* + * Hack -- breath type for "vault_aux_dragon()" + */ +static u32b vault_aux_dragon_mask4; + + +/* + * Helper function for "monster pit (dragon)" + */ +static bool_ vault_aux_dragon(int r_idx) +{ + monster_race *r_ptr = &r_info[r_idx]; + + /* Decline unique monsters */ + if (r_ptr->flags1 & (RF1_UNIQUE)) return (FALSE); + + /* Hack -- Require "d" or "D" monsters */ + if (!strchr("Dd", r_ptr->d_char)) return (FALSE); + + /* Hack -- Require correct "breath attack" */ + if (r_ptr->flags4 != vault_aux_dragon_mask4) return (FALSE); + + /* Okay */ + return (TRUE); +} + + +/* + * Helper function for "monster pit (demon)" + */ +static bool_ vault_aux_demon(int r_idx) +{ + monster_race *r_ptr = &r_info[r_idx]; + + /* Decline unique monsters */ + if (r_ptr->flags1 & (RF1_UNIQUE)) return (FALSE); + + /* Hack -- Require "U" monsters */ + if (!strchr("U", r_ptr->d_char)) return (FALSE); + + /* Okay */ + return (TRUE); +} + + +/* + * Type 5 -- Monster nests + * + * A monster nest is a "big" room, with an "inner" room, containing + * a "collection" of monsters of a given type strewn about the room. + * + * The monsters are chosen from a set of 64 randomly selected monster + * races, to allow the nest creation to fail instead of having "holes". + * + * Note the use of the "get_mon_num_prep()" function, and the special + * "get_mon_num_hook()" restriction function, to prepare the "monster + * allocation table" in such a way as to optimize the selection of + * "appropriate" non-unique monsters for the nest. + * + * Currently, a monster nest is one of + * a nest of "jelly" monsters (Dungeon level 5 and deeper) + * a nest of "animal" monsters (Dungeon level 30 and deeper) + * a nest of "undead" monsters (Dungeon level 50 and deeper) + * + * Note that the "get_mon_num()" function may (rarely) fail, in which + * case the nest will be empty, and will not affect the level rating. + * + * Note that "monster nests" will never contain "unique" monsters. + */ +static void build_type5(int by0, int bx0) +{ + int y, x, y1, x1, y2, x2, xval, yval; + int tmp, i; + cptr name; + bool_ empty = FALSE; + bool_ (*old_get_mon_num_hook)(int r_idx); + s16b what[64]; + + /* Try to allocate space for room. If fails, exit */ + if (!room_alloc(25, 11, TRUE, by0, bx0, &xval, &yval)) return; + + /* Large room */ + y1 = yval - 4; + y2 = yval + 4; + x1 = xval - 11; + x2 = xval + 11; + + /* Place the floor area */ + for (y = y1; y <= y2; y++) + { + for (x = x1; x <= x2; x++) + { + place_floor(y, x); + cave[y][x].info |= (CAVE_ROOM); + } + } + + /* Place the outer walls */ + build_rectangle(y1 - 1, x1 - 1, y2 + 1, x2 + 1, feat_wall_outer, CAVE_ROOM); + + /* Advance to the center room */ + y1 = y1 + 2; + y2 = y2 - 2; + x1 = x1 + 2; + x2 = x2 - 2; + + /* The inner walls */ + build_rectangle(y1 - 1, x1 - 1, y2 + 1, x2 + 1, feat_wall_inner, CAVE_ROOM); + + /* Place a secret door */ + switch (randint(4)) + { + case 1: + place_secret_door(y1 - 1, xval); + break; + case 2: + place_secret_door(y2 + 1, xval); + break; + case 3: + place_secret_door(yval, x1 - 1); + break; + case 4: + place_secret_door(yval, x2 + 1); + break; + } + + /* Hack -- Choose a nest type */ + tmp = randint(dun_level); + + old_get_mon_num_hook = get_mon_num_hook; + + if ((tmp < 25) && (rand_int(2) != 0)) + { + while (1) + { + template_race = randint(max_r_idx - 2); + + /* Reject uniques */ + if (r_info[template_race].flags1 & RF1_UNIQUE) continue; + + /* Reject OoD monsters in a loose fashion */ + if (((r_info[template_race].level) + randint(5)) > + (dun_level + randint(5))) continue; + + /* Don't like 'break's like this, but this cannot be made better */ + break; + } + + if ((dun_level >= (25 + randint(15))) && (rand_int(2) != 0)) + { + name = "symbol clone"; + get_mon_num_hook = vault_aux_symbol; + } + else + { + name = "clone"; + get_mon_num_hook = vault_aux_clone; + } + } + else if (tmp < 25) + /* Monster nest (jelly) */ + { + /* Describe */ + name = "jelly"; + + /* Restrict to jelly */ + get_mon_num_hook = vault_aux_jelly; + } + + else if (tmp < 50) + { + name = "treasure"; + get_mon_num_hook = vault_aux_treasure; + } + + /* Monster nest (animal) */ + else if (tmp < 65) + { + if (rand_int(3) == 0) + { + name = "kennel"; + get_mon_num_hook = vault_aux_kennel; + } + else + { + /* Describe */ + name = "animal"; + + /* Restrict to animal */ + get_mon_num_hook = vault_aux_animal; + } + } + + /* Monster nest (undead) */ + else + { + if (rand_int(3) == 0) + { + name = "chapel"; + get_mon_num_hook = vault_aux_chapel; + } + else + { + /* Describe */ + name = "undead"; + + /* Restrict to undead */ + get_mon_num_hook = vault_aux_undead; + } + } + + /* Prepare allocation table */ + get_mon_num_prep(); + + /* Pick some monster types */ + for (i = 0; i < 64; i++) + { + /* Get a (hard) monster type */ + what[i] = get_mon_num(dun_level + 10); + + /* Notice failure */ + if (!what[i]) empty = TRUE; + } + + /* Remove restriction */ + get_mon_num_hook = old_get_mon_num_hook; + + /* Prepare allocation table */ + get_mon_num_prep(); + + /* Oops */ + if (empty) return; + + /* Describe */ + if (cheat_room || p_ptr->precognition) + { + /* Room type */ + msg_format("Monster nest (%s)", name); + } + + /* Increase the level rating */ + rating += 10; + + /* (Sometimes) Cause a "special feeling" (for "Monster Nests") */ + if ((dun_level <= 40) && (randint(dun_level * dun_level + 50) < 300)) + { + good_item_flag = TRUE; + } + + /* Place some monsters */ + for (y = yval - 2; y <= yval + 2; y++) + { + for (x = xval - 9; x <= xval + 9; x++) + { + int r_idx = what[rand_int(64)]; + + /* Place that "random" monster (no groups) */ + (void)place_monster_aux(y, x, r_idx, FALSE, FALSE, MSTATUS_ENEMY); + } + } +} + + + +/* + * Type 6 -- Monster pits + * + * A monster pit is a "big" room, with an "inner" room, containing + * a "collection" of monsters of a given type organized in the room. + * + * Monster types in the pit (list out of date...) + * orc pit (Dungeon Level 5 and deeper) + * troll pit (Dungeon Level 20 and deeper) + * giant pit (Dungeon Level 40 and deeper) + * dragon pit (Dungeon Level 60 and deeper) + * demon pit (Dungeon Level 80 and deeper) + * + * The inside room in a monster pit appears as shown below, where the + * actual monsters in each location depend on the type of the pit + * + * ##################### + * #0000000000000000000# + * #0112233455543322110# + * #0112233467643322110# + * #0112233455543322110# + * #0000000000000000000# + * ##################### + * + * Note that the monsters in the pit are now chosen by using "get_mon_num()" + * to request 16 "appropriate" monsters, sorting them by level, and using + * the "even" entries in this sorted list for the contents of the pit. + * + * Hack -- all of the "dragons" in a "dragon" pit must be the same "color", + * which is handled by requiring a specific "breath" attack for all of the + * dragons. This may include "multi-hued" breath. Note that "wyrms" may + * be present in many of the dragon pits, if they have the proper breath. + * + * Note the use of the "get_mon_num_prep()" function, and the special + * "get_mon_num_hook()" restriction function, to prepare the "monster + * allocation table" in such a way as to optimize the selection of + * "appropriate" non-unique monsters for the pit. + * + * Note that the "get_mon_num()" function may (rarely) fail, in which case + * the pit will be empty, and will not effect the level rating. + * + * Note that "monster pits" will never contain "unique" monsters. + */ +static void build_type6(int by0, int bx0) +{ + int tmp, what[16]; + int i, j, y, x, y1, x1, y2, x2, xval, yval; + bool_ empty = FALSE; + cptr name; + bool_ (*old_get_mon_num_hook)(int r_idx); + + /* Try to allocate space for room. If fails, exit */ + if (!room_alloc(25, 11, TRUE, by0, bx0, &xval, &yval)) return; + + /* Large room */ + y1 = yval - 4; + y2 = yval + 4; + x1 = xval - 11; + x2 = xval + 11; + + /* Place the floor area */ + for (y = y1 - 1; y <= y2 + 1; y++) + { + for (x = x1 - 1; x <= x2 + 1; x++) + { + place_floor(y, x); + cave[y][x].info |= (CAVE_ROOM); + } + } + + /* Place the outer walls */ + build_rectangle(y1 - 1, x1 - 1, y2 + 1, x2 + 1, feat_wall_outer, CAVE_ROOM); + + /* Advance to the center room */ + y1 = y1 + 2; + y2 = y2 - 2; + x1 = x1 + 2; + x2 = x2 - 2; + + /* The inner walls */ + build_rectangle(y1 - 1, x1 - 1, y2 + 1, x2 + 1, feat_wall_outer, CAVE_ROOM); + + /* Place a secret door */ + switch (randint(4)) + { + case 1: + place_secret_door(y1 - 1, xval); + break; + case 2: + place_secret_door(y2 + 1, xval); + break; + case 3: + place_secret_door(yval, x1 - 1); + break; + case 4: + place_secret_door(yval, x2 + 1); + break; + } + + /* Choose a pit type */ + tmp = randint(dun_level); + + old_get_mon_num_hook = get_mon_num_hook; + + /* Orc pit */ + if (tmp < 20) + { + /* Message */ + name = "orc"; + + /* Restrict monster selection */ + get_mon_num_hook = vault_aux_orc; + } + + /* Troll pit */ + else if (tmp < 40) + { + /* Message */ + name = "troll"; + + /* Restrict monster selection */ + get_mon_num_hook = vault_aux_troll; + } + + /* Giant pit */ + else if (tmp < 55) + { + /* Message */ + name = "giant"; + + /* Restrict monster selection */ + get_mon_num_hook = vault_aux_giant; + } + + else if (tmp < 70) + { + if (randint(4) != 1) + { + /* Message */ + name = "ordered clones"; + + do + { + template_race = randint(max_r_idx - 2); + } + while ((r_info[template_race].flags1 & RF1_UNIQUE) + || (((r_info[template_race].level) + randint(5)) > + (dun_level + randint(5)))); + + /* Restrict selection */ + get_mon_num_hook = vault_aux_symbol; + } + else + { + name = "ordered chapel"; + get_mon_num_hook = vault_aux_chapel; + } + + } + + /* Dragon pit */ + else if (tmp < 80) + { + /* Pick dragon type */ + switch (rand_int(6)) + { + /* Black */ + case 0: + { + /* Message */ + name = "acid dragon"; + + /* Restrict dragon breath type */ + vault_aux_dragon_mask4 = RF4_BR_ACID; + + /* Done */ + break; + } + + /* Blue */ + case 1: + { + /* Message */ + name = "electric dragon"; + + /* Restrict dragon breath type */ + vault_aux_dragon_mask4 = RF4_BR_ELEC; + + /* Done */ + break; + } + + /* Red */ + case 2: + { + /* Message */ + name = "fire dragon"; + + /* Restrict dragon breath type */ + vault_aux_dragon_mask4 = RF4_BR_FIRE; + + /* Done */ + break; + } + + /* White */ + case 3: + { + /* Message */ + name = "cold dragon"; + + /* Restrict dragon breath type */ + vault_aux_dragon_mask4 = RF4_BR_COLD; + + /* Done */ + break; + } + + /* Green */ + case 4: + { + /* Message */ + name = "poison dragon"; + + /* Restrict dragon breath type */ + vault_aux_dragon_mask4 = RF4_BR_POIS; + + /* Done */ + break; + } + + /* Multi-hued */ + default: + { + /* Message */ + name = "multi-hued dragon"; + + /* Restrict dragon breath type */ + vault_aux_dragon_mask4 = (RF4_BR_ACID | RF4_BR_ELEC | + RF4_BR_FIRE | RF4_BR_COLD | + RF4_BR_POIS); + + /* Done */ + break; + } + + } + + /* Restrict monster selection */ + get_mon_num_hook = vault_aux_dragon; + } + + /* Demon pit */ + else + { + /* Message */ + name = "demon"; + + /* Restrict monster selection */ + get_mon_num_hook = vault_aux_demon; + } + + /* Prepare allocation table */ + get_mon_num_prep(); + + /* Pick some monster types */ + for (i = 0; i < 16; i++) + { + /* Get a (hard) monster type */ + what[i] = get_mon_num(dun_level + 10); + + /* Notice failure */ + if (!what[i]) empty = TRUE; + } + + /* Remove restriction */ + get_mon_num_hook = old_get_mon_num_hook; + + /* Prepare allocation table */ + get_mon_num_prep(); + + /* Oops */ + if (empty) return; + + /* XXX XXX XXX */ + /* Sort the entries */ + for (i = 0; i < 16 - 1; i++) + { + /* Sort the entries */ + for (j = 0; j < 16 - 1; j++) + { + int i1 = j; + int i2 = j + 1; + + int p1 = r_info[what[i1]].level; + int p2 = r_info[what[i2]].level; + + /* Bubble */ + if (p1 > p2) + { + int tmp = what[i1]; + what[i1] = what[i2]; + what[i2] = tmp; + } + } + } + + /* Select the entries */ + for (i = 0; i < 8; i++) + { + /* Every other entry */ + what[i] = what[i * 2]; + } + + /* Message */ + if (cheat_room || p_ptr->precognition) + { + /* Room type */ + msg_format("Monster pit (%s)", name); + + if (cheat_hear || p_ptr->precognition) + { + /* Contents */ + for (i = 0; i < 8; i++) + { + /* Message */ + msg_print(r_info[what[i]].name); + } + } + } + + /* Increase the level rating */ + rating += 10; + + /* (Sometimes) Cause a "special feeling" (for "Monster Pits") */ + if ((dun_level <= 40) && (randint(dun_level * dun_level + 50) < 300)) + { + good_item_flag = TRUE; + } + + /* Top and bottom rows */ + for (x = xval - 9; x <= xval + 9; x++) + { + place_monster_aux(yval - 2, x, what[0], FALSE, FALSE, MSTATUS_ENEMY); + place_monster_aux(yval + 2, x, what[0], FALSE, FALSE, MSTATUS_ENEMY); + } + + /* Middle columns */ + for (y = yval - 1; y <= yval + 1; y++) + { + place_monster_aux(y, xval - 9, what[0], FALSE, FALSE, MSTATUS_ENEMY); + place_monster_aux(y, xval + 9, what[0], FALSE, FALSE, MSTATUS_ENEMY); + + place_monster_aux(y, xval - 8, what[1], FALSE, FALSE, MSTATUS_ENEMY); + place_monster_aux(y, xval + 8, what[1], FALSE, FALSE, MSTATUS_ENEMY); + + place_monster_aux(y, xval - 7, what[1], FALSE, FALSE, MSTATUS_ENEMY); + place_monster_aux(y, xval + 7, what[1], FALSE, FALSE, MSTATUS_ENEMY); + + place_monster_aux(y, xval - 6, what[2], FALSE, FALSE, MSTATUS_ENEMY); + place_monster_aux(y, xval + 6, what[2], FALSE, FALSE, MSTATUS_ENEMY); + + place_monster_aux(y, xval - 5, what[2], FALSE, FALSE, MSTATUS_ENEMY); + place_monster_aux(y, xval + 5, what[2], FALSE, FALSE, MSTATUS_ENEMY); + + place_monster_aux(y, xval - 4, what[3], FALSE, FALSE, MSTATUS_ENEMY); + place_monster_aux(y, xval + 4, what[3], FALSE, FALSE, MSTATUS_ENEMY); + + place_monster_aux(y, xval - 3, what[3], FALSE, FALSE, MSTATUS_ENEMY); + place_monster_aux(y, xval + 3, what[3], FALSE, FALSE, MSTATUS_ENEMY); + + place_monster_aux(y, xval - 2, what[4], FALSE, FALSE, MSTATUS_ENEMY); + place_monster_aux(y, xval + 2, what[4], FALSE, FALSE, MSTATUS_ENEMY); + } + + /* Above/Below the center monster */ + for (x = xval - 1; x <= xval + 1; x++) + { + place_monster_aux(yval + 1, x, what[5], FALSE, FALSE, MSTATUS_ENEMY); + place_monster_aux(yval - 1, x, what[5], FALSE, FALSE, MSTATUS_ENEMY); + } + + /* Next to the center monster */ + place_monster_aux(yval, xval + 1, what[6], FALSE, FALSE, MSTATUS_ENEMY); + place_monster_aux(yval, xval - 1, what[6], FALSE, FALSE, MSTATUS_ENEMY); + + /* Center monster */ + place_monster_aux(yval, xval, what[7], FALSE, FALSE, MSTATUS_ENEMY); +} + +/* + * Hack -- fill in "vault" rooms + */ +static void build_vault(int yval, int xval, int ymax, int xmax, cptr data) +{ + int dx, dy, x, y, bwy[8], bwx[8], i; + + cptr t; + + cave_type *c_ptr; + + /* Clean the between gates arrays */ + for (i = 0; i < 8; i++) + { + bwy[i] = bwx[i] = 9999; + } + + /* Place dungeon features and objects */ + for (t = data, dy = 0; dy < ymax; dy++) + { + for (dx = 0; dx < xmax; dx++, t++) + { + /* Extract the location */ + x = xval - (xmax / 2) + dx; + y = yval - (ymax / 2) + dy; + + /* Hack -- skip "non-grids" */ + if (*t == ' ') continue; + + /* Access the grid */ + c_ptr = &cave[y][x]; + + /* Lay down a floor */ + place_floor(y, x); + + /* Part of a vault */ + c_ptr->info |= (CAVE_ROOM | CAVE_ICKY); + + /* Analyze the grid */ + switch (*t) + { + /* Granite wall (outer) */ + case '%': + { + cave_set_feat(y, x, FEAT_WALL_OUTER); + break; + } + + /* Granite wall (inner) */ + case '#': + { + cave_set_feat(y, x, FEAT_WALL_INNER); + break; + } + + /* Permanent wall (inner) */ + case 'X': + { + cave_set_feat(y, x, FEAT_PERM_INNER); + break; + } + + /* Treasure/trap */ + case '*': + { + if (rand_int(100) < 75) + { + place_object(y, x, FALSE, FALSE, OBJ_FOUND_VAULT); + } + else + { + place_trap(y, x); + } + break; + } + + /* Secret doors */ + case '+': + { + place_secret_door(y, x); + break; + } + + /* Trap */ + case '^': + { + place_trap(y, x); + break; + } + + /* Glass wall */ + case 'G': + { + cave_set_feat(y, x, FEAT_GLASS_WALL); + break; + } + + /* Illusion wall */ + case 'I': + { + cave_set_feat(y, x, FEAT_ILLUS_WALL); + break; + } + } + } + } + + /* Place dungeon monsters and objects */ + for (t = data, dy = 0; dy < ymax; dy++) + { + for (dx = 0; dx < xmax; dx++, t++) + { + /* Extract the grid */ + x = xval - (xmax / 2) + dx; + y = yval - (ymax / 2) + dy; + + /* Hack -- skip "non-grids" */ + if (*t == ' ') continue; + + /* Access the grid */ + c_ptr = &cave[y][x]; + + /* Analyze the symbol */ + switch (*t) + { + /* Monster */ + case '&': + { + monster_level = dun_level + 5; + place_monster(y, x, TRUE, TRUE); + monster_level = dun_level; + break; + } + + /* Meaner monster */ + case '@': + { + monster_level = dun_level + 11; + place_monster(y, x, TRUE, TRUE); + monster_level = dun_level; + break; + } + + /* Meaner monster, plus treasure */ + case '9': + { + monster_level = dun_level + 9; + place_monster(y, x, TRUE, TRUE); + monster_level = dun_level; + object_level = dun_level + 7; + place_object(y, x, TRUE, FALSE, OBJ_FOUND_VAULT); + object_level = dun_level; + break; + } + + /* Nasty monster and treasure */ + case '8': + { + monster_level = dun_level + 40; + place_monster(y, x, TRUE, TRUE); + monster_level = dun_level; + object_level = dun_level + 20; + place_object(y, x, TRUE, TRUE, OBJ_FOUND_VAULT); + object_level = dun_level; + break; + } + + /* Monster and/or object */ + case ',': + { + if (rand_int(100) < 50) + { + monster_level = dun_level + 3; + place_monster(y, x, TRUE, TRUE); + monster_level = dun_level; + } + if (rand_int(100) < 50) + { + object_level = dun_level + 7; + place_object(y, x, FALSE, FALSE, OBJ_FOUND_VAULT); + object_level = dun_level; + } + break; + } + + case 'p': + { + cave_set_feat(y, x, FEAT_PATTERN_START); + break; + } + + case 'a': + { + cave_set_feat(y, x, FEAT_PATTERN_1); + break; + } + + case 'b': + { + cave_set_feat(y, x, FEAT_PATTERN_2); + break; + } + + case 'c': + { + cave_set_feat(y, x, FEAT_PATTERN_3); + break; + } + + case 'd': + { + cave_set_feat(y, x, FEAT_PATTERN_4); + break; + } + + case 'P': + { + cave_set_feat(y, x, FEAT_PATTERN_END); + break; + } + + case 'B': + { + cave_set_feat(y, x, FEAT_PATTERN_XTRA1); + break; + } + + case 'A': + { + object_level = dun_level + 12; + place_object(y, x, TRUE, FALSE, OBJ_FOUND_VAULT); + object_level = dun_level; + break; + } + + + /* Between gates */ + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + { + /* Not found before */ + if (bwy[*t - '0'] == 9999) + { + cave_set_feat(y, x, FEAT_BETWEEN); + bwy[*t - '0'] = y; + bwx[*t - '0'] = x; + } + /* The second time */ + else + { + cave_set_feat(y, x, FEAT_BETWEEN); + c_ptr->special = bwx[*t - '0'] + (bwy[*t - '0'] << 8); + cave[bwy[*t - '0']][bwx[*t - '0']].special = x + (y << 8); + } + break; + } + } + } + } +} + +/* + * Type 7 -- simple vaults (see "v_info.txt") + */ +static void build_type7(int by0, int bx0) +{ + vault_type *v_ptr = NULL; + int dummy = 0, xval, yval; + + /* Pick a lesser vault */ + while (dummy < SAFE_MAX_ATTEMPTS) + { + dummy++; + + /* Access a random vault record */ + v_ptr = &v_info[rand_int(max_v_idx)]; + + /* Accept the first lesser vault */ + if (v_ptr->typ == 7) break; + } + + /* Try to allocate space for room. If fails, exit */ + if (!room_alloc(v_ptr->wid, v_ptr->hgt, FALSE, by0, bx0, &xval, &yval)) + { + if (cheat_room) msg_print("Could not allocate this vault here"); + return; + } + + if (dummy >= SAFE_MAX_ATTEMPTS) + { + if (cheat_room) + { + msg_print("Warning! Could not place lesser vault!"); + } + return; + } + + + /* Message */ + if (cheat_room || p_ptr->precognition) msg_print("Lesser Vault"); + + /* Boost the rating */ + rating += v_ptr->rat; + + /* (Sometimes) Cause a special feeling */ + if ((dun_level <= 50) || + (randint((dun_level - 40) * (dun_level - 40) + 50) < 400)) + { + good_item_flag = TRUE; + } + + /* Hack -- Build the vault */ + build_vault(yval, xval, v_ptr->hgt, v_ptr->wid, v_ptr->data); +} + + + +/* + * Type 8 -- greater vaults (see "v_info.txt") + */ +static void build_type8(int by0, int bx0) +{ + vault_type *v_ptr = NULL; + int dummy = 0, xval, yval; + + /* Pick a lesser vault */ + while (dummy < SAFE_MAX_ATTEMPTS) + { + dummy++; + + /* Access a random vault record */ + v_ptr = &v_info[rand_int(max_v_idx)]; + + /* Accept the first greater vault */ + if (v_ptr->typ == 8) break; + } + + /* Try to allocate space for room. If fails, exit */ + if (!room_alloc(v_ptr->wid, v_ptr->hgt, FALSE, by0, bx0, &xval, &yval)) + { + if (cheat_room) msg_print("Could not allocate this vault here"); + return; + } + + if (dummy >= SAFE_MAX_ATTEMPTS) + { + if (cheat_room) + { + msg_print("Warning! Could not place greater vault!"); + } + return; + } + + + /* Message */ + if (cheat_room || p_ptr->precognition) msg_print("Greater Vault"); + + /* Boost the rating */ + rating += v_ptr->rat; + + /* (Sometimes) Cause a special feeling */ + if ((dun_level <= 50) || + (randint((dun_level - 40) * (dun_level - 40) + 50) < 400)) + { + good_item_flag = TRUE; + } + + /* Hack -- Build the vault */ + build_vault(yval, xval, v_ptr->hgt, v_ptr->wid, v_ptr->data); +} + +/* + * DAG: + * Build an vertical oval room. + * For every grid in the possible square, check the distance. + * If it's less than or == than the radius, make it a room square. + * If its less, make it a normal grid. If it's == make it an outer + * wall. + */ +static void build_type9(int by0, int bx0) +{ + u16b info; + int rad, x, y, x0, y0; + + rad = 2 + rand_int(8); + + /* Try to allocate space for room. If fails, exit */ + if (!room_alloc(rad*2 + 1, rad*2 + 1, FALSE, by0, bx0, &x0, &y0)) return; + + info = (randint(dun_level) <= 5) ? (CAVE_ROOM|CAVE_GLOW) : CAVE_ROOM; + + for (x = x0 - rad; x <= x0 + rad; x++) + { + for (y = y0 - rad; y <= y0 + rad; y++) + { + if (distance(y0, x0, y, x) == rad) + { + cave[y][x].info |= info; + cave_set_feat(y, x, feat_wall_outer); + } + + if (distance(y0, x0, y, x) < rad) + { + cave[y][x].info |= info; + place_floor(y, x); + } + } + } +} + + +/* + * Store routine for the fractal cave generator + * this routine probably should be an inline function or a macro + */ +static void store_height(int x, int y, int x0, int y0, byte val, + int xhsize, int yhsize, int cutoff) +{ + /* Only write to points that are "blank" */ + if (cave[y + y0 - yhsize][x + x0 - xhsize].feat != 255) return; + + /* If on boundary set val > cutoff so walls are not as square */ + if (((x == 0) || (y == 0) || (x == xhsize * 2) || (y == yhsize * 2)) && + (val <= cutoff)) val = cutoff + 1; + + /* Store the value in height-map format */ + /* Meant to be temporary, hence no cave_set_feat */ + cave[y + y0 - yhsize][x + x0 - xhsize].feat = val; + + return; +} + + + +/* + * Explanation of the plasma fractal algorithm: + * + * A grid of points is created with the properties of a 'height-map' + * This is done by making the corners of the grid have a random value. + * The grid is then subdivided into one with twice the resolution. + * The new points midway between two 'known' points can be calculated + * by taking the average value of the 'known' ones and randomly adding + * or subtracting an amount proportional to the distance between those + * points. The final 'middle' points of the grid are then calculated + * by averaging all four of the originally 'known' corner points. An + * random amount is added or subtracted from this to get a value of the + * height at that point. The scaling factor here is adjusted to the + * slightly larger distance diagonally as compared to orthogonally. + * + * This is then repeated recursively to fill an entire 'height-map' + * A rectangular map is done the same way, except there are different + * scaling factors along the x and y directions. + * + * A hack to change the amount of correlation between points is done using + * the grd variable. If the current step size is greater than grd then + * the point will be random, otherwise it will be calculated by the + * above algorithm. This makes a maximum distance at which two points on + * the height map can affect each other. + * + * How fractal caves are made: + * + * When the map is complete, a cut-off value is used to create a cave. + * Heights below this value are "floor", and heights above are "wall". + * This also can be used to create lakes, by adding more height levels + * representing shallow and deep water/ lava etc. + * + * The grd variable affects the width of passages. + * The roug variable affects the roughness of those passages + * + * The tricky part is making sure the created cave is connected. This + * is done by 'filling' from the inside and only keeping the 'filled' + * floor. Walls bounding the 'filled' floor are also kept. Everything + * else is converted to the normal granite FEAT_WALL_EXTRA. + */ + + +/* + * Note that this uses the cave.feat array in a very hackish way + * the values are first set to zero, and then each array location + * is used as a "heightmap" + * The heightmap then needs to be converted back into the "feat" format. + * + * grd=level at which fractal turns on. smaller gives more mazelike caves + * roug=roughness level. 16=normal. higher values make things more + * convoluted small values are good for smooth walls. + * size=length of the side of the square cave system. + */ + +void generate_hmap(int y0, int x0, int xsiz, int ysiz, int grd, + int roug, int cutoff) +{ + int xhsize, yhsize, xsize, ysize, maxsize; + + /* + * fixed point variables- these are stored as 256 x normal value + * this gives 8 binary places of fractional part + 8 places of normal part + */ + u16b xstep, xhstep, ystep, yhstep, i, j, diagsize, xxsize, yysize; + + + /* Redefine size so can change the value if out of range */ + xsize = xsiz; + ysize = ysiz; + + /* Paranoia about size of the system of caves*/ + if (xsize > 254) xsize = 254; + if (xsize < 4) xsize = 4; + if (ysize > 254) ysize = 254; + if (ysize < 4) ysize = 4; + + /* Get offsets to middle of array */ + xhsize = xsize / 2; + yhsize = ysize / 2; + + /* Fix rounding problem */ + xsize = xhsize * 2; + ysize = yhsize * 2; + + /* + * Scale factor for middle points: + * About sqrt(2)*256 - correct for a square lattice + * approximately correct for everything else. + */ + diagsize = 362; + + /* Maximum of xsize and ysize */ + maxsize = (xsize > ysize) ? xsize : ysize; + + /* Clear the section */ + for (i = 0; i <= xsize; i++) + { + for (j = 0; j <= ysize; j++) + { + cave_type *c_ptr; + + /* Access the grid */ + c_ptr = &cave[j + y0 - yhsize][i + x0 - xhsize]; + + /* 255 is a flag for "not done yet" */ + c_ptr->feat = 255; + + /* Clear icky flag because may be redoing the cave */ + c_ptr->info &= ~(CAVE_ICKY); + } + } + + /* Set the corner values just in case grd>size. */ + store_height(0, 0, x0, y0, maxsize, xhsize, yhsize, cutoff); + store_height(0, ysize, x0, y0, maxsize, xhsize, yhsize, cutoff); + store_height(xsize, 0, x0, y0, maxsize, xhsize, yhsize, cutoff); + store_height(xsize, ysize, x0, y0, maxsize, xhsize, yhsize, cutoff); + + /* Set the middle square to be an open area. */ + store_height(xhsize, yhsize, x0, y0, 0, xhsize, yhsize, cutoff); + + + /* Initialise the step sizes */ + xstep = xhstep = xsize * 256; + ystep = yhstep = ysize * 256; + xxsize = xsize * 256; + yysize = ysize * 256; + + /* + * Fill in the rectangle with fractal height data - like the + * 'plasma fractal' in fractint + */ + while ((xstep / 256 > 1) || (ystep / 256 > 1)) + { + /* Halve the step sizes */ + xstep = xhstep; + xhstep /= 2; + ystep = yhstep; + yhstep /= 2; + + /* Middle top to bottom */ + for (i = xhstep; i <= xxsize - xhstep; i += xstep) + { + for (j = 0; j <= yysize; j += ystep) + { + /* If greater than 'grid' level then is random */ + if (xhstep / 256 > grd) + { + store_height(i / 256, j / 256, x0, y0, randint(maxsize), + xhsize, yhsize, cutoff); + } + else + { + cave_type *l, *r; + byte val; + + /* Left point */ + l = &cave[j / 256 + y0 - yhsize][(i - xhstep) / 256 + x0 - xhsize]; + + /* Right point */ + r = &cave[j / 256 + y0 - yhsize][(i + xhstep) / 256 + x0 - xhsize]; + + /* Average of left and right points + random bit */ + val = (l->feat + r->feat) / 2 + + (randint(xstep / 256) - xhstep / 256) * roug / 16; + + store_height(i / 256, j / 256, x0, y0, val, + xhsize, yhsize, cutoff); + } + } + } + + + /* Middle left to right */ + for (j = yhstep; j <= yysize - yhstep; j += ystep) + { + for (i = 0; i <= xxsize; i += xstep) + { + /* If greater than 'grid' level then is random */ + if (xhstep / 256 > grd) + { + store_height(i / 256, j / 256, x0, y0, randint(maxsize), + xhsize, yhsize, cutoff); + } + else + { + cave_type *u, *d; + byte val; + + /* Up point */ + u = &cave[(j - yhstep) / 256 + y0 - yhsize][i / 256 + x0 - xhsize]; + + /* Down point */ + d = &cave[(j + yhstep) / 256 + y0 - yhsize][i / 256 + x0 - xhsize]; + + /* Average of up and down points + random bit */ + val = (u->feat + d->feat) / 2 + + (randint(ystep / 256) - yhstep / 256) * roug / 16; + + store_height(i / 256, j / 256, x0, y0, val, + xhsize, yhsize, cutoff); + } + } + } + + /* Center */ + for (i = xhstep; i <= xxsize - xhstep; i += xstep) + { + for (j = yhstep; j <= yysize - yhstep; j += ystep) + { + /* If greater than 'grid' level then is random */ + if (xhstep / 256 > grd) + { + store_height(i / 256, j / 256, x0, y0, randint(maxsize), + xhsize, yhsize, cutoff); + } + else + { + cave_type *ul, *dl, *ur, *dr; + byte val; + + /* Up-left point */ + ul = &cave[(j - yhstep) / 256 + y0 - yhsize][(i - xhstep) / 256 + x0 - xhsize]; + + /* Down-left point */ + dl = &cave[(j + yhstep) / 256 + y0 - yhsize][(i - xhstep) / 256 + x0 - xhsize]; + + /* Up-right point */ + ur = &cave[(j - yhstep) / 256 + y0 - yhsize][(i + xhstep) / 256 + x0 - xhsize]; + + /* Down-right point */ + dr = &cave[(j + yhstep) / 256 + y0 - yhsize][(i + xhstep) / 256 + x0 - xhsize]; + + /* + * average over all four corners + scale by diagsize to + * reduce the effect of the square grid on the shape + * of the fractal + */ + val = (ul->feat + dl->feat + ur->feat + dr->feat) / 4 + + (randint(xstep / 256) - xhstep / 256) * + (diagsize / 16) / 256 * roug; + + store_height(i / 256, j / 256, x0, y0, val, + xhsize, yhsize , cutoff); + } + } + } + } +} + + +/* + * Convert from height-map back to the normal Angband cave format + */ +static bool_ hack_isnt_wall(int y, int x, int cutoff) +{ + /* Already done */ + if (cave[y][x].info & CAVE_ICKY) + { + return (FALSE); + } + + else + { + /* Show that have looked at this square */ + cave[y][x].info |= (CAVE_ICKY); + + /* If less than cutoff then is a floor */ + if (cave[y][x].feat <= cutoff) + { + place_floor(y, x); + return (TRUE); + } + + /* If greater than cutoff then is a wall */ + else + { + cave_set_feat(y, x, feat_wall_outer); + return (FALSE); + } + } +} + + +/* + * Quick and nasty fill routine used to find the connected region + * of floor in the middle of the cave + */ +static void fill_hack(int y0, int x0, int y, int x, int xsize, int ysize, + int cutoff, int *amount) +{ + int i, j; + + /* check 8 neighbours +self (self is caught in the isnt_wall function) */ + for (i = -1; i <= 1; i++) + { + for (j = -1; j <= 1; j++) + { + /* If within bounds */ + if ((x + i > 0) && (x + i < xsize) && + (y + j > 0) && (y + j < ysize)) + { + /* If not a wall or floor done before */ + if (hack_isnt_wall(y + j + y0 - ysize / 2, + x + i + x0 - xsize / 2, cutoff)) + { + /* then fill from the new point*/ + fill_hack(y0, x0, y + j, x + i, xsize, ysize, + cutoff, amount); + + /* keep tally of size of cave system */ + (*amount)++; + } + } + + /* Affect boundary */ + else + { + cave[y0 + y + j - ysize / 2][x0 + x + i - xsize / 2].info |= (CAVE_ICKY); + } + } + } +} + + +bool_ generate_fracave(int y0, int x0, int xsize, int ysize, + int cutoff, bool_ light, bool_ room) +{ + int x, y, i, amount, xhsize, yhsize; + cave_type *c_ptr; + + /* Offsets to middle from corner */ + xhsize = xsize / 2; + yhsize = ysize / 2; + + /* Reset tally */ + amount = 0; + + /* + * Select region connected to center of cave system + * this gets rid of alot of isolated one-sqaures that + * can make teleport traps instadeaths... + */ + fill_hack(y0, x0, yhsize, xhsize, xsize, ysize, cutoff, &amount); + + /* If tally too small, try again */ + if (amount < 10) + { + /* Too small -- clear area and try again later */ + for (x = 0; x <= xsize; ++x) + { + for (y = 0; y < ysize; ++y) + { + place_filler(y0 + y - yhsize, x0 + x - xhsize); + cave[y0 + y - yhsize][x0 + x - xhsize].info &= ~(CAVE_ICKY | CAVE_ROOM); + } + } + return FALSE; + } + + + /* + * Do boundaries -- check to see if they are next to a filled region + * If not then they are set to normal granite + * If so then they are marked as room walls + */ + for (i = 0; i <= xsize; ++i) + { + /* Access top boundary grid */ + c_ptr = &cave[0 + y0 - yhsize][i + x0 - xhsize]; + + /* Next to a 'filled' region? -- set to be room walls */ + if (c_ptr->info & CAVE_ICKY) + { + cave_set_feat(0 + y0 - yhsize, i + x0 - xhsize, feat_wall_outer); + + if (light) c_ptr->info |= (CAVE_GLOW); + if (room) + { + c_ptr->info |= (CAVE_ROOM); + } + else + { + place_filler(0 + y0 - yhsize, i + x0 - xhsize); + } + } + + /* Outside of the room -- set to be normal granite */ + else + { + place_filler(0 + y0 - yhsize, i + x0 - xhsize); + } + + /* Clear the icky flag -- don't need it any more */ + c_ptr->info &= ~(CAVE_ICKY); + + + /* Access bottom boundary grid */ + c_ptr = &cave[ysize + y0 - yhsize][i + x0 - xhsize]; + + /* Next to a 'filled' region? -- set to be room walls */ + if (c_ptr->info & CAVE_ICKY) + { + cave_set_feat(ysize + y0 - yhsize, i + x0 - xhsize, feat_wall_outer); + if (light) c_ptr->info |= (CAVE_GLOW); + if (room) + { + c_ptr->info |= (CAVE_ROOM); + } + else + { + place_filler(ysize + y0 - yhsize, i + x0 - xhsize); + } + } + + /* Outside of the room -- set to be normal granite */ + else + { + place_filler(ysize + y0 - yhsize, i + x0 - xhsize); + } + + /* Clear the icky flag -- don't need it any more */ + c_ptr->info &= ~(CAVE_ICKY); + } + + + /* Do the left and right boundaries minus the corners (done above) */ + for (i = 1; i < ysize; ++i) + { + /* Access left boundary grid */ + c_ptr = &cave[i + y0 - yhsize][0 + x0 - xhsize]; + + /* Next to a 'filled' region? -- set to be room walls */ + if (c_ptr->info & CAVE_ICKY) + { + cave_set_feat(i + y0 - yhsize, 0 + x0 - xhsize, feat_wall_outer); + if (light) c_ptr->info |= (CAVE_GLOW); + if (room) + { + c_ptr->info |= (CAVE_ROOM); + } + else + { + place_filler(i + y0 - yhsize, 0 + x0 - xhsize); + } + } + + /* Outside of the room -- set to be normal granite */ + else + { + place_filler(i + y0 - yhsize, 0 + x0 - xhsize); + } + + /* Clear the icky flag -- don't need it any more */ + c_ptr->info &= ~(CAVE_ICKY); + + + /* Access left boundary grid */ + c_ptr = &cave[i + y0 - yhsize][xsize + x0 - xhsize]; + + /* Next to a 'filled' region? -- set to be room walls */ + if (c_ptr->info & CAVE_ICKY) + { + cave_set_feat(i + y0 - yhsize, xsize + x0 - xhsize, feat_wall_outer); + if (light) c_ptr->info |= (CAVE_GLOW); + if (room) + { + c_ptr->info |= (CAVE_ROOM); + } + else + { + place_filler(i + y0 - yhsize, xsize + x0 - xhsize); + } + } + + /* Outside of the room -- set to be normal granite */ + else + { + place_filler(i + y0 - yhsize, xsize + x0 - xhsize); + } + + /* Clear the icky flag -- don't need it any more */ + c_ptr->info &= ~(CAVE_ICKY); + } + + + /* + * Do the rest: convert back to the normal format + * In other variants, may want to check to see if cave.feat< some value + * if so, set to be water:- this will make interesting pools etc. + * (I don't do this for standard Angband.) + */ + for (x = 1; x < xsize; ++x) + { + for (y = 1; y < ysize; ++y) + { + /* Access the grid */ + c_ptr = &cave[y + y0 - yhsize][x + x0 - xhsize]; + + /* A floor grid to be converted */ + if ((f_info[c_ptr->feat].flags1 & FF1_FLOOR) && + (c_ptr->info & CAVE_ICKY)) + + { + /* Clear the icky flag in the filled region */ + c_ptr->info &= ~(CAVE_ICKY); + + /* Set appropriate flags */ + if (light) c_ptr->info |= (CAVE_GLOW); + if (room) c_ptr->info |= (CAVE_ROOM); + } + + /* A wall grid to be convereted */ + else if ((c_ptr->feat == feat_wall_outer) && + (c_ptr->info & CAVE_ICKY)) + { + /* Clear the icky flag in the filled region */ + c_ptr->info &= ~(CAVE_ICKY); + + /* Set appropriate flags */ + if (light) c_ptr->info |= (CAVE_GLOW); + if (room) + { + c_ptr->info |= (CAVE_ROOM); + } + else + { + place_filler(y + y0 - yhsize, x + x0 - xhsize); + } + } + + /* None of the above -- clear the unconnected regions */ + else + { + place_filler(y + y0 - yhsize, x + x0 - xhsize); + c_ptr->info &= ~(CAVE_ICKY | CAVE_ROOM); + } + } + } + + /* + * XXX XXX XXX There is a slight problem when tunnels pierce the caves: + * Extra doors appear inside the system. (Its not very noticeable though.) + * This can be removed by "filling" from the outside in. This allows + * a separation from FEAT_WALL_OUTER with FEAT_WALL_INNER. (Internal + * walls are F.W.OUTER instead.) + * The extra effort for what seems to be only a minor thing (even + * non-existant if you think of the caves not as normal rooms, but as + * holes in the dungeon), doesn't seem worth it. + */ + + return (TRUE); +} + + +/* + * Makes a cave system in the center of the dungeon + */ +static void build_cavern(void) +{ + int grd, roug, cutoff, xsize, ysize, x0, y0; + bool_ done, light, room; + + light = done = room = FALSE; + if (dun_level <= randint(25)) light = TRUE; + + /* Make a cave the size of the dungeon */ + xsize = cur_wid - 1; + ysize = cur_hgt - 1; + x0 = xsize / 2; + y0 = ysize / 2; + + /* Paranoia: make size even */ + xsize = x0 * 2; + ysize = y0 * 2; + + while (!done) + { + /* Testing values for these parameters: feel free to adjust */ + grd = 1 << (randint(4) + 4); + + /* Want average of about 16 */ + roug = randint(8) * randint(4); + + /* About size/2 */ + cutoff = xsize / 2; + + /* Make it */ + generate_hmap(y0, x0, xsize, ysize, grd, roug, cutoff); + + /* Convert to normal format+ clean up*/ + done = generate_fracave(y0, x0, xsize, ysize, cutoff, light, room); + } +} + +/* + * Driver routine to create fractal cave system + */ +static void build_type10(int by0, int bx0) +{ + int grd, roug, cutoff, xsize, ysize, y0, x0; + + bool_ done, light, room; + + /* Get size: note 'Evenness'*/ + xsize = randint(22) * 2 + 6; + ysize = randint(15) * 2 + 6; + + /* Try to allocate space for room. If fails, exit */ + if (!room_alloc(xsize + 1, ysize + 1, FALSE, by0, bx0, &x0, &y0)) return; + + light = done = FALSE; + room = TRUE; + + if (dun_level <= randint(25)) light = TRUE; + + while (!done) + { + /* + * Note: size must be even or there are rounding problems + * This causes the tunnels not to connect properly to the room + */ + + /* Testing values for these parameters feel free to adjust */ + grd = 1 << (randint(4)); + + /* Want average of about 16 */ + roug = randint(8) * randint(4); + + /* About size/2 */ + cutoff = randint(xsize / 4) + randint(ysize / 4) + + randint(xsize / 4) + randint(ysize / 4); + + /* Make it */ + generate_hmap(y0, x0, xsize, ysize, grd, roug, cutoff); + + /* Convert to normal format + clean up*/ + done = generate_fracave(y0, x0, xsize, ysize, cutoff, light, room); + } +} + + +/* + * Random vault generation from Z 2.5.1 + */ + +/* + * Make a very small room centred at (x0, y0) + * + * This is used in crypts, and random elemental vaults. + * + * Note - this should be used only on allocated regions + * within another room. + */ +static void build_small_room(int x0, int y0) +{ + build_rectangle(y0 - 1, x0 - 1, y0 + 1, x0 + 1, feat_wall_inner, CAVE_ROOM); + + /* Place a secret door on one side */ + switch (rand_int(4)) + { + case 0: + { + place_secret_door(y0, x0 - 1); + break; + } + + case 1: + { + place_secret_door(y0, x0 + 1); + break; + } + + case 2: + { + place_secret_door(y0 - 1, x0); + break; + } + + case 3: + { + place_secret_door(y0 + 1, x0); + break; + } + } + + /* Add inner open space */ + place_floor(y0, x0); +} + + +/* + * Add a door to a location in a random vault + * + * Note that range checking has to be done in the calling routine. + * + * The doors must be INSIDE the allocated region. + */ +static void add_door(int x, int y) +{ + /* Need to have a wall in the center square */ + if (cave[y][x].feat != feat_wall_outer) return; + + /* + * Look at: + * x#x + * .#. + * x#x + * + * where x=don't care + * .=floor, #=wall + */ + + if (get_is_floor(x, y - 1) && get_is_floor(x, y + 1) && + (cave[y][x - 1].feat == feat_wall_outer) && + (cave[y][x + 1].feat == feat_wall_outer)) + { + /* secret door */ + place_secret_door(y, x); + + /* set boundarys so don't get wide doors */ + place_filler(y, x - 1); + place_filler(y, x + 1); + } + + + /* + * Look at: + * x#x + * .#. + * x#x + * + * where x = don't care + * .=floor, #=wall + */ + if ((cave[y - 1][x].feat == feat_wall_outer) && + (cave[y + 1][x].feat == feat_wall_outer) && + get_is_floor(x - 1, y) && get_is_floor(x + 1, y)) + { + /* secret door */ + place_secret_door(y, x); + + /* set boundarys so don't get wide doors */ + place_filler(y - 1, x); + place_filler(y + 1, x); + } +} + + +/* + * Fill the empty areas of a room with treasure and monsters. + */ +static void fill_treasure(int x1, int x2, int y1, int y2, int difficulty) +{ + int x, y, cx, cy, size; + s32b value; + + /* center of room:*/ + cx = (x1 + x2) / 2; + cy = (y1 + y2) / 2; + + /* Rough measure of size of vault= sum of lengths of sides */ + size = abs(x2 - x1) + abs(y2 - y1); + + for (x = x1; x <= x2; x++) + { + for (y = y1; y <= y2; y++) + { + /* + * Thing added based on distance to center of vault + * Difficulty is 1-easy to 10-hard + */ + value = ((static_cast<s32b>(distance(cx, cy, x, y)) * 100) / size) + + randint(10) - difficulty; + + /* Hack -- Empty square part of the time */ + if ((randint(100) - difficulty * 3) > 50) value = 20; + + /* If floor, shallow water or lava */ + if (get_is_floor(x, y) || + (cave[y][x].feat == FEAT_SHAL_WATER) || + (cave[y][x].feat == FEAT_SHAL_LAVA)) + { + /* The smaller 'value' is, the better the stuff */ + if (value < 0) + { + /* Meanest monster + treasure */ + monster_level = dun_level + 40; + place_monster(y, x, TRUE, TRUE); + monster_level = dun_level; + object_level = dun_level + 20; + place_object(y, x, TRUE, FALSE, OBJ_FOUND_FLOOR); + object_level = dun_level; + } + else if (value < 5) + { + /* Mean monster +treasure */ + monster_level = dun_level + 20; + place_monster(y, x, TRUE, TRUE); + monster_level = dun_level; + object_level = dun_level + 10; + place_object(y, x, TRUE, FALSE, OBJ_FOUND_FLOOR); + object_level = dun_level; + } + else if (value < 10) + { + /* Monster */ + monster_level = dun_level + 9; + place_monster(y, x, TRUE, TRUE); + monster_level = dun_level; + } + else if (value < 17) + { + /* Intentional Blank space */ + + /* + * (Want some of the vault to be empty + * so have room for group monsters. + * This is used in the hack above to lower + * the density of stuff in the vault.) + */ + } + else if (value < 23) + { + /* Object or trap */ + if (rand_int(100) < 25) + { + place_object(y, x, FALSE, FALSE, OBJ_FOUND_FLOOR); + } + else + { + place_trap(y, x); + } + } + else if (value < 30) + { + /* Monster and trap */ + monster_level = dun_level + 5; + place_monster(y, x, TRUE, TRUE); + monster_level = dun_level; + place_trap(y, x); + } + else if (value < 40) + { + /* Monster or object */ + if (rand_int(100) < 50) + { + monster_level = dun_level + 3; + place_monster(y, x, TRUE, TRUE); + monster_level = dun_level; + } + if (rand_int(100) < 50) + { + object_level = dun_level + 7; + place_object(y, x, FALSE, FALSE, OBJ_FOUND_FLOOR); + object_level = dun_level; + } + } + else if (value < 50) + { + /* Trap */ + place_trap(y, x); + } + else + { + /* Various Stuff */ + + /* 20% monster, 40% trap, 20% object, 20% blank space */ + if (rand_int(100) < 20) + { + place_monster(y, x, TRUE, TRUE); + } + else if (rand_int(100) < 50) + { + place_trap(y, x); + } + else if (rand_int(100) < 50) + { + place_object(y, x, FALSE, FALSE, OBJ_FOUND_FLOOR); + } + } + + } + } + } +} + + +/* + * Creates a random vault that looks like a collection of bubbles + * + * It works by getting a set of coordinates that represent the center of + * each bubble. The entire room is made by seeing which bubble center is + * closest. If two centers are equidistant then the square is a wall, + * otherwise it is a floor. The only exception is for squares really + * near a center, these are always floor. + * (It looks better than without this check.) + * + * Note: If two centers are on the same point then this algorithm will create a + * blank bubble filled with walls. - This is prevented from happening. + */ + +#define BUBBLENUM 10 /* number of bubbles */ + +static void build_bubble_vault(int x0, int y0, int xsize, int ysize) +{ + /* array of center points of bubbles */ + coord center[BUBBLENUM]; + + int i, j, k, x = 0, y = 0; + u16b min1, min2, temp; + bool_ done; + + /* Offset from center to top left hand corner */ + int xhsize = xsize / 2; + int yhsize = ysize / 2; + + if (cheat_room) msg_print("Bubble Vault"); + + /* Allocate center of bubbles */ + center[0].x = randint(xsize - 3) + 1; + center[0].y = randint(ysize - 3) + 1; + + for (i = 1; i < BUBBLENUM; i++) + { + done = FALSE; + + /* Get center and check to see if it is unique */ + for (k = 0; !done && (k < 2000); k++) + { + done = TRUE; + + x = randint(xsize - 3) + 1; + y = randint(ysize - 3) + 1; + + for (j = 0; j < i; j++) + { + /* Rough test to see if there is an overlap */ + if ((x == center[j].x) || (y == center[j].y)) done = FALSE; + } + } + + /* Too many failures */ + if (k >= 2000) return; + + center[i].x = x; + center[i].y = y; + } + + build_rectangle(y0 - yhsize, x0 - xhsize, + y0 - yhsize + ysize - 1, x0 - xhsize + xsize - 1, + feat_wall_outer, CAVE_ROOM | CAVE_ICKY); + + /* Fill in middle with bubbles */ + for (x = 1; x < xsize - 1; x++) + { + for (y = 1; y < ysize - 1; y++) + { + cave_type *c_ptr; + + /* Get distances to two closest centers */ + + /* Initialise */ + min1 = distance(x, y, center[0].x, center[0].y); + min2 = distance(x, y, center[1].x, center[1].y); + + if (min1 > min2) + { + /* Swap if in wrong order */ + temp = min1; + min1 = min2; + min2 = temp; + } + + /* Scan the rest */ + for (i = 2; i < BUBBLENUM; i++) + { + temp = distance(x, y, center[i].x, center[i].y); + + if (temp < min1) + { + /* Smallest */ + min2 = min1; + min1 = temp; + } + else if (temp < min2) + { + /* Second smallest */ + min2 = temp; + } + } + + /* Access the grid */ + c_ptr = &cave[y + y0 - yhsize][x + x0 - xhsize]; + + /* + * Boundary at midpoint+ not at inner region of bubble + * + * SCSCSC: was feat_wall_outer + */ + if (((min2 - min1) <= 2) && (!(min1 < 3))) + { + place_filler(y + y0 - yhsize, x + x0 - xhsize); + } + + /* Middle of a bubble */ + else + { + place_floor(y + y0 - yhsize, x + x0 - xhsize); + } + + /* Clean up rest of flags */ + c_ptr->info |= (CAVE_ROOM | CAVE_ICKY); + } + } + + /* Try to add some random doors */ + for (i = 0; i < 500; i++) + { + x = randint(xsize - 3) - xhsize + x0 + 1; + y = randint(ysize - 3) - yhsize + y0 + 1; + add_door(x, y); + } + + /* Fill with monsters and treasure, low difficulty */ + fill_treasure(x0 - xhsize + 1, x0 - xhsize + xsize - 2, + y0 - yhsize + 1, y0 - yhsize + ysize - 2, randint(5)); +} + + +/* + * Convert FEAT_WALL_EXTRA (used by random vaults) to normal dungeon wall + */ +static void convert_extra(int y1, int x1, int y2, int x2) +{ + int x, y; + + for (x = x1; x <= x2; x++) + { + for (y = y1; y <= y2; y++) + { + if (cave[y][x].feat == FEAT_WALL_OUTER) + { + place_filler(y, x); + } + } + } +} + + +/* + * Overlay a rectangular room given its bounds + * + * This routine is used by build_room_vault (hence FEAT_WALL_OUTER) + * The area inside the walls is not touched: only granite is removed + * and normal walls stay + */ +static void build_room(int x1, int x2, int y1, int y2) +{ + int x, y, xsize, ysize, temp; + + /* Check if rectangle has no width */ + if ((x1 == x2) || (y1 == y2)) return; + + /* initialize */ + if (x1 > x2) + { + /* Swap boundaries if in wrong order */ + temp = x1; + x1 = x2; + x2 = temp; + } + + if (y1 > y2) + { + /* Swap boundaries if in wrong order */ + temp = y1; + y1 = y2; + y2 = temp; + } + + /* Get total widths */ + xsize = x2 - x1; + ysize = y2 - y1; + + build_rectangle(y1, x1, y2, x2, feat_wall_outer, CAVE_ROOM | CAVE_ICKY); + + /* Middle */ + for (x = 1; x < xsize; x++) + { + for (y = 1; y < ysize; y++) + { + if (cave[y1 + y][x1 + x].feat == FEAT_WALL_OUTER) + { + /* Clear the untouched region */ + place_floor(y1 + y, x1 + x); + cave[y1 + y][x1 + x].info |= (CAVE_ROOM | CAVE_ICKY); + } + else + { + /* Make it a room -- but don't touch */ + cave[y1 + y][x1 + x].info |= (CAVE_ROOM | CAVE_ICKY); + } + } + } +} + + +/* + * Create a random vault that looks like a collection of overlapping rooms + */ +static void build_room_vault(int x0, int y0, int xsize, int ysize) +{ + int i, x1, x2, y1, y2, xhsize, yhsize; + + /* Get offset from center */ + xhsize = xsize / 2; + yhsize = ysize / 2; + + if (cheat_room) msg_print("Room Vault"); + + /* Fill area so don't get problems with arena levels */ + for (x1 = 0; x1 <= xsize; x1++) + { + int x = x0 - xhsize + x1; + + for (y1 = 0; y1 <= ysize; y1++) + { + int y = y0 - yhsize + y1; + + cave_set_feat(y, x, FEAT_WALL_OUTER); + cave[y][x].info &= ~(CAVE_ICKY); + } + } + + /* Add ten random rooms */ + for (i = 0; i < 10; i++) + { + x1 = randint(xhsize) * 2 + x0 - xhsize; + x2 = randint(xhsize) * 2 + x0 - xhsize; + y1 = randint(yhsize) * 2 + y0 - yhsize; + y2 = randint(yhsize) * 2 + y0 - yhsize; + + build_room(x1, x2, y1, y2); + } + + convert_extra(y0 - yhsize, x0 - xhsize, y0 - yhsize + ysize, + x0 - xhsize + xsize); + + /* Add some random doors */ + for (i = 0; i < 500; i++) + { + x1 = randint(xsize - 2) - xhsize + x0 + 1; + y1 = randint(ysize - 2) - yhsize + y0 + 1; + add_door(x1, y1); + } + + /* Fill with monsters and treasure, high difficulty */ + fill_treasure(x0 - xhsize + 1, x0 - xhsize + xsize - 1, + y0 - yhsize + 1, y0 - yhsize + ysize - 1, randint(5) + 5); +} + + +/* + * Create a random vault out of a fractal cave + */ +static void build_cave_vault(int x0, int y0, int xsiz, int ysiz) +{ + int grd, roug, cutoff, xhsize, yhsize, xsize, ysize, x, y; + bool_ done, light, room; + + /* Round to make sizes even */ + xhsize = xsiz / 2; + yhsize = ysiz / 2; + xsize = xhsize * 2; + ysize = yhsize * 2; + + if (cheat_room) msg_print("Cave Vault"); + + light = done = FALSE; + room = TRUE; + + while (!done) + { + /* Testing values for these parameters feel free to adjust */ + grd = 1 << rand_int(4); + + /* Want average of about 16 */ + roug = randint(8) * randint(4); + + /* About size/2 */ + cutoff = randint(xsize / 4) + randint(ysize / 4) + + randint(xsize / 4) + randint(ysize / 4); + + /* Make it */ + generate_hmap(y0, x0, xsize, ysize, grd, roug, cutoff); + + /* Convert to normal format + clean up */ + done = generate_fracave(y0, x0, xsize, ysize, cutoff, light, room); + } + + /* Set icky flag because is a vault */ + for (x = 0; x <= xsize; x++) + { + for (y = 0; y <= ysize; y++) + { + cave[y0 - yhsize + y][x0 - xhsize + x].info |= CAVE_ICKY; + } + } + + /* Fill with monsters and treasure, low difficulty */ + fill_treasure(x0 - xhsize + 1, x0 - xhsize + xsize - 1, + y0 - yhsize + 1, y0 - yhsize + ysize - 1, randint(5)); +} + + +/* + * Maze vault -- rectangular labyrinthine rooms + * + * maze vault uses two routines: + * r_visit - a recursive routine that builds the labyrinth + * build_maze_vault - a driver routine that calls r_visit and adds + * monsters, traps and treasure + * + * The labyrinth is built by creating a spanning tree of a graph. + * The graph vertices are at + * (x, y) = (2j + x1, 2k + y1) j = 0,...,m-1 k = 0,...,n-1 + * and the edges are the vertical and horizontal nearest neighbors. + * + * The spanning tree is created by performing a suitably randomized + * depth-first traversal of the graph. The only adjustable parameter + * is the rand_int(3) below; it governs the relative density of + * twists and turns in the labyrinth: smaller number, more twists. + */ +static void r_visit(int y1, int x1, int y2, int x2, + int node, int dir, int *visited) +{ + int i, j, m, n, temp, x, y, adj[4]; + + /* Dimensions of vertex array */ + m = (x2 - x1) / 2 + 1; + n = (y2 - y1) / 2 + 1; + + /* Mark node visited and set it to a floor */ + visited[node] = 1; + x = 2 * (node % m) + x1; + y = 2 * (node / m) + y1; + place_floor(y, x); + + /* Setup order of adjacent node visits */ + if (rand_int(3) == 0) + { + /* Pick a random ordering */ + for (i = 0; i < 4; i++) + { + adj[i] = i; + } + for (i = 0; i < 4; i++) + { + j = rand_int(4); + temp = adj[i]; + adj[i] = adj[j]; + adj[j] = temp; + } + dir = adj[0]; + } + else + { + /* Pick a random ordering with dir first */ + adj[0] = dir; + for (i = 1; i < 4; i++) + { + adj[i] = i; + } + for (i = 1; i < 4; i++) + { + j = 1 + rand_int(3); + temp = adj[i]; + adj[i] = adj[j]; + adj[j] = temp; + } + } + + for (i = 0; i < 4; i++) + { + switch (adj[i]) + { + /* (0,+) - check for bottom boundary */ + case 0: + { + if ((node / m < n - 1) && (visited[node + m] == 0)) + { + place_floor(y + 1, x); + r_visit(y1, x1, y2, x2, node + m, dir, visited); + } + break; + } + + /* (0,-) - check for top boundary */ + case 1: + { + if ((node / m > 0) && (visited[node - m] == 0)) + { + place_floor(y - 1, x); + r_visit(y1, x1, y2, x2, node - m, dir, visited); + } + break; + } + + /* (+,0) - check for right boundary */ + case 2: + { + if ((node % m < m - 1) && (visited[node + 1] == 0)) + { + place_floor(y, x + 1); + r_visit(y1, x1, y2, x2, node + 1, dir, visited); + } + break; + } + + /* (-,0) - check for left boundary */ + case 3: + { + if ((node % m > 0) && (visited[node - 1] == 0)) + { + place_floor(y, x - 1); + r_visit(y1, x1, y2, x2, node - 1, dir, visited); + } + break; + } + } + } +} + + +static void build_maze_vault(int x0, int y0, int xsize, int ysize) +{ + int y, x, dy, dx; + int y1, x1, y2, x2; + int m, n, num_vertices; + bool_ light; + cave_type *c_ptr; + + + if (cheat_room) msg_print("Maze Vault"); + + /* Choose lite or dark */ + light = (dun_level <= randint(25)); + + /* Pick a random room size - randomized by calling routine */ + dy = ysize / 2 - 1; + dx = xsize / 2 - 1; + + y1 = y0 - dy; + x1 = x0 - dx; + y2 = y0 + dy; + x2 = x0 + dx; + + /* Generate the room */ + for (y = y1 - 1; y <= y2 + 1; y++) + { + for (x = x1 - 1; x <= x2 + 1; x++) + { + c_ptr = &cave[y][x]; + + c_ptr->info |= (CAVE_ROOM | CAVE_ICKY); + + if ((x == x1 - 1) || (x == x2 + 1) || + (y == y1 - 1) || (y == y2 + 1)) + { + cave_set_feat(y, x, feat_wall_outer); + } + else + { + cave_set_feat(y, x, feat_wall_inner); + } + if (light) c_ptr->info |= (CAVE_GLOW); + } + } + + /* Dimensions of vertex array */ + m = dx + 1; + n = dy + 1; + num_vertices = m * n; + + /* Allocate an array for visited vertices */ + std::vector<int> visited(num_vertices, 0); + + /* Traverse the graph to create a spaning tree, pick a random root */ + r_visit(y1, x1, y2, x2, rand_int(num_vertices), 0, visited.data()); + + /* Fill with monsters and treasure, low difficulty */ + fill_treasure(x1, x2, y1, y2, randint(5)); +} + + +/* + * Build a "mini" checkerboard vault + * + * This is done by making a permanent wall maze and setting + * the diagonal sqaures of the checker board to be granite. + * The vault has two entrances on opposite sides to guarantee + * a way to get in even if the vault abuts a side of the dungeon. + */ +static void build_mini_c_vault(int x0, int y0, int xsize, int ysize) +{ + int dy, dx; + int y1, x1, y2, x2, y, x, total; + int m, n, num_vertices; + + if (cheat_room) msg_print("Mini Checker Board Vault"); + + /* Pick a random room size */ + dy = ysize / 2 - 1; + dx = xsize / 2 - 1; + + y1 = y0 - dy; + x1 = x0 - dx; + y2 = y0 + dy; + x2 = x0 + dx; + + + /* Generate the room */ + for (y = y1 - 1; y <= y2 + 1; y++) + { + for (x = x1 - 1; x <= x2 + 1; x++) + { + cave[y][x].info |= (CAVE_ROOM | CAVE_ICKY); + + /* Permanent walls */ + cave_set_feat(y, x, FEAT_PERM_INNER); + } + } + + + /* Dimensions of vertex array */ + m = dx + 1; + n = dy + 1; + num_vertices = m * n; + + /* Allocate an array for visited vertices */ + std::vector<int> visited(num_vertices, 0); + + /* Traverse the graph to create a spannng tree, pick a random root */ + r_visit(y1, x1, y2, x2, rand_int(num_vertices), 0, visited.data()); + + /* Make it look like a checker board vault */ + for (x = x1; x <= x2; x++) + { + for (y = y1; y <= y2; y++) + { + total = x - x1 + y - y1; + + /* If total is odd and is a floor, then make a wall */ + if ((total % 2 == 1) && get_is_floor(x, y)) + { + cave_set_feat(y, x, feat_wall_inner); + } + } + } + + /* Make a couple of entrances */ + if (rand_int(2) == 0) + { + /* Left and right */ + y = randint(dy) + dy / 2; + cave_set_feat(y1 + y, x1 - 1, feat_wall_outer); + cave_set_feat(y1 + y, x2 + 1, feat_wall_outer); + } + else + { + /* Top and bottom */ + x = randint(dx) + dx / 2; + cave_set_feat(y1 - 1, x1 + x, feat_wall_outer); + cave_set_feat(y2 + 1, x1 + x, feat_wall_outer); + } + + /* Fill with monsters and treasure, highest difficulty */ + fill_treasure(x1, x2, y1, y2, 10); +} + + +/* + * Build a town/ castle by using a recursive algorithm. + * Basically divide each region in a probalistic way to create + * smaller regions. When the regions get too small stop. + * + * The power variable is a measure of how well defended a region is. + * This alters the possible choices. + */ +static void build_recursive_room(int x1, int y1, int x2, int y2, int power) +{ + int xsize, ysize; + int x, y; + int choice; + + /* Temp variables */ + int t1, t2, t3, t4; + + xsize = x2 - x1; + ysize = y2 - y1; + + if ((power < 3) && (xsize > 12) && (ysize > 12)) + { + /* Need outside wall +keep */ + choice = 1; + } + else + { + if (power < 10) + { + /* Make rooms + subdivide */ + if ((randint(10) > 2) && (xsize < 8) && (ysize < 8)) + { + choice = 4; + } + else + { + choice = randint(2) + 1; + } + } + else + { + /* Mostly subdivide */ + choice = randint(3) + 1; + } + } + + /* Based on the choice made above, do something */ + switch (choice) + { + /* Outer walls */ + case 1: + { + /* Top and bottom */ + for (x = x1; x <= x2; x++) + { + cave_set_feat(y1, x, feat_wall_outer); + cave_set_feat(y2, x, feat_wall_outer); + } + + /* Left and right */ + for (y = y1 + 1; y < y2; y++) + { + cave_set_feat(y, x1, feat_wall_outer); + cave_set_feat(y, x2, feat_wall_outer); + } + + /* Make a couple of entrances */ + if (rand_int(2) == 0) + { + /* Left and right */ + y = randint(ysize) + y1; + place_floor(y, x1); + place_floor(y, x2); + } + else + { + /* Top and bottom */ + x = randint(xsize) + x1; + place_floor(y1, x); + place_floor(y2, x); + } + + /* Select size of keep */ + t1 = randint(ysize / 3) + y1; + t2 = y2 - randint(ysize / 3); + t3 = randint(xsize / 3) + x1; + t4 = x2 - randint(xsize / 3); + + /* Do outside areas */ + + /* Above and below keep */ + build_recursive_room(x1 + 1, y1 + 1, x2 - 1, t1, power + 1); + build_recursive_room(x1 + 1, t2, x2 - 1, y2, power + 1); + + /* Left and right of keep */ + build_recursive_room(x1 + 1, t1 + 1, t3, t2 - 1, power + 3); + build_recursive_room(t4, t1 + 1, x2 - 1, t2 - 1, power + 3); + + /* Make the keep itself: */ + x1 = t3; + x2 = t4; + y1 = t1; + y2 = t2; + xsize = x2 - x1; + ysize = y2 - y1; + power += 2; + + /* Fall through */ + } + + /* Try to build a room */ + case 4: + { + if ((xsize < 3) || (ysize < 3)) + { + for (y = y1; y < y2; y++) + { + for (x = x1; x < x2; x++) + { + cave_set_feat(y, x, feat_wall_inner); + } + } + + /* Too small */ + return; + } + + /* Make outside walls */ + + /* Top and bottom */ + for (x = x1 + 1; x <= x2 - 1; x++) + { + cave_set_feat(y1 + 1, x, feat_wall_inner); + cave_set_feat(y2 - 1, x, feat_wall_inner); + } + + /* Left and right */ + for (y = y1 + 1; y <= y2 - 1; y++) + { + cave_set_feat(y, x1 + 1, feat_wall_inner); + cave_set_feat(y, x2 - 1, feat_wall_inner); + } + + /* Make a door */ + y = randint(ysize - 3) + y1 + 1; + + if (rand_int(2) == 0) + { + /* Left */ + place_floor(y, x1 + 1); + } + else + { + /* Right */ + place_floor(y, x2 - 1); + } + + /* Build the room */ + build_recursive_room(x1 + 2, y1 + 2, x2 - 2, y2 - 2, power + 3); + + break; + } + + /* Try and divide vertically */ + case 2: + { + if (xsize < 3) + { + /* Too small */ + for (y = y1; y < y2; y++) + { + for (x = x1; x < x2; x++) + { + cave_set_feat(y, x, feat_wall_inner); + } + } + return; + } + + t1 = randint(xsize - 2) + x1 + 1; + build_recursive_room(x1, y1, t1, y2, power - 2); + build_recursive_room(t1 + 1, y1, x2, y2, power - 2); + + break; + } + + /* Try and divide horizontally */ + case 3: + { + if (ysize < 3) + { + /* Too small */ + for (y = y1; y < y2; y++) + { + for (x = x1; x < x2; x++) + { + cave_set_feat(y, x, feat_wall_inner); + } + } + return; + } + + t1 = randint(ysize - 2) + y1 + 1; + build_recursive_room(x1, y1, x2, t1, power - 2); + build_recursive_room(x1, t1 + 1, x2, y2, power - 2); + + break; + } + } +} + + +/* + * Build a castle + * + * Clear the region and call the recursive room routine. + * + * This makes a vault that looks like a castle or city in the dungeon. + */ +static void build_castle_vault(int x0, int y0, int xsize, int ysize) +{ + int dy, dx; + int y1, x1, y2, x2; + int y, x; + + /* Pick a random room size */ + dy = ysize / 2 - 1; + dx = xsize / 2 - 1; + + y1 = y0 - dy; + x1 = x0 - dx; + y2 = y0 + dy; + x2 = x0 + dx; + + if (cheat_room) msg_print("Castle Vault"); + + /* Generate the room */ + for (y = y1 - 1; y <= y2 + 1; y++) + { + for (x = x1 - 1; x <= x2 + 1; x++) + { + cave[y][x].info |= (CAVE_ROOM | CAVE_ICKY); + + /* Make everything a floor */ + place_floor(y, x); + } + } + + /* Make the castle */ + build_recursive_room(x1, y1, x2, y2, randint(5)); + + /* Fill with monsters and treasure, low difficulty */ + fill_treasure(x1, x2, y1, y2, randint(3)); +} + + +/* + * Add outer wall to a floored region + * + * Note: no range checking is done so must be inside dungeon + * This routine also stomps on doors + */ +static void add_outer_wall(int x, int y, int light, int x1, int y1, + int x2, int y2) +{ + int i, j; + + if (!in_bounds(y, x)) return; + + /* + * Hack -- Check to see if square has been visited before + * if so, then exit (use room flag to do this) + */ + if (cave[y][x].info & CAVE_ROOM) return; + + /* Set room flag */ + cave[y][x].info |= (CAVE_ROOM); + + if (get_is_floor(x, y)) + { + for (i = -1; i <= 1; i++) + { + for (j = -1; j <= 1; j++) + { + if ((x + i >= x1) && (x + i <= x2) && + (y + j >= y1) && (y + j <= y2)) + { + add_outer_wall(x + i, y + j, light, x1, y1, x2, y2); + if (light) cave[y][x].info |= CAVE_GLOW; + } + } + } + } + + /* Set bounding walls */ + else if (cave[y][x].feat == FEAT_WALL_EXTRA) + { + cave[y][x].feat = feat_wall_outer; + if (light == TRUE) cave[y][x].info |= CAVE_GLOW; + } + + /* Set bounding walls */ + else if (cave[y][x].feat == FEAT_PERM_OUTER) + { + if (light == TRUE) cave[y][x].info |= CAVE_GLOW; + } +} + + +/* + * Hacked distance formula - gives the 'wrong' answer + * + * Used to build crypts + */ +static int dist2(int x1, int y1, int x2, int y2, + int h1, int h2, int h3, int h4) +{ + int dx, dy; + dx = abs(x2 - x1); + dy = abs(y2 - y1); + + /* + * Basically this works by taking the normal pythagorean formula + * and using an expansion to express this in a way without the + * square root. This approximate formula is then perturbed to give + * the distorted results. (I found this by making a mistake when I was + * trying to fix the circular rooms.) + */ + + /* h1-h4 are constants that describe the metric */ + if (dx >= 2 * dy) return (dx + (dy * h1) / h2); + if (dy >= 2 * dx) return (dy + (dx * h1) / h2); + + /* 128/181 is approx. 1/sqrt(2) */ + return (((dx + dy) * 128) / 181 + + (dx * dx / (dy * h3) + dy * dy / (dx * h3)) * h4); +} + + +/* + * Build target vault + * + * This is made by two concentric "crypts" with perpendicular + * walls creating the cross-hairs. + */ +static void build_target_vault(int x0, int y0, int xsize, int ysize) +{ + int rad, x, y; + + int h1, h2, h3, h4; + + + /* Make a random metric */ + h1 = randint(32) - 16; + h2 = randint(16); + h3 = randint(32); + h4 = randint(32) - 16; + + if (cheat_room) msg_print("Target Vault"); + + /* Work out outer radius */ + if (xsize > ysize) + { + rad = ysize / 2; + } + else + { + rad = xsize / 2; + } + + /* Make floor */ + for (x = x0 - rad; x <= x0 + rad; x++) + { + for (y = y0 - rad; y <= y0 + rad; y++) + { + cave_type *c_ptr; + + /* Access the grid */ + c_ptr = &cave[y][x]; + + /* Clear room flag */ + c_ptr->info &= ~(CAVE_ROOM); + + /* Grids in vaults are required to be "icky" */ + c_ptr->info |= (CAVE_ICKY); + + /* Inside -- floor */ + if (dist2(y0, x0, y, x, h1, h2, h3, h4) <= rad - 1) + { + place_floor(y, x); + } + + /* Outside -- make it granite so that arena works */ + else + { + c_ptr->feat = FEAT_WALL_EXTRA; + } + + /* Proper boundary for arena */ + if (((y + rad) == y0) || ((y - rad) == y0) || + ((x + rad) == x0) || ((x - rad) == x0)) + { + cave_set_feat(y, x, feat_wall_outer); + } + } + } + + /* Find visible outer walls and set to be FEAT_OUTER */ + add_outer_wall(x0, y0, FALSE, x0 - rad - 1, y0 - rad - 1, + x0 + rad + 1, y0 + rad + 1); + + /* Add inner wall */ + for (x = x0 - rad / 2; x <= x0 + rad / 2; x++) + { + for (y = y0 - rad / 2; y <= y0 + rad / 2; y++) + { + if (dist2(y0, x0, y, x, h1, h2, h3, h4) == rad / 2) + { + /* Make an internal wall */ + cave_set_feat(y, x, feat_wall_inner); + } + } + } + + /* Add perpendicular walls */ + for (x = x0 - rad; x <= x0 + rad; x++) + { + cave_set_feat(y0, x, feat_wall_inner); + } + + for (y = y0 - rad; y <= y0 + rad; y++) + { + cave_set_feat(y, x0, feat_wall_inner); + } + + /* Make inner vault */ + for (y = y0 - 1; y <= y0 + 1; y++) + { + cave_set_feat(y, x0 - 1, feat_wall_inner); + cave_set_feat(y, x0 + 1, feat_wall_inner); + } + for (x = x0 - 1; x <= x0 + 1; x++) + { + cave_set_feat(y0 - 1, x, feat_wall_inner); + cave_set_feat(y0 + 1, x, feat_wall_inner); + } + + place_floor(y0, x0); + + + /* + * Add doors to vault + * + * Get two distances so can place doors relative to centre + */ + x = (rad - 2) / 4 + 1; + y = rad / 2 + x; + + add_door(x0 + x, y0); + add_door(x0 + y, y0); + add_door(x0 - x, y0); + add_door(x0 - y, y0); + add_door(x0, y0 + x); + add_door(x0, y0 + y); + add_door(x0, y0 - x); + add_door(x0, y0 - y); + + /* Fill with stuff - medium difficulty */ + fill_treasure(x0 - rad, x0 + rad, y0 - rad, y0 + rad, randint(3) + 3); +} + + +/* + * Random vaults + */ +static void build_type11(int by0, int bx0) +{ + int y0, x0, xsize, ysize, vtype; + + /* Get size -- gig enough to look good, small enough to be fairly common */ + xsize = randint(22) + 22; + ysize = randint(11) + 11; + + /* Allocate in room_map. If will not fit, exit */ + if (!room_alloc(xsize + 2, ysize + 2, FALSE, by0, bx0, &x0, &y0)) return; + + /* + * Boost the rating -- Higher than lesser vaults and lower than + * greater vaults + */ + rating += 10; + + /* (Sometimes) Cause a special feeling */ + if ((dun_level <= 50) || + (randint((dun_level - 40) * (dun_level - 40) + 1) < 400)) + { + good_item_flag = TRUE; + } + + /* Select type of vault */ + vtype = randint(8); + + switch (vtype) + { + /* Build an appropriate room */ + case 1: + { + build_bubble_vault(x0, y0, xsize, ysize); + break; + } + + case 2: + { + build_room_vault(x0, y0, xsize, ysize); + break; + } + + case 3: + { + build_cave_vault(x0, y0, xsize, ysize); + break; + } + + case 4: + { + build_maze_vault(x0, y0, xsize, ysize); + break; + } + + case 5: + { + build_mini_c_vault(x0, y0, xsize, ysize); + break; + } + + case 6: + { + build_castle_vault(x0, y0, xsize, ysize); + break; + } + + case 7: + { + build_target_vault(x0, y0, xsize, ysize); + break; + } + + /* I know how to add a few more... give me some time. */ + + /* Paranoia */ + default: + { + return; + } + } +} + +/* + * Crypt room generation from Z 2.5.1 + */ + +/* + * Build crypt room. + * For every grid in the possible square, check the (fake) distance. + * If it's less than the radius, make it a room square. + * + * When done fill from the inside to find the walls, + */ +static void build_type12(int by0, int bx0) +{ + int light, rad, x, y, x0, y0; + bool_ emptyflag = TRUE; + int h1, h2, h3, h4; + + /* Make a random metric */ + h1 = randint(32) - 16; + h2 = randint(16); + h3 = randint(32); + h4 = randint(32) - 16; + + /* Occasional light */ + light = (randint(dun_level) <= 5) ? TRUE : FALSE; + + rad = randint(9); + + /* Allocate in room_map. If will not fit, exit */ + if (!room_alloc(rad * 2 + 3, rad * 2 + 3, FALSE, by0, bx0, &x0, &y0)) return; + + /* Make floor */ + for (x = x0 - rad; x <= x0 + rad; x++) + { + for (y = y0 - rad; y <= y0 + rad; y++) + { + /* Clear room flag */ + cave[y][x].info &= ~(CAVE_ROOM); + + /* Inside -- floor */ + if (dist2(y0, x0, y, x, h1, h2, h3, h4) <= rad - 1) + { + place_floor(y, x); + } + else if (distance(y0, x0, y, x) < 3) + { + place_floor(y, x); + } + + /* Outside -- make it granite so that arena works */ + else + { + cave_set_feat(y, x, feat_wall_outer); + } + + /* Proper boundary for arena */ + if (((y + rad) == y0) || ((y - rad) == y0) || + ((x + rad) == x0) || ((x - rad) == x0)) + { + cave_set_feat(y, x, feat_wall_outer); + } + } + } + + /* Find visible outer walls and set to be FEAT_OUTER */ + add_outer_wall(x0, y0, light, x0 - rad - 1, y0 - rad - 1, + x0 + rad + 1, y0 + rad + 1); + + /* Check to see if there is room for an inner vault */ + for (x = x0 - 2; x <= x0 + 2; x++) + { + for (y = y0 - 2; y <= y0 + 2; y++) + { + if (!get_is_floor(x, y)) + { + /* Wall in the way */ + emptyflag = FALSE; + } + } + } + + if (emptyflag && (rand_int(2) == 0)) + { + /* Build the vault */ + build_small_room(x0, y0); + + /* Place a treasure in the vault */ + place_object(y0, x0, FALSE, FALSE, OBJ_FOUND_FLOOR); + + /* Let's guard the treasure well */ + vault_monsters(y0, x0, rand_int(2) + 3); + + /* Traps naturally */ + vault_traps(y0, x0, 4, 4, rand_int(3) + 2); + } +} + + +/* + * Constructs a tunnel between two points + * + * This function must be called BEFORE any streamers are created, + * since we use the special "granite wall" sub-types to keep track + * of legal places for corridors to pierce rooms. + * + * We use "door_flag" to prevent excessive construction of doors + * along overlapping corridors. + * + * We queue the tunnel grids to prevent door creation along a corridor + * which intersects itself. + * + * We queue the wall piercing grids to prevent a corridor from leaving + * a room and then coming back in through the same entrance. + * + * We "pierce" grids which are "outer" walls of rooms, and when we + * do so, we change all adjacent "outer" walls of rooms into "solid" + * walls so that no two corridors may use adjacent grids for exits. + * + * The "solid" wall check prevents corridors from "chopping" the + * corners of rooms off, as well as "silly" door placement, and + * "excessively wide" room entrances. + * + * Useful "feat" values: + * FEAT_WALL_EXTRA -- granite walls + * FEAT_WALL_INNER -- inner room walls + * FEAT_WALL_OUTER -- outer room walls + * FEAT_WALL_SOLID -- solid room walls + * FEAT_PERM_EXTRA -- shop walls (perma) + * FEAT_PERM_INNER -- inner room walls (perma) + * FEAT_PERM_OUTER -- outer room walls (perma) + * FEAT_PERM_SOLID -- dungeon border (perma) + */ +static void build_tunnel(int row1, int col1, int row2, int col2, bool_ water) +{ + int i, y, x; + int tmp_row, tmp_col; + int row_dir, col_dir; + int start_row, start_col; + int main_loop_count = 0; + + bool_ door_flag = FALSE; + + cave_type *c_ptr; + + + /* Reset the arrays */ + dun->tunn_n = 0; + dun->wall_n = 0; + + /* Save the starting location */ + start_row = row1; + start_col = col1; + + /* Start out in the correct direction */ + correct_dir(&row_dir, &col_dir, row1, col1, row2, col2); + + /* Keep going until done (or bored) */ + while ((row1 != row2) || (col1 != col2)) + { + /* Mega-Hack -- Paranoia -- prevent infinite loops */ + if (main_loop_count++ > 2000) break; + + /* Allow bends in the tunnel */ + if (rand_int(100) < DUN_TUN_CHG) + { + /* Acquire the correct direction */ + correct_dir(&row_dir, &col_dir, row1, col1, row2, col2); + + /* Random direction */ + if (rand_int(100) < DUN_TUN_RND) + { + rand_dir(&row_dir, &col_dir); + } + } + + /* Get the next location */ + tmp_row = row1 + row_dir; + tmp_col = col1 + col_dir; + + + /* Extremely Important -- do not leave the dungeon */ + while (!in_bounds(tmp_row, tmp_col)) + { + /* Acquire the correct direction */ + correct_dir(&row_dir, &col_dir, row1, col1, row2, col2); + + /* Random direction */ + if (rand_int(100) < DUN_TUN_RND) + { + rand_dir(&row_dir, &col_dir); + } + + /* Get the next location */ + tmp_row = row1 + row_dir; + tmp_col = col1 + col_dir; + } + + + /* Access the location */ + c_ptr = &cave[tmp_row][tmp_col]; + + + /* Avoid the edge of the dungeon */ + if (c_ptr->feat == FEAT_PERM_SOLID) continue; + + /* Avoid the edge of vaults */ + if (c_ptr->feat == FEAT_PERM_OUTER) continue; + + /* Avoid "solid" granite walls */ + if (c_ptr->feat == FEAT_WALL_SOLID) continue; + + /* + * Pierce "outer" walls of rooms + * Cannot trust feat code any longer... + */ + if ((c_ptr->feat == feat_wall_outer) && + (c_ptr->info & CAVE_ROOM)) + { + /* Acquire the "next" location */ + y = tmp_row + row_dir; + x = tmp_col + col_dir; + + /* Hack -- Avoid outer/solid permanent walls */ + if (cave[y][x].feat == FEAT_PERM_SOLID) continue; + if (cave[y][x].feat == FEAT_PERM_OUTER) continue; + + /* Hack -- Avoid outer/solid granite walls */ + if ((cave[y][x].feat == feat_wall_outer) && + (cave[y][x].info & CAVE_ROOM)) continue; + if (cave[y][x].feat == FEAT_WALL_SOLID) continue; + + /* Accept this location */ + row1 = tmp_row; + col1 = tmp_col; + + /* Save the wall location */ + if (dun->wall_n < WALL_MAX) + { + dun->wall[dun->wall_n].y = row1; + dun->wall[dun->wall_n].x = col1; + dun->wall_n++; + } + + /* Forbid re-entry near this piercing */ + for (y = row1 - 1; y <= row1 + 1; y++) + { + for (x = col1 - 1; x <= col1 + 1; x++) + { + /* Convert adjacent "outer" walls as "solid" walls */ + if ((cave[y][x].feat == feat_wall_outer) && + (cave[y][x].info & CAVE_ROOM)) + { + /* Change the wall to a "solid" wall */ + /* Mega-Hack -- to be brought back later... */ + cave_set_feat(y, x, FEAT_WALL_SOLID); + } + } + } + } + + /* Travel quickly through rooms */ + else if (c_ptr->info & (CAVE_ROOM)) + { + /* Accept the location */ + row1 = tmp_row; + col1 = tmp_col; + } + + /* Tunnel through all other walls */ + else if ((c_ptr->feat == d_info[dungeon_type].fill_type1) || + (c_ptr->feat == d_info[dungeon_type].fill_type2) || + (c_ptr->feat == d_info[dungeon_type].fill_type3)) + { + /* Accept this location */ + row1 = tmp_row; + col1 = tmp_col; + + /* Save the tunnel location */ + if (dun->tunn_n < TUNN_MAX) + { + dun->tunn[dun->tunn_n].y = row1; + dun->tunn[dun->tunn_n].x = col1; + dun->tunn_n++; + } + + /* Allow door in next grid */ + door_flag = FALSE; + } + + /* Handle corridor intersections or overlaps */ + else + { + /* Accept the location */ + row1 = tmp_row; + col1 = tmp_col; + + /* Collect legal door locations */ + if (!door_flag) + { + /* Save the door location */ + if (dun->door_n < DOOR_MAX) + { + dun->door[dun->door_n].y = row1; + dun->door[dun->door_n].x = col1; + dun->door_n++; + } + + /* No door in next grid */ + door_flag = TRUE; + } + + /* Hack -- allow pre-emptive tunnel termination */ + if (rand_int(100) >= DUN_TUN_CON) + { + /* Distance between row1 and start_row */ + tmp_row = row1 - start_row; + if (tmp_row < 0) tmp_row = ( -tmp_row); + + /* Distance between col1 and start_col */ + tmp_col = col1 - start_col; + if (tmp_col < 0) tmp_col = ( -tmp_col); + + /* Terminate the tunnel */ + if ((tmp_row > 10) || (tmp_col > 10)) break; + } + } + } + + + /* Turn the tunnel into corridor */ + for (i = 0; i < dun->tunn_n; i++) + { + /* Access the grid */ + y = dun->tunn[i].y; + x = dun->tunn[i].x; + + /* Access the grid */ + c_ptr = &cave[y][x]; + + /* Clear previous contents, add a floor */ + if (!water) + { + place_floor(y, x); + } + else + { + cave_set_feat(y, x, FEAT_SHAL_WATER); + } + } + + + /* Apply the piercings that we found */ + for (i = 0; i < dun->wall_n; i++) + { + /* Access the grid */ + y = dun->wall[i].y; + x = dun->wall[i].x; + + /* Access the grid */ + c_ptr = &cave[y][x]; + + /* Clear previous contents, add up floor */ + place_floor(y, x); + + /* Occasional doorway */ + if (!(dungeon_flags1 & DF1_NO_DOORS) && + (rand_int(100) < DUN_TUN_PEN)) + { + /* Place a random door */ + place_random_door(y, x); + } + } +} + + + + +/* + * Count the number of "corridor" grids adjacent to the given grid. + * + * Note -- Assumes "in_bounds(y1, x1)" + * + * XXX XXX This routine currently only counts actual "empty floor" + * grids which are not in rooms. We might want to also count stairs, + * open doors, closed doors, etc. + */ +static int next_to_corr(int y1, int x1) +{ + int i, y, x, k = 0; + + cave_type *c_ptr; + + /* Scan adjacent grids */ + for (i = 0; i < 4; i++) + { + /* Extract the location */ + y = y1 + ddy_ddd[i]; + x = x1 + ddx_ddd[i]; + + /* Skip non floors */ + if (!cave_floor_bold(y, x)) continue; + + /* Access the grid */ + c_ptr = &cave[y][x]; + + /* Skip non "empty floor" grids */ + if ((c_ptr->feat != d_info[dungeon_type].floor1) && + (c_ptr->feat != d_info[dungeon_type].floor2) && + (c_ptr->feat != d_info[dungeon_type].floor3)) + { + continue; + } + + /* Skip grids inside rooms */ + if (c_ptr->info & (CAVE_ROOM)) continue; + + /* Count these grids */ + k++; + } + + /* Return the number of corridors */ + return (k); +} + + +/* + * Determine if the given location is "between" two walls, + * and "next to" two corridor spaces. XXX XXX XXX + * + * Assumes "in_bounds(y,x)" + */ +static bool_ possible_doorway(int y, int x) +{ + /* Count the adjacent corridors */ + if (next_to_corr(y, x) >= 2) + { + /* Check Vertical */ + if ((f_info[cave[y - 1][x].feat].flags1 & FF1_WALL) && + (f_info[cave[y + 1][x].feat].flags1 & FF1_WALL)) + { + return (TRUE); + } + + /* Check Horizontal */ + if ((f_info[cave[y][x - 1].feat].flags1 & FF1_WALL) && + (f_info[cave[y][x + 1].feat].flags1 & FF1_WALL)) + { + return (TRUE); + } + } + + /* No doorway */ + return (FALSE); +} + + +/* + * Places doors around y, x position + */ +static void try_doors(int y, int x) +{ + bool_ dir_ok[4]; + int i, k, n; + int yy, xx; + + /* Paranoia */ + /* if (!in_bounds(y, x)) return; */ + + /* Some dungeons don't have doors at all */ + if (dungeon_flags1 & (DF1_NO_DOORS)) return; + + /* Reset tally */ + n = 0; + + /* Look four cardinal directions */ + for (i = 0; i < 4; i++) + { + /* Assume NG */ + dir_ok[i] = FALSE; + + /* Access location */ + yy = y + ddy_ddd[i]; + xx = x + ddx_ddd[i]; + + /* Out of level boundary */ + if (!in_bounds(yy, xx)) continue; + + /* Ignore walls */ + if (f_info[cave[yy][xx].feat].flags1 & (FF1_WALL)) continue; + + /* Ignore room grids */ + if (cave[yy][xx].info & (CAVE_ROOM)) continue; + + /* Not a doorway */ + if (!possible_doorway(yy, xx)) continue; + + /* Accept the direction */ + dir_ok[i] = TRUE; + + /* Count good spots */ + n++; + } + + /* Use the traditional method 75% of time */ + if (rand_int(100) < 75) + { + for (i = 0; i < 4; i++) + { + /* Bad locations */ + if (!dir_ok[i]) continue; + + /* Place one of various kinds of doors */ + if (rand_int(100) < DUN_TUN_JCT) + { + /* Access location */ + yy = y + ddy_ddd[i]; + xx = x + ddx_ddd[i]; + + /* Place a door */ + place_random_door(yy, xx); + } + } + } + + /* Use alternative method */ + else + { + /* A crossroad */ + if (n == 4) + { + /* Clear OK flags XXX */ + for (i = 0; i < 4; i++) dir_ok[i] = FALSE; + + /* Put one or two secret doors */ + dir_ok[rand_int(4)] = TRUE; + dir_ok[rand_int(4)] = TRUE; + } + + /* A T-shaped intersection or two possible doorways */ + else if ((n == 3) || (n == 2)) + { + /* Pick one random location from the list */ + k = rand_int(n); + + for (i = 0; i < 4; i++) + { + /* Reject all but k'th OK direction */ + if (dir_ok[i] && (k-- != 0)) dir_ok[i] = FALSE; + } + } + + /* Place secret door(s) */ + for (i = 0; i < 4; i++) + { + /* Bad location */ + if (!dir_ok[i]) continue; + + /* Access location */ + yy = y + ddy_ddd[i]; + xx = x + ddx_ddd[i]; + + /* Place a secret door */ + place_secret_door(yy, xx); + } + } +} + + +/* + * Attempt to build a room of the given type at the given block + * + * Note that we restrict the number of "crowded" rooms to reduce + * the chance of overflowing the monster list during level creation. + */ +static bool_ room_build(int y, int x, int typ) +{ + /* Restrict level */ + if ((dun_level < roomdep[typ]) && !ironman_rooms) return (FALSE); + + /* Restrict "crowded" rooms */ + if (dun->crowded && ((typ == 5) || (typ == 6))) return (FALSE); + + /* Build a room */ + switch (typ) + { + /* Build an appropriate room */ + case 12: + build_type12(y, x); + break; + case 11: + build_type11(y, x); + break; + case 10: + build_type10(y, x); + break; + case 9: + build_type9 (y, x); + break; + case 8: + build_type8 (y, x); + break; + case 7: + build_type7 (y, x); + break; + case 6: + build_type6 (y, x); + break; + case 5: + build_type5 (y, x); + break; + case 4: + build_type4 (y, x); + break; + case 3: + build_type3 (y, x); + break; + case 2: + build_type2 (y, x); + break; + case 1: + build_type1 (y, x); + break; + + /* Paranoia */ + default: + return (FALSE); + } + + /* Success */ + return (TRUE); +} + +/* + * Set level boundaries + */ +static void set_bounders(bool_ empty_level) +{ + int y, x; + + /* Special boundary walls -- Top */ + for (x = 0; x < cur_wid; x++) + { + /* XXX XXX */ + if (empty_level) cave[0][x].mimic = fill_type[rand_int(100)]; + else cave[0][x].mimic = cave[0][x].feat; + + /* Clear previous contents, add "solid" perma-wall */ + cave_set_feat(0, x, FEAT_PERM_SOLID); + } + + /* Special boundary walls -- Bottom */ + for (x = 0; x < cur_wid; x++) + { + /* XXX XXX */ + if (empty_level) cave[cur_hgt - 1][x].mimic = fill_type[rand_int(100)]; + else cave[cur_hgt - 1][x].mimic = cave[cur_hgt - 1][x].feat; + + /* Clear previous contents, add "solid" perma-wall */ + cave_set_feat(cur_hgt - 1, x, FEAT_PERM_SOLID); + } + + /* Special boundary walls -- Left */ + for (y = 1; y < cur_hgt - 1; y++) + { + /* XXX XXX */ + if (empty_level) cave[y][0].mimic = fill_type[rand_int(100)]; + else cave[y][0].mimic = cave[y][0].feat; + + /* Clear previous contents, add "solid" perma-wall */ + cave_set_feat(y, 0, FEAT_PERM_SOLID); + } + + /* Special boundary walls -- Right */ + for (y = 1; y < cur_hgt - 1; y++) + { + /* XXX XXX */ + if (empty_level) cave[y][cur_wid - 1].mimic = fill_type[rand_int(100)]; + else cave[y][cur_wid - 1].mimic = cave[y][cur_wid - 1].feat; + + /* Clear previous contents, add "solid" perma-wall */ + cave_set_feat(y, cur_wid - 1, FEAT_PERM_SOLID); + } +} + +/* Needed to refill empty levels */ +static void fill_level(bool_ use_floor, byte smooth); + +/* + * Generate a normal dungeon level + */ +bool_ level_generate_dungeon() +{ + int i, k, y, x, y1, x1, branch = get_branch(); + dungeon_info_type *d_ptr = &d_info[dungeon_type]; + + int max_vault_ok = 2; + + bool_ destroyed = FALSE; + bool_ empty_level = FALSE; + bool_ cavern = FALSE; + s16b town_level = 0; + + /* Is it a town level ? */ + for (i = 0; i < TOWN_DUNGEON; i++) + { + if (d_ptr->t_level[i] == dun_level) town_level = d_ptr->t_idx[i]; + } + + /* Check for arena level */ + if ((dungeon_flags1 & (DF1_EMPTY)) || + (empty_levels && (rand_int(EMPTY_LEVEL) == 0))) + { + empty_level = TRUE; + + if (cheat_room || p_ptr->precognition) + { + msg_print("Arena level."); + } + + /* Refill the level with floor tiles */ + fill_level(empty_level, d_ptr->fill_method); + } + + /* Possible cavern */ + if ((dungeon_flags1 & DF1_CAVERN) && (rand_int(dun_level / 2) > DUN_CAVERN)) + { + cavern = TRUE; + + /* Make a large fractal cave in the middle of the dungeon */ + if (cheat_room) + { + msg_print("Cavern on level."); + } + + build_cavern(); + } + + /* Possible "destroyed" level */ + if ((dun_level > 10) && (rand_int(DUN_DEST) == 0)) + { + destroyed = TRUE; + } + + /* Hack -- No destroyed "quest" levels */ + if (is_quest(dun_level)) destroyed = FALSE; + + /* Hack -- No destroyed "small" levels */ + if ((cur_wid != MAX_WID) || (cur_hgt != MAX_HGT)) destroyed = FALSE; + + /* Hack -- No destroyed levels */ + if (dungeon_flags1 & DF1_NO_DESTROY) destroyed = FALSE; + + /* Actual maximum number of rooms on this level */ + dun->row_rooms = cur_hgt / BLOCK_HGT; + dun->col_rooms = cur_wid / BLOCK_WID; + + + /* Initialize the room table */ + for (y = 0; y < dun->row_rooms; y++) + { + for (x = 0; x < dun->col_rooms; x++) + { + dun->room_map[y][x] = FALSE; + } + } + + /* No "crowded" rooms yet */ + dun->crowded = FALSE; + + /* No rooms yet */ + dun->cent_n = 0; + + /* Pick a block for the room */ + y = rand_int(dun->row_rooms); + x = rand_int(dun->col_rooms); + + /* Align dungeon rooms */ + if (dungeon_align) + { + /* Slide some rooms right */ + if ((x % 3) == 0) x++; + + /* Slide some rooms left */ + if ((x % 3) == 2) x--; + } + + /* Ugly */ + { + struct hook_build_room1_in in = { y, x }; + process_hooks_new(HOOK_BUILD_ROOM1, &in, NULL); + } + + /* Build some rooms */ + for (i = 0; i < DUN_ROOMS; i++) + { + /* Pick a block for the room */ + y = rand_int(dun->row_rooms); + x = rand_int(dun->col_rooms); + + /* Align dungeon rooms */ + if (dungeon_align) + { + /* Slide some rooms right */ + if ((x % 3) == 0) x++; + + /* Slide some rooms left */ + if ((x % 3) == 2) x--; + } + + /* Destroyed levels are boring */ + if (destroyed) + { + /* The deeper you are, the more cavelike the rooms are */ + + /* no caves when cavern exists: they look bad */ + k = randint(100); + + if (!cavern && (k < dun_level)) + { + /* Type 10 -- Fractal cave */ + if (room_build(y, x, 10)) continue; + } + else + { + /* Attempt a "trivial" room */ + if ((dungeon_flags1 & DF1_CIRCULAR_ROOMS) && + room_build(y, x, 9)) + { + continue; + } + else if (room_build(y, x, 1)) continue; + } + + /* Never mind */ + continue; + } + + /* Attempt an "unusual" room -- no vaults on town levels */ + if (!town_level && + (ironman_rooms || (rand_int(DUN_UNUSUAL) < dun_level))) + { + /* Roll for room type */ + k = (ironman_rooms ? 0 : rand_int(100)); + + /* Attempt a very unusual room */ /* test hack */ + if (ironman_rooms || (rand_int(DUN_UNUSUAL) < dun_level)) + { + /* Type 8 -- Greater vault (10%) */ + if (k < 10) + { + if (max_vault_ok > 1) + { + if (room_build(y, x, 8)) continue; + } + else + { + if (cheat_room) msg_print("Refusing a greater vault."); + } + } + + /* Type 7 -- Lesser vault (15%) */ + if (k < 25) + { + if (max_vault_ok > 0) + { + if (room_build(y, x, 7)) continue; + } + else + { + if (cheat_room) msg_print("Refusing a lesser vault."); + } + } + + + /* Type 5 -- Monster nest (15%) */ + if ((k < 40) && room_build(y, x, 5)) continue; + + /* Type 6 -- Monster pit (15%) */ + if ((k < 55) && room_build(y, x, 6)) continue; + + /* Type 11 -- Random vault (5%) */ + if ((k < 60) && room_build(y, x, 11)) continue; + } + + /* Type 4 -- Large room (25%) */ + if ((k < 25) && room_build(y, x, 4)) continue; + + /* Type 3 -- Cross room (20%) */ + if ((k < 45) && room_build(y, x, 3)) continue; + + /* Type 2 -- Overlapping (20%) */ + if ((k < 65) && room_build(y, x, 2)) continue; + + /* Type 10 -- Fractal cave (15%) */ + if ((k < 80) && room_build(y, x, 10)) continue; + + /* Type 9 -- Circular (10%) */ + /* Hack - build standard rectangular rooms if needed */ + if (k < 90) + { + if ((dungeon_flags1 & DF1_CIRCULAR_ROOMS) && room_build(y, x, 1)) continue; + else if (room_build(y, x, 9)) continue; + } + + /* Type 12 -- Crypt (10%) */ + if ((k < 100) && room_build(y, x, 12)) continue; + } + + /* Attempt a trivial room */ + if (dungeon_flags1 & DF1_CAVE) + { + if (room_build(y, x, 10)) continue; + } + else + { + if ((dungeon_flags1 & DF1_CIRCULAR_ROOMS) && room_build(y, x, 9)) continue; + else if (room_build(y, x, 1)) continue; + } + } + + /* If no rooms are allocated... */ + while (dun->cent_n == 0) + { + /* ...force the creation of a small rectangular room */ + (void)room_build(0, 0, 1); + } + + /* Hack -- Scramble the room order */ + for (i = 0; i < dun->cent_n; i++) + { + int pick1 = rand_int(dun->cent_n); + int pick2 = rand_int(dun->cent_n); + y1 = dun->cent[pick1].y; + x1 = dun->cent[pick1].x; + dun->cent[pick1].y = dun->cent[pick2].y; + dun->cent[pick1].x = dun->cent[pick2].x; + dun->cent[pick2].y = y1; + dun->cent[pick2].x = x1; + } + + /* Start with no tunnel doors */ + dun->door_n = 0; + + /* Hack -- connect the first room to the last room */ + y = dun->cent[dun->cent_n - 1].y; + x = dun->cent[dun->cent_n - 1].x; + + /* Connect all the rooms together */ + for (i = 0; i < dun->cent_n; i++) + { + /* Connect the room to the previous room */ + build_tunnel(dun->cent[i].y, dun->cent[i].x, y, x, FALSE); + + /* Remember the "previous" room */ + y = dun->cent[i].y; + x = dun->cent[i].x; + } + + /* Mega-Hack -- Convert FEAT_WALL_SOLID back into outer walls */ + for (y = 0; y < cur_hgt; y++) + { + for (x = 0; x < cur_wid; x++) + { + if (cave[y][x].feat == FEAT_WALL_SOLID) + { + cave_set_feat(y, x, feat_wall_outer); + } + } + } + + /* Place intersection doors */ + for (i = 0; i < dun->door_n; i++) + { + /* Extract junction location */ + y = dun->door[i].y; + x = dun->door[i].x; + + /* Try placing doors */ + try_doors(y, x); + } + + if (strcmp(game_module, "ToME") == 0) + { + /* Hack -- Add some magma streamers */ + if ((dungeon_type == DUNGEON_MORDOR) || (dungeon_type == DUNGEON_ANGBAND)) + for (i = 0; i < DUN_STR_MAG; i++) + { + build_streamer(FEAT_MAGMA, DUN_STR_MC); + } + + /* Hack -- Add some quartz streamers */ + if ((dungeon_type == DUNGEON_MORDOR) || (dungeon_type == DUNGEON_ANGBAND)) + for (i = 0; i < DUN_STR_QUA; i++) + { + build_streamer(FEAT_QUARTZ, DUN_STR_QC); + } + } + + /* Add some sand streamers */ + if ((dungeon_flags1 & DF1_SAND_VEIN) && !rand_int(4)) + { + if ((cheat_room) || (p_ptr->precognition)) msg_print("Sand vein."); + build_streamer(FEAT_SANDWALL, DUN_STR_SC); + } + + /* Destroy the level if necessary */ + if (destroyed) destroy_level(); + + /* Create the town if needed */ + if (town_level) + { + town_gen(town_level); + } + + /* Hack -- Add some rivers if requested */ + if ((dungeon_flags1 & DF1_WATER_RIVER) && !rand_int(4)) + { + if (cheat_room || p_ptr->precognition) msg_print("River of water."); + add_river(FEAT_DEEP_WATER, FEAT_SHAL_WATER); + } + if ((dungeon_flags1 & DF1_LAVA_RIVER) && !rand_int(4)) + { + if ((cheat_room) || (p_ptr->precognition)) msg_print("River of lava."); + add_river(FEAT_DEEP_LAVA, FEAT_SHAL_LAVA); + } + + if (dungeon_flags1 & DF1_WATER_RIVERS) + { + int max = 3 + rand_int(2); + bool_ said = FALSE; + + for (i = 0; i < max; i++) + { + if (rand_int(3) == 0) + { + add_river(FEAT_DEEP_WATER, FEAT_SHAL_WATER); + if (!said && ((cheat_room) || (p_ptr->precognition))) msg_print("Rivers of water."); + said = TRUE; + } + } + } + + if (dungeon_flags1 & DF1_LAVA_RIVERS) + { + int max = 2 + rand_int(2); + bool_ said = FALSE; + + for (i = 0; i < max; i++) + { + if (rand_int(3) == 0) + { + add_river(FEAT_DEEP_LAVA, FEAT_SHAL_LAVA); + if (!said && ((cheat_room) || (p_ptr->precognition))) msg_print("Rivers of lava."); + said = TRUE; + } + } + } + + /* Add streamers of trees, water, or lava -KMW- */ + if (!(dungeon_flags1 & DF1_NO_STREAMERS)) + { + int num; + + /* + * Flat levels (was: levels 1--2) + * + * Small trees (penetrate walls) + */ + if ((dungeon_flags1 & DF1_FLAT) && (randint(20) > 15)) + { + num = randint(DUN_STR_QUA); + + for (i = 0; i < num; i++) + { + build_streamer2(FEAT_SMALL_TREES, 1); + } + } + + /* + * Levels 1 -- 33 (was: 1 -- 19) + * + * Shallow water (preserve walls) + * Deep water (penetrate walls) + */ + if (!(dun_level <= 33) && (randint(20) > 15)) + { + num = randint(DUN_STR_QUA - 1); + + for (i = 0; i < num; i++) + { + build_streamer2(FEAT_SHAL_WATER, 0); + } + + if (randint(20) > 15) + { + num = randint(DUN_STR_QUA); + + for (i = 0; i < num; i++) + { + build_streamer2(FEAT_DEEP_WATER, 1); + } + } + } + + /* + * Levels 34 -- (was: 20 --) + */ + else if (dun_level > 33) + { + /* + * Shallow lava (preserve walls) + * Deep lava (penetrate walls) + */ + if (randint(20) > 15) + { + num = randint(DUN_STR_QUA); + + for (i = 0; i < num; i++) + { + build_streamer2(FEAT_SHAL_LAVA, 0); + } + + if (randint(20) > 15) + { + num = randint(DUN_STR_QUA - 1); + + for (i = 0; i < num; i++) + { + build_streamer2(FEAT_DEEP_LAVA, 1); + } + } + } + + /* + * Shallow water (preserve walls) + * Deep water (penetrate walls) + */ + else if (randint(20) > 15) + { + num = randint(DUN_STR_QUA - 1); + + for (i = 0; i < num; i++) + { + build_streamer2(FEAT_SHAL_WATER, 0); + } + + if (randint(20) > 15) + { + num = randint(DUN_STR_QUA); + + for (i = 0; i < num; i++) + { + build_streamer2(FEAT_DEEP_WATER, 1); + } + } + } + } + } + + /* Hack, seems like once a room overrode the level boundaries, this is BAD */ + set_bounders(empty_level); + + /* Determine the character location */ + if (!new_player_spot(branch)) + return FALSE; + + return TRUE; +} + +/* + * Bring the imprinted pets from the old level + */ +static void replace_all_friends() +{ + if (p_ptr->wild_mode) + { + return; + } + + /* Scan every saved pet */ + for (int i = 0; i < max_m_idx; i++) + { + if ((km_list[i].r_idx) && (km_list[i].status == MSTATUS_COMPANION)) + { + int y = p_ptr->py; + int x = p_ptr->px; + + /* Find a suitable location */ + get_pos_player(5, &y, &x); + cave_type *c_ptr = &cave[y][x]; + + /* Get a m_idx to use */ + c_ptr->m_idx = m_pop(); + monster_type *m_ptr = &m_list[c_ptr->m_idx]; + + /* Actualy place the monster */ + m_list[c_ptr->m_idx] = km_list[i]; + m_ptr->fy = y; + m_ptr->fx = x; + m_ptr->hold_o_idxs.clear(); // Objects have been removed previously by caller + } + } +} + +/* + * Save the imprinted pets from the old level + */ +static void save_all_friends() +{ + if (p_ptr->old_wild_mode) return; + + for (int i = 0; i < max_m_idx; i++) { + km_list[i] = m_list[i]; + } +} + + + +/* + * Build probability tables for walls and floors and set feat_wall_outer + * and feat_wall_inner according to the current information in d_info.txt + * + * *hint* *hint* with this made extern, and we no longer have to + * store fill_type and floor_type in the savefile... + */ +static void init_feat_info(void) +{ + dungeon_info_type *d_ptr = &d_info[dungeon_type]; + int i; + int cur_depth, max_depth; + int p1, p2; + int floor_lim1, floor_lim2; + int fill_lim1, fill_lim2; + + + /* Retrieve dungeon depth info (base 1, to avoid zero divide errors) */ + cur_depth = (dun_level - d_ptr->mindepth) + 1; + max_depth = (d_ptr->maxdepth - d_ptr->mindepth) + 1; + + + /* Set room wall types */ + feat_wall_outer = d_ptr->outer_wall; + feat_wall_inner = d_ptr->inner_wall; + + + /* Setup probability info -- Floors */ + p1 = d_ptr->floor_percent1[0]; + p2 = d_ptr->floor_percent1[1]; + floor_lim1 = p1 + (p2 - p1) * cur_depth / max_depth; + + p1 = d_ptr->floor_percent2[0]; + p2 = d_ptr->floor_percent2[1]; + floor_lim2 = floor_lim1 + p1 + (p2 - p1) * cur_depth / max_depth; + + /* Setup probability info -- Fillers */ + p1 = d_ptr->fill_percent1[0]; + p2 = d_ptr->fill_percent1[1]; + fill_lim1 = p1 + (p2 - p1) * cur_depth / max_depth; + + p1 = d_ptr->fill_percent2[0]; + p2 = d_ptr->fill_percent2[1]; + fill_lim2 = fill_lim1 + p1 + (p2 - p1) * cur_depth / max_depth; + + + /* Fill the arrays of floors and walls in the good proportions */ + for (i = 0; i < 100; i++) + { + if (i < floor_lim1) + { + floor_type[i] = d_ptr->floor1; + } + else if (i < floor_lim2) + { + floor_type[i] = d_ptr->floor2; + } + else + { + floor_type[i] = d_ptr->floor3; + } + + if (i < fill_lim1) + { + fill_type[i] = d_ptr->fill_type1; + } + else if (i < fill_lim2) + { + fill_type[i] = d_ptr->fill_type2; + } + else + { + fill_type[i] = d_ptr->fill_type3; + } + } +} + + +/* + * Fill a level with wall type specified in A: or L: line of d_info.txt + * + * 'use_floor', when it is TRUE, tells the function to use floor type + * terrains (L:) instead of walls (A:). + * + * Filling behaviour can be controlled by the second parameter 'smooth', + * with the following options available: + * + * smooth behaviour + * ------ ------------------------------------------------------------ + * 0 Fill the entire level with fill_type1 / floor1 + * 1 All the grids are randomly selected (== --P5.1.2) + * 2 Slightly smoothed -- look like scattered patches + * 3 More smoothed -- tend to look like caverns / small scale map + * 4-- Max smoothing -- tend to look like landscape/island/ + * continent etc. + * + * I put it here, because there's another filler generator in + * wild.c, but it works better there, in fact... + * + * CAVEAT: smoothness of 3 or greater doesn't work well with the + * current secret door implementation. Outer walls also need some + * rethinking. + * + * -- pelpel + */ + +/* + * Thou shalt not invoke the name of thy RNG in vain. + * The Angband RNG generates 28 bit pseudo-random number, hence + * 28 / 2 = 14 + */ +#define MAX_SHIFTS 14 + +static void fill_level(bool_ use_floor, byte smooth) +{ + int y, x; + int step; + int shift; + + + /* Convert smoothness to initial step */ + if (smooth == 0) step = 0; + else if (smooth == 1) step = 1; + else if (smooth == 2) step = 2; + else if (smooth == 3) step = 4; + else step = 8; + + /* + * Paranoia -- step must be less than or equal to a half of + * width or height, whichever shorter + */ + if ((cur_hgt < 16) && (step > 4)) step = 4; + if ((cur_wid < 16) && (step > 4)) step = 4; + + + /* Special case -- simple fill */ + if (step == 0) + { + byte filler; + + /* Pick a filler XXX XXX XXX */ + if (use_floor) filler = d_info[dungeon_type].floor1; + else filler = d_info[dungeon_type].fill_type1; + + /* Fill the level with the filler without calling RNG */ + for (y = 0; y < cur_hgt; y++) + { + for (x = 0; x < cur_wid; x++) + { + cave_set_feat(y, x, filler); + } + } + + /* Done */ + return; + } + + + /* + * Fill starting positions -- every 'step' grids horizontally and + * vertically + */ + for (y = 0; y < cur_hgt; y += step) + { + for (x = 0; x < cur_wid; x += step) + { + /* + * Place randomly selected terrain feature using the prebuilt + * probability table + * + * By slightly modifying this, you can build streamers as + * well as normal fillers all at once, but this calls for + * modifications to the other part of the dungeon generator. + */ + if (use_floor) place_floor(y, x); + else place_filler(y, x); + } + } + + + /* + * Fill spaces between, randomly picking one of their neighbours + * + * This simple yet powerful algorithm was described by Mike Anderson: + * + * A B A | B A a B + * -> --+-- -> d e b + * D C D | C D c C + * + * a can be either A or B, b B or C, c C or D and d D or A. + * e is chosen from A, B, C and D. + * Subdivide and repeat the process as many times as you like. + * + * All the nasty tricks that obscure this simplicity are mine (^ ^;) + */ + + /* Initialise bit shift counter */ + shift = MAX_SHIFTS; + + /* Repeat subdivision until all the grids are filled in */ + while ((step = step >> 1) > 0) + { + bool_ y_even, x_even; + s16b y_wrap, x_wrap; + s16b y_sel, x_sel; + u32b selector = 0; + + /* Hacklette -- Calculate wrap-around locations */ + y_wrap = ((cur_hgt - 1) / (step * 2)) * (step * 2); + x_wrap = ((cur_wid - 1) / (step * 2)) * (step * 2); + + /* Initialise vertical phase */ + y_even = 0; + + for (y = 0; y < cur_hgt; y += step) + { + /* Flip vertical phase */ + y_even = !y_even; + + /* Initialise horizontal phase */ + x_even = 0; + + for (x = 0; x < cur_wid; x += step) + { + /* Flip horizontal phase */ + x_even = !x_even; + + /* Already filled in by previous iterations */ + if (y_even && x_even) continue; + + /* + * Retrieve next two bits from pseudo-random bit sequence + * + * You can do well not caring so much about their randomness. + * + * This is not really necessary, but I don't like to invoke + * relatively expensive RNG when we can do with much smaller + * number of calls. + */ + if (shift >= MAX_SHIFTS) + { + selector = rand_int(0x10000000L); + shift = 0; + } + else + { + selector >>= 2; + shift++; + } + + /* Vertically in sync */ + if (y_even) y_sel = y; + + /* Bit 1 selects neighbouring y */ + else y_sel = (selector & 2) ? y + step : y - step; + + /* Horizontally in sync */ + if (x_even) x_sel = x; + + /* Bit 0 selects neighbouring x */ + else x_sel = (selector & 1) ? x + step : x - step; + + /* Hacklette -- Fix out of range indices by wrapping around */ + if (y_sel >= cur_hgt) y_sel = 0; + else if (y_sel < 0) y_sel = y_wrap; + if (x_sel >= cur_wid) x_sel = 0; + else if (x_sel < 0) x_sel = x_wrap; + + /* + * Fill the grid with terrain feature of the randomly + * picked up neighbour + */ + cave_set_feat(y, x, cave[y_sel][x_sel].feat); + } + } + } +} + + +/** + * @brief double a grid tile. Used for the double-size dungeons + */ +static void supersize_grid_tile(int sy, int sx, int ty, int tx) +{ + /* Displacements for copied grid tiles */ + constexpr std::size_t n_disp = 4; + int disp[n_disp][2] = { + { 0, 0 }, + { 0, +1 }, + { +1, 0 }, + { +1, +1 } + }; + + /* Acquire the grid tile and monster */ + cave_type *cc_ptr = &cave[sy][sx]; + monster_type *m_ptr = &m_list[cc_ptr->m_idx]; + + /* Save the list of objects */ + auto const object_idxs(cc_ptr->o_idxs); + + /* Save the monster */ + auto m_idx = cc_ptr->m_idx; + + /* Create pointers to each of the target grid tiles */ + cave_type *c_ptr[n_disp]; + for (std::size_t i = 0; i < n_disp; i++) + { + c_ptr[i] = &cave[ty + disp[i][0]][tx + disp[i][1]]; + } + + /* Now we copy around the grid tiles. Objects and + monsters are "removed" for now. */ + for (std::size_t i = 0; i < 4; i++) + { + c_ptr[i] = &cave[ty + disp[i][0]][tx + disp[i][1]]; + + /* Copy grid */ + *c_ptr[i] = *cc_ptr; + c_ptr[i]->o_idxs.clear(); // ... except objects in the tile + c_ptr[i]->m_idx = 0; // ... except monsters in the tile + + /* Void gates need special attention */ + if (cc_ptr->feat == FEAT_BETWEEN) + { + int xxx = cc_ptr->special & 0xFF; + int yyy = cc_ptr->special >> 8; + + xxx *= 2; + yyy *= 2; + xxx += disp[i][1]; + yyy += disp[i][0]; + c_ptr[i]->special = xxx + (yyy << 8); + } + } + + /* Scatter objects randomly into the destination grid tiles */ + for (auto const o_idx: object_idxs) + { + std::size_t i = static_cast<std::size_t>(rand_int(4)); + /* Put object into grid tile */ + c_ptr[i]->o_idxs.push_back(o_idx); + /* Give object its location */ + object_type *o_ptr = &o_list[o_idx]; + o_ptr->iy = ty + disp[i][0]; + o_ptr->ix = tx + disp[i][1]; + } + + /* Scatter move monster randomly into one of the destination grid tiles */ + if (m_idx != 0) + { + std::size_t i = static_cast<std::size_t>(rand_int(4)); + /* Place monster into grid tile */ + c_ptr[i]->m_idx = cc_ptr->m_idx; + /* Give the monster its location */ + m_ptr->fy = ty + disp[i][0]; + m_ptr->fx = tx + disp[i][1]; + } +} + + +/* + * Generate a new dungeon level + * + * Note that "dun_body" adds about 4000 bytes of memory to the stack. + */ +static bool_ cave_gen(void) +{ + dungeon_info_type *d_ptr = &d_info[dungeon_type]; + + int max_vault_ok = 2; + + bool_ empty_level = FALSE; + + level_generator_type *generator; + + dun_data dun_body; + + char generator_name[100]; + + if (!get_dungeon_generator(generator_name)) + strnfmt(generator_name, 99, "%s", d_ptr->generator); + + /* + * We generate a double dungeon. First we should halve the desired + * width/height, generate the dungeon normally, then double it + * in both directions + */ + if (dungeon_flags1 & DF1_DOUBLE) + { + cur_wid /= 2; + cur_hgt /= 2; + } + + /* Fill the arrays of floors and walls in the good proportions */ + init_feat_info(); + + /* Set the correct monster hook */ + set_mon_num_hook(); + + /* Prepare allocation table */ + get_mon_num_prep(); + + /* Global data */ + dun = &dun_body; + + if (!(max_panel_rows)) max_vault_ok--; + if (!(max_panel_cols)) max_vault_ok--; + + /* + * Hack -- Start with fill_type's + * + * Need a way to know appropriate smoothing factor for the current + * dungeon. Maybe we need another d_info flag/value. + */ + fill_level(empty_level, d_ptr->fill_method); + + set_bounders(empty_level); + + /* + * Call the good level generator + */ + generator = level_generators; + while (generator) + { + if (!strcmp(generator->name, generator_name)) + { + if (!generator->generator()) + return FALSE; + break; + } + + generator = generator->next; + } + + /* Generate stairs */ + { + /* Is there a dungeon branch ? */ + if (int branch = get_branch()) + { + /* Place 5 down stair some walls */ + alloc_stairs(FEAT_MORE, 5, 3, branch); + } + + /* Is there a father dungeon branch ? */ + if (int branch = get_fbranch()) + { + /* Place 1 down stair some walls */ + alloc_stairs(FEAT_LESS, 5, 3, branch); + } + + if ((dun_level < d_ptr->maxdepth) || ((dun_level == d_ptr->maxdepth) && (dungeon_flags1 & DF1_FORCE_DOWN))) + { + /* Place 3 or 4 down stairs near some walls */ + alloc_stairs((dungeon_flags1 & DF1_FLAT) ? FEAT_WAY_MORE : FEAT_MORE, rand_range(3, 4), 3, 0); + + /* Place 0 or 1 down shafts near some walls */ + if (!(dungeon_flags2 & DF2_NO_SHAFT)) alloc_stairs((dungeon_flags1 & DF1_FLAT) ? FEAT_WAY_MORE : FEAT_SHAFT_DOWN, rand_range(0, 1), 3, 0); + } + + if ((dun_level > d_ptr->mindepth) || ((dun_level == d_ptr->mindepth) && (!(dungeon_flags1 & DF1_NO_UP)))) + { + /* Place 1 or 2 up stairs near some walls */ + alloc_stairs((dungeon_flags1 & DF1_FLAT) ? FEAT_WAY_LESS : FEAT_LESS, rand_range(1, 2), 3, 0); + + /* Place 0 or 1 up shafts near some walls */ + if (!(dungeon_flags2 & DF2_NO_SHAFT)) alloc_stairs((dungeon_flags1 & DF1_FLAT) ? FEAT_WAY_LESS : FEAT_SHAFT_UP, rand_range(0, 1), 3, 0); + } + } + + process_hooks_new(HOOK_GEN_LEVEL, NULL, NULL); + + /* Basic "amount" */ + int k = (dun_level / 3); + if (k > 10) k = 10; + if (k < 2) k = 2; + + /* Place monsters */ + { + + /* + * Pick a base number of monsters + */ + int i = d_ptr->min_m_alloc_level; + + /* To make small levels a bit more playable */ + if ((cur_hgt < MAX_HGT) || (cur_wid < MAX_WID)) + { + int small_tester = i; + + i = (i * cur_hgt) / MAX_HGT; + i = (i * cur_wid) / MAX_WID; + i += 1; + + if (i > small_tester) i = small_tester; + else if (cheat_hear) + { + msg_format("Reduced monsters base from %d to %d", small_tester, i); + } + } + + i += randint(8); + + /* Put some monsters in the dungeon */ + for (i = i + k; i > 0; i--) + { + (void)alloc_monster(0, TRUE); + } + } + + /* Check fates */ + for (std::size_t i = 0; i < MAX_FATES; i++) + { + /* Ignore empty slots */ + if (fates[i].fate == FATE_NONE) continue; + + /* Check dungeon depth */ + if (fates[i].level != dun_level) continue; + + /* Non-serious fates don't always fire */ + if ((!fates[i].serious) && (randint(2) != 1)) continue; + + /* Player meets his/her fate now... */ + fate_flag = TRUE; + + switch (fates[i].fate) + { + case FATE_FIND_O: + { + int oy = p_ptr->py + 1; + int ox = p_ptr->px; + object_type *q_ptr, forge; + + /* Get local object */ + q_ptr = &forge; + + /* Mega-Hack */ + object_prep(q_ptr, fates[i].o_idx); + + /* Mega-Hack */ + apply_magic(q_ptr, dun_level, TRUE, TRUE, fates[i].serious); + + get_pos_player(10, &oy, &ox); + + /* Drop it from the heaven */ + drop_near(q_ptr, -1, oy, ox); + + /* Make it icky */ + fates[i].icky = TRUE; + break; + } + case FATE_FIND_R: + { + int oy = p_ptr->py + 1; + int ox = p_ptr->px; + + get_pos_player(10, &oy, &ox); + + place_monster_one(oy, ox, fates[i].r_idx, 0, fates[i].serious, MSTATUS_ENEMY); + + fates[i].icky = TRUE; + break; + } + case FATE_FIND_A: + { + int oy = p_ptr->py + 1; + int ox = p_ptr->px; + object_type *q_ptr = NULL, forge; + + get_pos_player(10, &oy, &ox); + + /* XXX XXX XXX Grant a randart */ + if (fates[i].a_idx == 0) + { + int obj_lev; + s16b k_idx; + + /* Apply restriction */ + get_obj_num_hook = kind_is_artifactable; + + /* Object level a la find object fates */ + obj_lev = max_dlv[dungeon_type] + randint(10); + + /* Rebuild allocation table */ + get_obj_num_prep(); + + /* Roll for an object */ + k_idx = get_obj_num(obj_lev); + + /* Reset restriction */ + get_obj_num_hook = kind_is_legal; + + /* Invalidate the allocation table */ + alloc_kind_table_valid = FALSE; + + /* Get a local object */ + q_ptr = &forge; + + /* Wipe it */ + object_wipe(q_ptr); + + /* Create the object */ + object_prep(q_ptr, k_idx); + + /* SoAC it */ + create_artifact(q_ptr, FALSE, TRUE); + + /* Drop the artifact from heaven */ + drop_near(q_ptr, -1, oy, ox); + } + + /* Grant a normal artefact */ + else if (a_info[fates[i].a_idx].cur_num == 0) + { + artifact_type *a_ptr = &a_info[fates[i].a_idx]; + s16b I_kind; + + /* Get local object */ + q_ptr = &forge; + + /* Wipe the object */ + object_wipe(q_ptr); + + /* Acquire the "kind" index */ + I_kind = lookup_kind(a_ptr->tval, a_ptr->sval); + + /* Create the artifact */ + object_prep(q_ptr, I_kind); + + /* Save the name */ + q_ptr->name1 = fates[i].a_idx; + + apply_magic(q_ptr, -1, TRUE, TRUE, TRUE); + + /* Drop the artifact from heaven */ + drop_near(q_ptr, -1, oy, ox); + } + + fates[i].icky = TRUE; + break; + } + } + } + + /* Re-scan the list to eliminate the inutile fate */ + for (std::size_t i = 0; i < MAX_FATES; i++) + { + switch (fates[i].fate) + { + case FATE_FIND_A: + { + if (a_info[fates[i].a_idx].cur_num == 1) fates[i].icky = TRUE; + break; + } + case FATE_FIND_R: + { + if ((r_info[fates[i].r_idx].cur_num == 1) && (r_info[fates[i].r_idx].flags1 & RF1_UNIQUE)) fates[i].icky = TRUE; + break; + } + } + } + + /* Place traps and rubble */ + { + /* Place some traps in the dungeon */ + alloc_object(ALLOC_SET_BOTH, ALLOC_TYP_TRAP, randint(k * 2)); + + /* Put some rubble in corridors */ + alloc_object(ALLOC_SET_CORR, ALLOC_TYP_RUBBLE, randint(k)); + } + + /* Place objects and treasure */ + { + /* Put some objects in rooms */ + if (dungeon_type != DUNGEON_DEATH) alloc_object(ALLOC_SET_ROOM, ALLOC_TYP_OBJECT, randnor(DUN_AMT_ROOM, 3)); + + /* Put some objects/gold in the dungeon */ + if (dungeon_type != DUNGEON_DEATH) alloc_object(ALLOC_SET_BOTH, ALLOC_TYP_OBJECT, randnor(DUN_AMT_ITEM, 3)); + if (dungeon_type != DUNGEON_DEATH) alloc_object(ALLOC_SET_BOTH, ALLOC_TYP_GOLD, randnor(DUN_AMT_GOLD, 3)); + } + + /* Place random features such as altars and void gates, etc. */ + { + /* Put some altars */ + alloc_object(ALLOC_SET_ROOM, ALLOC_TYP_ALTAR, randnor(DUN_AMT_ALTAR, 3)); + + /* Put some between gates */ + alloc_object(ALLOC_SET_ROOM, ALLOC_TYP_BETWEEN, randnor(DUN_AMT_BETWEEN, 3)); + + /* Put some fountains */ + alloc_object(ALLOC_SET_ROOM, ALLOC_TYP_FOUNTAIN, randnor(DUN_AMT_FOUNTAIN, 3)); + } + + /* Put an Artifact and Artifact Guardian is requested */ + if (d_ptr->final_guardian && (d_ptr->maxdepth == dun_level)) + { + int oy; + int ox; + int m_idx, tries = 10000; + + /* Find a good position */ + while (tries) + { + /* Get a random spot */ + oy = randint(cur_hgt - 4) + 2; + ox = randint(cur_wid - 4) + 2; + + /* Is it a good spot ? */ + if (cave_empty_bold(oy, ox)) break; + + /* One less try */ + tries--; + } + + /* Place the guardian */ + m_allow_special[d_ptr->final_guardian] = TRUE; + place_monster_one(oy, ox, d_ptr->final_guardian, 0, FALSE, MSTATUS_ENEMY); + m_allow_special[d_ptr->final_guardian] = FALSE; + + + m_idx = cave[oy][ox].m_idx; + + if (!m_idx && wizard) cmsg_print(TERM_L_RED, "WARNING: Could not place guardian."); + + /* + * If guardian is successfully created and his/her/its + * treasure hasn't been found, let him/her/it own that + */ + if (m_idx && d_ptr->final_artifact && + (a_info[d_ptr->final_artifact].cur_num == 0)) + { + artifact_type *a_ptr = &a_info[d_ptr->final_artifact]; + object_type *q_ptr, forge, *o_ptr; + int I_kind, o_idx; + + /* Get new object */ + o_idx = o_pop(); + + /* Proceed only if there's an object slot available */ + if (o_idx) + { + a_allow_special[d_ptr->final_artifact] = TRUE; + + /* Get local object */ + q_ptr = &forge; + + /* Wipe the object */ + object_wipe(q_ptr); + + /* Acquire the "kind" index */ + I_kind = lookup_kind(a_ptr->tval, a_ptr->sval); + + /* Create the artifact */ + object_prep(q_ptr, I_kind); + + /* Save the name */ + q_ptr->name1 = d_ptr->final_artifact; + + /* Actually create it */ + apply_magic(q_ptr, -1, TRUE, TRUE, TRUE); + + /* Where it is found ? */ + q_ptr->found = OBJ_FOUND_MONSTER; + q_ptr->found_aux1 = d_ptr->final_guardian; + q_ptr->found_aux2 = 0; + q_ptr->found_aux3 = dungeon_type; + q_ptr->found_aux4 = level_or_feat(dungeon_type, dun_level); + + a_allow_special[d_ptr->final_artifact] = FALSE; + + /* Get the item */ + o_ptr = &o_list[o_idx]; + + /* Structure copy */ + object_copy(o_ptr, q_ptr); + + /* Build a stack */ + o_ptr->held_m_idx = m_idx; + o_ptr->ix = 0; + o_ptr->iy = 0; + + m_list[m_idx].hold_o_idxs.push_back(o_idx); + } + } + + if (m_idx && d_ptr->final_object && + (k_info[d_ptr->final_object].artifact == FALSE)) + { + object_type *q_ptr, forge, *o_ptr; + int o_idx; + + /* Get new object */ + o_idx = o_pop(); + + /* Proceed only if there's an object slot available */ + if (o_idx) + { + /* Get local object */ + q_ptr = &forge; + + k_allow_special[d_ptr->final_object] = TRUE; + + /* Wipe the object */ + object_wipe(q_ptr); + + /* Create the final object */ + object_prep(q_ptr, d_ptr->final_object); + apply_magic(q_ptr, 1, FALSE, FALSE, FALSE); + + /* Where it is found ? */ + q_ptr->found = OBJ_FOUND_MONSTER; + q_ptr->found_aux1 = d_ptr->final_guardian; + q_ptr->found_aux2 = 0; + q_ptr->found_aux3 = dungeon_type; + q_ptr->found_aux4 = level_or_feat(dungeon_type, dun_level); + + k_allow_special[d_ptr->final_object] = FALSE; + + k_info[d_ptr->final_object].artifact = TRUE; + + /* Get the item */ + o_ptr = &o_list[o_idx]; + + /* Structure copy */ + object_copy(o_ptr, q_ptr); + + /* Build a stack */ + o_ptr->held_m_idx = m_idx; + o_ptr->ix = 0; + o_ptr->iy = 0; + + m_list[m_idx].hold_o_idxs.push_back(o_idx); + } + } + } + + if ((empty_level) && (randint(DARK_EMPTY) != 1 || (randint(100) > dun_level))) + wiz_lite(); + + /* Now double the generated dungeon */ + if (dungeon_flags1 & DF1_DOUBLE) + { + /* + * We begin at the bottom-right corner and move upwards + * to the left. This avoids the need for an extra copy of + * the cave array. + * + * We double the border permanent walls, too. + */ + int y = cur_hgt - 1; + int y1 = y * 2; + for (; y >= 0; y--, y1 -= 2) + { + int x = cur_wid - 1; + int x1 = x * 2; + for (; x >= 0; x--, x1 -= 2) + { + supersize_grid_tile(y, x, y1, x1); + } + } + + /* Set the width/height ... */ + cur_wid *= 2; + cur_hgt *= 2; + + /* ... and player position to the right place */ + p_ptr->py *= 2; + p_ptr->px *= 2; + } + + return TRUE; +} + + + +/* + * Creates a special level + */ + +/* Mega-Hack */ +#define REGEN_HACK 0x02 + +bool_ build_special_level(void) +{ + char buf[80]; + int y, x, ystart = 2, xstart = 2; + s16b level; + + /* No special levels on the surface */ + if (!dun_level) return FALSE; + + level = dun_level - d_info[dungeon_type].mindepth; + if ((!get_dungeon_save(buf)) && (special_lvl[level][dungeon_type])) return FALSE; + if (!get_dungeon_special(buf)) return FALSE; + + /* Big town */ + cur_hgt = MAX_HGT; + cur_wid = MAX_WID; + + /* Determine number of panels */ + max_panel_rows = (cur_hgt / SCREEN_HGT) * 2 - 2; + max_panel_cols = (cur_wid / SCREEN_WID) * 2 - 2; + + /* Assume illegal panel */ + panel_row_min = max_panel_rows * (SCREEN_HGT / 2); + panel_col_min = max_panel_cols * (SCREEN_WID / 2); + + /* Start with perm walls */ + for (y = 0; y < cur_hgt; y++) + { + for (x = 0; x < cur_wid; x++) + { + cave_set_feat(y, x, FEAT_PERM_SOLID); + } + } + /* Set the correct monster hook */ + set_mon_num_hook(); + + /* Prepare allocation table */ + get_mon_num_prep(); + + init_flags = INIT_CREATE_DUNGEON | INIT_POSITION; + process_dungeon_file(buf, &ystart, &xstart, cur_hgt, cur_wid, TRUE, TRUE); + + special_lvl[level][dungeon_type] = REGEN_HACK; + generate_special_feeling = TRUE; + + /* Special feeling because it's special */ + good_item_flag = TRUE; + + /* + * Hack -- It's better/more dangerous than a greater vault. + * Better to have a rating field in special level description. + */ + rating += 40; + + return TRUE; +} + +/* + * Prepare regeneration of a special level, which should not happen, + * but just in case... + */ +static void wipe_special_level(void) +{ + s16b level; + char buf[80]; + + /* No special levels on the surface */ + if (!dun_level) return; + + process_hooks_new(HOOK_LEVEL_REGEN, NULL, NULL); + + /* Calculate relative depth */ + level = dun_level - d_info[dungeon_type].mindepth; + + /* No special level at this depth */ + if ((!get_dungeon_save(buf)) && + special_lvl[level][dungeon_type]) return; + if (!get_dungeon_special(buf)) return; + + /* Clear the Mega-Hack flag */ + if (special_lvl[level][dungeon_type] == REGEN_HACK) + special_lvl[level][dungeon_type] = FALSE; +} + +/* + * Finalise generation of a special level + */ +static void finalise_special_level(void) +{ + s16b level; + char buf[80]; + + /* No special levels on the surface */ + if (!dun_level) return; + + process_hooks_new(HOOK_LEVEL_END_GEN, NULL, NULL); + + /* Calculate relative depth */ + level = dun_level - d_info[dungeon_type].mindepth; + + /* No special level at this depth */ + if ((!get_dungeon_save(buf)) && + special_lvl[level][dungeon_type]) return; + if (!get_dungeon_special(buf)) return; + + /* Set the "generated" flag */ + if (special_lvl[level][dungeon_type] == REGEN_HACK) + special_lvl[level][dungeon_type] = TRUE; +} + +/* + * Give some magical energy to the each grid of the level + */ +static void generate_grid_mana() +{ + int y, x, mana, mult; + bool_ xtra_magic = FALSE; + + if (randint(XTRA_MAGIC) == 1) + { + xtra_magic = TRUE; + + if (cheat_room || p_ptr->precognition) + { + msg_print("Magical level"); + } + } + + mult = ((xtra_magic) ? 3 : 2); + + for (y = 0; y < cur_hgt; y++) + { + for (x = 0; x < cur_wid; x++) + { + cave_type *c_ptr = &cave[y][x]; + + /* Calculate the amount of mana in each grid */ + mana = mult * m_bonus(255, dun_level) / 2; + if (xtra_magic) mana += 10 + rand_int(10); + + /* Never more than 255 or less than 0(paranoia) */ + if (mana < 0) mana = 0; + if (mana > 255) mana = 255; + + c_ptr->mana = mana; + } + } +} + + +/* + * Generates a random dungeon level -RAK- + * + * Hack -- regenerate any "overflow" levels + * + * Hack -- allow auto-scumming via a gameplay option. + */ +void generate_cave(void) +{ + dungeon_info_type *d_ptr = &d_info[dungeon_type]; + int tester_1, tester_2; + int y, x, num, i; + bool_ loaded = FALSE; + char buf[80]; + s16b town_level = 0; + + /* The dungeon is not ready */ + character_dungeon = FALSE; + generate_special_feeling = FALSE; + + /* Initialize the flags with the basic dungeon flags */ + if (!dun_level) + { + dungeon_flags1 = d_info[DUNGEON_WILDERNESS].flags1; + dungeon_flags2 = d_info[DUNGEON_WILDERNESS].flags2; + } + else + { + dungeon_flags1 = d_ptr->flags1; + dungeon_flags2 = d_ptr->flags2; + } + + /* Is it a town level ? */ + for (i = 0; i < TOWN_DUNGEON; i++) + { + if (d_ptr->t_level[i] == dun_level) town_level = d_ptr->t_idx[i]; + } + + /* Save the imprinted monsters */ + save_all_friends(); + wipe_m_list(); + + /* Seed the RNG if appropriate */ + if (town_level) + { + Rand_quick = TRUE; + Rand_value = town_info[town_level].seed; + } + + process_hooks_new(HOOK_GEN_LEVEL_BEGIN, NULL, NULL); + + /* Try to load a saved level */ + if (get_dungeon_save(buf)) + { + /* No effects */ + for (i = 0; i < MAX_EFFECTS; i++) + { + effects[i].time = 0; + } + + /* Start with a blank cave */ + for (y = 0; y < MAX_HGT; y++) + { + for (x = 0; x < MAX_WID; x++) + { + /* Wipe */ + cave[y][x].wipe(); + + /* No features */ + cave_set_feat(y, x, FEAT_PERM_INNER); + } + } + + loaded = load_dungeon(buf); + } + + /* No saved level -- generate new one */ + if (!loaded) + { + if (!get_dungeon_special(buf) || + !special_lvl[dun_level - d_info[dungeon_type].mindepth][dungeon_type]) + { + get_level_flags(); + } + + /* Generate */ + for (num = 0; TRUE; num++) + { + bool_ okay = TRUE; + + cptr why = NULL; + + /* No effects */ + for (i = 0; i < MAX_EFFECTS; i++) + { + effects[i].time = 0; + } + + /* Start with a blank cave */ + for (y = 0; y < MAX_HGT; y++) + { + for (x = 0; x < MAX_WID; x++) + { + /* Wipe */ + cave[y][x].wipe(); + + /* No features */ + cave_set_feat(y, x, FEAT_PERM_INNER); + } + } + + + /* XXX XXX XXX XXX */ + o_max = 1; + + + /* Mega-Hack -- no player yet */ + p_ptr->px = p_ptr->py = 0; + + + /* Mega-Hack -- no panel yet */ + panel_row_min = 0; + panel_row_max = 0; + panel_col_min = 0; + panel_col_max = 0; + + + /* Reset the monster generation level */ + if (dungeon_type != DUNGEON_DEATH) monster_level = dun_level; + else monster_level = (p_ptr->lev * 2) + 10 + rand_int(40); + + /* Reset the object generation level */ + object_level = dun_level; + + /* Nothing special here yet */ + good_item_flag = FALSE; + + /* Nothing good here yet */ + rating = 0; + + /* No ambush here yet */ + ambush_flag = FALSE; + + /* No fated level here yet */ + fate_flag = FALSE; + + /* Quest levels -KMW- */ + if (p_ptr->inside_quest) + { + process_hooks_new(HOOK_GEN_QUEST, NULL, NULL); + } + + /* Special levels */ + else if (build_special_level()) + { + /* nothing */ + } + + /* Build the town */ + else if (!dun_level) + { + /* Big town */ + cur_hgt = MAX_HGT; + cur_wid = MAX_WID; + + /* Determine number of panels */ + max_panel_rows = (cur_hgt / SCREEN_HGT) * 2 - 2; + max_panel_cols = (cur_wid / SCREEN_WID) * 2 - 2; + + /* Assume illegal panel */ + panel_row_min = max_panel_rows * (SCREEN_HGT / 2); + panel_col_min = max_panel_cols * (SCREEN_WID / 2); + + /* Big wilderness mode */ + if (!p_ptr->wild_mode) + { + /* Make the wilderness */ + wilderness_gen(); + } + + /* Small wilderness mode */ + else + { + /* Make the wilderness */ + wilderness_gen_small(); + } + + + okay = TRUE; + } + + /* Build a dungeon level */ + else + { + /* Requested size level */ + if (d_ptr->size_x != -1) + { + if (cheat_room || p_ptr->precognition) + { + msg_print ("A 'size' dungeon level."); + } + + cur_hgt = d_ptr->size_y * SCREEN_HGT; + cur_wid = d_ptr->size_x * SCREEN_WID; + + /* Determine number of panels */ + max_panel_rows = (cur_hgt / SCREEN_HGT) * 2 - 2; + max_panel_cols = (cur_wid / SCREEN_WID) * 2 - 2; + + /* Assume illegal panel */ + panel_row_min = max_panel_rows * (SCREEN_HGT / 2); + panel_col_min = max_panel_cols * (SCREEN_WID / 2); + + if (cheat_room) + { + msg_format("X:%d, Y:%d.", max_panel_cols, max_panel_rows); + } + } + /* Very small (1 x 1 panel) level */ + else if (!(dungeon_flags1 & DF1_BIG) && + (dungeon_flags1 & DF1_SMALLEST)) + { + if (cheat_room || p_ptr->precognition) + { + msg_print ("A 'small' dungeon level."); + } + + cur_hgt = SCREEN_HGT; + cur_wid = SCREEN_WID; + + /* Determine number of panels */ + max_panel_rows = 1; + max_panel_cols = 1; + + /* Assume illegal panel */ + panel_row_min = max_panel_rows * (SCREEN_HGT / 2); + panel_col_min = max_panel_cols * (SCREEN_WID / 2); + + if (cheat_room) + { + msg_format("X:1, Y:1."); + } + } + + /* Small level */ + else if (!(dungeon_flags1 & DF1_BIG) && + (always_small_level || + (dungeon_flags1 & DF1_SMALL) || + (small_levels && rand_int(SMALL_LEVEL) == 0))) + { + if (cheat_room || p_ptr->precognition) + { + msg_print ("A 'small' dungeon level."); + } + + tester_1 = rand_range(1, (MAX_HGT / SCREEN_HGT)); + tester_2 = rand_range(1, (MAX_WID / SCREEN_WID) - 1); + + cur_hgt = tester_1 * SCREEN_HGT; + cur_wid = tester_2 * SCREEN_WID; + + /* Determine number of panels */ + max_panel_rows = (cur_hgt / SCREEN_HGT) * 2 - 2; + max_panel_cols = (cur_wid / SCREEN_WID) * 2 - 2; + + /* Assume illegal panel */ + panel_row_min = max_panel_rows * (SCREEN_HGT / 2); + panel_col_min = max_panel_cols * (SCREEN_WID / 2); + + if (cheat_room) + { + msg_format("X:%d, Y:%d.", max_panel_cols, max_panel_rows); + } + } + + /* Normal level */ + else + { + /* Use full panels */ + cur_hgt = MAX_HGT; + cur_wid = MAX_WID; + + /* Determine number of panels */ + max_panel_rows = (cur_hgt / SCREEN_HGT) * 2 - 2; + max_panel_cols = (cur_wid / SCREEN_WID) * 2 - 2; + + /* Assume illegal panel */ + panel_row_min = max_panel_rows * (SCREEN_HGT / 2); + panel_col_min = max_panel_cols * (SCREEN_WID / 2); + } + + /* Generate a level */ + if (!cave_gen()) + { + why = "could not place player"; + okay = FALSE; + } + } + + /* Extract the feeling */ + if (rating > 100) feeling = 2; + else if (rating > 80) feeling = 3; + else if (rating > 60) feeling = 4; + else if (rating > 40) feeling = 5; + else if (rating > 30) feeling = 6; + else if (rating > 20) feeling = 7; + else if (rating > 10) feeling = 8; + else if (rating > 0) feeling = 9; + else feeling = 10; + + /* Hack -- Have a special feeling sometimes */ + if (good_item_flag && !p_ptr->preserve) feeling = 1; + + /* It takes 1000 game turns for "feelings" to recharge */ + if ((turn - old_turn) < 1000) feeling = 0; + + /* Hack -- no feeling in the town */ + if (!dun_level) feeling = 0; + + + /* Prevent object over-flow */ + if (o_max >= max_o_idx) + { + /* Message */ + why = "too many objects"; + + /* Message */ + okay = FALSE; + } + + /* Prevent monster over-flow */ + if (m_max >= max_m_idx) + { + /* Message */ + why = "too many monsters"; + + /* Message */ + okay = FALSE; + } + + /* Mega-Hack -- "auto-scum" */ + if (auto_scum && (num < 100) && !p_ptr->inside_quest && dun_level) + { + /* Require "goodness" */ + if ((feeling > 9) || + ((dun_level >= 5) && (feeling > 8)) || + ((dun_level >= 10) && (feeling > 7)) || + ((dun_level >= 20) && (feeling > 6)) || + ((dun_level >= 40) && (feeling > 5))) + { + /* Give message to cheaters */ + if (cheat_room || cheat_hear || + cheat_peek || cheat_xtra || p_ptr->precognition) + { + /* Message */ + why = "boring level"; + } + + /* Try again */ + okay = FALSE; + } + } + + /* Accept */ + if (okay || town_level) break; + + /* Message */ + if (why) msg_format("Generation restarted (%s)", why); + + /* Wipe the objects */ + wipe_o_list(); + + /* Wipe the monsters */ + wipe_m_list(); + + /* Clear the fate icky flags */ + for (i = 0; i < MAX_FATES; i++) fates[i].icky = FALSE; + + /* + * Mega-Hack -- Reset special level flag if necessary + * XXX XXX XXX + */ + wipe_special_level(); + } + + /* Give some mana to each grid -- DG */ + generate_grid_mana(); + } + + /* Put the kept monsters -- DG */ + if (!p_ptr->wild_mode) + { + replace_all_friends(); + } + + /* Hack -- Clear used up fates */ + for (i = 0; i < MAX_FATES; i++) + { + if (fates[i].icky) + { + /* Mark the artefact as generated */ + if ((fates[i].fate == FATE_FIND_A) && fates[i].a_idx) + { + a_info[fates[i].a_idx].cur_num = 1; + } + fates[i].fate = FATE_NONE; + fates[i].icky = FALSE; + } + } + + /* Set special level generated flag if applicable */ + finalise_special_level(); + + /* No teleporatations yet */ + last_teleportation_y = -1; + last_teleportation_x = -1; + + /* Mark the dungeon town as found */ + if (town_level) + { + /* Set the known flag */ + town_info[town_level].flags |= (TOWN_KNOWN); + } + + /* The dungeon is ready */ + character_dungeon = TRUE; + + /* Remember when this level was "created" */ + old_turn = turn; + + /* Provide astral chars with the full map */ + if (p_ptr->astral && dun_level) + { + wiz_lite_extra(); + } + + /* Player should get the first move upon entering the dungeon */ + p_ptr->energy = 100; +} |