#include "AppHdr.h"
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <list>
#include <set>
#include <sstream>
#include <algorithm>
#include "abyss.h"
#include "artefact.h"
#include "branch.h"
#include "chardump.h"
#include "cloud.h"
#include "defines.h"
#include "effects.h"
#include "enum.h"
#include "externs.h"
#include "directn.h"
#include "dungeon.h"
#include "files.h"
#include "itemname.h"
#include "itemprop.h"
#include "items.h"
#include "l_defs.h"
#include "los.h"
#include "makeitem.h"
#include "mapdef.h"
#include "mapmark.h"
#include "maps.h"
#include "message.h"
#include "misc.h"
#include "mon-util.h"
#include "monplace.h"
#include "notes.h"
#include "place.h"
#include "player.h"
#include "random.h"
#include "religion.h"
#include "spells3.h"
#include "state.h"
#include "stuff.h"
#include "tags.h"
#include "terrain.h"
#include "traps.h"
#include "travel.h"
#include "view.h"
#ifdef WIZARD
#include "cio.h"
#endif
#define YOU_DUNGEON_VAULTS_KEY "you_dungeon_vaults_key"
#define YOU_PORTAL_VAULT_MAPS_KEY "you_portal_vault_maps_key"
spec_room lua_special_room_spec;
int lua_special_room_level;
struct dist_feat
{
int dist;
dungeon_feature_type feat;
dist_feat(int _d = 0, dungeon_feature_type _f = DNGN_UNSEEN)
: dist(_d), feat(_f)
{
}
};
const struct coord_def OrthCompass[4] =
{
coord_def(0, -1), coord_def(0, 1), coord_def(-1, 0), coord_def(1, 0)
};
coord_def spec_room::random_spot() const
{
return coord_def( random_range(tl.x, br.x-1), random_range(tl.y, br.y-1) );
}
static void _build_dungeon_level(int level_number, int level_type);
static bool _valid_dungeon_level(int level_number, int level_type);
static bool _find_in_area(int sx, int sy, int ex, int ey,
dungeon_feature_type feature);
static bool _make_box(int room_x1, int room_y1, int room_x2, int room_y2,
dungeon_feature_type floor=DNGN_UNSEEN,
dungeon_feature_type wall=DNGN_UNSEEN,
dungeon_feature_type avoid=DNGN_UNSEEN);
static void _replace_area(const coord_def& p1, const coord_def& p2,
dungeon_feature_type replace,
dungeon_feature_type feature,
unsigned mmask = 0, bool needs_update = false);
static void _replace_area(int sx, int sy, int ex, int ey,
dungeon_feature_type replace,
dungeon_feature_type feature,
unsigned mmask = 0, bool needs_update = false)
{
_replace_area( coord_def(sx, sy), coord_def(ex, ey),
replace, feature, mmask, needs_update );
}
static builder_rc_type _builder_by_type(int level_number, char level_type);
static builder_rc_type _builder_by_branch(int level_number);
static builder_rc_type _builder_normal(int level_number, char level_type,
spec_room &s);
static builder_rc_type _builder_basic(int level_number);
static void _builder_extras(int level_number, int level_type);
static void _builder_items(int level_number, char level_type, int items_wanted);
static void _builder_monsters(int level_number, char level_type, int mon_wanted);
static void _place_specific_stair(dungeon_feature_type stair,
const std::string &tag = "",
int dl = 0, bool vault_only = false);
static void _place_branch_entrances(int dlevel, char level_type);
static void _place_extra_vaults();
static void _place_minivaults(const std::string &tag = "",
int fewest = -1, int most = -1,
bool force = false);
static void _place_traps( int level_number );
static void _place_fog_machines( int level_number );
static void _prepare_swamp();
static void _prepare_shoals( int level_number );
static void _prepare_water( int level_number );
static void _check_doors();
static void _hide_doors();
static void _make_trail(int xs, int xr, int ys, int yr,int corrlength,
int intersect_chance,
int no_corr, dungeon_feature_type begin,
dungeon_feature_type end=DNGN_UNSEEN);
static bool _make_room(int sx,int sy,int ex,int ey,int max_doors, int doorlevel);
static void _place_pool(dungeon_feature_type pool_type, unsigned char pool_x1,
unsigned char pool_y1, unsigned char pool_x2,
unsigned char pool_y2);
static void _many_pools(dungeon_feature_type pool_type);
static bool _join_the_dots_rigorous(const coord_def &from,
const coord_def &to,
unsigned mapmask,
bool early_exit = false);
static void _build_river(dungeon_feature_type river_type); static void _build_lake(dungeon_feature_type lake_type);
static void _bigger_room();
static void _plan_main(int level_number, int force_plan);
static char _plan_1(int level_number);
static char _plan_2(int level_number);
static char _plan_3(int level_number);
static char _plan_4(char forbid_x1, char forbid_y1, char forbid_x2,
char forbid_y2, dungeon_feature_type force_wall);
static char _plan_5();
static char _plan_6(int level_number);
static void _portal_vault_level(int level_number);
static void _labyrinth_level(int level_number);
static void _box_room(int bx1, int bx2, int by1, int by2,
dungeon_feature_type wall_type);
static int _box_room_doors( int bx1, int bx2, int by1, int by2, int new_doors);
static void _city_level(int level_number);
static void _diamond_rooms(int level_number);
static void _pick_float_exits(vault_placement &place,
std::vector<coord_def> &targets);
static void _connect_vault(const vault_placement &vp);
static void _place_shops(int level_number, int nshops = 0);
static object_class_type _item_in_shop(unsigned char shop_type);
static bool _treasure_area(int level_number, unsigned char ta1_x,
unsigned char ta2_x, unsigned char ta1_y,
unsigned char ta2_y);
static void _special_room(int level_number, spec_room &sr,
const map_def *vault);
static void _specr_2(spec_room &sr);
static void _big_room(int level_number);
static void _chequerboard(spec_room &sr, dungeon_feature_type target,
dungeon_feature_type floor1,
dungeon_feature_type floor2);
static void _roguey_level(int level_number, spec_room &sr, bool make_stairs);
static bool _build_secondary_vault(int level_number, const map_def *vault,
int rune_subst = -1,
bool clobber = false,
bool make_no_exits = false,
const coord_def &where = coord_def(-1, -1));
static bool _build_vaults(int level_number,
const map_def *vault,
int rune_subst = -1, bool build_only = false,
bool check_collisions = false,
bool make_no_exits = false,
const coord_def &where = coord_def(-1, -1));
static void _vault_grid( vault_placement &,
int vgrid,
const coord_def& where,
bool recursive = false);
static const map_def *_dgn_random_map_for_place(bool minivault);
static void _dgn_load_colour_grid();
static void _dgn_map_colour_fixup();
static dungeon_feature_type _pick_an_altar();
static void _place_altar();
static void _place_altars();
typedef std::list<coord_def> coord_list;
static void _dgn_set_floor_colours();
static bool _fixup_interlevel_connectivity();
map_mask dgn_Map_Mask;
std::vector<vault_placement> Level_Vaults;
std::vector<vault_placement> Temp_Vaults;
std::set<std::string> Level_Unique_Maps;
std::set<std::string> Level_Unique_Tags;
std::string dgn_Build_Method;
std::string dgn_Layout_Type;
bool Generating_Level = false;
static int can_create_vault = true;
static bool dgn_level_vetoed = false;
static bool use_random_maps = true;
static bool dgn_check_connectivity = false;
static int dgn_zones = 0;
static CrawlHashTable _you_vault_list;
static std::string _portal_vault_map_name;
struct coloured_feature
{
dungeon_feature_type feature;
int colour;
coloured_feature() : feature(DNGN_UNSEEN), colour(BLACK) { }
coloured_feature(dungeon_feature_type f, int c)
: feature(f), colour(c)
{
}
};
struct dgn_colour_override_manager
{
dgn_colour_override_manager()
{
_dgn_load_colour_grid();
}
~dgn_colour_override_manager()
{
_dgn_map_colour_fixup();
}
};
typedef FixedArray< coloured_feature, GXM, GYM > dungeon_colour_grid;
static std::auto_ptr<dungeon_colour_grid> dgn_colour_grid;
typedef std::map<std::string, std::string> callback_map;
static callback_map level_type_post_callbacks;
bool builder(int level_number, int level_type)
{
const std::set<std::string> uniq_tags = you.uniq_map_tags;
const std::set<std::string> uniq_names = you.uniq_map_names;
unwind_bool levelgen(Generating_Level, true);
int tries = 20;
while (tries-- > 0)
{
#ifdef DEBUG_DIAGNOSTICS
mapgen_report_map_build_start();
#endif
dgn_reset_level();
if (tries < 5)
use_random_maps = false;
_build_dungeon_level(level_number, level_type);
_dgn_set_floor_colours();
#ifdef DEBUG_DIAGNOSTICS
if (dgn_level_vetoed)
mapgen_report_map_veto();
#endif
if (!dgn_level_vetoed && _valid_dungeon_level(level_number, level_type))
{
#if DEBUG_MONS_SCAN
debug_mons_scan();
#endif
if (dgn_Build_Method.size() > 0 && dgn_Build_Method[0] == ' ')
dgn_Build_Method = dgn_Build_Method.substr(1);
env.properties[BUILD_METHOD_KEY] = dgn_Build_Method;
env.properties[LAYOUT_TYPE_KEY] = dgn_Layout_Type;
env.properties[LEVEL_ID_KEY] = level_id::current().describe();
if (!_you_vault_list.empty())
{
const std::string lev = level_id::current().describe();
CrawlHashTable &all_vaults =
you.props[YOU_DUNGEON_VAULTS_KEY].get_table();
CrawlHashTable &this_level = all_vaults[lev].get_table();
this_level = _you_vault_list;
}
else if (!_portal_vault_map_name.empty())
{
CrawlVector &vault_maps =
you.props[YOU_PORTAL_VAULT_MAPS_KEY].get_vector();
vault_maps.push_back(_portal_vault_map_name);
}
if (you.level_type == LEVEL_PORTAL_VAULT)
{
CrawlVector &vault_names =
you.props[YOU_PORTAL_VAULT_NAMES_KEY].get_vector();
vault_names.push_back(you.level_type_name);
}
dgn_Layout_Type.clear();
Level_Unique_Maps.clear();
Level_Unique_Tags.clear();
_dgn_map_colour_fixup();
return (true);
}
you.uniq_map_tags = uniq_tags;
you.uniq_map_names = uniq_names;
}
if (!crawl_state.map_stat_gen)
{
save_game(true,
make_stringf("Unable to generate level for '%s'!",
level_id::current().describe().c_str()).c_str());
}
dgn_Layout_Type.clear();
return (false);
}
void level_welcome_messages()
{
for (int i = 0, size = Level_Vaults.size(); i < size; ++i)
{
const std::vector<std::string> &msgs
= Level_Vaults[i].map.welcome_messages;
for (int j = 0, msize = msgs.size(); j < msize; ++j)
mpr(msgs[j].c_str());
}
}
void level_clear_vault_memory()
{
Level_Vaults.clear();
Temp_Vaults.clear();
dgn_Map_Mask.init(0);
}
void dgn_flush_map_memory()
{
you.uniq_map_tags.clear();
you.uniq_map_names.clear();
}
static void _dgn_load_colour_grid()
{
dgn_colour_grid.reset(new dungeon_colour_grid);
dungeon_colour_grid &dcgrid(*dgn_colour_grid);
for (int y = Y_BOUND_1; y <= Y_BOUND_2; ++y)
for (int x = X_BOUND_1; x <= X_BOUND_2; ++x)
if (env.grid_colours[x][y] != BLACK)
{
dcgrid[x][y]
= coloured_feature(grd[x][y], env.grid_colours[x][y]);
}
}
static void _dgn_map_colour_fixup()
{
if (!dgn_colour_grid.get())
return;
const dungeon_colour_grid &dcgrid(*dgn_colour_grid);
for (int y = Y_BOUND_1; y <= Y_BOUND_2; ++y)
for (int x = X_BOUND_1; x <= X_BOUND_2; ++x)
if (dcgrid[x][y].colour != BLACK
&& grd[x][y] != dcgrid[x][y].feature
&& (grd[x][y] != DNGN_UNDISCOVERED_TRAP
|| dcgrid[x][y].feature != DNGN_FLOOR))
{
env.grid_colours[x][y] = BLACK;
}
dgn_colour_grid.reset(NULL);
}
bool set_level_flags(unsigned long flags, bool silent)
{
bool could_control = allow_control_teleport(true);
bool could_map = player_in_mappable_area();
unsigned long old_flags = env.level_flags;
env.level_flags |= flags;
bool can_control = allow_control_teleport(true);
bool can_map = player_in_mappable_area();
if (you.skills[SK_TRANSLOCATIONS] > 0
&& could_control && !can_control && !silent)
{
mpr("You sense the appearance of a powerful magical force "
"which warps space.", MSGCH_WARN);
}
if (could_map && !can_map && !silent)
{
mpr("A powerful force appears that prevents you from "
"remembering where you've been.", MSGCH_WARN);
}
return (old_flags != env.level_flags);
}
bool unset_level_flags(unsigned long flags, bool silent)
{
bool could_control = allow_control_teleport(true);
bool could_map = player_in_mappable_area();
unsigned long old_flags = env.level_flags;
env.level_flags &= ~flags;
bool can_control = allow_control_teleport(true);
bool can_map = player_in_mappable_area();
if (you.skills[SK_TRANSLOCATIONS] > 0
&& !could_control && can_control && !silent)
{
mpr("You sense the disappearance of a powerful magical force "
"which warped space.", MSGCH_RECOVERY);
}
if (!could_map && can_map && !silent)
{
mpr("You sense the disappearance of the force that prevented you "
"from remembering where you've been.", MSGCH_RECOVERY);
}
return (old_flags != env.level_flags);
}
void dgn_set_grid_colour_at(const coord_def &c, int colour)
{
if (colour != BLACK)
{
env.grid_colours(c) = colour;
if (!dgn_colour_grid.get())
dgn_colour_grid.reset(new dungeon_colour_grid);
(*dgn_colour_grid)(c) = coloured_feature(grd(c), colour);
}
}
static void _dgn_register_vault(const map_def &map)
{
if (!map.has_tag("allow_dup"))
you.uniq_map_names.insert(map.name);
if (map.has_tag("luniq"))
Level_Unique_Maps.insert(map.name);
std::vector<std::string> tags = split_string(" ", map.tags);
for (int t = 0, ntags = tags.size(); t < ntags; ++t)
{
const std::string &tag = tags[t];
if (tag.find("uniq_") == 0)
you.uniq_map_tags.insert(tag);
else if (tag.find("luniq_") == 0)
Level_Unique_Tags.insert(tag);
}
}
static bool _is_passable_ignore_vault(const coord_def &c)
{
return (is_travelsafe_square(c, false, true) || grd(c) == DNGN_SECRET_DOOR);
}
bool dgn_square_is_passable(const coord_def &c)
{
return (!(dgn_Map_Mask(c) & MMT_OPAQUE) && (_is_passable_ignore_vault(c)));
}
static inline void _dgn_point_record_stub(const coord_def &) { }
template <class point_record>
static bool _dgn_fill_zone(
const coord_def &start, int zone,
point_record &record_point,
bool (*passable)(const coord_def &) = dgn_square_is_passable,
bool (*iswanted)(const coord_def &) = NULL)
{
bool ret = false;
std::list<coord_def> points[2];
int cur = 0;
for (points[cur].push_back(start); !points[cur].empty(); )
{
for (std::list<coord_def>::const_iterator i = points[cur].begin();
i != points[cur].end(); ++i)
{
const coord_def &c(*i);
travel_point_distance[c.x][c.y] = zone;
if (iswanted && iswanted(c))
ret = true;
for (int yi = -1; yi <= 1; ++yi)
for (int xi = -1; xi <= 1; ++xi)
{
if (!xi && !yi)
continue;
const coord_def cp(c.x + xi, c.y + yi);
if (!map_bounds(cp)
|| travel_point_distance[cp.x][cp.y] || !passable(cp))
{
continue;
}
travel_point_distance[cp.x][cp.y] = zone;
record_point(cp);
points[!cur].push_back(cp);
}
}
points[cur].clear();
cur = !cur;
}
return (ret);
}
static bool _is_perm_down_stair(const coord_def &c)
{
switch (grd(c))
{
case DNGN_STONE_STAIRS_DOWN_I:
case DNGN_STONE_STAIRS_DOWN_II:
case DNGN_STONE_STAIRS_DOWN_III:
case DNGN_EXIT_HELL:
case DNGN_EXIT_PANDEMONIUM:
case DNGN_TRANSIT_PANDEMONIUM:
case DNGN_EXIT_ABYSS:
return (true);
default:
return (false);
}
}
static bool _is_bottom_exit_stair(const coord_def &c)
{
switch (grd(c))
{
case DNGN_STONE_STAIRS_UP_I:
case DNGN_STONE_STAIRS_UP_II:
case DNGN_STONE_STAIRS_UP_III:
case DNGN_EXIT_HELL:
case DNGN_RETURN_FROM_ORCISH_MINES:
case DNGN_RETURN_FROM_HIVE:
case DNGN_RETURN_FROM_LAIR:
case DNGN_RETURN_FROM_SLIME_PITS:
case DNGN_RETURN_FROM_VAULTS:
case DNGN_RETURN_FROM_CRYPT:
case DNGN_RETURN_FROM_HALL_OF_BLADES:
case DNGN_RETURN_FROM_ZOT:
case DNGN_RETURN_FROM_TEMPLE:
case DNGN_RETURN_FROM_SNAKE_PIT:
case DNGN_RETURN_FROM_ELVEN_HALLS:
case DNGN_RETURN_FROM_TOMB:
case DNGN_RETURN_FROM_SWAMP:
case DNGN_RETURN_FROM_SHOALS:
case DNGN_EXIT_PANDEMONIUM:
case DNGN_TRANSIT_PANDEMONIUM:
case DNGN_EXIT_ABYSS:
return (true);
default:
return (false);
}
}
static bool _is_exit_stair(const coord_def &c)
{
switch (grd(c))
{
case DNGN_STONE_STAIRS_DOWN_I:
case DNGN_STONE_STAIRS_DOWN_II:
case DNGN_STONE_STAIRS_DOWN_III:
case DNGN_ESCAPE_HATCH_DOWN:
case DNGN_STONE_STAIRS_UP_I:
case DNGN_STONE_STAIRS_UP_II:
case DNGN_STONE_STAIRS_UP_III:
case DNGN_ESCAPE_HATCH_UP:
case DNGN_EXIT_HELL:
case DNGN_RETURN_FROM_ORCISH_MINES:
case DNGN_RETURN_FROM_HIVE:
case DNGN_RETURN_FROM_LAIR:
case DNGN_RETURN_FROM_SLIME_PITS:
case DNGN_RETURN_FROM_VAULTS:
case DNGN_RETURN_FROM_CRYPT:
case DNGN_RETURN_FROM_HALL_OF_BLADES:
case DNGN_RETURN_FROM_ZOT:
case DNGN_RETURN_FROM_TEMPLE:
case DNGN_RETURN_FROM_SNAKE_PIT:
case DNGN_RETURN_FROM_ELVEN_HALLS:
case DNGN_RETURN_FROM_TOMB:
case DNGN_RETURN_FROM_SWAMP:
case DNGN_RETURN_FROM_SHOALS:
case DNGN_EXIT_PANDEMONIUM:
case DNGN_TRANSIT_PANDEMONIUM:
case DNGN_EXIT_ABYSS:
return (true);
default:
return (false);
}
}
int process_disconnected_zones(int x1, int y1, int x2, int y2,
bool choose_stairless,
dungeon_feature_type fill)
{
memset(travel_point_distance, 0, sizeof(travel_distance_grid_t));
int nzones = 0;
int ngood = 0;
for (int y = y1; y <= y2 ; ++y)
for (int x = x1; x <= x2; ++x)
{
if (!map_bounds(x, y)
|| travel_point_distance[x][y]
|| !dgn_square_is_passable(coord_def(x, y)))
{
continue;
}
bool found_exit_stair =
_dgn_fill_zone(coord_def(x, y), ++nzones,
_dgn_point_record_stub,
dgn_square_is_passable,
choose_stairless ? (at_branch_bottom() ?
_is_bottom_exit_stair :
_is_exit_stair) : NULL);
if (choose_stairless && found_exit_stair)
++ngood;
else if (fill)
{
for (int fy = y1; fy <= y2 ; ++fy)
for (int fx = x1; fx <= x2; ++fx)
if (travel_point_distance[fx][fy] == nzones)
grd[fx][fy] = fill;
}
}
return (nzones - ngood);
}
static int _dgn_count_disconnected_zones(bool choose_stairless)
{
return process_disconnected_zones(0, 0, GXM-1, GYM-1, choose_stairless,
(dungeon_feature_type)0);
}
static void _fixup_pandemonium_stairs()
{
for (int i = 0; i < GXM; i++)
for (int j = 0; j < GYM; j++)
{
if (grd[i][j] >= DNGN_STONE_STAIRS_UP_I
&& grd[i][j] <= DNGN_ESCAPE_HATCH_UP)
{
if (one_chance_in(50))
grd[i][j] = DNGN_EXIT_PANDEMONIUM;
else
grd[i][j] = DNGN_FLOOR;
}
if (grd[i][j] >= DNGN_ENTER_LABYRINTH
&& grd[i][j] <= DNGN_ESCAPE_HATCH_DOWN)
{
grd[i][j] = DNGN_TRANSIT_PANDEMONIUM;
}
}
}
static void _mask_vault(const vault_placement &place, unsigned mask)
{
for (int y = place.pos.y + place.size.y - 1; y >= place.pos.y; --y)
for (int x = place.pos.x + place.size.x - 1; x >= place.pos.x; --x)
{
if (place.map.in_map(coord_def(x - place.pos.x, y - place.pos.y)))
dgn_Map_Mask[x][y] |= mask;
}
}
void dgn_register_place(const vault_placement &place, bool register_vault)
{
if (register_vault)
_dgn_register_vault(place.map);
if (!place.map.has_tag("layout"))
_mask_vault(place, MMT_VAULT | MMT_NO_DOOR);
if (place.map.has_tag("no_monster_gen"))
_mask_vault(place, MMT_NO_MONS);
if (place.map.has_tag("no_item_gen"))
_mask_vault(place, MMT_NO_ITEM);
if (place.map.has_tag("no_pool_fixup"))
_mask_vault(place, MMT_NO_POOL);
if (place.map.has_tag("no_wall_fixup"))
_mask_vault(place, MMT_NO_WALL);
if (!place.map.has_tag("transparent"))
_mask_vault(place, MMT_OPAQUE);
for (int y = place.pos.y + place.size.y - 1; y >= place.pos.y; --y)
for (int x = place.pos.x + place.size.x - 1; x >= place.pos.x; --x)
if (place.map.in_map(coord_def(x - place.pos.x, y - place.pos.y)))
{
int key = place.map.map.glyph(x - place.pos.x, y - place.pos.y);
const keyed_mapspec* spec = place.map.mapspec_for_key(key);
if (spec != NULL)
{
dgn_Map_Mask[x][y] |= (short)spec->map_mask.flags_set;
dgn_Map_Mask[x][y] &= ~((short)spec->map_mask.flags_unset);
}
}
set_branch_flags(place.map.branch_flags.flags_set, true);
unset_branch_flags(place.map.branch_flags.flags_unset, true);
set_level_flags(place.map.level_flags.flags_set, true);
unset_level_flags(place.map.level_flags.flags_unset, true);
if (place.map.random_mons.size() > 0)
{
ASSERT(you.level_type == LEVEL_PORTAL_VAULT);
set_vault_mon_list(place.map.random_mons);
}
if (place.map.floor_colour != BLACK)
env.floor_colour = place.map.floor_colour;
if (place.map.rock_colour != BLACK)
env.rock_colour = place.map.rock_colour;
#ifdef USE_TILE
if (place.map.rock_tile != 0)
env.tile_default.wall = place.map.rock_tile;
if (place.map.floor_tile != 0)
env.tile_default.floor = place.map.floor_tile;
#endif
}
static bool _ensure_vault_placed(bool vault_success,
bool disable_further_vaults)
{
if (!vault_success)
dgn_level_vetoed = true;
else if (disable_further_vaults)
can_create_vault = false;
return (vault_success);
}
static bool _ensure_vault_placed_ex( bool vault_success, const map_def *vault )
{
return _ensure_vault_placed( vault_success,
(!vault->has_tag("extra")
&& vault->orient == MAP_ENCOMPASS) );
}
static coord_def _find_level_feature(int feat)
{
for (int y = 1; y < GYM; ++y)
for (int x = 1; x < GXM; ++x)
{
if (grd[x][y] == feat)
return coord_def(x, y);
}
return coord_def(0, 0);
}
static bool _has_connected_stone_stairs_from(const coord_def &c)
{
flood_find<feature_grid, coord_predicate> ff(env.grid, in_bounds);
ff.add_feat(DNGN_STONE_STAIRS_DOWN_I);
ff.add_feat(DNGN_STONE_STAIRS_DOWN_II);
ff.add_feat(DNGN_STONE_STAIRS_DOWN_III);
ff.add_feat(DNGN_STONE_STAIRS_UP_I);
ff.add_feat(DNGN_STONE_STAIRS_UP_II);
ff.add_feat(DNGN_STONE_STAIRS_UP_III);
coord_def where = ff.find_first_from(c, dgn_Map_Mask);
return (where.x || !ff.did_leave_vault());
}
static bool _has_connected_downstairs_from(const coord_def &c)
{
flood_find<feature_grid, coord_predicate> ff(env.grid, in_bounds);
ff.add_feat(DNGN_STONE_STAIRS_DOWN_I);
ff.add_feat(DNGN_STONE_STAIRS_DOWN_II);
ff.add_feat(DNGN_STONE_STAIRS_DOWN_III);
ff.add_feat(DNGN_ESCAPE_HATCH_DOWN);
coord_def where = ff.find_first_from(c, dgn_Map_Mask);
return (where.x || !ff.did_leave_vault());
}
static bool _is_level_stair_connected()
{
coord_def up = _find_level_feature(DNGN_STONE_STAIRS_UP_I);
if (up.x && up.y)
return _has_connected_downstairs_from(up);
return (false);
}
static bool _valid_dungeon_level(int level_number, int level_type)
{
if (level_number == 0 && level_type == LEVEL_DUNGEON)
return _is_level_stair_connected();
return (true);
}
void dgn_reset_level()
{
dgn_level_vetoed = false;
Level_Unique_Maps.clear();
Level_Unique_Tags.clear();
_portal_vault_map_name.clear();
_you_vault_list.clear();
dgn_Build_Method.clear();
dgn_Layout_Type.clear();
level_clear_vault_memory();
dgn_colour_grid.reset(NULL);
can_create_vault = true;
use_random_maps = true;
dgn_check_connectivity = false;
dgn_zones = 0;
env.properties.clear();
env.properties[LEVEL_VAULTS_KEY].new_table(SV_STR);
env.properties[TEMP_VAULTS_KEY].new_table(SV_STR);
env.properties[LEVEL_EXTRAS_KEY].new_vector(SV_STR);
grd.init(DNGN_ROCK_WALL);
env.grid_colours.init(BLACK);
env.map.init(map_cell());
for (int i = 0; i < MAX_TRAPS; i++)
env.trap[i].type = TRAP_UNASSIGNED;
for (int i = 0; i < MAX_ITEMS; i++)
init_item( i );
for (int i = 0; i < MAX_MONSTERS; i++)
menv[i].reset();
env.mons_alloc.init(MONS_NO_MONSTER);
env.cgrid.init(EMPTY_CLOUD);
const cloud_struct empty;
env.cloud.init(empty);
mgrd.init(NON_MONSTER);
igrd.init(NON_ITEM);
for (int shcount = 0; shcount < MAX_SHOPS; shcount++)
env.shop[shcount].type = SHOP_UNASSIGNED;
env.markers.clear();
if (you.level_type == LEVEL_DUNGEON)
env.level_flags = branches[you.where_are_you].default_level_flags;
else if (you.level_type == LEVEL_LABYRINTH || you.level_type == LEVEL_ABYSS)
{
env.level_flags = LFLAG_NO_TELE_CONTROL | LFLAG_NO_MAGIC_MAP;
if (you.level_type == LEVEL_ABYSS)
env.level_flags |= LFLAG_NOT_MAPPABLE;
}
else
env.level_flags = 0;
if (you.level_type == LEVEL_DUNGEON)
{
if (you.where_are_you == BRANCH_ECUMENICAL_TEMPLE)
env.spawn_random_rate = 0;
else
env.spawn_random_rate = 240;
}
else if (you.level_type == LEVEL_ABYSS
|| you.level_type == LEVEL_PANDEMONIUM)
{
env.spawn_random_rate = 50;
}
else
env.spawn_random_rate = 0;
env.floor_colour = BLACK;
env.rock_colour = BLACK;
lua_special_room_spec.created = false;
lua_special_room_spec.tl.set(-1, -1);
lua_special_room_level = -1;
}
static void _build_layout_skeleton(int level_number, int level_type,
spec_room &sr)
{
builder_rc_type skip_build = _builder_by_type(level_number, level_type);
if (skip_build == BUILD_QUIT) return;
if (skip_build == BUILD_CONTINUE)
{
skip_build = _builder_by_branch(level_number);
if (skip_build == BUILD_QUIT || dgn_level_vetoed)
return;
}
if (!dgn_level_vetoed && skip_build == BUILD_CONTINUE)
{
if (!player_in_branch(BRANCH_SWAMP) && !player_in_branch(BRANCH_SHOALS))
skip_build = _builder_normal(level_number, level_type, sr);
if (!dgn_level_vetoed && skip_build == BUILD_CONTINUE)
{
if (!player_in_branch(BRANCH_SHOALS))
skip_build = _builder_basic(level_number);
if (!dgn_level_vetoed && skip_build == BUILD_CONTINUE)
_builder_extras(level_number, level_type);
}
}
}
static int _num_items_wanted(int level_number)
{
if (level_number > 5 && one_chance_in(500 - 5 * level_number))
return (10 + random2avg(90, 2)); else
return (3 + roll_dice(3, 11));
}
static int _num_mons_wanted(int level_type)
{
if (level_type == LEVEL_ABYSS
|| player_in_branch(BRANCH_ECUMENICAL_TEMPLE))
{
return 0;
}
int mon_wanted = roll_dice( 3, 10 );
if (player_in_hell())
mon_wanted += roll_dice( 3, 8 );
else if (player_in_branch(BRANCH_HALL_OF_BLADES))
mon_wanted += roll_dice( 6, 8 );
if (mon_wanted > 60)
mon_wanted = 60;
return mon_wanted;
}
static void _fixup_walls()
{
if (player_in_branch(BRANCH_DIS)
|| player_in_branch(BRANCH_VAULTS)
|| player_in_branch(BRANCH_CRYPT))
{
dungeon_feature_type vault_wall = DNGN_METAL_WALL;
if (player_in_branch(BRANCH_VAULTS))
{
vault_wall = DNGN_ROCK_WALL;
const int bdepth = player_branch_depth();
if (bdepth > 2)
vault_wall = DNGN_STONE_WALL;
if (bdepth > 4)
vault_wall = DNGN_METAL_WALL;
if (bdepth > 6 && one_chance_in(10))
vault_wall = DNGN_GREEN_CRYSTAL_WALL;
}
else if (player_in_branch(BRANCH_CRYPT))
vault_wall = DNGN_STONE_WALL;
_replace_area(0, 0, GXM-1, GYM-1, DNGN_ROCK_WALL, vault_wall,
MMT_NO_WALL);
}
}
static void _fixup_misplaced_items()
{
for (int i = 0; i < MAX_ITEMS; i++)
{
item_def& item(mitm[i]);
if (!is_valid_item(item) || (item.pos.x == 0)
|| item.held_by_monster())
{
continue;
}
if (in_bounds(item.pos))
{
dungeon_feature_type feat = grd(item.pos);
if (feat >= DNGN_MINITEM)
continue;
#ifdef DEBUG_DIAGNOSTICS
mprf(MSGCH_DIAGNOSTICS,
"Item buggily placed in feature at (%d, %d).",
item.pos.x, item.pos.y);
#endif
}
else
{
#ifdef DEBUG_DIAGNOSTICS
mprf(MSGCH_DIAGNOSTICS,
"Item buggily placed out of bounds at (%d, %d).",
item.pos.x, item.pos.y);
#endif
}
item.base_type = OBJ_UNASSIGNED;
item.quantity = 0;
item.pos.reset();
}
}
static void _fixup_branch_stairs()
{
if (your_branch().exit_stairs != NUM_FEATURES
&& player_branch_depth() == 1
&& you.level_type == LEVEL_DUNGEON)
{
const dungeon_feature_type exit = your_branch().exit_stairs;
for (rectangle_iterator ri(1); ri; ++ri)
{
if (grd(*ri) >= DNGN_STONE_STAIRS_UP_I
&& grd(*ri) <= DNGN_ESCAPE_HATCH_UP)
{
if (grd(*ri) == DNGN_STONE_STAIRS_UP_I)
env.markers.add(new map_feature_marker(*ri, grd(*ri)));
grd(*ri) = exit;
}
}
}
if (player_branch_depth() == 1 && at_branch_bottom())
{
for (rectangle_iterator ri(1); ri; ++ri)
{
if (grd(*ri) >= DNGN_STONE_STAIRS_DOWN_I
&& grd(*ri) <= DNGN_ESCAPE_HATCH_UP)
{
grd(*ri) = DNGN_FLOOR;
}
}
}
else if (at_branch_bottom() && you.level_type == LEVEL_DUNGEON)
{
for (rectangle_iterator ri(1); ri; ++ri)
{
if (grd(*ri) >= DNGN_STONE_STAIRS_DOWN_I
&& grd(*ri) <= DNGN_ESCAPE_HATCH_DOWN)
{
grd(*ri) = DNGN_ESCAPE_HATCH_UP;
}
}
}
}
static bool _fixup_stone_stairs(bool preserve_vault_stairs)
{
const unsigned int max_stairs = 20;
FixedVector<coord_def, max_stairs> up_stairs;
FixedVector<coord_def, max_stairs> down_stairs;
unsigned int num_up_stairs = 0;
unsigned int num_down_stairs = 0;
for (int x = 1; x < GXM; ++x)
for (int y = 1; y < GYM; ++y)
{
const coord_def c(x,y);
if (grd(c) >= DNGN_STONE_STAIRS_DOWN_I
&& grd(c) <= DNGN_STONE_STAIRS_DOWN_III
&& num_down_stairs < max_stairs)
{
down_stairs[num_down_stairs++] = c;
}
else if (grd(c) >= DNGN_STONE_STAIRS_UP_I
&& grd(c) <= DNGN_STONE_STAIRS_UP_III
&& num_up_stairs < max_stairs)
{
up_stairs[num_up_stairs++] = c;
}
}
bool success = true;
for (unsigned int i = 0; i < 2; i++)
{
FixedVector<coord_def, max_stairs>& stair_list = (i == 0 ? up_stairs
: down_stairs);
unsigned int num_stairs;
dungeon_feature_type base;
dungeon_feature_type replace;
if (i == 0)
{
num_stairs = num_up_stairs;
replace = DNGN_ESCAPE_HATCH_UP;
base = DNGN_STONE_STAIRS_UP_I;
}
else
{
num_stairs = num_down_stairs;
replace = DNGN_ESCAPE_HATCH_DOWN;
base = DNGN_STONE_STAIRS_DOWN_I;
}
if (player_in_branch(BRANCH_HALL_OF_ZOT))
replace = DNGN_GRANITE_STATUE;
if (num_stairs > 3)
{
for (unsigned int s1 = 0; s1 < num_stairs; s1++)
{
if (num_stairs <= 3)
break;
for (unsigned int s2 = s1 + 1; s2 < num_stairs; s2++)
{
if (num_stairs <= 3)
break;
if (preserve_vault_stairs
&& (dgn_Map_Mask(stair_list[s2]) & MMT_VAULT))
{
continue;
}
flood_find<feature_grid, coord_predicate> ff(env.grid,
in_bounds);
ff.add_feat(grd(stair_list[s2]));
dungeon_feature_type save = grd(stair_list[s1]);
grd(stair_list[s1]) = DNGN_FLOOR;
coord_def where = ff.find_first_from(stair_list[s1],
dgn_Map_Mask);
if (where.x)
{
grd(stair_list[s2]) = replace;
num_stairs--;
stair_list[s2] = stair_list[num_stairs];
s2--;
}
grd(stair_list[s1]) = save;
}
}
while (num_stairs > 3)
{
int remove = random2(num_stairs);
if (preserve_vault_stairs)
{
int start = remove;
do
{
if (!(dgn_Map_Mask(stair_list[remove]) & MMT_VAULT))
break;
remove = (remove + 1) % num_stairs;
}
while (start != remove);
if (start == remove)
break;
}
grd(stair_list[remove]) = replace;
stair_list[remove] = stair_list[--num_stairs];
}
}
if (num_stairs > 3 && preserve_vault_stairs)
{
success = false;
continue;
}
if (num_stairs == 0)
continue;
if (level_id::current() == level_id(BRANCH_MAIN_DUNGEON, 1) && i == 0)
continue;
for (int s = num_stairs; s < 3; s++)
{
coord_def gc;
do
{
gc.x = random2(GXM);
gc.y = random2(GYM);
}
while (grd(gc) != DNGN_FLOOR);
mprf("add stair %d at pos(%d, %d)", s, gc.x, gc.y);
grd(gc) = base;
stair_list[num_stairs++] = gc;
}
ASSERT(num_stairs == 3);
for (int s = 0; s < 4; s++)
{
int s1 = s % num_stairs;
int s2 = (s1 + 1) % num_stairs;
ASSERT(grd(stair_list[s2]) >= base
&& grd(stair_list[s2]) <= base + 3);
if (grd(stair_list[s1]) == grd(stair_list[s2]))
{
grd(stair_list[s2]) = (dungeon_feature_type)
(base + (grd(stair_list[s2])-base+1) % 3);
}
}
}
return success;
}
static bool _add_feat_if_missing(bool (*iswanted)(const coord_def &),
dungeon_feature_type feat)
{
memset(travel_point_distance, 0, sizeof(travel_distance_grid_t));
int nzones = 0;
for (int y = 0; y < GYM; ++y)
for (int x = 0; x < GXM; ++x)
{
const coord_def gc(x, y);
if (!map_bounds(x, y)
|| travel_point_distance[x][y]
|| !dgn_square_is_passable(gc))
{
continue;
}
if (_dgn_fill_zone(gc, ++nzones, _dgn_point_record_stub,
dgn_square_is_passable, iswanted))
{
continue;
}
bool found_feature = false;
for (int y2 = 0; y2 < GYM && !found_feature; ++y2)
for (int x2 = 0; x2 < GXM && !found_feature; ++x2)
{
if (grd[x2][y2] == feat)
found_feature = true;
}
if (found_feature)
continue;
int i = 0;
while (i++ < 2000)
{
coord_def rnd(random2(GXM), random2(GYM));
if (grd(rnd) != DNGN_FLOOR)
continue;
if (travel_point_distance[rnd.x][rnd.y] != nzones)
continue;
grd(rnd) = feat;
return (true);
}
for (int y2 = 0; y2 < GYM; ++y2)
for (int x2 = 0; x2 < GXM; ++x2)
{
coord_def rnd(x2, y2);
if (grd(rnd) != DNGN_FLOOR)
continue;
if (travel_point_distance[rnd.x][rnd.y] != nzones)
continue;
grd(rnd) = feat;
return (true);
}
#ifdef DEBUG_DIAGNOSTICS
dump_map("debug.map", true);
#endif
return (false);
}
return (true);
}
static bool _add_connecting_escape_hatches()
{
if (branches[you.where_are_you].branch_flags & BFLAG_ISLANDED)
return (true);
if (you.level_type != LEVEL_DUNGEON)
return (true);
if (at_branch_bottom())
return (_dgn_count_disconnected_zones(true) == 0);
return (_add_feat_if_missing(_is_perm_down_stair, DNGN_ESCAPE_HATCH_DOWN));
}
static bool _branch_entrances_are_connected()
{
for (int y = 0; y < GYM; ++y)
for (int x = 0; x < GXM; ++x)
{
coord_def gc(x,y);
if (!feat_is_branch_stairs(grd(gc)))
continue;
if (!_has_connected_stone_stairs_from(gc))
return (false);
}
return (true);
}
static void _dgn_verify_connectivity(unsigned nvaults)
{
if (dgn_level_vetoed)
return;
if (dgn_zones && nvaults != Level_Vaults.size())
{
const int newzones = _dgn_count_disconnected_zones(false);
#ifdef DEBUG_DIAGNOSTICS
std::ostringstream vlist;
for (unsigned i = nvaults; i < Level_Vaults.size(); ++i)
{
if (i > nvaults)
vlist << ", ";
vlist << Level_Vaults[i].map.name;
}
mprf(MSGCH_DIAGNOSTICS, "Dungeon has %d zones after placing %s.",
newzones, vlist.str().c_str());
#endif
if (newzones > dgn_zones)
{
dgn_level_vetoed = true;
#ifdef DEBUG_DIAGNOSTICS
mprf(MSGCH_DIAGNOSTICS,
"VETO: %s broken by [%s] (had %d zones, "
"now have %d zones.",
level_id::current().describe().c_str(),
vlist.str().c_str(), dgn_zones, newzones);
#endif
return;
}
}
if (you.level_type == LEVEL_DUNGEON
&& !(branches[you.where_are_you].branch_flags & BFLAG_ISLANDED)
&& _dgn_count_disconnected_zones(true) > 0)
{
dgn_level_vetoed = true;
#ifdef DEBUG_DIAGNOSTICS
mprf(MSGCH_DIAGNOSTICS,
"VETO: %s has isolated areas with no stairs.",
level_id::current().describe().c_str());
#endif
return;
}
if (!_fixup_stone_stairs(true))
{
#ifdef DEBUG_DIAGNOSTICS
mprf(MSGCH_DIAGNOSTICS, "Warning: failed to preserve vault stairs.");
#endif
_fixup_stone_stairs(false);
}
if (!_branch_entrances_are_connected())
{
dgn_level_vetoed = true;
#ifdef DEBUG_DIAGNOSTICS
mprf(MSGCH_DIAGNOSTICS,
"VETO: %s has a disconnected branch entrance.",
level_id::current().describe().c_str());
#endif
return;
}
if (!_add_connecting_escape_hatches())
{
dgn_level_vetoed = true;
#ifdef DEBUG_DIAGNOSTICS
mprf(MSGCH_DIAGNOSTICS,
"VETO: %s failed to get connecting escape hatches.",
level_id::current().describe().c_str());
#endif
return;
}
if (!_fixup_interlevel_connectivity())
{
dgn_level_vetoed = true;
#ifdef DEBUG_DIAGNOSTICS
mprf(MSGCH_DIAGNOSTICS,
"VETO: %s failed to ensure interlevel connectivity.",
level_id::current().describe().c_str());
#endif
return;
}
}
static void _build_dungeon_level(int level_number, int level_type)
{
spec_room sr;
_build_layout_skeleton(level_number, level_type, sr);
if (you.level_type == LEVEL_LABYRINTH
|| you.level_type == LEVEL_PORTAL_VAULT
|| dgn_level_vetoed)
{
return;
}
if (sr.created && !sr.hooked_up)
_specr_2(sr);
if (player_in_branch(BRANCH_SWAMP))
_prepare_swamp();
else if (player_in_branch(BRANCH_SHOALS))
_prepare_shoals(level_number);
if (dgn_level_vetoed)
return;
_check_doors();
if (!player_in_branch(BRANCH_DIS) && !player_in_branch(BRANCH_VAULTS))
_hide_doors();
_replace_area(0, 0, GXM-1, GYM-1, DNGN_BUILDER_SPECIAL_WALL,
DNGN_ROCK_WALL);
_replace_area(0, 0, GXM-1, GYM-1, DNGN_BUILDER_SPECIAL_FLOOR, DNGN_FLOOR);
const unsigned nvaults = Level_Vaults.size();
dgn_check_connectivity = !player_in_branch(BRANCH_SHOALS);
_place_minivaults();
_place_branch_entrances( level_number, level_type );
_place_extra_vaults();
if (level_type == LEVEL_DUNGEON && your_branch().has_shops)
_place_shops(level_number);
_dgn_verify_connectivity(nvaults);
if (dgn_level_vetoed)
return;
if (level_type != LEVEL_ABYSS)
_place_traps(level_number);
_place_fog_machines(level_number);
_builder_items(level_number, level_type, _num_items_wanted(level_number));
_builder_monsters(level_number, level_type, _num_mons_wanted(level_type));
_fixup_walls();
_fixup_branch_stairs();
_place_altars();
_fixup_misplaced_items();
link_items();
if (!player_in_branch(BRANCH_COCYTUS)
&& !player_in_branch(BRANCH_SWAMP)
&& !player_in_branch(BRANCH_SHOALS))
{
_prepare_water( level_number );
}
if (level_type == LEVEL_PANDEMONIUM)
_fixup_pandemonium_stairs();
}
static char _fix_black_colour(char incol)
{
if (incol == BLACK)
return LIGHTGREY;
else
return incol;
}
void dgn_set_colours_from_monsters()
{
if (env.mons_alloc[9] < 0 || env.mons_alloc[9] == MONS_NO_MONSTER
|| env.mons_alloc[9] >= NUM_MONSTERS)
{
if (env.floor_colour == BLACK)
env.floor_colour = LIGHTGREY;
}
else
{
env.floor_colour =
_fix_black_colour(mons_class_colour(env.mons_alloc[9]));
}
if (env.mons_alloc[8] < 0 || env.mons_alloc[8] == MONS_NO_MONSTER
|| env.mons_alloc[8] >= NUM_MONSTERS)
{
if (env.rock_colour == BLACK)
env.rock_colour = BROWN;
}
else
{
env.rock_colour =
_fix_black_colour(mons_class_colour(env.mons_alloc[8]));
}
}
static void _dgn_set_floor_colours()
{
unsigned char old_floor_colour = env.floor_colour;
unsigned char old_rock_colour = env.rock_colour;
if (you.level_type == LEVEL_PANDEMONIUM || you.level_type == LEVEL_ABYSS)
dgn_set_colours_from_monsters();
else if (you.level_type == LEVEL_DUNGEON)
{
const int youbranch = you.where_are_you;
env.floor_colour = branches[youbranch].floor_colour;
env.rock_colour = branches[youbranch].rock_colour;
}
if (old_floor_colour != BLACK)
env.floor_colour = old_floor_colour;
if (old_rock_colour != BLACK)
env.rock_colour = old_rock_colour;
if (env.floor_colour == BLACK)
env.floor_colour = LIGHTGREY;
if (env.rock_colour == BLACK)
env.rock_colour = BROWN;
}
static void _check_doors()
{
for (int x = 1; x < GXM-1; x++)
for (int y = 1; y < GYM-1; y++)
{
if (!feat_is_closed_door(grd[x][y]))
continue;
int solid_count = 0;
if (feat_is_solid( grd[x - 1][y] ))
solid_count++;
if (feat_is_solid( grd[x + 1][y] ))
solid_count++;
if (feat_is_solid( grd[x][y - 1] ))
solid_count++;
if (feat_is_solid( grd[x][y + 1] ))
solid_count++;
grd[x][y] = (solid_count < 2 ? DNGN_FLOOR
: DNGN_CLOSED_DOOR);
}
}
static void _hide_doors()
{
unsigned char dx = 0, dy = 0; unsigned char wall_count = 0;
for (dx = 1; dx < GXM-1; dx++)
for (dy = 1; dy < GYM-1; dy++)
{
if (grd[dx][dy] == DNGN_CLOSED_DOOR && one_chance_in(4)
&& unforbidden(coord_def(dx, dy), MMT_NO_DOOR))
{
wall_count = 0;
if (grd[dx - 1][dy] == DNGN_ROCK_WALL)
wall_count++;
if (grd[dx + 1][dy] == DNGN_ROCK_WALL)
wall_count++;
if (grd[dx][dy - 1] == DNGN_ROCK_WALL)
wall_count++;
if (grd[dx][dy + 1] == DNGN_ROCK_WALL)
wall_count++;
if (wall_count > 1)
grd[dx][dy] = DNGN_SECRET_DOOR;
}
}
}
static void _place_ellipse(int x, int y, int a, int b,
dungeon_feature_type feat, int margin)
{
for (int i = std::max(x-a, margin); i < std::min(x+a, GXM-margin); ++i)
for (int j = std::max(y-b, margin); j < std::min(y+b, GYM-margin); ++j)
if ( (x-i)*(x-i)*b*b + (y-j)*(y-j)*a*a
<= a*a*b*b/2 + roll_dice(2, a*a*b*b)/4 + random2(3) )
{
grd[i][j] = feat;
}
}
int count_feature_in_box(int x0, int y0, int x1, int y1,
dungeon_feature_type feat)
{
int result = 0;
for (int i = x0; i < x1; ++i)
for (int j = y0; j < y1; ++j)
{
if (grd[i][j] == feat)
++result;
}
return result;
}
int count_antifeature_in_box(int x0, int y0, int x1, int y1,
dungeon_feature_type feat)
{
return (x1-x0)*(y1-y0) - count_feature_in_box(x0, y0, x1, y1, feat);
}
int count_neighbours(int x, int y, dungeon_feature_type feat)
{
return count_feature_in_box(x-1, y-1, x+2, y+2, feat);
}
static void _replace_in_grid(int x1, int y1, int x2, int y2,
dungeon_feature_type oldfeat,
dungeon_feature_type newfeat)
{
for (int x = x1; x < x2; ++x)
for (int y = y1; y < y2; ++y)
{
if (grd[x][y] == oldfeat)
grd[x][y] = newfeat;
}
}
static void _connected_flood(int margin, int i, int j, bool taken[GXM][GYM])
{
if (i < margin || i >= GXM - margin || j < margin || j >= GYM - margin
|| taken[i][j])
{
return;
}
taken[i][j] = true;
for (int idelta = -1; idelta <= 1; ++idelta)
for (int jdelta = -1; jdelta <= 1; ++jdelta)
_connected_flood(margin, i + idelta, j + jdelta, taken);
}
static int _count_connected(int margin)
{
bool taken[GXM][GYM];
for (int i = margin; i < GXM - margin; ++i)
for (int j = margin; j < GYM - margin; ++j)
taken[i][j] = feat_is_water(grd[i][j]);
int count = 0;
for (int i = margin; i < GXM - margin; ++i)
for (int j = margin; j < GYM - margin; ++j)
if (!taken[i][j])
{
++count;
_connected_flood(margin,i,j,taken);
}
return count;
}
static void _place_base_islands(int margin, int num_islands, int estradius,
coord_def centres[10])
{
for (int i = 0; i < num_islands; ++i)
{
int b = (2 * estradius + roll_dice(3, estradius)) / 4;
b = std::max(b, 4);
b = std::min(b, (GYM - 2*margin - 2) / 2);
int a = b + roll_dice(2, b)/3; a = std::min(a, (GXM - 2*margin - 2) / 2);
int island_distance = estradius*estradius * (2 + num_islands/3);
const int rnd_x_size = GXM - 2*(margin+a) - 1;
const int rnd_y_size = GYM - 2*(margin+b) - 1;
ASSERT(rnd_x_size > 0);
ASSERT(rnd_y_size > 0);
bool centre_ok = false;
int tries = 1000;
do
{
centre_ok = true;
centres[i].x = margin + a + random2(rnd_x_size);
centres[i].y = margin + b + random2(rnd_y_size);
for (int j = 0; j < i; ++j)
{
if (distance(centres[i].x, centres[i].y,
centres[j].x, centres[j].y) < island_distance)
{
centre_ok = false;
break;
}
}
if (island_distance && !one_chance_in(num_islands))
--island_distance;
}
while (!centre_ok && --tries > 0);
#ifdef DEBUG_DIAGNOSTICS
if (tries == 0)
mprf(MSGCH_ERROR, "Failure to place island #%d!", i);
#endif
_place_ellipse( centres[i].x, centres[i].y, a, b, DNGN_FLOOR, margin);
}
}
typedef std::pair<coord_def, dungeon_feature_type> located_feature;
typedef std::list<located_feature> located_feature_list;
bool _is_critical_feature(dungeon_feature_type feat)
{
return (feat == DNGN_ENTER_PORTAL_VAULT
|| feat == DNGN_ENTER_LABYRINTH
|| feat == DNGN_ENTER_SHOP);
}
static located_feature_list _save_critical_features()
{
located_feature_list result;
for (rectangle_iterator ri(1); ri; ++ri)
if (_is_critical_feature(grd(*ri)))
result.push_back(located_feature(*ri, grd(*ri)));
return result;
}
static void _restore_critical_features(const located_feature_list& lfl)
{
for (located_feature_list::const_iterator ci = lfl.begin();
ci != lfl.end(); ++ci)
{
grd(ci->first) = ci->second;
}
}
static void _prepare_shoals(int level_number)
{
const located_feature_list lfl = _save_critical_features();
dgn_Build_Method += make_stringf(" shoals [%d]", level_number);
dgn_Layout_Type = "shoals";
const int margin = 6;
int num_islands = player_branch_depth() + 1;
if (at_branch_bottom())
num_islands += random2(3);
const int estradius = 50 / num_islands - (num_islands == 2 ? 5 : 0);
int num_tries = 0;
coord_def centres[10];
do
{
for (int x = margin; x < GXM-margin; ++x)
for (int y = margin; y < GYM-margin; ++y)
grd[x][y] = DNGN_DEEP_WATER;
_place_base_islands(margin, num_islands, estradius, centres);
}
while (++num_tries < 100 && _count_connected(margin) != num_islands);
#ifdef DEBUG_DIAGNOSTICS
mprf(MSGCH_DIAGNOSTICS, "Num tries: %d Connected components: %d",
num_tries, _count_connected(margin));
#endif
for (int i = margin; i < GXM - margin; ++i)
for (int j = margin; j < GYM - margin; ++j)
if (grd[i][j] == DNGN_DEEP_WATER
&& count_neighbours(i, j, DNGN_FLOOR) > random2(random2(3)+1))
{
grd[i][j] = DNGN_SHALLOW_WATER;
}
for (int banks = 0; banks < 8; ++banks)
{
int xsize = 3 + random2(3); int ysize = 3 + random2(3);
int xb = margin + 2 + random2(GXM - 2 * margin - xsize - 2);
int yb = margin + 2 + random2(GYM - 2 * margin - ysize - 2);
bool ok_place = true;
for (int i = xb; i < xb + xsize; ++i)
for (int j = yb; j < yb + ysize; ++j)
{
if (grd[i][j] != DNGN_DEEP_WATER)
{
ok_place = false;
break;
}
}
if (ok_place)
{
for (int i = xb; i < xb + xsize; ++i)
for (int j = yb; j < yb + ysize; ++j)
{
if (!one_chance_in(3))
grd[i][j] = DNGN_SHALLOW_WATER;
}
}
}
_replace_in_grid(margin, margin, GXM-margin, GYM-margin,
DNGN_SHALLOW_WATER, DNGN_WATER_RESERVED);
for (int iteration = 0; iteration < 6; ++iteration)
{
for (int x = margin; x < GXM - margin; ++x)
for (int y = margin; y < GYM - margin; ++y)
if (grd[x][y] == DNGN_DEEP_WATER)
{
int badness = count_neighbours(x, y, DNGN_WATER_RESERVED);
if (random2(badness) >= 2 && coinflip())
grd[x][y] = DNGN_LAVA;
}
_replace_in_grid(margin, margin, GXM-margin, GYM-margin,
DNGN_LAVA, DNGN_WATER_RESERVED);
}
_replace_in_grid(margin, margin, GXM-margin, GYM-margin,
DNGN_WATER_RESERVED, DNGN_SHALLOW_WATER);
const int margin2 = margin - 1;
_replace_in_grid(margin2, margin2, GXM-margin2, GYM-margin2,
DNGN_ROCK_WALL, DNGN_DEEP_WATER);
_replace_area(0, 0, GXM-1, GYM-1, DNGN_BUILDER_SPECIAL_WALL,
DNGN_ROCK_WALL);
_replace_area(0, 0, GXM-1, GYM-1, DNGN_ROCK_WALL, DNGN_OPEN_SEA);
_restore_critical_features(lfl);
if (at_branch_bottom())
{
int j;
const coord_def d1(1,0), d2(-1,0);
for (j = 0; j < num_islands; ++j)
{
if (!_is_critical_feature(grd(centres[j]))
&& !_is_critical_feature(grd(centres[j] + d1))
&& !_is_critical_feature(grd(centres[j] + d2)))
{
break;
}
}
if (j == num_islands)
{
j = 0;
mprf(MSGCH_ERROR, "Clobbering critical features: %d %d %d.",
grd(centres[j]), grd(centres[j] + d1), grd(centres[j] + d2));
}
grd[centres[j].x ][centres[j].y] = DNGN_STONE_STAIRS_UP_I;
grd[centres[j].x+1][centres[j].y] = DNGN_STONE_STAIRS_UP_II;
grd[centres[j].x-1][centres[j].y] = DNGN_STONE_STAIRS_UP_III;
const map_def *vault = random_map_for_tag("shoal_rune");
_build_secondary_vault(level_number, vault, -1, false, true,
centres[1]);
for (int i = 2; i < num_islands; ++i)
{
do
vault = random_map_for_tag("shoal");
while (!vault);
_build_secondary_vault(level_number, vault, -1, false, true,
centres[i]);
}
}
else
{
for (int i = 0; i < 3; ++i)
{
int x, y;
do
{
x = margin + random2(GXM - 2*margin);
y = margin + random2(GYM - 2*margin);
}
while (grd[x][y] != DNGN_FLOOR);
grd[x][y]
= static_cast<dungeon_feature_type>(DNGN_STONE_STAIRS_DOWN_I + i);
do
{
x = margin + random2(GXM - 2*margin);
y = margin + random2(GYM - 2*margin);
}
while (grd[x][y] != DNGN_FLOOR);
grd[x][y]
= static_cast<dungeon_feature_type>(DNGN_STONE_STAIRS_UP_I + i);
}
}
}
static void _prepare_swamp()
{
dgn_Build_Method += " swamp";
dgn_Layout_Type = "swamp";
const int margin = 5;
for (int i = margin; i < (GXM - margin); i++)
for (int j = margin; j < (GYM - margin); j++)
{
if (!unforbidden(coord_def(i, j), MMT_VAULT))
continue;
if (grd[i][j] == DNGN_CLOSED_DOOR || grd[i][j] == DNGN_SECRET_DOOR)
grd[i][j] = DNGN_FLOOR;
if (grd[i][j] == DNGN_FLOOR && one_chance_in(3))
grd[i][j] = DNGN_SHALLOW_WATER;
if (grd[i][j] == DNGN_ROCK_WALL)
{
const int temp_rand = random2(6);
if (temp_rand > 0) {
grd[i][j] = ((temp_rand > 2) ? DNGN_SHALLOW_WATER : DNGN_DEEP_WATER); }
}
}
}
static void _prepare_water( int level_number )
{
int i, j, k, l; unsigned char which_grid;
for (i = 1; i < (GXM - 1); i++)
for (j = 1; j < (GYM - 1); j++)
{
if (!unforbidden(coord_def(i, j), MMT_NO_POOL))
continue;
if (grd[i][j] == DNGN_DEEP_WATER)
{
for (k = -1; k < 2; k++)
for (l = -1; l < 2; l++)
if (k != 0 || l != 0)
{
which_grid = grd[i + k][j + l];
if (which_grid == DNGN_SHALLOW_WATER
&& one_chance_in( 8 + level_number ))
{
grd[i][j] = DNGN_SHALLOW_WATER;
}
else if (which_grid >= DNGN_FLOOR
&& x_chance_in_y(80 - level_number * 4,
100))
{
grd[i][j] = DNGN_SHALLOW_WATER;
}
}
}
}
}
static bool _find_in_area(int sx, int sy, int ex, int ey,
dungeon_feature_type feature)
{
int x,y;
if (feature != 0)
{
for (x = sx; x <= ex; x++)
for (y = sy; y <= ey; y++)
{
if (grd[x][y] == feature)
return (true);
}
}
return (false);
}
static bool _make_box(int room_x1, int room_y1, int room_x2, int room_y2,
dungeon_feature_type floor,
dungeon_feature_type wall,
dungeon_feature_type avoid)
{
int bx,by;
if (_find_in_area(room_x1, room_y1, room_x2, room_y2, avoid))
return (false);
if (wall != 0)
{
for (bx = room_x1; bx <= room_x2; bx++)
{
grd[bx][room_y1] = wall;
grd[bx][room_y2] = wall;
}
for (by = room_y1+1; by < room_y2; by++)
{
grd[room_x1][by] = wall;
grd[room_x2][by] = wall;
}
}
if (floor != 0)
{
for (bx = room_x1 + 1; bx < room_x2; bx++)
for (by = room_y1 + 1; by < room_y2; by++)
grd[bx][by] = floor;
}
return (true);
}
static builder_rc_type _builder_by_type(int level_number, char level_type)
{
if (level_type == LEVEL_PORTAL_VAULT)
{
_portal_vault_level(level_number);
return (BUILD_QUIT);
}
if (level_type == LEVEL_LABYRINTH)
{
_labyrinth_level(level_number);
return (BUILD_QUIT);
}
if (level_type == LEVEL_ABYSS)
{
generate_abyss();
return (BUILD_SKIP);
}
if (level_type == LEVEL_PANDEMONIUM)
{
int which_demon = -1;
if (one_chance_in(4))
{
do
{
which_demon = random2(4);
if (one_chance_in(4))
{
which_demon = -1;
break;
}
}
while (you.unique_creatures[MONS_MNOLEG + which_demon]);
}
if (which_demon >= 0)
{
const char *pandemon_level_names[] =
{
"mnoleg", "lom_lobon", "cerebov", "gloorx_vloq"
};
const map_def *vault =
random_map_for_tag(pandemon_level_names[which_demon], false);
ASSERT(vault);
if (!vault)
{
end(1, false, "Failed to find Pandemonium level %s!\n",
pandemon_level_names[which_demon]);
}
_ensure_vault_placed( _build_vaults(level_number, vault), true );
}
else
{
_plan_main(level_number, 0);
const map_def *vault = random_map_for_tag("pan", true);
ASSERT( vault );
_build_secondary_vault(level_number, vault);
}
return BUILD_SKIP;
}
return BUILD_CONTINUE;
}
static void _portal_vault_level(int level_number)
{
dgn_Build_Method += make_stringf(" portal_vault_level [%d]",
level_number);
dgn_Layout_Type = "portal vault";
const std::string trimmed_name =
replace_all(trimmed_string(you.level_type_tag), " ", "_");
ASSERT(!trimmed_name.empty());
const char* level_name = trimmed_name.c_str();
const map_def *vault = random_map_for_place(level_id::current(), false);
#ifdef WIZARD
if (!vault && you.wizard && map_count_for_tag(level_name, false) > 1)
{
char buf[80];
do
{
mprf(MSGCH_PROMPT, "Which %s (ESC or ENTER for random): ",
level_name);
if (cancelable_get_line(buf, sizeof buf))
break;
std::string name = buf;
trim_string(name);
if (name.empty())
break;
lowercase(name);
name = replace_all(name, " ", "_");
vault = find_map_by_name(you.level_type_tag + "_" + name);
if (!vault)
mprf(MSGCH_DIAGNOSTICS, "No such %s, try again.",
level_name);
}
while (!vault);
}
#endif
if (!vault)
vault = random_map_for_tag(level_name, false);
if (vault)
_ensure_vault_placed( _build_vaults(level_number, vault), true );
else
{
_plan_main(level_number, 0);
_place_minivaults(level_name, 1, 1, true);
if (Level_Vaults.empty())
{
mprf(MSGCH_ERROR, "No maps or tags named '%s'.",
level_name);
ASSERT(false);
end(-1);
}
}
link_items();
callback_map::const_iterator
i = level_type_post_callbacks.find(you.level_type_tag);
if (i != level_type_post_callbacks.end())
dlua.callfn(i->second.c_str(), 0, 0);
}
static const map_def *_random_portal_vault(const std::string &tag)
{
return random_map_for_tag(tag, true);
}
static bool _place_portal_vault(int stair, const std::string &tag, int dlevel)
{
const map_def *vault = _random_portal_vault(tag);
if (!vault)
return (false);
return _build_secondary_vault(dlevel, vault, stair);
}
static const map_def *_dgn_random_map_for_place(bool minivault)
{
const level_id lid = level_id::current();
const map_def *vault = random_map_for_place(lid, minivault);
if (!vault
&& lid.branch == BRANCH_MAIN_DUNGEON
&& lid.depth == 1 && !Options.tutorial_left)
{
vault = random_map_for_tag("entry");
}
return (vault);
}
static builder_rc_type _builder_by_branch(int level_number)
{
const map_def *vault = _dgn_random_map_for_place(false);
if (vault)
{
dgn_Build_Method += " random_map_for_place";
_ensure_vault_placed_ex( _build_vaults(level_number, vault), vault );
return BUILD_SKIP;
}
switch (you.where_are_you)
{
case BRANCH_LAIR:
if (!one_chance_in(3))
break;
case BRANCH_HIVE:
case BRANCH_SLIME_PITS:
case BRANCH_ORCISH_MINES:
{
int iterations;
if (at_branch_bottom())
iterations = 600 + random2(600);
else
iterations = 100 + random2(500);
spotty_level(false, iterations, false);
return BUILD_SKIP;
}
default:
break;
}
return BUILD_CONTINUE;
}
static void _place_minivaults(const std::string &tag, int lo, int hi,
bool force)
{
const level_id curr = level_id::current();
if (lo == -1)
lo = hi = 1;
int nvaults = random_range(lo, hi);
if (!tag.empty())
{
for (int i = 0; i < nvaults; ++i)
{
const map_def *vault = random_map_for_tag(tag, true);
if (!vault)
return;
_build_secondary_vault(you.your_level, vault);
}
return;
}
if (use_random_maps)
{
const map_def *vault = NULL;
if ((vault = random_map_for_place(level_id::current(), true)))
_build_secondary_vault(you.your_level, vault);
do
{
vault = random_map_in_depth(level_id::current(), true);
if (vault)
_build_secondary_vault(you.your_level, vault);
}
while (vault && vault->has_tag("extra"));
}
}
static builder_rc_type _builder_normal(int level_number, char level_type,
spec_room &sr)
{
UNUSED( level_type );
bool skipped = false;
const map_def *vault = _dgn_random_map_for_place(false);
if (!vault && use_random_maps && can_create_vault)
{
vault = random_map_in_depth(level_id::current());
if (vault && !player_in_branch(BRANCH_MAIN_DUNGEON)
&& vault->orient != MAP_ENCOMPASS)
{
vault = NULL;
}
}
if (vault)
{
dgn_Build_Method += " normal_random_map_for_place";
_ensure_vault_placed_ex( _build_vaults(level_number, vault), vault );
return BUILD_SKIP;
}
if (player_in_branch( BRANCH_DIS ))
{
_city_level(level_number);
return BUILD_SKIP;
}
if (player_in_branch( BRANCH_VAULTS ))
{
if (one_chance_in(3))
_city_level(level_number);
else
_plan_main(level_number, 4);
return BUILD_SKIP;
}
if (level_number > 7 && level_number < 23)
{
if (one_chance_in(16))
{
spotty_level(false, 0, coinflip());
return BUILD_SKIP;
}
if (one_chance_in(16))
{
_bigger_room();
return BUILD_SKIP;
}
}
if (level_number > 2 && level_number < 23 && one_chance_in(3))
{
_plan_main(level_number, 0);
return BUILD_SKIP;
}
if (one_chance_in(3))
skipped = true;
if (!skipped && one_chance_in(7))
{
bool just_roguey = coinflip();
_roguey_level(level_number, sr, just_roguey);
if (just_roguey)
return BUILD_SKIP;
}
else
{
if (!skipped && level_number > 13 && one_chance_in(8))
{
if (one_chance_in(3))
_city_level(level_number);
else
_plan_main(level_number, 4);
return BUILD_SKIP;
}
}
#ifdef DEBUG_SPECIAL_ROOMS
if (!sr.created)
#else
if (!sr.created && one_chance_in(5))
#endif
{
const map_def *sroom = random_map_for_tag("special_room", true);
if (sroom != NULL)
_special_room(level_number, sr, sroom);
}
return BUILD_CONTINUE;
}
static builder_rc_type _builder_basic(int level_number)
{
dgn_Build_Method += make_stringf(" basic [%d]", level_number);
dgn_Layout_Type = "basic";
int temp_rand;
int doorlevel = random2(11);
int corrlength = 2 + random2(14);
int roomsize = 4 + random2(5) + random2(6);
int no_corr = (one_chance_in(100) ? 500 + random2(500)
: 30 + random2(200));
int intersect_chance = (one_chance_in(20) ? 400 : random2(20));
_make_trail( 35, 30, 35, 20, corrlength, intersect_chance, no_corr,
DNGN_STONE_STAIRS_DOWN_I, DNGN_STONE_STAIRS_UP_I );
_make_trail( 10, 15, 10, 15, corrlength, intersect_chance, no_corr,
DNGN_STONE_STAIRS_DOWN_II, DNGN_STONE_STAIRS_UP_II );
_make_trail( 50, 20, 10, 15, corrlength, intersect_chance, no_corr,
DNGN_STONE_STAIRS_DOWN_III, DNGN_STONE_STAIRS_UP_III);
if (one_chance_in(4))
{
_make_trail( 10, 20, 40, 20, corrlength, intersect_chance, no_corr,
DNGN_ESCAPE_HATCH_DOWN );
}
if (one_chance_in(4))
{
_make_trail( 50, 20, 40, 20, corrlength, intersect_chance, no_corr,
DNGN_ESCAPE_HATCH_UP );
}
if (level_number > 1 && one_chance_in(16))
_big_room(level_number);
if (random2(level_number) > 6 && one_chance_in(3))
_diamond_rooms(level_number);
int i, no_rooms, max_doors;
int sx,sy,ex,ey, time_run;
temp_rand = random2(750);
time_run = 0;
no_rooms = ((temp_rand > 63) ? (5 + random2avg(29, 2)) : (temp_rand > 14) ? 100 : 1);
max_doors = 2 + random2(8);
for (i = 0; i < no_rooms; i++)
{
sx = 8 + random2(50);
sy = 8 + random2(40);
ex = sx + 2 + random2(roomsize);
ey = sy + 2 + random2(roomsize);
if (!_make_room(sx,sy,ex,ey,max_doors, doorlevel))
{
time_run++;
i--;
}
if (time_run > 30)
{
time_run = 0;
i++;
}
}
no_rooms = 1 + random2(3);
max_doors = 1;
for (i = 0; i < no_rooms; i++)
{
sx = 8 + random2(55);
sy = 8 + random2(45);
ex = sx + 5 + random2(6);
ey = sy + 5 + random2(6);
if (!_make_room(sx,sy,ex,ey,max_doors, doorlevel))
{
time_run++;
i--;
}
if (time_run > 30)
{
time_run = 0;
i++;
}
}
return BUILD_CONTINUE;
}
static void _builder_extras( int level_number, int level_type )
{
UNUSED( level_type );
if (level_number > 6 && one_chance_in(10))
{
_many_pools(level_number < 11 || coinflip() ? DNGN_DEEP_WATER
: DNGN_LAVA);
return;
}
dungeon_feature_type river_type
= (one_chance_in( 5 + level_number ) ? DNGN_SHALLOW_WATER
: DNGN_DEEP_WATER);
if (level_number > 11
&& (one_chance_in(5) || (level_number > 15 && !one_chance_in(5))))
{
river_type = DNGN_LAVA;
}
if (player_in_branch( BRANCH_GEHENNA ))
{
river_type = DNGN_LAVA;
if (coinflip())
_build_river( river_type );
else
_build_lake( river_type );
}
else if (player_in_branch( BRANCH_COCYTUS ))
{
river_type = DNGN_DEEP_WATER;
if (coinflip())
_build_river( river_type );
else
_build_lake( river_type );
}
if (level_number > 8 && one_chance_in(16))
_build_river( river_type );
else if (level_number > 8 && one_chance_in(12))
{
_build_lake( (river_type != DNGN_SHALLOW_WATER) ? river_type
: DNGN_DEEP_WATER );
}
}
static bool _shaft_is_in_corridor(const coord_def& c)
{
const coord_def adjs[] = { coord_def(-1,0), coord_def(1,0),
coord_def(0,-1), coord_def(0,1) };
for (unsigned int i = 0; i < ARRAYSZ(adjs); ++i)
{
const coord_def spot = c + adjs[i];
if (!inside_level_bounds(spot) || grd(spot) < DNGN_SHALLOW_WATER)
return (true);
}
return (false);
}
static void _place_traps(int level_number)
{
const int num_traps = num_traps_for_place(level_number);
ASSERT(num_traps >= 0);
ASSERT(num_traps <= MAX_TRAPS);
for (int i = 0; i < num_traps; i++)
{
trap_def& ts(env.trap[i]);
if (ts.type != TRAP_UNASSIGNED)
continue;
int tries;
for (tries = 0; tries < 200; ++tries)
{
ts.pos.x = random2(GXM);
ts.pos.y = random2(GYM);
if (in_bounds(ts.pos) && grd(ts.pos) == DNGN_FLOOR)
break;
}
if (tries == 200)
break;
ts.type = random_trap_for_place(level_number);
if (ts.type == TRAP_SHAFT && level_number <= 7)
{
if (_shaft_is_in_corridor(ts.pos))
{
ts.type = random_trap_for_place(level_number);
if (ts.type == TRAP_SHAFT)
ts.type = TRAP_ALARM;
}
}
grd(ts.pos) = DNGN_UNDISCOVERED_TRAP;
ts.prepare_ammo();
}
}
static void _place_fog_machines(int level_number)
{
int i;
int num_fogs = num_fogs_for_place(level_number);
ASSERT(num_fogs >= 0);
for (i = 0; i < num_fogs; i++)
{
fog_machine_data data = random_fog_for_place(level_number);
if (!valid_fog_machine_data(data))
{
mpr("Invalid fog machine data, bailing.", MSGCH_DIAGNOSTICS);
return;
}
int tries = 200;
int x, y;
dungeon_feature_type feat;
do
{
x = random2(GXM);
y = random2(GYM);
feat = grd[x][y];
}
while (feat <= DNGN_MAXWALL && --tries > 0);
if (tries <= 0)
break;
place_fog_machine(data, x, y);
}
}
static void _place_specific_feature(dungeon_feature_type feat)
{
int sx, sy;
do
{
sx = random_range(X_BOUND_1 + 1, X_BOUND_2 - 1);
sy = random_range(Y_BOUND_1 + 1, Y_BOUND_2 - 1);
}
while (grd[sx][sy] != DNGN_FLOOR || mgrd[sx][sy] != NON_MONSTER);
grd[sx][sy] = feat;
}
static void _place_specific_stair(dungeon_feature_type stair,
const std::string &tag,
int dlevel,
bool vault_only)
{
if ((tag.empty() || !_place_portal_vault(stair, tag, dlevel))
&& !vault_only)
{
_place_specific_feature(stair);
}
}
static void _place_extra_vaults()
{
while (true)
{
if (!player_in_branch(BRANCH_MAIN_DUNGEON)
&& use_random_maps
&& can_create_vault)
{
const map_def *vault = random_map_in_depth(level_id::current());
if (!vault || vault->orient == MAP_ENCOMPASS)
break;
if (vault && _build_secondary_vault(you.your_level, vault, -1))
{
const map_def &map(*vault);
if (map.has_tag("extra"))
continue;
can_create_vault = false;
}
}
break;
}
}
static void _place_branch_entrances(int dlevel, char level_type)
{
int sx, sy;
if (level_type != LEVEL_DUNGEON)
return;
if (player_in_branch( BRANCH_MAIN_DUNGEON ))
{
if (dlevel >= 20 && dlevel <= 27)
_place_specific_stair(DNGN_ENTER_HELL, "hell_entry", dlevel);
if (dlevel >= 20 && dlevel <= 50 && (dlevel == 23 || one_chance_in(4)))
_place_specific_stair(DNGN_ENTER_PANDEMONIUM, "pan_entry", dlevel);
if (dlevel >= 20 && dlevel <= 30 && (dlevel == 24 || one_chance_in(3)))
_place_specific_stair(DNGN_ENTER_ABYSS, "abyss_entry", dlevel);
if (dlevel == 26)
{
for (sx = 1; sx < GXM; sx++)
for (sy = 1; sy < GYM; sy++)
if (grd[sx][sy] >= DNGN_STONE_STAIRS_DOWN_I
&& grd[sx][sy] <= DNGN_ESCAPE_HATCH_DOWN)
{
grd[sx][sy] = DNGN_ENTER_ZOT;
}
}
}
for (int i = 0; i < NUM_BRANCHES; ++i)
{
if (branches[i].entry_stairs != NUM_FEATURES
&& player_in_branch(branches[i].parent_branch)
&& player_branch_depth() == branches[i].startdepth)
{
#ifdef DEBUG_DIAGNOSTICS
mprf(MSGCH_DIAGNOSTICS, "Placing stair to %s",
branches[i].shortname);
#endif
std::string entry_tag = std::string(branches[i].abbrevname);
entry_tag += "_entry";
lowercase(entry_tag);
_place_specific_stair( branches[i].entry_stairs, entry_tag, dlevel);
}
}
}
static void _make_trail(int xs, int xr, int ys, int yr, int corrlength,
int intersect_chance, int no_corr,
dungeon_feature_type begin,
dungeon_feature_type end)
{
int x_start, y_start; int x_ps, y_ps; int finish = 0;
int length = 0;
int temp_rand;
int dir_x = 0;
int dir_y = 0;
int dir_x2, dir_y2;
do
{
x_start = xs + random2(xr);
y_start = ys + random2(yr);
}
while (grd[x_start][y_start] != DNGN_ROCK_WALL
&& grd[x_start][y_start] != DNGN_FLOOR);
if (begin != DNGN_UNSEEN)
grd[x_start][y_start] = begin;
x_ps = x_start;
y_ps = y_start;
do {
dir_x2 = ((x_ps < 15) ? 1 : 0);
if (x_ps > 65)
dir_x2 = -1;
dir_y2 = ((y_ps < 15) ? 1 : 0);
if (y_ps > 55)
dir_y2 = -1;
temp_rand = random2(10);
if (coinflip())
{
if (dir_x2 != 0 && temp_rand < 6)
dir_x = dir_x2;
if (dir_x2 == 0 || temp_rand >= 6)
dir_x = (coinflip()? -1 : 1);
dir_y = 0;
}
else
{
if (dir_y2 != 0 && temp_rand < 6)
dir_y = dir_y2;
if (dir_y2 == 0 || temp_rand >= 6)
dir_y = (coinflip()? -1 : 1);
dir_x = 0;
}
if (dir_x == 0 && dir_y == 0)
continue;
if (x_ps < X_BOUND_1 + 3)
{
dir_x = 1;
dir_y = 0;
}
if (y_ps < Y_BOUND_1 + 3)
{
dir_y = 1;
dir_x = 0;
}
if (x_ps > (X_BOUND_2 - 3))
{
dir_x = -1;
dir_y = 0;
}
if (y_ps > (Y_BOUND_2 - 3))
{
dir_y = -1;
dir_x = 0;
}
if (dir_x == 0 || length == 0)
length = random2(corrlength) + 2;
int bi = 0;
for (bi = 0; bi < length; bi++)
{
if (x_ps < X_BOUND_1 + 4)
{
dir_y = 0; dir_x = 1;
}
if (x_ps > (X_BOUND_2 - 4))
{
dir_y = 0; dir_x = -1;
}
if (y_ps < Y_BOUND_1 + 4)
{
dir_y = 1;
dir_x = 0; }
if (y_ps > (Y_BOUND_2 - 4))
{
dir_y = -1;
dir_x = 0; }
if (grd[x_ps + dir_x][y_ps + dir_y] == DNGN_BUILDER_SPECIAL_WALL)
break;
if (grd[x_ps + 2 * dir_x][y_ps + 2 * dir_y] == DNGN_FLOOR
&& !one_chance_in(intersect_chance))
{
break;
}
x_ps += dir_x;
y_ps += dir_y;
if (grd[x_ps][y_ps] == DNGN_ROCK_WALL)
grd[x_ps][y_ps] = DNGN_FLOOR;
}
if (finish == no_corr - 1 && grd[x_ps][y_ps] != DNGN_FLOOR)
finish -= 2;
finish++;
}
while (finish < no_corr);
if (end != DNGN_UNSEEN)
grd[x_ps][y_ps] = end;
}
static int _good_door_spot(int x, int y)
{
if (!feat_is_solid(grd[x][y]) && grd[x][y] < DNGN_ENTER_PANDEMONIUM
|| feat_is_closed_door(grd[x][y]))
{
return 1;
}
return 0;
}
static bool _make_room(int sx,int sy,int ex,int ey,int max_doors, int doorlevel)
{
int find_door = 0;
int diag_door = 0;
int rx, ry;
for (rx = sx; rx <= ex; rx++)
{
find_door += _good_door_spot(rx,sy);
find_door += _good_door_spot(rx,ey);
}
for (ry = sy + 1; ry < ey; ry++)
{
find_door += _good_door_spot(sx,ry);
find_door += _good_door_spot(ex,ry);
}
diag_door += _good_door_spot(sx,sy);
diag_door += _good_door_spot(ex,sy);
diag_door += _good_door_spot(sx,ey);
diag_door += _good_door_spot(ex,ey);
if ((diag_door + find_door) > 1 && max_doors == 1)
return (false);
if (find_door == 0 || find_door > max_doors)
return (false);
if (_find_in_area(sx, sy, ex, ey, DNGN_BUILDER_SPECIAL_WALL))
return (false);
for (rx = sx; rx <= ex; rx++)
for (ry = sy; ry <= ey; ry++)
{
if (grd[rx][ry] <= DNGN_FLOOR)
grd[rx][ry] = DNGN_FLOOR;
}
for (ry = sy + 1; ry < ey; ry++)
{
if (grd[sx-1][ry] == DNGN_FLOOR
&& feat_is_solid(grd[sx-1][ry-1])
&& feat_is_solid(grd[sx-1][ry+1]))
{
if (x_chance_in_y(doorlevel, 10))
grd[sx-1][ry] = DNGN_CLOSED_DOOR;
}
if (grd[ex+1][ry] == DNGN_FLOOR
&& feat_is_solid(grd[ex+1][ry-1])
&& feat_is_solid(grd[ex+1][ry+1]))
{
if (x_chance_in_y(doorlevel, 10))
grd[ex+1][ry] = DNGN_CLOSED_DOOR;
}
}
for (rx = sx + 1; rx < ex; rx++)
{
if (grd[rx][sy-1] == DNGN_FLOOR
&& feat_is_solid(grd[rx-1][sy-1])
&& feat_is_solid(grd[rx+1][sy-1]))
{
if (x_chance_in_y(doorlevel, 10))
grd[rx][sy-1] = DNGN_CLOSED_DOOR;
}
if (grd[rx][ey+1] == DNGN_FLOOR
&& feat_is_solid(grd[rx-1][ey+1])
&& feat_is_solid(grd[rx+1][ey+1]))
{
if (x_chance_in_y(doorlevel, 10))
grd[rx][ey+1] = DNGN_CLOSED_DOOR;
}
}
return (true);
}
static monster_type _choose_unique_by_depth(int step)
{
int ret;
switch (step)
{
case 0: ret = random_choose(MONS_TERENCE, MONS_JESSICA, MONS_IJYB,
MONS_SIGMUND, -1);
break;
case 1: ret = random_choose(MONS_IJYB, MONS_SIGMUND, MONS_BLORK_THE_ORC,
MONS_EDMUND, MONS_PRINCE_RIBBIT, MONS_PURGY,
MONS_MENKAURE, MONS_DUVESSA, -1);
break;
case 2: ret = random_choose(MONS_BLORK_THE_ORC, MONS_EDMUND, MONS_PSYCHE,
MONS_EROLCHA, MONS_PRINCE_RIBBIT, MONS_GRUM,
MONS_GASTRONOK, MONS_MAURICE, -1);
break;
case 3: ret = random_choose(MONS_PSYCHE, MONS_EROLCHA, MONS_DONALD, MONS_URUG,
MONS_MICHAEL, MONS_EUSTACHIO, MONS_SONJA, MONS_GRUM,
MONS_JOSEPH, MONS_ERICA, MONS_JOSEPHINE, MONS_JOZEF,
MONS_HAROLD, MONS_NORBERT, MONS_GASTRONOK,
MONS_MAURICE, -1);
break;
case 4: ret = random_choose(MONS_URUG, MONS_MICHAEL, MONS_EUSTACHIO, MONS_SONJA,
MONS_SNORG, MONS_ERICA, MONS_JOSEPHINE, MONS_HAROLD,
MONS_ROXANNE, MONS_RUPERT, MONS_NORBERT, MONS_JOZEF,
MONS_AZRAEL, MONS_NESSOS, MONS_AGNES,
MONS_MAUD, MONS_LOUISE, MONS_NERGALLE, MONS_KIRKE, -1);
break;
case 5: ret = random_choose(MONS_SNORG, MONS_LOUISE, MONS_FRANCIS, MONS_FRANCES,
MONS_RUPERT, MONS_WAYNE, MONS_DUANE, MONS_NORRIS,
MONS_AZRAEL, MONS_NESSOS, MONS_NERGALLE,
MONS_ROXANNE, MONS_SAINT_ROKA, MONS_KIRKE, -1);
break;
case 6: default:
ret = random_choose(MONS_FRANCIS, MONS_FRANCES, MONS_WAYNE, MONS_DUANE,
MONS_XTAHUA, MONS_NORRIS, MONS_FREDERICK,
MONS_MARGERY, MONS_BORIS, MONS_SAINT_ROKA, -1);
}
return static_cast<monster_type>(ret);
}
static monster_type _pick_unique(int lev)
{
if (player_in_branch(BRANCH_SLIME_PITS))
{
if (player_branch_depth() > 1 && one_chance_in(3))
return MONS_DISSOLUTION;
return MONS_NO_MONSTER;
}
else if (player_in_branch(BRANCH_LAIR))
{
if (player_branch_depth() > 5 && one_chance_in(3))
return MONS_GASTRONOK;
else if (player_branch_depth() < 3 && one_chance_in(4))
return MONS_PRINCE_RIBBIT;
else
return MONS_NO_MONSTER;
}
int which_unique =
((lev <= 3) ? _choose_unique_by_depth(0) :
(lev <= 7) ? _choose_unique_by_depth(1) :
(lev <= 9) ? _choose_unique_by_depth(2) :
(lev <= 13) ? _choose_unique_by_depth(3) :
(lev <= 16) ? _choose_unique_by_depth(4) :
(lev <= 19) ? _choose_unique_by_depth(5) :
_choose_unique_by_depth(6));
if (which_unique == MONS_AZRAEL
&& (player_in_branch(BRANCH_SWAMP) || player_in_branch(BRANCH_SHOALS)))
{
return MONS_NO_MONSTER;
}
if (player_in_branch(BRANCH_VESTIBULE_OF_HELL))
{
if (one_chance_in(7))
which_unique = MONS_MURRAY;
}
else if (player_in_branch(BRANCH_HALL_OF_ZOT))
{
if (one_chance_in(3))
which_unique = MONS_TIAMAT;
}
else if (player_in_branch(BRANCH_SHOALS))
{
if (player_branch_depth() > 1 && coinflip())
which_unique = MONS_POLYPHEMUS;
else if (player_branch_depth() > 2 && coinflip())
which_unique = MONS_ILSUIW;
}
else if (player_in_branch(BRANCH_TOMB) || player_in_branch(BRANCH_CRYPT))
{
if (one_chance_in(10))
which_unique = MONS_KHUFU;
}
return static_cast<monster_type>(which_unique);
}
static int _place_uniques(int level_number, char level_type)
{
if (level_number <= 0 || level_type != LEVEL_DUNGEON
|| !your_branch().has_uniques)
{
return 0;
}
int num_placed = 0;
while (one_chance_in(3))
{
monster_type which_unique = MONS_NO_MONSTER;
while (which_unique == MONS_NO_MONSTER
|| you.unique_creatures[which_unique])
{
which_unique = _pick_unique(level_number);
if (which_unique == MONS_NO_MONSTER
|| you.unique_creatures[which_unique] && !one_chance_in(3))
{
which_unique = MONS_NO_MONSTER;
break;
}
}
if (which_unique == MONS_NO_MONSTER)
break;
mgen_data mg(which_unique, BEH_SLEEP, 0, 0,
coord_def(), MHITNOT, MG_PERMIT_BANDS,
GOD_NO_GOD, MONS_NO_MONSTER, 0, BLACK,
level_number, PROX_ANYWHERE);
mg.map_mask = MMT_NO_MONS;
const int mindex = place_monster(mg);
if (mindex != -1)
{
#ifdef DEBUG_DIAGNOSTICS
mprf(MSGCH_DIAGNOSTICS, "Placed %s",
menv[mindex].name(DESC_NOCAP_A).c_str());
#endif
++num_placed;
}
}
return num_placed;
}
static int _place_monster_vector(std::vector<monster_type> montypes,
int level_number, int num_to_place)
{
int result = 0;
mgen_data mg;
mg.power = level_number;
mg.behaviour = BEH_SLEEP;
mg.flags |= MG_PERMIT_BANDS;
mg.map_mask |= MMT_NO_MONS;
for (int i = 0; i < num_to_place; i++)
{
mg.cls = montypes[random2(montypes.size())];
if (place_monster(mg) != -1)
++result;
}
return result;
}
static void _place_aquatic_monsters(int level_number, char level_type)
{
int lava_spaces = 0, water_spaces = 0;
std::vector<monster_type> swimming_things(4u, MONS_NO_MONSTER);
for (int x = 0; x < GXM; x++)
for (int y = 0; y < GYM; y++)
{
if (grd[x][y] == DNGN_LAVA)
lava_spaces++;
if (feat_is_water(grd[x][y]))
water_spaces++;
}
if (lava_spaces > 49 && level_number > 6)
{
for (int i = 0; i < 4; i++)
{
swimming_things[i] = static_cast<monster_type>(
MONS_LAVA_WORM + random2(3) );
if (one_chance_in(30))
swimming_things[i] = MONS_SALAMANDER;
}
_place_monster_vector(swimming_things, level_number,
std::min(random2avg(9, 2)
+ (random2(lava_spaces) / 10), 15));
}
if (water_spaces > 49)
{
for (int i = 0; i < 4; i++)
{
swimming_things[i] =
static_cast<monster_type>(MONS_BIG_FISH + random2(4));
if (player_in_branch( BRANCH_SWAMP ) && !one_chance_in(3))
swimming_things[i] = MONS_SWAMP_WORM;
else if (player_in_branch( BRANCH_SHOALS ))
{
if (one_chance_in(3))
swimming_things[i] = MONS_MERFOLK;
else if (one_chance_in(5))
swimming_things[i] = MONS_MERMAID;
else if (one_chance_in(8))
swimming_things[i] = MONS_SIREN;
else if (one_chance_in(20))
swimming_things[i] = MONS_KRAKEN;
}
}
if (!player_in_branch(BRANCH_SWAMP)
&& level_number >= 9 && one_chance_in(4))
{
swimming_things[3] = MONS_SHARK;
}
if (level_number >= 25 && one_chance_in(5))
swimming_things[0] = MONS_WATER_ELEMENTAL;
if (player_in_branch(BRANCH_COCYTUS))
swimming_things[3] = MONS_WATER_ELEMENTAL;
_place_monster_vector(swimming_things, level_number,
std::min(random2avg(9, 2)
+ (random2(water_spaces) / 10), 15));
}
}
static void _builder_monsters(int level_number, char level_type, int mon_wanted)
{
if (level_type == LEVEL_PANDEMONIUM
|| player_in_branch(BRANCH_ECUMENICAL_TEMPLE))
{
return;
}
for (int i = 0; i < mon_wanted; i++)
{
mgen_data mg;
mg.behaviour = BEH_SLEEP;
mg.power = level_number;
mg.flags |= MG_PERMIT_BANDS;
mg.map_mask |= MMT_NO_MONS;
place_monster(mg);
}
_place_uniques(level_number, level_type);
if (!player_in_branch(BRANCH_CRYPT)) _place_aquatic_monsters(level_number, level_type);
else
{
if (one_chance_in(3))
mons_place(mgen_data(MONS_CURSE_SKULL, BEH_SLEEP));
if (one_chance_in(7))
mons_place(mgen_data(MONS_CURSE_SKULL, BEH_SLEEP));
}
}
static void _builder_items(int level_number, char level_type, int items_wanted)
{
UNUSED( level_type );
int i = 0;
object_class_type specif_type = OBJ_RANDOM;
int items_levels = level_number;
int item_no;
if (player_in_branch( BRANCH_VAULTS ))
{
items_levels *= 15;
items_levels /= 10;
}
else if (player_in_branch( BRANCH_ORCISH_MINES ))
specif_type = OBJ_GOLD;
if (player_in_branch( BRANCH_VESTIBULE_OF_HELL )
|| player_in_hell()
|| player_in_branch( BRANCH_SLIME_PITS )
|| player_in_branch( BRANCH_HALL_OF_BLADES )
|| player_in_branch( BRANCH_ECUMENICAL_TEMPLE ))
{
return;
}
else
{
for (i = 0; i < items_wanted; i++)
{
items( 1, specif_type, OBJ_RANDOM, false, items_levels, 250,
MMT_NO_ITEM );
}
if (player_in_branch( BRANCH_MAIN_DUNGEON )
&& level_number < 5 && coinflip())
{
item_no = items( 0, OBJ_WEAPONS, WPN_KNIFE, false, 0, 250,
MMT_NO_ITEM );
if (item_no != NON_ITEM)
{
mitm[item_no].plus = 0;
mitm[item_no].plus2 = 0;
mitm[item_no].flags = 0; mitm[item_no].special = 0; }
}
}
}
static void _specr_2(spec_room &sr)
{
coord_def c, delta;
int i = 0;
if (!sr.created)
return;
bool is_ok = false;
for (int tries = 0; tries < 100 && !is_ok; ++tries)
{
is_ok = true;
switch (random2(4))
{
case 0:
c.set(random_range(sr.tl.x, sr.br.x - 1), sr.tl.y);
delta.set(0, -1);
break;
case 1:
c.set(random_range(sr.tl.x, sr.br.x - 1), sr.br.y);
delta.set(0, 1);
break;
case 2:
c.set(sr.tl.x, random_range(sr.tl.y, sr.br.y - 1));
delta.set(-1, 0);
break;
case 3:
c.set(sr.br.x, random_range(sr.tl.y, sr.br.y - 1));
delta.set(1, 0);
break;
}
coord_def s = c;
for (i = 0; i < 100; i++)
{
s += delta;
if (!in_bounds(s))
{
is_ok = false;
break;
}
if (i > 0)
{
bool found_floor = false;
for (int j = 0; j < 4; ++j)
{
const coord_def spot = s + OrthCompass[j];
if (!in_bounds(spot))
is_ok = false;
else if (grd(spot) == DNGN_FLOOR)
found_floor = true;
}
if ( found_floor )
break;
}
}
}
if (!is_ok)
return;
coord_def s = c;
for (int j = 0; j < i + 2; j++)
{
if (grd(s) == DNGN_BUILDER_SPECIAL_WALL)
grd(s) = DNGN_CLOSED_DOOR;
if (j > 0
&& grd(s + delta) > DNGN_MINWALL
&& grd(s + delta) < DNGN_FLOOR)
{
grd(s) = DNGN_BUILDER_SPECIAL_FLOOR;
}
if (grd(s) == DNGN_ROCK_WALL)
grd(s) = DNGN_BUILDER_SPECIAL_FLOOR;
s += delta;
}
sr.hooked_up = true;
}
static void _special_room(int level_number, spec_room &sr,
const map_def *vault)
{
ASSERT(vault);
std::string extra = make_stringf("special room [%s %d]",
vault->name.c_str(), level_number);
env.properties[LEVEL_EXTRAS_KEY].get_vector().push_back(extra);
int room_x1 = 8 + random2(55);
int room_y1 = 8 + random2(45);
int room_x2 = room_x1 + 4 + random2avg(6,2);
int room_y2 = room_y1 + 4 + random2avg(6,2);
_make_box(room_x1, room_y1, room_x2, room_y2,
DNGN_BUILDER_SPECIAL_FLOOR, DNGN_BUILDER_SPECIAL_WALL);
sr.created = true;
sr.hooked_up = false;
sr.tl.set(room_x1 + 1, room_y1 + 1);
sr.br.set(room_x2 - 1, room_y2 - 1);
lua_special_room_spec = sr;
lua_special_room_level = level_number;
_build_secondary_vault( level_number, vault, -1, false, false, sr.tl);
lua_special_room_spec.created = false;
lua_special_room_spec.tl.set(-1, -1);
lua_special_room_level = -1;
}
static bool _may_overwrite_pos(const int i, const int j)
{
const dungeon_feature_type grid = grd[i][j];
if (grid >= DNGN_ENTER_SHOP && grid <= DNGN_EXIT_PORTAL_VAULT
|| grid == DNGN_EXIT_HELL)
{
return (false);
}
return (mgrd[i][j] == NON_MONSTER && igrd[i][j] == NON_ITEM);
}
static void _build_rooms(const dgn_region_list &excluded,
const std::vector<coord_def> &connections_needed,
int nrooms)
{
int which_room = 0;
const bool exclusive = !one_chance_in(10);
const int maxrooms = 30;
dgn_region rom[maxrooms];
std::vector<coord_def> connections = connections_needed;
for (int i = 0; i < nrooms; i++)
{
dgn_region &myroom = rom[which_room];
int overlap_tries = 200;
do
{
myroom.size.set(3 + random2(8), 3 + random2(8));
myroom.pos.set(
random_range(MAPGEN_BORDER,
GXM - MAPGEN_BORDER - 1 - myroom.size.x),
random_range(MAPGEN_BORDER,
GYM - MAPGEN_BORDER - 1 - myroom.size.y));
}
while (myroom.overlaps(excluded, dgn_Map_Mask) && overlap_tries-- > 0);
if (overlap_tries < 0)
continue;
if (connections.size())
{
const coord_def c = connections[0];
if (join_the_dots(c, myroom.random_edge_point(), MMT_VAULT))
connections.erase( connections.begin() );
}
if (i > 0 && exclusive)
{
const coord_def end = myroom.end();
bool found_collision = false;
for (int cnx = myroom.pos.x - 1;
cnx < end.x && !found_collision; cnx++)
{
for (int cny = myroom.pos.y - 1;
cny < end.y; cny++)
{
if (grd[cnx][cny] != DNGN_ROCK_WALL)
{
found_collision = true;
break;
}
}
}
if (found_collision)
continue;
}
const coord_def end = myroom.end();
_replace_area(myroom.pos.x, myroom.pos.y, end.x, end.y,
DNGN_ROCK_WALL, DNGN_FLOOR);
if (which_room > 0)
{
_join_the_dots_rigorous(myroom.random_edge_point(),
rom[which_room - 1].random_edge_point(),
MMT_VAULT);
}
which_room++;
if (which_room >= maxrooms)
break;
}
}
static int _away_from_edge(int x, int left_edge, int right_edge)
{
if (x < left_edge)
return (1);
else if (x > right_edge)
return (-1);
else
return (coinflip()? 1 : -1);
}
static coord_def _dig_away_dir(const vault_placement &place,
const coord_def &pos)
{
bool x_edge =
pos.x == place.pos.x || pos.x == place.pos.x + place.size.x - 1;
bool y_edge =
pos.y == place.pos.y || pos.y == place.pos.y + place.size.y - 1;
if (!x_edge && !y_edge)
{
const coord_def rel = pos - place.pos;
for (int yi = -1; yi <= 1; ++yi)
for (int xi = -1; xi <= 1; ++xi)
{
if (!xi == !yi)
continue;
const coord_def mv(rel.x + xi, rel.y + yi);
if (!place.map.in_map(mv))
return (mv - rel);
}
}
if (x_edge && y_edge)
{
if (coinflip())
x_edge = false;
else
y_edge = false;
}
coord_def dig_dir;
if (x_edge)
{
if (place.size.x == 1)
{
dig_dir.x = _away_from_edge(pos.x, MAPGEN_BORDER * 2,
GXM - MAPGEN_BORDER * 2);
}
else
dig_dir.x = pos.x == place.pos.x? -1 : 1;
}
if (y_edge)
{
if (place.size.y == 1)
{
dig_dir.y = _away_from_edge(pos.y, MAPGEN_BORDER * 2,
GYM - MAPGEN_BORDER * 2);
}
else
dig_dir.y = pos.y == place.pos.y? -1 : 1;
}
return (dig_dir);
}
static void _dig_away_from(vault_placement &place, const coord_def &pos)
{
coord_def dig_dir = _dig_away_dir(place, pos);
coord_def dig_at = pos;
bool dug = false;
for (int i = 0; i < GXM; i++)
{
dig_at += dig_dir;
if (dig_at.x < MAPGEN_BORDER || dig_at.x > (GXM - MAPGEN_BORDER - 1)
|| dig_at.y < MAPGEN_BORDER || dig_at.y > (GYM - MAPGEN_BORDER - 1))
{
break;
}
if (grd(dig_at) == DNGN_ROCK_WALL)
{
grd(dig_at) = DNGN_FLOOR;
dug = true;
}
else if (grd(dig_at) == DNGN_FLOOR && i > 0)
{
int adjacent_count = 0;
for (int yi = -1; yi <= 1; ++yi)
for (int xi = -1; xi <= 1; ++xi)
{
if (!xi && !yi)
continue;
if (!cell_is_solid(dig_at + coord_def(xi, yi))
&& ++adjacent_count >= 2)
{
return;
}
}
}
}
}
static void _dig_vault_loose( vault_placement &place,
std::vector<coord_def> &targets )
{
for (int i = 0, size = targets.size(); i < size; ++i)
_dig_away_from(place, targets[i]);
}
static bool _grid_needs_exit(int x, int y)
{
return (!cell_is_solid(x, y)
|| feat_is_closed_door(grd[x][y])
|| grd[x][y] == DNGN_SECRET_DOOR);
}
static bool _map_feat_is_on_edge(const vault_placement &place,
const coord_def &c)
{
for (int xi = c.x - 1; xi <= c.x + 1; ++xi)
for (int yi = c.y - 1; yi <= c.y + 1; ++yi)
{
if (!place.map.in_map(coord_def(xi, yi) - place.pos))
return (true);
}
return (false);
}
static void _pick_internal_float_exits(const vault_placement &place,
std::vector<coord_def> &exits)
{
for (int y = place.pos.y + 1; y < place.pos.y + place.size.y - 1; ++y)
for (int x = place.pos.x + 1; x < place.pos.x + place.size.x - 1; ++x)
if (_grid_needs_exit(x, y)
&& _map_feat_is_on_edge(place, coord_def(x, y)))
{
exits.push_back( coord_def(x, y) );
}
}
static void _pick_float_exits(vault_placement &place,
std::vector<coord_def> &targets)
{
std::vector<coord_def> possible_exits;
for (int y = place.pos.y; y < place.pos.y + place.size.y; ++y)
{
if (_grid_needs_exit(place.pos.x, y))
possible_exits.push_back( coord_def(place.pos.x, y) );
if (_grid_needs_exit(place.pos.x + place.size.x - 1, y))
{
possible_exits.push_back(
coord_def(place.pos.x + place.size.x - 1, y) );
}
}
for (int x = place.pos.x + 1; x < place.pos.x + place.size.x - 1; ++x)
{
if (_grid_needs_exit(x, place.pos.y))
possible_exits.push_back( coord_def(x, place.pos.y) );
if (_grid_needs_exit(x, place.pos.y + place.size.y - 1))
{
possible_exits.push_back(
coord_def(x, place.pos.y + place.size.y - 1) );
}
}
_pick_internal_float_exits(place, possible_exits);
if (possible_exits.empty())
{
#ifdef DEBUG_DIAGNOSTICS
mprf(MSGCH_ERROR, "Unable to find exit from %s",
place.map.name.c_str());
#endif
return;
}
const int npoints = possible_exits.size();
int nexits = npoints < 6? npoints : npoints / 8 + 1;
if (nexits > 10)
nexits = 10;
while (nexits-- > 0)
{
int which_exit = random2( possible_exits.size() );
targets.push_back( possible_exits[which_exit] );
possible_exits.erase( possible_exits.begin() + which_exit );
}
}
static std::vector<coord_def> _external_connection_points(
const vault_placement &place,
const std::vector<coord_def> &target_connections)
{
std::vector<coord_def> ex_connection_points;
for (int i = 0, size = target_connections.size(); i < size; ++i)
{
const coord_def &p = target_connections[i];
ex_connection_points.push_back(p + _dig_away_dir(place, p));
}
return (ex_connection_points);
}
static coord_def _find_random_grid(int grid, unsigned mask)
{
for (int i = 0; i < 100; ++i)
{
coord_def c( random_range(MAPGEN_BORDER,
GXM - MAPGEN_BORDER - 1),
random_range(MAPGEN_BORDER,
GYM - MAPGEN_BORDER - 1) );
if (unforbidden(c, mask) && grd(c) == grid)
return c;
}
return coord_def(0, 0);
}
static void _connect_vault(const vault_placement &vp)
{
std::vector<coord_def> exc = _external_connection_points(vp, vp.exits);
for (int i = 0, size = exc.size(); i < size; ++i)
{
const coord_def &p = exc[i];
const coord_def floor = _find_random_grid(DNGN_FLOOR, MMT_VAULT);
if (!floor.x && !floor.y)
continue;
join_the_dots(p, floor, MMT_VAULT, true);
}
}
static dungeon_feature_type _dgn_find_rune_subst(const std::string &tag)
{
const std::string suffix("_entry");
const std::string::size_type psuffix = tag.find(suffix);
if (psuffix == std::string::npos)
return (DNGN_FLOOR);
const std::string key = tag.substr(0, psuffix);
if (key == "bzr")
return (DNGN_ENTER_PORTAL_VAULT);
else if (key == "lab")
return (DNGN_ENTER_LABYRINTH);
else if (key == "hell")
return (DNGN_ENTER_HELL);
else if (key == "pan")
return (DNGN_ENTER_PANDEMONIUM);
else if (key == "abyss")
return (DNGN_ENTER_ABYSS);
else
{
for (int i = 0; i < NUM_BRANCHES; ++i)
if (branches[i].entry_stairs != NUM_FEATURES
&& !strcasecmp(branches[i].abbrevname, key.c_str()))
{
return (branches[i].entry_stairs);
}
}
return (DNGN_FLOOR);
}
static dungeon_feature_type _dgn_find_rune_subst_tags(const std::string &tags)
{
std::vector<std::string> words = split_string(" ", tags);
for (int i = 0, size = words.size(); i < size; ++i)
{
const dungeon_feature_type feat = _dgn_find_rune_subst(words[i]);
if (feat != DNGN_FLOOR)
return (feat);
}
return (DNGN_FLOOR);
}
void _fixup_after_vault()
{
link_items();
env.markers.activate_all();
you_teleport_now(false, false);
setup_environment_effects();
}
bool dgn_place_map(const map_def *mdef, bool clobber, bool make_no_exits,
const coord_def &where)
{
const dgn_colour_override_manager colour_man;
bool did_map = false;
if (mdef->orient == MAP_ENCOMPASS && !Generating_Level)
{
if (clobber)
{
unwind_bool levgen(Generating_Level, true);
dgn_reset_level();
dungeon_events.clear();
const bool res = dgn_place_map(mdef, clobber, make_no_exits,
where);
_fixup_after_vault();
return (res);
}
else
{
mprf(MSGCH_DIAGNOSTICS,
"Cannot generate encompass map '%s' without clobber=true",
mdef->name.c_str());
return (false);
}
}
dungeon_feature_type rune_subst = DNGN_FLOOR;
if (mdef->has_tag_suffix("_entry"))
rune_subst = _dgn_find_rune_subst_tags(mdef->tags);
did_map = _build_secondary_vault(you.your_level, mdef, rune_subst,
clobber, make_no_exits, where);
if (did_map && !Generating_Level)
{
const vault_placement &vp = Level_Vaults[Level_Vaults.size() - 1];
for (int y = vp.pos.y; y < vp.pos.y + vp.size.y; ++y)
for (int x = vp.pos.x; x < vp.pos.x + vp.size.x; ++x)
{
std::vector<map_marker *> markers =
env.markers.get_markers_at(coord_def(x, y));
for (int i = 0, size = markers.size(); i < size; ++i)
markers[i]->activate();
if (!see_cell(x, y))
set_terrain_changed(x, y);
}
setup_environment_effects();
}
return (did_map);
}
static bool _build_secondary_vault(int level_number, const map_def *vault,
int rune_subst, bool clobber,
bool no_exits, const coord_def &where)
{
if (_build_vaults(level_number, vault, rune_subst, true, !clobber,
no_exits, where))
{
const vault_placement &vp = Level_Vaults[ Level_Vaults.size() - 1 ];
if (!player_in_branch(BRANCH_SHOALS))
_connect_vault(vp);
return (true);
}
return (false);
}
static bool _build_vaults(int level_number, const map_def *vault,
int rune_subst,
bool build_only, bool check_collisions,
bool make_no_exits, const coord_def &where)
{
FixedVector < char, 10 > stair_exist;
char stx, sty;
if (dgn_check_connectivity && !dgn_zones)
dgn_zones = _dgn_count_disconnected_zones(false);
vault_placement place;
place.level_number = level_number;
place.rune_subst = rune_subst;
if (map_bounds(where))
place.pos = where;
const int gluggy = vault_main(place, vault, check_collisions);
if (gluggy == MAP_NONE)
return (false);
place.apply_grid();
dgn_register_place(place, true);
std::vector<coord_def> &target_connections = place.exits;
if (target_connections.empty() && gluggy != MAP_ENCOMPASS
&& (!place.map.is_minivault() || place.map.has_tag("mini_float")))
{
_pick_float_exits(place, target_connections);
}
if (make_no_exits)
target_connections.clear();
Level_Vaults.push_back(place);
remember_vault_placement(vault->has_tag("extra")
? LEVEL_EXTRAS_KEY: LEVEL_VAULTS_KEY,
place);
#ifdef DEBUG_DIAGNOSTICS
if (crawl_state.map_stat_gen)
mapgen_report_map_use(place.map);
#endif
bool is_layout = place.map.has_tag("layout");
if (gluggy == MAP_ENCOMPASS && !is_layout || build_only)
return (true);
const bool dis_wallify = place.map.has_tag("dis");
const int v1x = place.pos.x;
const int v1y = place.pos.y;
const int v2x = place.pos.x + place.size.x - 1;
const int v2y = place.pos.y + place.size.y - 1;
#ifdef DEBUG_DIAGNOSTICS
mprf(MSGCH_DIAGNOSTICS,
"Vault: (%d,%d)-(%d,%d); Dis: %s",
v1x, v1y, v2x, v2y,
dis_wallify? "yes" : "no");
#endif
if (dis_wallify)
{
_plan_4(v1x, v1y, v2x, v2y, DNGN_METAL_WALL);
}
else if (!is_layout)
{
dgn_region_list excluded_regions;
excluded_regions.push_back( dgn_region::absolute(v1x, v1y, v2x, v2y) );
int nrooms = random_range(15, 90);
if (gluggy == MAP_FLOAT)
nrooms += 10;
std::vector<coord_def> ex_connection_points =
_external_connection_points(place, target_connections);
_build_rooms(excluded_regions, ex_connection_points, nrooms);
_dig_vault_loose(place, target_connections);
}
unsigned char pos_x, pos_y;
for (stx = 0; stx < 10; stx++)
stair_exist[stx] = 0;
for (stx = 0; stx < GXM; stx++)
for (sty = 0; sty < GYM; sty++)
if (grd[stx][sty] >= DNGN_STONE_STAIRS_DOWN_I
&& grd[stx][sty] <= DNGN_ESCAPE_HATCH_UP)
{
stair_exist[grd[stx][sty] - DNGN_STONE_STAIRS_DOWN_I] = 1;
}
if (player_in_branch( BRANCH_DIS ))
{
for (sty = 0; sty < 5; sty++)
stair_exist[sty] = 1;
for (sty = 6; sty < 10; sty++)
stair_exist[sty] = 0;
}
for (int j = 0; j < (coinflip()? 4 : 3); j++)
for (int i = 0; i < 2; i++)
{
const dungeon_feature_type stair
= static_cast<dungeon_feature_type>(
j + ((i == 0) ? DNGN_STONE_STAIRS_DOWN_I
: DNGN_STONE_STAIRS_UP_I));
if (stair_exist[stair - DNGN_STONE_STAIRS_DOWN_I] == 1)
continue;
int tries = 10000;
do
{
pos_x = random_range(X_BOUND_1 + 1, X_BOUND_2 - 1);
pos_y = random_range(Y_BOUND_1 + 1, Y_BOUND_2 - 1);
}
while ((grd[pos_x][pos_y] != DNGN_FLOOR
|| (!is_layout && pos_x >= v1x && pos_x <= v2x
&& pos_y >= v1y && pos_y <= v2y))
&& tries-- > 0);
if (tries <= 0)
{
#ifdef DEBUG_DIAGNOSTICS
dump_map("debug.map", true);
end(1, false,
"Failed to create level: vault stairs for %s "
"(layout: %s) failed",
place.map.name.c_str(), is_layout? "yes" : "no");
#endif
pos_x = you.pos().x;
pos_y = you.pos().y;
}
grd[pos_x][pos_y] = stair;
}
return (true);
}
static const object_class_type _acquirement_item_classes[] = {
OBJ_WEAPONS,
OBJ_ARMOUR,
OBJ_WEAPONS,
OBJ_JEWELLERY,
OBJ_BOOKS,
OBJ_STAVES,
OBJ_MISCELLANY
};
static void _dgn_place_item_explicit(const item_spec &spec,
const coord_def& where,
int level)
{
if (spec.base_type == OBJ_UNASSIGNED)
return;
object_class_type base_type = spec.base_type;
bool acquire = false;
if (spec.level >= 0)
level = spec.level;
else
{
bool adjust_type = true;
switch (spec.level)
{
case ISPEC_DAMAGED:
case ISPEC_BAD:
level = spec.level;
break;
case ISPEC_GOOD:
level = 5 + level * 2;
break;
case ISPEC_SUPERB:
level = MAKE_GOOD_ITEM;
break;
case ISPEC_ACQUIREMENT:
acquire = true;
break;
default:
adjust_type = false;
break;
}
if (adjust_type && base_type == OBJ_RANDOM)
base_type = RANDOM_ELEMENT(_acquirement_item_classes);
}
const int item_made =
(acquire ?
acquirement_create_item(base_type, spec.acquirement_source,
true, where)
: items( spec.allow_uniques, base_type,
spec.sub_type, true, level, spec.race, 0,
spec.ego ));
if (item_made != NON_ITEM && item_made != -1)
{
item_def &item(mitm[item_made]);
item.pos = where;
item.inscription.clear();
origin_reset(item);
if (is_stackable_item(item) && spec.qty > 0)
{
item.quantity = spec.qty;
if (is_blood_potion(item))
init_stack_blood_potions(item);
}
if (spec.plus >= 0 && item.base_type == OBJ_BOOKS
&& item.sub_type == BOOK_MANUAL)
{
item.plus = spec.plus;
}
}
if (grd(where) == DNGN_DEEP_WATER)
grd(where) = DNGN_SHALLOW_WATER;
else if (grd(where) <= DNGN_MINMOVE || grd(where) == DNGN_LAVA)
grd(where) = DNGN_FLOOR;
}
void dgn_place_multiple_items(item_list &list,
const coord_def& where, int level)
{
const int size = list.size();
for (int i = 0; i < size; ++i)
_dgn_place_item_explicit(list.get_item(i), where, level);
}
static void _dgn_place_item_explicit(int index, const coord_def& where,
vault_placement &place,
int level)
{
item_list &sitems = place.map.items;
if (index < 0 || index >= static_cast<int>(sitems.size()))
{
mprf(MSGCH_DIAGNOSTICS, "Map '%s' requested invalid item index: %d",
place.map.name.c_str(), index);
return;
}
const item_spec spec = sitems.get_item(index);
_dgn_place_item_explicit(spec, where, level);
}
static void _dgn_give_mon_spec_items(mons_spec &mspec,
const int mindex,
const int mid,
const int monster_level)
{
monsters &mon(menv[mindex]);
unwind_var<int> save_speedinc(mon.speed_increment);
for (int i = 0; i < NUM_MONSTER_SLOTS; i++)
if (mon.inv[i] != NON_ITEM)
{
item_def &item(mitm[mon.inv[i]]);
mon.unequip(item, i, 0, true);
destroy_item(mon.inv[i], true);
mon.inv[i] = NON_ITEM;
}
item_make_species_type racial = MAKE_ITEM_RANDOM_RACE;
if (mons_genus(mid) == MONS_ORC)
racial = MAKE_ITEM_ORCISH;
else if (mons_genus(mid) == MONS_ELF)
racial = MAKE_ITEM_ELVEN;
item_list &list = mspec.items;
const int size = list.size();
for (int i = 0; i < size; ++i)
{
item_spec spec = list.get_item(i);
if (spec.base_type == OBJ_UNASSIGNED)
continue;
if (spec.base_type == OBJ_ARMOUR || spec.base_type == OBJ_WEAPONS
|| spec.base_type == OBJ_MISSILES)
{
spec.allow_uniques = 0;
if (spec.ego == 0)
spec.ego = SP_FORBID_EGO;
}
if (spec.race == MAKE_ITEM_RANDOM_RACE)
{
if (racial != MAKE_ITEM_ELVEN || spec.base_type != OBJ_ARMOUR
|| (spec.sub_type != ARM_CLOAK
&& spec.sub_type != ARM_BOOTS))
{
spec.race = racial;
}
}
int item_level = monster_level;
if (spec.level >= 0)
item_level = spec.level;
else
{
switch (spec.level)
{
case ISPEC_GOOD:
item_level = 5 + item_level * 2;
break;
case ISPEC_SUPERB:
item_level = MAKE_GOOD_ITEM;
break;
}
}
const int item_made = items( spec.allow_uniques, spec.base_type,
spec.sub_type, true, item_level,
spec.race, 0, spec.ego );
if (item_made != NON_ITEM && item_made != -1)
{
item_def &item(mitm[item_made]);
if (!mon.pickup_item(item, 0, true))
destroy_item(item_made, true);
}
}
}
int dgn_place_monster(mons_spec &mspec,
int monster_level, const coord_def& where,
bool force_pos, bool generate_awake, bool patrolling)
{
if (mspec.mid != -1)
{
const monster_type mid = static_cast<monster_type>(mspec.mid);
const bool m_generate_awake = (generate_awake || mspec.generate_awake);
const bool m_patrolling = (patrolling || mspec.patrolling);
const bool m_band = mspec.band;
const int mlev = mspec.mlevel;
if (mlev)
{
if (mlev > 0)
monster_level = mlev;
else if (mlev == -8)
monster_level = 4 + monster_level * 2;
else if (mlev == -9)
monster_level += 5;
}
if (mid != RANDOM_MONSTER && mid < NUM_MONSTERS)
{
if (mons_is_unique(mid) && you.unique_creatures[mid]
&& !crawl_state.arena)
{
return (-1);
}
const monster_type montype = mons_class_is_zombified(mid)
? mspec.monbase
: mid;
const habitat_type habitat = mons_class_primary_habitat(montype);
if (!monster_habitable_grid(montype, grd(where)))
dungeon_terrain_changed(where, habitat2grid(habitat));
}
mgen_data mg(mid);
if (mg.cls == RANDOM_MONSTER && mspec.place.is_valid())
{
int lev = mspec.place.absdepth();
if (mlev == -8)
lev = 4 + lev * 2;
else if (mlev == -9)
lev += 5;
int tries = 100;
do
mg.cls = pick_random_monster(mspec.place, lev, lev);
while (mg.cls != MONS_NO_MONSTER
&& mons_class_is_zombified(mspec.monbase)
&& !mons_zombie_size(mg.cls)
&& tries-- > 0);
if (mg.cls == MONS_NO_MONSTER
|| (mons_class_is_zombified(mspec.monbase)
&& !mons_zombie_size(mg.cls)))
{
mg.cls = RANDOM_MONSTER;
}
}
mg.power = monster_level;
mg.behaviour = (m_generate_awake) ? BEH_WANDER : BEH_SLEEP;
switch (mspec.attitude)
{
case ATT_FRIENDLY:
mg.behaviour = BEH_FRIENDLY;
break;
case ATT_NEUTRAL:
mg.behaviour = BEH_NEUTRAL;
break;
case ATT_GOOD_NEUTRAL:
mg.behaviour = BEH_GOOD_NEUTRAL;
break;
case ATT_STRICT_NEUTRAL:
mg.behaviour = BEH_STRICT_NEUTRAL;
break;
default:
break;
}
mg.base_type = mspec.monbase;
mg.number = mspec.number;
mg.colour = mspec.colour;
mg.mname = mspec.monname;
coord_def place(where);
if (!force_pos && mgrd(place) != NON_MONSTER
&& (mg.cls < NUM_MONSTERS || mg.cls == RANDOM_MONSTER))
{
const monster_type habitat_target =
mg.cls == RANDOM_MONSTER ? MONS_GIANT_BAT : mg.cls;
place = find_newmons_square_contiguous(habitat_target, where, 0);
}
mg.pos = place;
if (mons_class_is_zombified(mg.base_type))
{
if (mons_class_is_zombified(mg.cls))
mg.base_type = MONS_NO_MONSTER;
else
std::swap(mg.base_type, mg.cls);
}
if (m_patrolling)
mg.flags |= MG_PATROLLING;
if (m_band)
mg.flags |= MG_PERMIT_BANDS;
const int mindex = place_monster(mg, true);
if (mindex != -1 && mspec.items.size() > 0)
_dgn_give_mon_spec_items(mspec, mindex, mid, monster_level);
return (mindex);
}
return (-1);
}
static bool _dgn_place_monster( const vault_placement &place, mons_spec &mspec,
int monster_level, const coord_def& where)
{
const bool generate_awake
= mspec.generate_awake || place.map.has_tag("generate_awake");
const bool patrolling
= mspec.patrolling || place.map.has_tag("patrolling");
return (-1 != dgn_place_monster(mspec, monster_level, where, false,
generate_awake, patrolling));
}
static bool _dgn_place_one_monster( const vault_placement &place,
mons_list &mons, int monster_level,
const coord_def& where)
{
for (int i = 0, size = mons.size(); i < size; ++i)
{
mons_spec spec = mons.get_monster(i);
if (_dgn_place_monster(place, spec, monster_level, where))
return (true);
}
return (false);
}
dungeon_feature_type map_feature(map_def *map, const coord_def &c, int rawfeat)
{
if (rawfeat == -1)
rawfeat = map->glyph_at(c);
if (rawfeat == ' ')
return (NUM_FEATURES);
keyed_mapspec *mapsp = map? map->mapspec_for_key(rawfeat) : NULL;
if (mapsp)
{
const feature_spec f = mapsp->get_feat();
if (f.trap >= 0)
{
if (f.feat == 1)
return trap_category(static_cast<trap_type>(f.trap));
else
return (DNGN_UNDISCOVERED_TRAP);
}
else if (f.feat >= 0)
return static_cast<dungeon_feature_type>(f.feat);
else if (f.glyph >= 0)
return map_feature(NULL, c, rawfeat);
else if (f.shop >= 0)
return (DNGN_ENTER_SHOP);
return (DNGN_FLOOR);
}
return ((rawfeat == 'x') ? DNGN_ROCK_WALL :
(rawfeat == 'X') ? DNGN_PERMAROCK_WALL :
(rawfeat == 'c') ? DNGN_STONE_WALL :
(rawfeat == 'v') ? DNGN_METAL_WALL :
(rawfeat == 'b') ? DNGN_GREEN_CRYSTAL_WALL :
(rawfeat == 'a') ? DNGN_WAX_WALL :
(rawfeat == 'm') ? DNGN_CLEAR_ROCK_WALL :
(rawfeat == 'n') ? DNGN_CLEAR_STONE_WALL :
(rawfeat == 'o') ? DNGN_CLEAR_PERMAROCK_WALL :
(rawfeat == 't') ? DNGN_TREES :
(rawfeat == '+') ? DNGN_CLOSED_DOOR :
(rawfeat == '=') ? DNGN_SECRET_DOOR :
(rawfeat == 'w') ? DNGN_DEEP_WATER :
(rawfeat == 'W') ? DNGN_SHALLOW_WATER :
(rawfeat == 'l') ? DNGN_LAVA :
(rawfeat == '>') ? DNGN_ESCAPE_HATCH_DOWN :
(rawfeat == '<') ? DNGN_ESCAPE_HATCH_UP :
(rawfeat == '}') ? DNGN_STONE_STAIRS_DOWN_I :
(rawfeat == '{') ? DNGN_STONE_STAIRS_UP_I :
(rawfeat == ')') ? DNGN_STONE_STAIRS_DOWN_II :
(rawfeat == '(') ? DNGN_STONE_STAIRS_UP_II :
(rawfeat == ']') ? DNGN_STONE_STAIRS_DOWN_III :
(rawfeat == '[') ? DNGN_STONE_STAIRS_UP_III :
(rawfeat == 'A') ? DNGN_STONE_ARCH :
(rawfeat == 'B') ? DNGN_ALTAR_ZIN :
(rawfeat == 'C') ? _pick_an_altar() : (rawfeat == 'F') ? DNGN_GRANITE_STATUE :
(rawfeat == 'I') ? DNGN_ORCISH_IDOL :
(rawfeat == 'G') ? DNGN_GRANITE_STATUE :
(rawfeat == 'T') ? DNGN_FOUNTAIN_BLUE :
(rawfeat == 'U') ? DNGN_FOUNTAIN_SPARKLING :
(rawfeat == 'V') ? DNGN_PERMADRY_FOUNTAIN :
(rawfeat == 'Y') ? DNGN_FOUNTAIN_BLOOD :
(rawfeat == '\0')? DNGN_ROCK_WALL
: DNGN_FLOOR); }
static void _vault_grid( vault_placement &place,
int vgrid,
const coord_def& where,
bool recursive )
{
keyed_mapspec *mapsp = (recursive ? NULL
: place.map.mapspec_for_key(vgrid));
if (mapsp)
{
const feature_spec f = mapsp->get_feat();
if (f.trap >= 0)
{
const trap_type trap =
(f.trap == TRAP_INDEPTH)
? random_trap_for_place(place.level_number)
: static_cast<trap_type>(f.trap);
place_specific_trap(where, trap);
if (f.feat == 1)
grd(where) = trap_category(trap);
}
else if (f.feat >= 0)
{
grd(where) = static_cast<dungeon_feature_type>( f.feat );
vgrid = -1;
}
else if (f.glyph >= 0)
{
_vault_grid( place, f.glyph, where, true );
}
else if (f.shop >= 0)
place_spec_shop(place.level_number, where, f.shop);
else
grd(where) = DNGN_FLOOR;
mons_list &mons = mapsp->get_monsters();
_dgn_place_one_monster(place, mons, place.level_number, where);
item_list &items = mapsp->get_items();
dgn_place_multiple_items(items, where, place.level_number);
return;
}
grd(where) = ((vgrid == -1) ? grd(where) :
(vgrid == 'x') ? DNGN_ROCK_WALL :
(vgrid == 'X') ? DNGN_PERMAROCK_WALL :
(vgrid == 'c') ? DNGN_STONE_WALL :
(vgrid == 'v') ? DNGN_METAL_WALL :
(vgrid == 'b') ? DNGN_GREEN_CRYSTAL_WALL :
(vgrid == 'a') ? DNGN_WAX_WALL :
(vgrid == 'm') ? DNGN_CLEAR_ROCK_WALL :
(vgrid == 'n') ? DNGN_CLEAR_STONE_WALL :
(vgrid == 'o') ? DNGN_CLEAR_PERMAROCK_WALL :
(vgrid == 't') ? DNGN_TREES :
(vgrid == '+') ? DNGN_CLOSED_DOOR :
(vgrid == '=') ? DNGN_SECRET_DOOR :
(vgrid == 'w') ? DNGN_DEEP_WATER :
(vgrid == 'W') ? DNGN_SHALLOW_WATER :
(vgrid == 'l') ? DNGN_LAVA :
(vgrid == '>') ? DNGN_ESCAPE_HATCH_DOWN :
(vgrid == '<') ? DNGN_ESCAPE_HATCH_UP :
(vgrid == '}') ? DNGN_STONE_STAIRS_DOWN_I :
(vgrid == '{') ? DNGN_STONE_STAIRS_UP_I :
(vgrid == ')') ? DNGN_STONE_STAIRS_DOWN_II :
(vgrid == '(') ? DNGN_STONE_STAIRS_UP_II :
(vgrid == ']') ? DNGN_STONE_STAIRS_DOWN_III :
(vgrid == '[') ? DNGN_STONE_STAIRS_UP_III :
(vgrid == 'A') ? DNGN_STONE_ARCH :
(vgrid == 'B') ?
static_cast<dungeon_feature_type>(
DNGN_ALTAR_FIRST_GOD + place.altar_count) : (vgrid == 'C') ? _pick_an_altar() : (vgrid == 'I') ? DNGN_ORCISH_IDOL :
(vgrid == 'G') ? DNGN_GRANITE_STATUE :
(vgrid == 'T') ? DNGN_FOUNTAIN_BLUE :
(vgrid == 'U') ? DNGN_FOUNTAIN_SPARKLING :
(vgrid == 'V') ? DNGN_PERMADRY_FOUNTAIN :
(vgrid == 'Y') ? DNGN_FOUNTAIN_BLOOD :
(vgrid == '\0')? DNGN_ROCK_WALL
: DNGN_FLOOR);
if (grd(where) == DNGN_ALTAR_JIYVA && jiyva_is_dead())
grd(where) = DNGN_FLOOR;
switch (vgrid)
{
case 'B':
place.altar_count++;
break;
case '@':
place.exits.push_back( where );
break;
case '^':
place_specific_trap(where, TRAP_RANDOM);
break;
case '~':
place_specific_trap(where, random_trap_for_place(place.level_number));
break;
}
if ((vgrid == '=' || vgrid == '+')
&& (where.x == place.pos.x || where.y == place.pos.y
|| where.x == place.pos.x + place.size.x - 1
|| where.y == place.pos.y + place.size.y - 1))
{
place.exits.push_back( where );
}
switch (vgrid)
{
case '$':
case '%':
case '*':
case '|':
case 'O': {
int item_made = NON_ITEM;
object_class_type which_class = OBJ_RANDOM;
unsigned char which_type = OBJ_RANDOM;
int which_depth;
int spec = 250;
if (vgrid == '$')
{
which_class = OBJ_GOLD;
which_type = OBJ_RANDOM;
}
else if (vgrid == '%' || vgrid == '*')
{
which_class = OBJ_RANDOM;
which_type = OBJ_RANDOM;
}
else if (vgrid == '|' || (vgrid == 'O' && place.num_runes > 0))
{
which_class = RANDOM_ELEMENT(_acquirement_item_classes);
which_type = OBJ_RANDOM;
}
else
{
if (place.rune_subst != -1)
{
grd(where) =
static_cast<dungeon_feature_type>(place.rune_subst);
break;
}
which_class = OBJ_MISCELLANY;
which_type = MISC_RUNE_OF_ZOT;
place.num_runes++;
if (you.level_type == LEVEL_PANDEMONIUM)
{
if (place.map.has_tag("mnoleg"))
spec = RUNE_MNOLEG;
else if (place.map.has_tag("lom_lobon"))
spec = RUNE_LOM_LOBON;
else if (place.map.has_tag("gloorx_vloq"))
spec = RUNE_GLOORX_VLOQ;
else if (place.map.has_tag("cerebov"))
spec = RUNE_CEREBOV;
else
spec = RUNE_DEMONIC;
}
else if (you.level_type == LEVEL_ABYSS)
spec = RUNE_ABYSSAL;
else
spec = you.where_are_you;
}
which_depth = place.level_number;
if (vgrid == '|' || vgrid == 'O')
which_depth = MAKE_GOOD_ITEM;
else if (vgrid == '*')
which_depth = 5 + (place.level_number * 2);
item_made = items( 1, which_class, which_type, true,
which_depth, spec );
if (item_made != NON_ITEM)
{
mitm[item_made].pos = where;
}
}
break;
}
if (vgrid >= 'd' && vgrid <= 'k')
_dgn_place_item_explicit(vgrid - 'd', where, place, place.level_number);
if (vgrid >= '0' && vgrid <= '9')
{
int monster_level;
mons_spec monster_type_thing(RANDOM_MONSTER);
monster_level = place.level_number;
if (vgrid == '8')
monster_level = 4 + (place.level_number * 2);
else if (vgrid == '9')
monster_level = 5 + place.level_number;
if (monster_level > 30) monster_level = 30;
if (vgrid != '8' && vgrid != '9' && vgrid != '0')
{
monster_type_thing = place.map.mons.get_monster(vgrid - '1');
monster_type mt = static_cast<monster_type>(monster_type_thing.mid);
if (place.map.place.is_valid()
&& !invalid_monster_type(mt)
&& mons_is_unique(mt)
&& you.unique_creatures[mt])
{
mprf(MSGCH_ERROR, "ERROR: %s already generated somewhere "
"else; please file a bug report.",
mons_type_name(mt, DESC_CAP_THE).c_str());
you.unique_creatures[mt] = false;
}
}
_dgn_place_monster(place, monster_type_thing, monster_level, where);
}
}
void replace_area_wrapper(dungeon_feature_type old_feat,
dungeon_feature_type new_feat)
{
ASSERT(old_feat != new_feat);
_replace_area(0, 0, GXM-1, GYM-1, old_feat, new_feat, 0, false);
}
static void _replace_area( const coord_def& p1, const coord_def& p2,
dungeon_feature_type replace,
dungeon_feature_type feature, unsigned mapmask,
bool needs_update)
{
for (rectangle_iterator ri(p1, p2); ri; ++ri)
{
if (grd(*ri) == replace && unforbidden(*ri, mapmask))
{
grd(*ri) = feature;
if (needs_update && is_terrain_seen(*ri))
{
set_envmap_obj(*ri, feature);
#ifdef USE_TILE
env.tile_bk_bg(*ri) = feature;
#endif
}
}
}
}
bool unforbidden(const coord_def &c, unsigned mask)
{
return (!mask || !(dgn_Map_Mask(c) & mask));
}
struct coord_comparator
{
coord_def target;
coord_comparator(const coord_def &t) : target(t) { }
static int dist(const coord_def &a, const coord_def &b)
{
const coord_def del = a - b;
return std::abs(del.x) * GYM + std::abs(del.y);
}
bool operator () (const coord_def &a, const coord_def &b) const
{
return dist(a, target) < dist(b, target);
}
};
typedef std::set<coord_def, coord_comparator> coord_set;
static void _jtd_init_surrounds(coord_set &coords, unsigned mapmask,
const coord_def &c)
{
for (int yi = -1; yi <= 1; ++yi)
for (int xi = -1; xi <= 1; ++xi)
{
if (!xi == !yi)
continue;
const coord_def cx(c.x + xi, c.y + yi);
if (!in_bounds(cx) || travel_point_distance[cx.x][cx.y]
|| !unforbidden(cx, mapmask))
{
continue;
}
coords.insert(cx);
travel_point_distance[cx.x][cx.y] = (-xi + 2) * 4 + (-yi + 2);
}
}
static bool _join_the_dots_pathfind(coord_set &coords,
const coord_def &from, const coord_def &to,
unsigned mapmask, bool early_exit)
{
coord_def curr = from;
while (true)
{
int &tpd = travel_point_distance[curr.x][curr.y];
tpd = !tpd? -1000 : -tpd;
if (curr == to)
break;
_jtd_init_surrounds(coords, mapmask, curr);
if (coords.empty())
break;
curr = *coords.begin();
coords.erase(coords.begin());
}
if (curr != to)
return (false);
while (curr != from)
{
if (unforbidden(curr, mapmask))
grd(curr) = DNGN_FLOOR;
const int dist = travel_point_distance[curr.x][curr.y];
ASSERT(dist < 0 && dist != -1000);
curr += coord_def(-dist / 4 - 2, (-dist % 4) - 2);
}
if (unforbidden(curr, mapmask))
grd(curr) = DNGN_FLOOR;
return (true);
}
static bool _join_the_dots_rigorous(const coord_def &from,
const coord_def &to,
unsigned mapmask,
bool early_exit)
{
memset(travel_point_distance, 0, sizeof(travel_distance_grid_t));
const coord_comparator comp(to);
coord_set coords(comp);
const bool found = _join_the_dots_pathfind(coords, from, to, mapmask,
early_exit);
return (found);
}
bool join_the_dots(const coord_def &from, const coord_def &to,
unsigned mapmask, bool early_exit)
{
if (from == to)
return (true);
int join_count = 0;
coord_def at = from;
do
{
join_count++;
const dungeon_feature_type feat = grd(at);
if (early_exit && at != from && feat_is_traversable(feat))
return (true);
if (unforbidden(at, MMT_VAULT) && !feat_is_traversable(feat))
grd(at) = DNGN_FLOOR;
if (join_count > 10000) return (false);
if (at.x < to.x
&& unforbidden(coord_def(at.x + 1, at.y), mapmask))
{
at.x++;
continue;
}
if (at.x > to.x
&& unforbidden(coord_def(at.x - 1, at.y), mapmask))
{
at.x--;
continue;
}
if (at.y > to.y
&& unforbidden(coord_def(at.x, at.y - 1), mapmask))
{
at.y--;
continue;
}
if (at.y < to.y
&& unforbidden(coord_def(at.x, at.y + 1), mapmask))
{
at.y++;
continue;
}
break;
}
while (at != to && in_bounds(at));
if (in_bounds(at) && unforbidden(at, mapmask))
grd(at) = DNGN_FLOOR;
return (at == to);
}
static void _place_pool(dungeon_feature_type pool_type, unsigned char pool_x1,
unsigned char pool_y1, unsigned char pool_x2,
unsigned char pool_y2)
{
int i, j;
unsigned char left_edge, right_edge;
if (pool_type == DNGN_LAVA
&& (player_in_branch(BRANCH_CRYPT) || player_in_branch(BRANCH_TOMB)))
{
pool_type = DNGN_SHALLOW_WATER;
}
if (pool_x1 >= pool_x2 - 4 || pool_y1 >= pool_y2 - 4)
return;
left_edge = pool_x1 + 2 + random2(pool_x2 - pool_x1);
right_edge = pool_x2 - 2 - random2(pool_x2 - pool_x1);
for (j = pool_y1 + 1; j < pool_y2 - 1; j++)
{
for (i = pool_x1 + 1; i < pool_x2 - 1; i++)
{
if (i >= left_edge && i <= right_edge && grd[i][j] == DNGN_FLOOR)
grd[i][j] = pool_type;
}
if (j - pool_y1 < (pool_y2 - pool_y1) / 2 || one_chance_in(4))
{
if (left_edge > pool_x1 + 1)
left_edge -= random2(3);
if (right_edge < pool_x2 - 1)
right_edge += random2(3);
}
if (left_edge < pool_x2 - 1
&& (j - pool_y1 >= (pool_y2 - pool_y1) / 2
|| left_edge <= pool_x1 + 2 || one_chance_in(4)))
{
left_edge += random2(3);
}
if (right_edge > pool_x1 + 1
&& (j - pool_y1 >= (pool_y2 - pool_y1) / 2
|| right_edge >= pool_x2 - 2 || one_chance_in(4)))
{
right_edge -= random2(3);
}
}
}
static void _many_pools(dungeon_feature_type pool_type)
{
if (player_in_branch( BRANCH_COCYTUS ))
pool_type = DNGN_DEEP_WATER;
else if (player_in_branch( BRANCH_GEHENNA ))
pool_type = DNGN_LAVA;
const int num_pools = 20 + random2avg(9, 2);
int pools = 0;
std::string extra = make_stringf("many pools [%d %d]", (int) pool_type,
num_pools);
env.properties[LEVEL_EXTRAS_KEY].get_vector().push_back(extra);
for (int timeout = 0; pools < num_pools && timeout < 30000; ++timeout)
{
const int i = random_range(X_BOUND_1 + 1, X_BOUND_2 - 21);
const int j = random_range(Y_BOUND_1 + 1, Y_BOUND_2 - 21);
const int k = i + 2 + roll_dice( 2, 9 );
const int l = j + 2 + roll_dice( 2, 9 );
if (count_antifeature_in_box(i, j, k, l, DNGN_FLOOR) == 0)
{
_place_pool(pool_type, i, j, k, l);
pools++;
}
}
}
static dungeon_feature_type _pick_an_altar()
{
dungeon_feature_type altar_type;
int temp_rand;
if (player_in_branch(BRANCH_ECUMENICAL_TEMPLE)
|| you.level_type == LEVEL_LABYRINTH)
{
altar_type = DNGN_FLOOR;
}
else if (you.level_type == LEVEL_DUNGEON && !one_chance_in(5))
{
switch (you.where_are_you)
{
case BRANCH_CRYPT:
altar_type = (coinflip() ? DNGN_ALTAR_KIKUBAAQUDGHA
: DNGN_ALTAR_YREDELEMNUL);
break;
case BRANCH_ORCISH_MINES: temp_rand = random2(10);
altar_type = ((temp_rand == 0) ? DNGN_ALTAR_VEHUMET :
(temp_rand == 1) ? DNGN_ALTAR_MAKHLEB :
(temp_rand == 2) ? DNGN_ALTAR_OKAWARU :
(temp_rand == 3) ? DNGN_ALTAR_TROG :
(temp_rand == 4) ? DNGN_ALTAR_XOM
: DNGN_ALTAR_BEOGH);
break;
case BRANCH_VAULTS: temp_rand = random2(7);
altar_type = ((temp_rand == 0) ? DNGN_ALTAR_ELYVILON :
(temp_rand == 1) ? DNGN_ALTAR_SIF_MUNA :
(temp_rand == 2) ? DNGN_ALTAR_SHINING_ONE :
(temp_rand == 3
|| temp_rand == 4) ? DNGN_ALTAR_OKAWARU
: DNGN_ALTAR_ZIN);
break;
case BRANCH_HALL_OF_BLADES:
altar_type = DNGN_ALTAR_OKAWARU;
break;
case BRANCH_ELVEN_HALLS: temp_rand = random2(4);
altar_type = ((temp_rand == 0) ? DNGN_ALTAR_VEHUMET :
(temp_rand == 1) ? DNGN_ALTAR_SIF_MUNA :
(temp_rand == 2) ? DNGN_ALTAR_XOM
: DNGN_ALTAR_MAKHLEB);
break;
case BRANCH_SLIME_PITS:
altar_type = jiyva_is_dead() ? DNGN_FLOOR : DNGN_ALTAR_JIYVA;
break;
case BRANCH_TOMB:
altar_type = DNGN_ALTAR_KIKUBAAQUDGHA;
break;
default:
do
{
altar_type =
static_cast<dungeon_feature_type>(DNGN_ALTAR_FIRST_GOD +
random2(NUM_GODS - 1));
}
while (altar_type == DNGN_ALTAR_NEMELEX_XOBEH
|| altar_type == DNGN_ALTAR_LUGONU
|| altar_type == DNGN_ALTAR_BEOGH
|| altar_type == DNGN_ALTAR_JIYVA);
break;
}
}
else
{
temp_rand = random2(9);
altar_type = ((temp_rand == 0) ? DNGN_ALTAR_ZIN :
(temp_rand == 1) ? DNGN_ALTAR_SHINING_ONE :
(temp_rand == 2) ? DNGN_ALTAR_KIKUBAAQUDGHA :
(temp_rand == 3) ? DNGN_ALTAR_XOM :
(temp_rand == 4) ? DNGN_ALTAR_OKAWARU :
(temp_rand == 5) ? DNGN_ALTAR_MAKHLEB :
(temp_rand == 6) ? DNGN_ALTAR_SIF_MUNA :
(temp_rand == 7) ? DNGN_ALTAR_TROG
: DNGN_ALTAR_ELYVILON);
}
return (altar_type);
}
static void _place_altars()
{
if (you.your_level < 4)
return;
if (you.level_type == LEVEL_DUNGEON)
{
int prob = your_branch().altar_chance;
while (prob)
{
if (random2(100) >= prob)
break;
#ifdef DEBUG_DIAGNOSTICS
mprf(MSGCH_DIAGNOSTICS, "Placing an altar");
#endif
_place_altar();
prob /= 5;
}
}
}
static void _place_altar()
{
for (int numtry = 0; numtry < 5000; ++numtry)
{
int px = 15 + random2(55);
int py = 15 + random2(45);
const int numfloors = count_feature_in_box(px-2, py-2, px+3, py+3,
DNGN_FLOOR);
const int numgood =
count_feature_in_box(px-2, py-2, px+3, py+3, DNGN_ROCK_WALL) +
count_feature_in_box(px-2, py-2, px+3, py+3, DNGN_CLOSED_DOOR) +
count_feature_in_box(px-2, py-2, px+3, py+3, DNGN_SECRET_DOOR) +
count_feature_in_box(px-2, py-2, px+3, py+3, DNGN_FLOOR);
if (numgood < 5*5 || numfloors == 0)
continue;
bool mon_there = false;
for (int i = px - 2; i <= px + 2; i++)
for (int j = py - 2; j <= py + 2; j++)
{
if (mgrd[i][j] != NON_MONSTER)
mon_there = true;
}
if (mon_there)
continue;
for (int i = px - 2; i <= px + 2; i++)
for (int j = py - 2; j <= py + 2; j++)
grd[i][j] = DNGN_FLOOR;
grd[px][py] = _pick_an_altar();
break;
}
}
static void _place_shops(int level_number, int nshops)
{
int temp_rand = 0;
coord_def shop_place;
bool allow_bazaars = false;
temp_rand = random2(125);
if (!nshops)
{
#if DEBUG_SHOPS
nshops = MAX_SHOPS;
#else
nshops = ((temp_rand > 28) ? 0 : (temp_rand > 4) ? 1 : random_range(1, MAX_RANDOM_SHOPS));
if (nshops == 0 || level_number < 3)
return;
#endif
allow_bazaars = true;
}
for (int i = 0; i < nshops; i++)
{
int timeout = 0;
do
{
shop_place.set(random_range(X_BOUND_1 + 1, X_BOUND_2 - 1),
random_range(Y_BOUND_1 + 1, Y_BOUND_2 - 1));
timeout++;
if (timeout > 10000)
return;
}
while (grd(shop_place) != DNGN_FLOOR);
if (allow_bazaars && level_number > 9 && level_number < 27
&& one_chance_in(30 - level_number))
{
_place_specific_stair(DNGN_ENTER_PORTAL_VAULT,
"bzr_entry", level_number, true);
allow_bazaars = false;
}
else
place_spec_shop(level_number, shop_place, SHOP_RANDOM);
}
}
static bool _need_varied_selection(shop_type shop)
{
return (shop == SHOP_BOOK);
}
void place_spec_shop( int level_number,
const coord_def& where,
int force_s_type, bool representative )
{
int orb = 0;
int i = 0;
int j = 0; int item_level;
bool note_status = notes_are_active();
activate_notes(false);
for (i = 0; i < MAX_SHOPS; i++)
if (env.shop[i].type == SHOP_UNASSIGNED)
break;
if (i == MAX_SHOPS)
return;
for (j = 0; j < 3; j++)
env.shop[i].keeper_name[j] = 1 + random2(200);
env.shop[i].level = level_number * 2;
env.shop[i].type = static_cast<shop_type>(
(force_s_type != SHOP_RANDOM) ? force_s_type
: random2(NUM_SHOPS));
if (env.shop[i].type == SHOP_FOOD)
{
env.shop[i].greed = 10 + random2(5);
}
else if (env.shop[i].type != SHOP_WEAPON_ANTIQUE
&& env.shop[i].type != SHOP_ARMOUR_ANTIQUE
&& env.shop[i].type != SHOP_GENERAL_ANTIQUE)
{
env.shop[i].greed = 10 + random2(5) + random2(level_number / 2);
}
else
env.shop[i].greed = 15 + random2avg(19, 2) + random2(level_number);
if (you.level_type == LEVEL_PORTAL_VAULT && you.level_type_tag == "bazaar")
{
int factor = random2(8) + 12;
#ifdef DEBUG_DIAGNOSTICS
mprf(MSGCH_DIAGNOSTICS, "shop type %d: original greed = %d, factor = %d",
env.shop[i].type, env.shop[i].greed, factor);
mprf(MSGCH_DIAGNOSTICS, "discount at shop %d is %d%%", i, (20-factor)*5);
#endif
factor *= env.shop[i].greed;
factor /= 20;
env.shop[i].greed = factor;
}
int plojy = 5 + random2avg(12, 3);
if (representative)
plojy = env.shop[i].type == SHOP_WAND? NUM_WANDS : 16;
int stocked[NUM_BOOKS];
if (_need_varied_selection(env.shop[i].type))
{
for (int k = 0; k < NUM_BOOKS; k++)
stocked[k] = 0;
}
for (j = 0; j < plojy; j++)
{
if (env.shop[i].type != SHOP_WEAPON_ANTIQUE
&& env.shop[i].type != SHOP_ARMOUR_ANTIQUE
&& env.shop[i].type != SHOP_GENERAL_ANTIQUE)
{
item_level = level_number + random2((level_number + 1) * 2);
}
else
{
item_level = level_number + random2((level_number + 1) * 3);
}
if (you.level_type == LEVEL_PORTAL_VAULT
&& you.level_type_tag == "bazaar")
{
int help = random2(item_level) + 1;
item_level += help;
if (item_level > level_number * 5)
item_level = level_number * 5;
}
while (true)
{
const int subtype = representative? j : OBJ_RANDOM;
orb = items( 1, _item_in_shop(env.shop[i].type), subtype, true,
one_chance_in(4)? MAKE_GOOD_ITEM : item_level,
MAKE_ITEM_RANDOM_RACE );
if (orb != NON_ITEM && _need_varied_selection(env.shop[i].type))
{
if (!one_chance_in(stocked[mitm[orb].sub_type] + 1))
{
mitm[orb].clear();
orb = NON_ITEM; }
}
if (orb != NON_ITEM
&& mitm[orb].base_type != OBJ_GOLD
&& (env.shop[i].type != SHOP_GENERAL_ANTIQUE
|| (mitm[orb].base_type != OBJ_MISSILES
&& mitm[orb].base_type != OBJ_FOOD)))
{
break;
}
if (orb != NON_ITEM)
mitm[orb].clear();
}
if (orb == NON_ITEM)
break;
item_def& item( mitm[orb] );
if (_need_varied_selection(env.shop[i].type) && !is_artefact(item))
stocked[item.sub_type]++;
if (representative && item.base_type == OBJ_WANDS)
item.plus = 7;
item.pos.set(0, 5+i);
if (env.shop[i].type != SHOP_WEAPON_ANTIQUE
&& env.shop[i].type != SHOP_ARMOUR_ANTIQUE
&& env.shop[i].type != SHOP_GENERAL_ANTIQUE)
{
set_ident_flags( item, ISFLAG_IDENT_MASK );
}
}
env.shop[i].pos = where;
grd(where) = DNGN_ENTER_SHOP;
activate_notes(note_status);
}
static object_class_type _item_in_shop(unsigned char shop_type)
{
switch (shop_type)
{
case SHOP_WEAPON:
if (one_chance_in(5))
return (OBJ_MISSILES);
case SHOP_WEAPON_ANTIQUE:
return (OBJ_WEAPONS);
case SHOP_ARMOUR:
case SHOP_ARMOUR_ANTIQUE:
return (OBJ_ARMOUR);
case SHOP_GENERAL:
case SHOP_GENERAL_ANTIQUE:
return (OBJ_RANDOM);
case SHOP_JEWELLERY:
return (OBJ_JEWELLERY);
case SHOP_WAND:
return (OBJ_WANDS);
case SHOP_BOOK:
return (OBJ_BOOKS);
case SHOP_FOOD:
return (OBJ_FOOD);
case SHOP_DISTILLERY:
return (OBJ_POTIONS);
case SHOP_SCROLL:
return (OBJ_SCROLLS);
}
return (OBJ_RANDOM);
}
void spotty_level(bool seeded, int iterations, bool boxy)
{
dgn_Build_Method += make_stringf(" spotty_level [%d%s%s]",
iterations, seeded ? " seeeded" : "",
boxy ? " boxy" : "");
if (dgn_Layout_Type == "open" || dgn_Layout_Type == "cross")
dgn_Layout_Type = "open";
else if (iterations >= 100)
dgn_Layout_Type = "caves";
else if (dgn_Layout_Type.empty())
dgn_Layout_Type = "spotty";
else
dgn_Layout_Type += "_spotty";
int i, j, k, l;
if (!seeded)
{
for (i = DNGN_STONE_STAIRS_DOWN_I; i < DNGN_ESCAPE_HATCH_UP; i++)
{
if (i == DNGN_ESCAPE_HATCH_DOWN
|| i == DNGN_STONE_STAIRS_UP_I
&& !player_in_branch( BRANCH_SLIME_PITS ))
{
continue;
}
do
{
j = random_range(X_BOUND_1 + 4, X_BOUND_2 - 4);
k = random_range(Y_BOUND_1 + 4, Y_BOUND_2 - 4);
}
while (grd[j][k] != DNGN_ROCK_WALL
&& grd[j + 1][k] != DNGN_ROCK_WALL);
grd[j][k] = static_cast<dungeon_feature_type>(i);
if (i == DNGN_STONE_STAIRS_DOWN_I
&& !player_in_branch( BRANCH_SLIME_PITS ))
{
grd[j + 1][k] = DNGN_STONE_STAIRS_UP_I;
}
if (grd[j][k - 1] == DNGN_ROCK_WALL)
grd[j][k - 1] = DNGN_FLOOR;
if (grd[j][k + 1] == DNGN_ROCK_WALL)
grd[j][k + 1] = DNGN_FLOOR;
if (grd[j - 1][k] == DNGN_ROCK_WALL)
grd[j - 1][k] = DNGN_FLOOR;
if (grd[j + 1][k] == DNGN_ROCK_WALL)
grd[j + 1][k] = DNGN_FLOOR;
}
}
l = iterations;
if (l == 0)
l = 200 + random2( (boxy ? 750 : 1500) );
for (i = 0; i < l; i++)
{
do
{
j = random_range(X_BOUND_1 + 4, X_BOUND_2 - 4);
k = random_range(Y_BOUND_1 + 4, Y_BOUND_2 - 4);
}
while (grd[j][k] == DNGN_ROCK_WALL
&& grd[j - 1][k] == DNGN_ROCK_WALL
&& grd[j + 1][k] == DNGN_ROCK_WALL
&& grd[j][k - 1] == DNGN_ROCK_WALL
&& grd[j][k + 1] == DNGN_ROCK_WALL
&& grd[j - 2][k] == DNGN_ROCK_WALL
&& grd[j + 2][k] == DNGN_ROCK_WALL
&& grd[j][k - 2] == DNGN_ROCK_WALL
&& grd[j][k + 2] == DNGN_ROCK_WALL);
if (grd[j][k] == DNGN_ROCK_WALL)
grd[j][k] = DNGN_FLOOR;
if (grd[j][k - 1] == DNGN_ROCK_WALL)
grd[j][k - 1] = DNGN_FLOOR;
if (grd[j][k + 1] == DNGN_ROCK_WALL)
grd[j][k + 1] = DNGN_FLOOR;
if (grd[j - 1][k] == DNGN_ROCK_WALL)
grd[j - 1][k] = DNGN_FLOOR;
if (grd[j + 1][k] == DNGN_ROCK_WALL)
grd[j + 1][k] = DNGN_FLOOR;
if (boxy)
{
if (grd[j - 1][k - 1] == DNGN_ROCK_WALL)
grd[j - 1][k - 1] = DNGN_FLOOR;
if (grd[j + 1][k + 1] == DNGN_ROCK_WALL)
grd[j + 1][k + 1] = DNGN_FLOOR;
if (grd[j - 1][k + 1] == DNGN_ROCK_WALL)
grd[j - 1][k + 1] = DNGN_FLOOR;
if (grd[j + 1][k - 1] == DNGN_ROCK_WALL)
grd[j + 1][k - 1] = DNGN_FLOOR;
}
}
}
void smear_feature(int iterations, bool boxy, dungeon_feature_type feature,
int x1, int y1, int x2, int y2)
{
for (int i = 0; i < iterations; i++)
{
int x, y;
bool diagonals, straights;
do
{
x = random_range(x1+1, x2-1);
y = random_range(y1+1, y2-1);
diagonals = grd[x+1][y+1] == feature ||
grd[x-1][y+1] == feature ||
grd[x-1][y-1] == feature ||
grd[x+1][y-1] == feature;
straights = grd[x+1][y] == feature ||
grd[x-1][y] == feature ||
grd[x][y+1] == feature ||
grd[x][y-1] == feature;
}
while (grd[x][y] == feature || !straights && (boxy || !diagonals));
grd[x][y] = feature;
}
}
static void _bigger_room()
{
dgn_Build_Method += " bigger_room";
dgn_Layout_Type = "open";
unsigned char i, j;
for (i = 10; i < (GXM - 10); i++)
for (j = 10; j < (GYM - 10); j++)
{
if (grd[i][j] == DNGN_ROCK_WALL)
grd[i][j] = DNGN_FLOOR;
}
_many_pools(DNGN_DEEP_WATER);
if (one_chance_in(3))
{
if (coinflip())
_build_river( DNGN_DEEP_WATER );
else
_build_lake( DNGN_DEEP_WATER );
}
int pair_count = coinflip() ? 4 : 3;
for (j = 0; j < pair_count; j++)
for (i = 0; i < 2; i++)
{
_place_specific_stair( static_cast<dungeon_feature_type>(
j + ((i == 0) ? DNGN_STONE_STAIRS_DOWN_I
: DNGN_STONE_STAIRS_UP_I)) );
}
}
static void _plan_main(int level_number, int force_plan)
{
dgn_Build_Method += make_stringf(" plan_main [%d%s]", level_number,
force_plan ? " force_plan" : "");
dgn_Layout_Type = "rooms";
int do_stairs = 0;
dungeon_feature_type special_grid = (one_chance_in(3) ? DNGN_METAL_WALL
: DNGN_STONE_WALL);
int i,j;
if (!force_plan)
force_plan = 1 + random2(12);
do_stairs = ((force_plan == 1) ? _plan_1(level_number) :
(force_plan == 2) ? _plan_2(level_number) :
(force_plan == 3) ? _plan_3(level_number) :
(force_plan == 4) ? _plan_4(0, 0, 0, 0, NUM_FEATURES) :
(force_plan == 5) ? (one_chance_in(9) ? _plan_5()
: _plan_3(level_number)) :
(force_plan == 6) ? _plan_6(level_number)
: _plan_3(level_number));
if (do_stairs == 3 || do_stairs == 1)
spotty_level(true, 0, coinflip());
if (do_stairs == 2 || do_stairs == 3)
{
int pair_count = coinflip()?4:3;
for (j = 0; j < pair_count; j++)
for (i = 0; i < 2; i++)
{
_place_specific_stair( static_cast<dungeon_feature_type>(
j + ((i == 0)? DNGN_STONE_STAIRS_DOWN_I
: DNGN_STONE_STAIRS_UP_I)));
}
}
if (one_chance_in(20))
_replace_area(0, 0, GXM-1, GYM-1, DNGN_ROCK_WALL, special_grid);
}
static char _plan_1(int level_number)
{
dgn_Build_Method += make_stringf(" plan_1 [%d]", level_number);
dgn_Layout_Type = "open";
const map_def *vault = find_map_by_name("layout_forbidden_donut");
ASSERT(vault);
bool success = _build_vaults(level_number, vault);
_ensure_vault_placed(success, false);
return 0;
}
static char _plan_2(int level_number)
{
dgn_Build_Method += " plan_2";
dgn_Layout_Type = "cross";
const map_def *vault = find_map_by_name("layout_cross");
ASSERT(vault);
bool success = _build_vaults(level_number, vault);
_ensure_vault_placed(success, false);
return 0;
}
static char _plan_3(int level_number)
{
dgn_Build_Method += " plan_3";
dgn_Layout_Type = "rooms";
const map_def *vault = find_map_by_name("layout_rooms");
ASSERT(vault);
bool success = _build_vaults(level_number, vault);
_ensure_vault_placed(success, false);
return 2;
}
static char _plan_4(char forbid_x1, char forbid_y1, char forbid_x2,
char forbid_y2, dungeon_feature_type force_wall)
{
dgn_Build_Method += make_stringf(" plan_4 [%d,%d %d,%d %d]",
(int) forbid_x1, (int) forbid_y1,
(int) forbid_x2, (int) forbid_y2,
(int) force_wall);
dgn_Layout_Type = "city";
int temp_rand;
int number_boxes = 5000;
dungeon_feature_type drawing = DNGN_ROCK_WALL;
char b1x, b1y, b2x, b2y;
int i;
temp_rand = random2(81);
number_boxes = ((temp_rand > 48) ? 4000 : (temp_rand > 24) ? 3000 : (temp_rand > 8) ? 5000 : (temp_rand > 0) ? 2000 : 1000);
if (force_wall != NUM_FEATURES)
drawing = force_wall;
else
{
temp_rand = random2(18);
drawing = ((temp_rand > 7) ? DNGN_ROCK_WALL : (temp_rand > 2) ? DNGN_STONE_WALL : DNGN_METAL_WALL); }
_replace_area(10, 10, (GXM - 10), (GYM - 10), DNGN_ROCK_WALL, DNGN_FLOOR);
for (i = 0; i < number_boxes; i++)
{
b1x = 11 + random2(45);
b1y = 11 + random2(35);
b2x = b1x + 3 + random2(7) + random2(5);
b2y = b1y + 3 + random2(7) + random2(5);
if (forbid_x1 != 0 || forbid_x2 != 0)
{
if (b1x <= forbid_x2 && b1x >= forbid_x1
&& b1y <= forbid_y2 && b1y >= forbid_y1)
{
continue;
}
if (b2x <= forbid_x2 && b2x >= forbid_x1
&& b2y <= forbid_y2 && b2y >= forbid_y1)
{
continue;
}
}
if (count_antifeature_in_box(b1x-1, b1y-1, b2x+1, b2y+1, DNGN_FLOOR))
continue;
if (force_wall == NUM_FEATURES)
{
temp_rand = random2(1200);
if (temp_rand < 417) drawing = DNGN_ROCK_WALL;
else if (temp_rand < 156) drawing = DNGN_STONE_WALL;
else if (temp_rand < 40) drawing = DNGN_METAL_WALL;
}
temp_rand = random2(210);
if (temp_rand > 71) _replace_area(b1x, b1y, b2x, b2y, DNGN_FLOOR, drawing);
else _box_room(b1x, b2x - 1, b1y, b2y - 1, drawing);
}
if (forbid_x1 == 0 && one_chance_in(4)) {
spec_room sr;
sr.tl.set(25, 25);
sr.br.set(55, 45);
int oblique_max = 0;
if (!one_chance_in(4))
oblique_max = 5 + random2(20);
dungeon_feature_type feature = DNGN_FLOOR;
if (one_chance_in(10))
feature = coinflip()? DNGN_DEEP_WATER : DNGN_LAVA;
octa_room(sr, oblique_max, feature);
}
return 2;
}
static char _plan_5()
{
dgn_Build_Method += " plan_5";
dgn_Layout_Type = "misc";
unsigned char imax = 5 + random2(20);
for (unsigned char i = 0; i < imax; i++)
{
join_the_dots(
coord_def( random2(GXM - 20) + 10, random2(GYM - 20) + 10 ),
coord_def( random2(GXM - 20) + 10, random2(GYM - 20) + 10 ),
MMT_VAULT );
}
if (!one_chance_in(4))
spotty_level(true, 100, coinflip());
return 2;
}
static char _plan_6(int level_number)
{
dgn_Build_Method += make_stringf(" plan_6 [%d]", level_number);
dgn_Layout_Type = "open";
const map_def *vault = find_map_by_name("layout_big_octagon");
ASSERT(vault);
bool success = _build_vaults(level_number, vault);
_ensure_vault_placed(success, false);
if ((player_in_branch(BRANCH_MAIN_DUNGEON) && level_number > 20
|| you.level_type == LEVEL_PANDEMONIUM)
&& coinflip())
{
grd[40][36] = DNGN_ENTER_ABYSS;
grd[41][36] = DNGN_ENTER_ABYSS;
}
return (0);
}
bool octa_room(spec_room &sr, int oblique_max,
dungeon_feature_type type_floor)
{
dgn_Build_Method += make_stringf(" octa_room [%d %d]", oblique_max,
(int) type_floor);
int x,y;
if ((player_in_branch( BRANCH_CRYPT ) || player_in_branch( BRANCH_TOMB ))
&& type_floor == DNGN_LAVA)
{
type_floor = DNGN_SHALLOW_WATER;
}
int oblique = oblique_max;
for (x = sr.tl.x; x < sr.br.x; x++)
{
for (y = sr.tl.y + oblique; y < sr.br.y - oblique; y++)
if (grd[x][y] == DNGN_BUILDER_SPECIAL_WALL)
return (false);
if (x > sr.tl.x - oblique_max)
oblique += 2;
if (oblique > 0)
oblique--;
}
oblique = oblique_max;
for (x = sr.tl.x; x < sr.br.x; x++)
{
for (y = sr.tl.y + oblique; y < sr.br.y - oblique; y++)
{
if (grd[x][y] == DNGN_ROCK_WALL)
grd[x][y] = type_floor;
if (grd[x][y] == DNGN_FLOOR && type_floor == DNGN_SHALLOW_WATER)
grd[x][y] = DNGN_SHALLOW_WATER;
if (grd[x][y] == DNGN_CLOSED_DOOR && !feat_is_solid(type_floor))
grd[x][y] = DNGN_FLOOR; }
if (x > sr.br.x - oblique_max)
oblique += 2;
if (oblique > 0)
oblique--;
}
return (true);
}
static void _find_maze_neighbours(const coord_def &c,
const dgn_region ®ion,
coord_list &ns)
{
std::vector<coord_def> coords;
for (int yi = -2; yi <= 2; yi += 2)
for (int xi = -2; xi <= 2; xi += 2)
{
if (!!xi == !!yi)
continue;
const coord_def cp(c.x + xi, c.y + yi);
if (region.contains(cp))
coords.push_back(cp);
}
while (!coords.empty())
{
const int n = random2(coords.size());
ns.push_back(coords[n]);
coords.erase( coords.begin() + n );
}
}
static void _labyrinth_maze_recurse(const coord_def &c, const dgn_region &where)
{
coord_list neighbours;
_find_maze_neighbours(c, where, neighbours);
coord_list deferred;
for (coord_list::iterator i = neighbours.begin();
i != neighbours.end(); ++i)
{
const coord_def &nc = *i;
if (grd(nc) != DNGN_ROCK_WALL)
continue;
grd(nc) = DNGN_FLOOR;
grd(c + (nc - c) / 2) = DNGN_FLOOR;
if (!one_chance_in(5))
_labyrinth_maze_recurse(nc, where);
else
deferred.push_back(nc);
}
for (coord_list::iterator i = deferred.begin(); i != deferred.end(); ++i)
_labyrinth_maze_recurse(*i, where);
}
static void _labyrinth_build_maze(coord_def &e, const dgn_region &lab)
{
_labyrinth_maze_recurse(lab.random_point(), lab);
do
e = lab.random_point();
while (grd(e) != DNGN_FLOOR);
}
static void _labyrinth_place_items(const coord_def &end)
{
int num_items = 8 + random2avg(12, 2);
for (int i = 0; i < num_items; i++)
{
const object_class_type glopop =
static_cast<object_class_type>(
random_choose_weighted(14, OBJ_WEAPONS,
14, OBJ_ARMOUR,
3, OBJ_MISSILES,
3, OBJ_MISCELLANY,
10, OBJ_WANDS,
10, OBJ_SCROLLS,
10, OBJ_JEWELLERY,
8, OBJ_BOOKS,
8, OBJ_STAVES,
0));
const int treasure_item =
items( 1, glopop, OBJ_RANDOM, true,
one_chance_in(3)? you.your_level * 3 : MAKE_GOOD_ITEM,
MAKE_ITEM_RANDOM_RACE );
if (treasure_item != NON_ITEM)
mitm[treasure_item].pos = end;
}
}
static void _labyrinth_place_exit(const coord_def &end)
{
_labyrinth_place_items(end);
mons_place(mgen_data::sleeper_at(MONS_MINOTAUR, end, MG_PATROLLING));
grd(end) = DNGN_ESCAPE_HATCH_UP;
}
static bool _has_no_floor_neighbours(const coord_def &pos, bool recurse = false)
{
for (int x = -1; x <= 1; x++)
for (int y = -1; y <= 1; y++)
{
if (x == 0 && y == 0)
continue;
const coord_def p = pos + coord_def(x, y);
if (!in_bounds(p))
return (true);
if (recurse)
{
if (grd(p) == DNGN_FLOOR)
return (false);
}
else if (_has_no_floor_neighbours(p, true))
return (true);
}
return (recurse);
}
static void _change_labyrinth_border(const dgn_region ®ion,
const dungeon_feature_type wall)
{
const coord_def &end = region.pos + region.size;
for (int y = region.pos.y-1; y <= end.y; ++y)
for (int x = region.pos.x-1; x <= end.x; ++x)
{
const coord_def c(x, y);
if (!in_bounds(c)) continue;
if (grd(c) == wall || !feat_is_wall(grd(c)))
continue;
if (_has_no_floor_neighbours(c))
grd[x][y] = wall;
}
}
static void _change_walls_from_centre(const dgn_region ®ion,
const coord_def ¢re,
bool rectangular,
unsigned mmask,
dungeon_feature_type wall,
const std::vector<dist_feat> &ldist)
{
if (ldist.empty())
return;
const coord_def &end = region.pos + region.size;
for (int y = region.pos.y; y < end.y; ++y)
for (int x = region.pos.x; x < end.x; ++x)
{
const coord_def c(x, y);
if (grd(c) != wall || !unforbidden(c, mmask))
continue;
const int distance =
rectangular? (c - centre).rdist() : (c - centre).abs();
for (int i = 0, size = ldist.size(); i < size; ++i)
{
if (distance <= ldist[i].dist)
{
grd(c) = ldist[i].feat;
break;
}
}
}
}
static void _change_walls_from_centre(const dgn_region ®ion,
const coord_def &c,
bool rectangular,
dungeon_feature_type wall,
...)
{
std::vector<dist_feat> ldist;
va_list args;
va_start(args, wall);
while (true)
{
const int dist = va_arg(args, int);
if (!dist)
break;
const dungeon_feature_type feat =
static_cast<dungeon_feature_type>( va_arg(args, int) );
ldist.push_back(dist_feat(dist, feat));
}
_change_walls_from_centre(region, c, rectangular, MMT_VAULT, wall, ldist);
}
static void _place_extra_lab_minivaults(int level_number)
{
std::set<const map_def*> vaults_used;
while (true)
{
const map_def *vault = random_map_for_tag("lab", false);
if (!vault || vaults_used.find(vault) != vaults_used.end())
break;
vaults_used.insert(vault);
if (!_build_secondary_vault(level_number, vault))
break;
}
}
static bool _has_vault_in_radius(const coord_def &pos, int radius,
unsigned mask)
{
for (int yi = -radius; yi <= radius; ++yi)
for (int xi = -radius; xi <= radius; ++xi)
{
const coord_def p = pos + coord_def(xi, yi);
if (!in_bounds(p))
continue;
if (!unforbidden(p, mask))
return (true);
}
return (false);
}
static coord_def _labyrinth_find_entry_point(const dgn_region ®,
const coord_def &end)
{
const int min_distance = 28 * 28;
for (int i = 0; i < 2000; ++i)
{
const coord_def place = reg.random_point();
if (grd(place) != DNGN_FLOOR)
continue;
if ((place - end).abs() < min_distance)
continue;
if (_has_vault_in_radius(place, 6, MMT_VAULT))
continue;
return (place);
}
coord_def unfound;
return (unfound);
}
static void _labyrinth_place_entry_point(const dgn_region ®ion,
const coord_def &pos)
{
const coord_def p = _labyrinth_find_entry_point(region, pos);
if (in_bounds(p))
env.markers.add(new map_feature_marker(p, DNGN_ENTER_LABYRINTH));
}
static bool _is_deadend(const coord_def pos)
{
std::vector<coord_def> dirs;
dirs.push_back(coord_def(0, -1));
dirs.push_back(coord_def(1, 0));
dirs.push_back(coord_def(0, 1));
dirs.push_back(coord_def(-1, 0));
int count_neighbours = 0;
for (unsigned int i = 0; i < dirs.size(); i++)
{
coord_def p = pos + dirs[i];
if (!in_bounds(p))
continue;
if (grd(p) == DNGN_FLOOR)
count_neighbours++;
}
return (count_neighbours <= 1);
}
static coord_def _find_random_deadend(const dgn_region ®ion)
{
int tries = 0;
coord_def result;
bool floor_pos = false;
while (++tries < 50)
{
coord_def pos = region.random_point();
if (grd(pos) != DNGN_FLOOR)
continue;
else if (!floor_pos)
{
result = pos;
floor_pos = true;
}
if (!_is_deadend(pos))
continue;
return (pos);
}
return (result);
}
static void _labyrinth_add_blood_trail(const dgn_region ®ion)
{
if (one_chance_in(5))
return;
int count_trails = 1 + one_chance_in(5) + one_chance_in(7);
int tries = 0;
while (++tries < 20)
{
const coord_def start = _find_random_deadend(region);
const coord_def dest = region.random_point();
monster_pathfind mp;
if (!mp.init_pathfind(dest, start))
continue;
const std::vector<coord_def> path = mp.backtrack();
if (path.size() < 10)
continue;
env.map(start).property |= FPROP_BLOODY;
#ifdef WIZARD
env.map(start).property |= FPROP_HIGHLIGHT;
#endif
bleed_onto_floor(start, MONS_HUMAN, 150, true, false);
for (unsigned int step = 0; step < path.size(); step++)
{
coord_def pos = path[step];
if (step < 2 || step < 12 && coinflip()
|| step >= 12 && one_chance_in(step/4))
{
env.map(pos).property |= FPROP_BLOODY;
}
#ifdef WIZARD
env.map(pos).property |= FPROP_HIGHLIGHT;
#endif
if (step >= 10 && one_chance_in(7))
break;
}
if (--count_trails > 0)
continue;
break;
}
}
static bool _find_random_nonmetal_wall(const dgn_region ®ion,
coord_def &pos)
{
int tries = 0;
while (++tries < 50)
{
pos = region.random_point();
if (!in_bounds(pos))
continue;
if (grd(pos) == DNGN_ROCK_WALL || grd(pos) == DNGN_STONE_WALL)
return (true);
}
return (false);
}
static bool _grid_has_wall_neighbours(const coord_def pos, const coord_def dir)
{
std::vector<coord_def> dirs;
dirs.push_back(coord_def(0, -1));
dirs.push_back(coord_def(1, 0));
dirs.push_back(coord_def(0, 1));
dirs.push_back(coord_def(-1, 0));
for (unsigned int i = 0; i < dirs.size(); i++)
{
coord_def p = pos + dirs[i];
if (!in_bounds(p))
continue;
if (dirs[i] == dir)
continue;
if (grd(p) == DNGN_ROCK_WALL || grd(p) == DNGN_STONE_WALL)
return (true);
}
return (false);
}
static void _vitrify_wall_neighbours(const coord_def pos)
{
const int clear_plus = DNGN_CLEAR_ROCK_WALL - DNGN_ROCK_WALL;
std::vector<coord_def> dirs;
dirs.push_back(coord_def(0, -1));
dirs.push_back(coord_def(1, 0));
dirs.push_back(coord_def(0, 1));
dirs.push_back(coord_def(-1, 0));
for (unsigned int i = 0; i < dirs.size(); i++)
{
coord_def p = pos + dirs[i];
if (!in_bounds(p))
continue;
if (testbits(env.map(p).property, FPROP_VAULT))
continue;
if (grd(p) == DNGN_ROCK_WALL || grd(p) == DNGN_STONE_WALL)
{
grd(p) = static_cast<dungeon_feature_type>(grd(p) + clear_plus);
#ifdef WIZARD
env.map(p).property |= FPROP_HIGHLIGHT;
#endif
if (one_chance_in(3) || _grid_has_wall_neighbours(p, dirs[i]))
_vitrify_wall_neighbours(p);
}
}
}
static void _labyrinth_add_glass_walls(const dgn_region ®ion)
{
int glass_num = random2(3) + random2(4);
if (!glass_num)
return;
const int clear_plus = DNGN_CLEAR_ROCK_WALL - DNGN_ROCK_WALL;
coord_def pos;
while (0 < glass_num--)
{
if (!_find_random_nonmetal_wall(region, pos))
break;
if (_has_vault_in_radius(pos, 6, MMT_VAULT))
continue;
grd(pos) = static_cast<dungeon_feature_type>(grd(pos) + clear_plus);
#ifdef WIZARD
env.map(pos).property |= FPROP_HIGHLIGHT;
#endif
_vitrify_wall_neighbours(pos);
}
}
static void _labyrinth_level(int level_number)
{
dgn_Build_Method += make_stringf(" labyrinth [%d]", level_number);
dgn_Layout_Type = "labyrinth";
dgn_region lab = dgn_region::absolute( LABYRINTH_BORDER,
LABYRINTH_BORDER,
GXM - LABYRINTH_BORDER - 1,
GYM - LABYRINTH_BORDER - 1 );
const map_def *vault = random_map_for_tag("minotaur", false);
vault_placement place;
coord_def end;
_labyrinth_build_maze(end, lab);
if (!vault || !_build_secondary_vault(level_number, vault))
{
vault = NULL;
_labyrinth_place_exit(end);
}
else
{
const vault_placement &rplace = *(Level_Vaults.end() - 1);
if (rplace.map.has_tag("generate_loot"))
{
for (int y = rplace.pos.y;
y <= rplace.pos.y + rplace.size.y - 1; ++y)
{
for (int x = rplace.pos.x;
x <= rplace.pos.x + rplace.size.x - 1; ++x)
{
if (grd[x][y] == DNGN_ESCAPE_HATCH_UP)
{
_labyrinth_place_items(coord_def(x, y));
break;
}
}
}
}
place.pos = rplace.pos;
place.size = rplace.size;
}
if (vault)
end = place.pos + place.size / 2;
_place_extra_lab_minivaults(level_number);
_change_walls_from_centre(lab, end, false,
DNGN_ROCK_WALL,
15 * 15, DNGN_METAL_WALL,
34 * 34, DNGN_STONE_WALL,
0);
_change_labyrinth_border(lab, DNGN_METAL_WALL);
_labyrinth_add_blood_trail(lab);
_labyrinth_add_glass_walls(lab);
_labyrinth_place_entry_point(lab, end);
link_items();
}
static bool _is_wall(int x, int y)
{
return feat_is_wall(grd[x][y]);
}
static int _box_room_door_spot(int x, int y)
{
if ( grd[x-1][y] == DNGN_CLOSED_DOOR
&& _is_wall(x-1,y-1) && _is_wall(x-1,y+1)
|| grd[x+1][y] == DNGN_CLOSED_DOOR
&& _is_wall(x+1,y-1) && _is_wall(x+1,y+1)
|| grd[x][y-1] == DNGN_CLOSED_DOOR
&& _is_wall(x-1,y-1) && _is_wall(x+1,y-1)
|| grd[x][y+1] == DNGN_CLOSED_DOOR
&& _is_wall(x-1,y+1) && _is_wall(x+1,y+1))
{
grd[x][y] = DNGN_CLOSED_DOOR;
return 2;
}
bool nor = _is_wall(x, y-1);
bool sou = _is_wall(x, y+1);
bool eas = _is_wall(x-1, y);
bool wes = _is_wall(x+1, y);
if (nor == sou && eas == wes && nor != eas)
return 1;
return 0;
}
static int _box_room_doors( int bx1, int bx2, int by1, int by2, int new_doors)
{
int good_doors[200]; int spot;
int i, j;
int doors_placed = new_doors;
if (2 * (bx2-bx1 + by2-by1) > 200)
return 0;
int spot_count = 0;
for (i = bx1 + 1; i < bx2; i++)
{
good_doors[spot_count ++] = _box_room_door_spot(i, by1);
good_doors[spot_count ++] = _box_room_door_spot(i, by2);
}
for (i = by1+1; i < by2; i++)
{
good_doors[spot_count ++] = _box_room_door_spot(bx1, i);
good_doors[spot_count ++] = _box_room_door_spot(bx2, i);
}
if (new_doors == 0)
{
for (i = 0; i < spot_count; i++)
if (good_doors[i] == 2)
doors_placed++;
return doors_placed;
}
j = 0;
for (i = 0; i < spot_count; i++)
if (good_doors[i] == 1)
j++;
if (new_doors > j)
new_doors = j;
while (new_doors > 0 && spot_count > 0)
{
spot = random2(spot_count);
if (good_doors[spot] != 1)
continue;
j = 0;
for (i = bx1 + 1; i < bx2; i++)
{
if (spot == j++)
{
grd[i][by1] = DNGN_CLOSED_DOOR;
break;
}
if (spot == j++)
{
grd[i][by2] = DNGN_CLOSED_DOOR;
break;
}
}
for (i = by1 + 1; i < by2; i++)
{
if (spot == j++)
{
grd[bx1][i] = DNGN_CLOSED_DOOR;
break;
}
if (spot == j++)
{
grd[bx2][i] = DNGN_CLOSED_DOOR;
break;
}
}
good_doors[spot] = 2;
new_doors --;
}
return (doors_placed);
}
static void _box_room(int bx1, int bx2, int by1, int by2,
dungeon_feature_type wall_type)
{
if ((player_in_branch( BRANCH_CRYPT ) || player_in_branch( BRANCH_TOMB ))
&& wall_type == DNGN_LAVA)
{
wall_type = DNGN_SHALLOW_WATER;
}
int temp_rand, new_doors, doors_placed;
_replace_area(bx1, by1, bx2, by1, DNGN_FLOOR, wall_type);
_replace_area(bx1, by2, bx2, by2, DNGN_FLOOR, wall_type);
_replace_area(bx1, by1+1, bx1, by2-1, DNGN_FLOOR, wall_type);
_replace_area(bx2, by1+1, bx2, by2-1, DNGN_FLOOR, wall_type);
doors_placed = _box_room_doors(bx1, bx2, by1, by2, 0);
temp_rand = random2(100);
new_doors = (temp_rand > 45) ? 2 :
(temp_rand > 22) ? 1
: 3;
if ((bx2-bx1)*(by2-by1) < 36 && new_doors > 1)
new_doors--;
new_doors -= doors_placed;
if (new_doors > 0)
_box_room_doors(bx1, bx2, by1, by2, new_doors);
}
static void _city_level(int level_number)
{
dgn_Build_Method += make_stringf(" city_level [%d]", level_number);
dgn_Layout_Type = "city";
int temp_rand; dungeon_feature_type wall_type;
dungeon_feature_type wall_type_room;
int xs = 0, ys = 0;
int x1 = 0, x2 = 0;
int y1 = 0, y2 = 0;
int i,j;
temp_rand = random2(8);
wall_type = ((temp_rand > 4) ? DNGN_ROCK_WALL : (temp_rand > 1) ? DNGN_STONE_WALL : DNGN_METAL_WALL);
if (one_chance_in(100))
wall_type = DNGN_GREEN_CRYSTAL_WALL;
_make_box( 7, 7, GXM-7, GYM-7, DNGN_FLOOR );
for (i = 0; i < 5; i++)
for (j = 0; j < 4; j++)
{
xs = 8 + (i * 13);
ys = 8 + (j * 14);
x1 = xs + random2avg(5, 2);
y1 = ys + random2avg(5, 2);
x2 = xs + 11 - random2avg(5, 2);
y2 = ys + 11 - random2avg(5, 2);
temp_rand = random2(280);
if (temp_rand > 39) {
wall_type_room = ((temp_rand > 63) ? wall_type : (temp_rand > 54) ? DNGN_STONE_WALL : (temp_rand > 45) ? DNGN_ROCK_WALL : DNGN_METAL_WALL);
if (one_chance_in(250))
wall_type_room = DNGN_GREEN_CRYSTAL_WALL;
_box_room(x1, x2, y1, y2, wall_type_room);
if (x2 - x1 > 5 && y2 - y1 > 5 && one_chance_in(8))
{
_box_room(x1 + 2, x2 - 2, y1 + 2, y2 - 2, wall_type);
if (one_chance_in(3))
{
_treasure_area(level_number, x1 + 3, x2 - 3,
y1 + 3, y2 - 3);
}
}
}
}
int stair_count = coinflip() ? 4 : 3;
for (j = 0; j < stair_count; j++)
for (i = 0; i < 2; i++)
{
_place_specific_stair( static_cast<dungeon_feature_type>(
j + ((i == 0) ? DNGN_STONE_STAIRS_DOWN_I
: DNGN_STONE_STAIRS_UP_I)) );
}
}
static bool _treasure_area(int level_number, unsigned char ta1_x,
unsigned char ta2_x, unsigned char ta1_y,
unsigned char ta2_y)
{
ta2_x++;
ta2_y++;
if (ta2_x <= ta1_x || ta2_y <= ta1_y)
return (false);
if ((ta2_x - ta1_x) * (ta2_y - ta1_y) >= 40)
return (false);
coord_def tl(ta1_x, ta1_y);
coord_def br(ta2_x-1, ta2_y-1);
for (rectangle_iterator ri(tl, br); ri; ++ri)
{
if (grd(*ri) != DNGN_FLOOR || coinflip())
continue;
int item_made = items( 1, OBJ_RANDOM, OBJ_RANDOM, true,
random2( level_number * 2 ),
MAKE_ITEM_RANDOM_RACE );
if (item_made != NON_ITEM)
mitm[item_made].pos = *ri;
}
return (true);
}
static void _diamond_rooms(int level_number)
{
char numb_diam = 1 + random2(10);
dungeon_feature_type type_floor = DNGN_DEEP_WATER;
int runthru = 0;
int i, oblique_max;
if (player_in_branch( BRANCH_DIS ) || player_in_branch( BRANCH_TARTARUS ))
return;
if (level_number > 5 + random2(5) && coinflip())
type_floor = DNGN_SHALLOW_WATER;
if (level_number > 10 + random2(5) && coinflip())
type_floor = DNGN_DEEP_WATER;
if (level_number > 17 && coinflip())
type_floor = DNGN_LAVA;
if (level_number > 10 && one_chance_in(15))
type_floor = (coinflip() ? DNGN_STONE_WALL : DNGN_ROCK_WALL);
if (level_number > 12 && one_chance_in(20))
type_floor = DNGN_METAL_WALL;
if (player_in_branch(BRANCH_GEHENNA))
type_floor = DNGN_LAVA;
else if (player_in_branch(BRANCH_COCYTUS))
type_floor = DNGN_DEEP_WATER;
for (i = 0; i < numb_diam; i++)
{
spec_room sr;
sr.tl.set(8 + random2(43), 8 + random2(35));
sr.br.set(5 + random2(15), 5 + random2(10));
sr.br += sr.tl;
oblique_max = (sr.br.x - sr.tl.x) / 2;
if (!octa_room(sr, oblique_max, type_floor))
{
runthru++;
if (runthru > 9)
runthru = 0;
else
{
i--;
continue;
}
}
}
}
static void _big_room(int level_number)
{
dungeon_feature_type type_floor = DNGN_FLOOR;
dungeon_feature_type type_2 = DNGN_FLOOR;
int i, j, k, l;
spec_room sr;
int oblique;
if (one_chance_in(4))
{
oblique = 5 + random2(20);
sr.tl.set(8 + random2(30), 8 + random2(22));
sr.br.set(20 + random2(10), 20 + random2(8));
sr.br += sr.tl;
if (!one_chance_in(5) || level_number < 8 + random2(8))
{
octa_room(sr, oblique, DNGN_FLOOR);
return;
}
type_floor = DNGN_LAVA;
if (level_number > 7)
{
type_floor = (x_chance_in_y(14, level_number) ? DNGN_DEEP_WATER
: DNGN_LAVA);
}
octa_room(sr, oblique, type_floor);
}
sr.tl.set(8 + random2(30), 8 + random2(22));
sr.br.set(20 + random2(10), 20 + random2(8));
sr.br += sr.tl;
if (count_feature_in_box(sr.tl, sr.br, DNGN_BUILDER_SPECIAL_WALL) > 0)
return;
if (level_number > 7 && one_chance_in(4))
{
type_floor = (x_chance_in_y(14, level_number) ? DNGN_DEEP_WATER
: DNGN_LAVA);
}
_replace_area(sr.tl, sr.br, DNGN_ROCK_WALL, type_floor);
_replace_area(sr.tl, sr.br, DNGN_CLOSED_DOOR, type_floor);
if (type_floor == DNGN_FLOOR)
{
const int minwall = DNGN_RNDWALL_MIN;
const int range = DNGN_RNDWALL_MAX - DNGN_RNDWALL_MIN + 1;
type_2 = static_cast<dungeon_feature_type>(minwall + random2(range));
}
if (player_in_branch( BRANCH_CRYPT ) || player_in_branch( BRANCH_TOMB ))
{
if (type_floor == DNGN_LAVA)
type_floor = DNGN_SHALLOW_WATER;
if (type_2 == DNGN_LAVA)
type_2 = DNGN_SHALLOW_WATER;
}
if (one_chance_in(4))
_chequerboard( sr, type_floor, type_floor, type_2 );
else if (one_chance_in(6))
{
i = sr.tl.x;
j = sr.tl.y;
k = sr.br.x;
l = sr.br.y;
do
{
i += 2 + random2(3);
j += 2 + random2(3);
k -= 2 + random2(3);
l -= 2 + random2(3);
if (i >= k - 3)
break;
if (j >= l - 3)
break;
_box_room(i, k, j, l, DNGN_STONE_WALL);
}
while (level_number < 1500); }
}
static void _chequerboard( spec_room &sr, dungeon_feature_type target,
dungeon_feature_type floor1,
dungeon_feature_type floor2 )
{
if (sr.br.x < sr.tl.x || sr.br.y < sr.tl.y)
return;
for (rectangle_iterator ri(sr.tl, sr.br); ri; ++ri)
if (grd(*ri) == target)
grd(*ri) = ((ri->x + ri->y) % 2) ? floor2 : floor1;
}
static void _roguey_level(int level_number, spec_room &sr, bool make_stairs)
{
dgn_Build_Method += make_stringf(" roguey_level [%d%s]", level_number,
make_stairs ? " make_stairs" : "");
int bcount_x, bcount_y;
int cn = 0;
int i;
FixedVector < unsigned char, 30 > rox1;
FixedVector < unsigned char, 30 > rox2;
FixedVector < unsigned char, 30 > roy1;
FixedVector < unsigned char, 30 > roy2;
for (bcount_y = 0; bcount_y < 5; bcount_y++)
for (bcount_x = 0; bcount_x < 5; bcount_x++)
{
rox1[cn] = bcount_x * 13 + 8 + random2(4);
roy1[cn] = bcount_y * 11 + 8 + random2(4);
rox2[cn] = rox1[cn] + 3 + random2(8);
roy2[cn] = roy1[cn] + 3 + random2(6);
if (rox2[cn] > GXM-8)
rox2[cn] = GXM-8;
cn++;
}
cn = 0;
for (i = 0; i < 25; i++)
{
_replace_area( rox1[i], roy1[i], rox2[i], roy2[i],
DNGN_ROCK_WALL, DNGN_FLOOR );
if (rox2[i] - rox1[i] > 5 && roy2[i] - roy1[i] > 5
&& x_chance_in_y(3, 100 - level_number))
{
if (!one_chance_in(4))
{
_box_room( rox1[i] + 2, rox2[i] - 2, roy1[i] + 2,
roy2[i] - 2, (coinflip() ? DNGN_STONE_WALL
: DNGN_ROCK_WALL) );
}
else
{
_box_room( rox1[i] + 2, rox2[i] - 2, roy1[i] + 2,
roy2[i] - 2, DNGN_METAL_WALL );
}
if (coinflip())
{
_treasure_area( level_number, rox1[i] + 3, rox2[i] - 3,
roy1[i] + 3, roy2[i] - 3 );
}
}
}
char last_room = 0;
int bp;
for (bp = 0; bp < 2; bp++)
for (i = 0; i < 25; i++)
{
if (bp == 0 && (!(i % 5) || i == 0))
continue;
if (bp == 1 && i < 5)
continue;
coord_def pos, jpos;
switch (bp)
{
case 0:
last_room = i - 1;
pos.x = rox1[i]; pos.y = roy1[i] + random2(roy2[i] - roy1[i]);
jpos.x = rox2[last_room]; jpos.y = roy1[last_room]
+ random2(roy2[last_room] - roy1[last_room]);
break;
case 1:
last_room = i - 5;
pos.y = roy1[i]; pos.x = rox1[i] + random2(rox2[i] - rox1[i]);
jpos.y = roy2[last_room]; jpos.x = rox1[last_room]
+ random2(rox2[last_room] - rox1[last_room]);
break;
}
while (pos != jpos)
{
if ( coinflip() )
{
if ( pos.x < jpos.x )
pos.x++;
else if ( pos.x > jpos.x )
pos.x--;
}
else
{
if ( pos.y < jpos.y )
pos.y++;
else if ( pos.y > jpos.y )
pos.y--;
}
if (grd(pos) == DNGN_ROCK_WALL)
grd(pos) = DNGN_FLOOR;
}
if (grd(pos) == DNGN_FLOOR)
{
if ((grd[pos.x + 1][pos.y] == DNGN_ROCK_WALL
&& grd[pos.x - 1][pos.y] == DNGN_ROCK_WALL)
|| (grd[pos.x][pos.y + 1] == DNGN_ROCK_WALL
&& grd[pos.x][pos.y - 1] == DNGN_ROCK_WALL))
{
grd(pos) = DNGN_GRANITE_STATUE;
}
}
}
const map_def *sroom = NULL;
#ifdef DEBUG_SPECIAL_ROOMS
if ((sroom = random_map_for_tag("special_room", true)) != NULL)
#else
if (one_chance_in(10)
&& (sroom = random_map_for_tag("special_room", true)) != NULL)
#endif
{
int spec_room_done = random2(25);
sr.created = true;
sr.hooked_up = true;
sr.tl.set( rox1[spec_room_done], roy1[spec_room_done] );
sr.br.set( rox2[spec_room_done], roy2[spec_room_done] );
_special_room( level_number, sr, sroom );
_replace_area(sr.tl.x-1, sr.tl.y-1, sr.br.x+1,sr.tl.y-1,
DNGN_ROCK_WALL, DNGN_BUILDER_SPECIAL_WALL);
_replace_area(sr.tl.x-1, sr.tl.y-1, sr.br.x+1,sr.tl.y-1,
DNGN_FLOOR, DNGN_BUILDER_SPECIAL_FLOOR);
_replace_area(sr.tl.x-1, sr.tl.y-1, sr.br.x+1,sr.tl.y-1,
DNGN_CLOSED_DOOR, DNGN_BUILDER_SPECIAL_FLOOR);
_replace_area(sr.tl.x-1, sr.br.y+1, sr.br.x+1,sr.br.y+1,
DNGN_ROCK_WALL, DNGN_BUILDER_SPECIAL_WALL);
_replace_area(sr.tl.x-1, sr.br.y+1, sr.br.x+1,sr.br.y+1,
DNGN_FLOOR, DNGN_BUILDER_SPECIAL_FLOOR);
_replace_area(sr.tl.x-1, sr.br.y+1, sr.br.x+1,sr.br.y+1,
DNGN_CLOSED_DOOR, DNGN_BUILDER_SPECIAL_FLOOR);
_replace_area(sr.tl.x-1, sr.tl.y-1, sr.tl.x-1, sr.br.y+1,
DNGN_ROCK_WALL, DNGN_BUILDER_SPECIAL_WALL);
_replace_area(sr.tl.x-1, sr.tl.y-1, sr.tl.x-1, sr.br.y+1,
DNGN_FLOOR, DNGN_BUILDER_SPECIAL_FLOOR);
_replace_area(sr.tl.x-1, sr.tl.y-1, sr.tl.x-1, sr.br.y+1,
DNGN_CLOSED_DOOR, DNGN_BUILDER_SPECIAL_FLOOR);
_replace_area(sr.br.x+1, sr.tl.y-1, sr.br.x+1, sr.br.y+1,
DNGN_ROCK_WALL, DNGN_BUILDER_SPECIAL_WALL);
_replace_area(sr.br.x+1, sr.tl.y-1, sr.br.x+1, sr.br.y+1,
DNGN_FLOOR, DNGN_BUILDER_SPECIAL_FLOOR);
_replace_area(sr.br.x+1, sr.tl.y-1, sr.br.x+1, sr.br.y+1,
DNGN_CLOSED_DOOR, DNGN_BUILDER_SPECIAL_FLOOR);
}
if (!make_stairs)
return;
int stair_count = coinflip() ? 4 : 3;
for (int j = 0; j < stair_count; j++)
for (i = 0; i < 2; i++)
{
_place_specific_stair( static_cast<dungeon_feature_type>(
j + ((i == 0) ? DNGN_STONE_STAIRS_DOWN_I
: DNGN_STONE_STAIRS_UP_I)) );
}
}
bool place_specific_trap(const coord_def& where, trap_type spec_type)
{
if (spec_type == TRAP_RANDOM || spec_type == TRAP_NONTELEPORT
|| spec_type == TRAP_SHAFT && !is_valid_shaft_level())
{
trap_type forbidden1 = NUM_TRAPS;
trap_type forbidden2 = NUM_TRAPS;
if (spec_type == TRAP_NONTELEPORT)
{
forbidden1 = TRAP_SHAFT;
forbidden2 = TRAP_TELEPORT;
}
else if (!is_valid_shaft_level())
forbidden1 = TRAP_SHAFT;
do
spec_type = static_cast<trap_type>( random2(NUM_TRAPS) );
while (spec_type == forbidden1 || spec_type == forbidden2);
}
for (int tcount = 0; tcount < MAX_TRAPS; tcount++)
if (env.trap[tcount].type == TRAP_UNASSIGNED)
{
env.trap[tcount].type = spec_type;
env.trap[tcount].pos = where;
grd(where) = DNGN_UNDISCOVERED_TRAP;
env.trap[tcount].prepare_ammo();
return (true);
}
return (false);
}
static void _build_river( dungeon_feature_type river_type ) {
int i,j;
int y, width;
if (player_in_branch( BRANCH_CRYPT ) || player_in_branch( BRANCH_TOMB ))
return;
std::string extra = make_stringf("river [%d]", (int) river_type);
env.properties[LEVEL_EXTRAS_KEY].get_vector().push_back(extra);
width = 3 + random2(4);
y = 10 - width + random2avg( GYM-10, 3 );
for (i = 5; i < (GXM - 5); i++)
{
if (one_chance_in(3)) y++;
if (one_chance_in(3)) y--;
if (coinflip()) width++;
if (coinflip()) width--;
if (width < 2) width = 2;
if (width > 6) width = 6;
for (j = y; j < y+width ; j++)
if (j >= 5 && j <= GYM - 5)
{
if (!one_chance_in(200) && _may_overwrite_pos(i, j))
{
if (width == 2 && river_type == DNGN_DEEP_WATER
&& coinflip())
{
grd[i][j] = DNGN_SHALLOW_WATER;
}
else
grd[i][j] = river_type;
env.markers.remove_markers_at(coord_def(i, j), MAT_ANY);
}
}
}
}
static void _build_lake(dungeon_feature_type lake_type) {
int i, j;
int x1, y1, x2, y2;
if (player_in_branch(BRANCH_CRYPT) || player_in_branch(BRANCH_TOMB))
return;
std::string extra = make_stringf("lake [%d]", (int) lake_type);
env.properties[LEVEL_EXTRAS_KEY].get_vector().push_back(extra);
x1 = 5 + random2(GXM - 30);
y1 = 5 + random2(GYM - 30);
x2 = x1 + 4 + random2(16);
y2 = y1 + 8 + random2(12);
for (j = y1; j < y2; j++)
{
if (coinflip()) x1 += random2(3);
if (coinflip()) x1 -= random2(3);
if (coinflip()) x2 += random2(3);
if (coinflip()) x2 -= random2(3);
if (j - y1 < (y2 - y1) / 2)
{
x2 += random2(3);
x1 -= random2(3);
}
else
{
x2 -= random2(3);
x1 += random2(3);
}
for (i = x1; i < x2 ; i++)
if (j >= 5 && j <= GYM - 5 && i >= 5 && i <= GXM - 5)
{
if (!one_chance_in(200) && _may_overwrite_pos(i,j))
{
grd[i][j] = lake_type;
env.markers.remove_markers_at(coord_def(i, j), MAT_ANY);
}
}
}
}
struct nearest_point
{
coord_def target;
coord_def nearest;
int distance;
nearest_point(const coord_def &t) : target(t), nearest(), distance(-1)
{
}
void operator () (const coord_def &c)
{
if (grd(c) == DNGN_FLOOR)
{
const int ndist = (c - target).abs();
if (distance == -1 || ndist < distance)
{
distance = ndist;
nearest = c;
}
}
}
};
inline static bool _dgn_square_travel_ok(const coord_def &c)
{
const dungeon_feature_type feat = grd(c);
return (feat_is_traversable(feat) || feat_is_trap(feat)
|| feat == DNGN_SECRET_DOOR);
}
static coord_def _dgn_find_closest_to_stone_stairs(coord_def base_pos)
{
memset(travel_point_distance, 0, sizeof(travel_distance_grid_t));
init_travel_terrain_check(false);
nearest_point np(base_pos);
for (int y = 0; y < GYM; ++y)
for (int x = 0; x < GXM; ++x)
{
if (!travel_point_distance[x][y] && feat_is_stone_stair(grd[x][y]))
_dgn_fill_zone(coord_def(x, y), 1, np, _dgn_square_travel_ok);
}
return (np.nearest);
}
coord_def dgn_find_feature_marker(dungeon_feature_type feat)
{
std::vector<map_marker*> markers = env.markers.get_all();
for (int i = 0, size = markers.size(); i < size; ++i)
{
map_marker *mark = markers[i];
if (mark->get_type() == MAT_FEATURE
&& dynamic_cast<map_feature_marker*>(mark)->feat == feat)
{
return (mark->pos);
}
}
coord_def unfound;
return (unfound);
}
static coord_def _dgn_find_labyrinth_entry_point()
{
return (dgn_find_feature_marker(DNGN_ENTER_LABYRINTH));
}
coord_def dgn_find_nearby_stair(dungeon_feature_type stair_to_find,
coord_def base_pos, bool find_closest)
{
#ifdef DEBUG_DIAGNOSTICS
mprf(MSGCH_DIAGNOSTICS,
"Level entry point on %sstair: %d (%s)",
find_closest? "closest " : "",
stair_to_find,
dungeon_feature_name(stair_to_find));
#endif
if (stair_to_find == DNGN_EXIT_PORTAL_VAULT)
{
const coord_def pos(dgn_find_feature_marker(stair_to_find));
if (in_bounds(pos))
{
if (map_marker *marker = env.markers.find(pos, MAT_FEATURE))
env.markers.remove(marker);
return (pos);
}
#ifdef DEBUG_DIAGNOSTICS
mprf(MSGCH_WARN, "Ouch, no portal vault exit point!");
#endif
stair_to_find = DNGN_FLOOR;
}
if (stair_to_find == DNGN_ESCAPE_HATCH_UP
|| stair_to_find == DNGN_ESCAPE_HATCH_DOWN)
{
const coord_def pos(_dgn_find_closest_to_stone_stairs(base_pos));
if (in_bounds(pos))
return (pos);
}
if (stair_to_find == DNGN_STONE_ARCH)
{
const coord_def pos(dgn_find_feature_marker(stair_to_find));
if (in_bounds(pos) && grd(pos) == stair_to_find)
return (pos);
}
if (stair_to_find == DNGN_ENTER_LABYRINTH)
{
const coord_def pos(_dgn_find_labyrinth_entry_point());
if (in_bounds(pos))
return (pos);
#ifdef DEBUG_DIAGNOSTICS
mpr("Oops, couldn't find labyrinth entry marker.", MSGCH_DIAGNOSTICS);
#endif
stair_to_find = DNGN_FLOOR;
}
if (stair_to_find == your_branch().exit_stairs)
{
const coord_def pos(dgn_find_feature_marker(DNGN_STONE_STAIRS_UP_I));
if (in_bounds(pos) && grd(pos) == stair_to_find)
return (pos);
}
int basex = base_pos.x;
int basey = base_pos.y;
if (!in_bounds(basex, basey))
{
basex = 0;
basey = 0;
}
coord_def result;
int found = 0;
int best_dist = 1 + GXM*GXM + GYM*GYM;
for (int xcode = 0; xcode < GXM; ++xcode )
{
const int xsign = ((xcode % 2) ? 1 : -1);
const int xdiff = xsign * (xcode + 1)/2;
const int xpos = (basex + xdiff + GXM) % GXM;
for (int ycode = 0; ycode < GYM; ++ycode)
{
const int ysign = ((ycode % 2) ? 1 : -1);
const int ydiff = ysign * (ycode + 1)/2;
const int ypos = (basey + ydiff + GYM) % GYM;
const int dist = (xpos-basex)*(xpos-basex)
+ (ypos-basey)*(ypos-basey);
if (grd[xpos][ypos] == stair_to_find)
{
found++;
if (find_closest)
{
if (dist < best_dist)
{
best_dist = dist;
result.x = xpos;
result.y = ypos;
}
}
else if (one_chance_in( found ))
{
result.x = xpos;
result.y = ypos;
}
}
}
}
if (found)
return result;
best_dist = 1 + GXM*GXM + GYM*GYM;
for (int xcode = 0; xcode < GXM; ++xcode )
{
const int xsign = ((xcode % 2) ? 1 : -1);
const int xdiff = xsign * (xcode + 1)/2;
const int xpos = (basex + xdiff + GXM) % GXM;
for (int ycode = 0; ycode < GYM; ++ycode)
{
const int ysign = ((ycode % 2) ? 1 : -1);
const int ydiff = ysign * (ycode + 1)/2;
const int ypos = (basey + ydiff + GYM) % GYM;
bool good_stair;
const int looking_at = grd[xpos][ypos];
if (stair_to_find <= DNGN_ESCAPE_HATCH_DOWN )
{
good_stair = (looking_at >= DNGN_STONE_STAIRS_DOWN_I
&& looking_at <= DNGN_ESCAPE_HATCH_DOWN);
}
else
{
good_stair = (looking_at >= DNGN_STONE_STAIRS_UP_I
&& looking_at <= DNGN_ESCAPE_HATCH_UP);
}
const int dist = (xpos-basex)*(xpos-basex)
+ (ypos-basey)*(ypos-basey);
if (good_stair)
{
found++;
if (find_closest && dist < best_dist)
{
best_dist = dist;
result.x = xpos;
result.y = ypos;
}
else if (one_chance_in( found ))
{
result.x = xpos;
result.y = ypos;
}
}
}
}
if (found)
return result;
for (int xpos = 0; xpos < GXM; xpos++)
for (int ypos = 0; ypos < GYM; ypos++)
{
if (grd[xpos][ypos] >= DNGN_FLOOR
&& (you.level_type == LEVEL_DUNGEON
|| unforbidden(coord_def(xpos, ypos), MMT_VAULT)))
{
found++;
if (one_chance_in( found ))
{
result.x = xpos;
result.y = ypos;
}
}
}
const coord_def pos(dgn_find_feature_marker(stair_to_find));
if (in_bounds(pos))
return (pos);
if (you.level_type == LEVEL_PORTAL_VAULT
&& stair_to_find != DNGN_STONE_ARCH)
{
return dgn_find_nearby_stair(DNGN_STONE_ARCH, base_pos, find_closest);
}
ASSERT( found );
return result;
}
void dgn_set_lt_callback(std::string level_type_tag,
std::string callback_name)
{
ASSERT(!level_type_tag.empty());
ASSERT(!callback_name.empty());
level_type_post_callbacks[level_type_tag] = callback_name;
}
bool dgn_region::overlaps(const dgn_region &other) const
{
return (between(pos.x, other.pos.x, other.pos.x + other.size.x - 1)
|| between(pos.x + size.x - 1, other.pos.x,
other.pos.x + other.size.x - 1))
&& (between(pos.y, other.pos.y, other.pos.y + other.size.y - 1)
|| between(pos.y + size.y - 1, other.pos.y,
other.pos.y + other.size.y - 1));
}
bool dgn_region::overlaps_any(const dgn_region_list ®ions) const
{
for (dgn_region_list::const_iterator i = regions.begin();
i != regions.end(); ++i)
{
if (overlaps(*i))
return (true);
}
return (false);
}
bool dgn_region::overlaps(const dgn_region_list ®ions,
const map_mask &mask) const
{
return overlaps_any(regions) && overlaps(mask);
}
bool dgn_region::overlaps(const map_mask &mask) const
{
const coord_def endp = pos + size;
for (int y = pos.y; y < endp.y; ++y)
for (int x = pos.x; x < endp.x; ++x)
{
if (mask[x][y])
return (true);
}
return (false);
}
coord_def dgn_region::random_edge_point() const
{
return x_chance_in_y(size.x, size.x + size.y) ?
coord_def( pos.x + random2(size.x),
coinflip()? pos.y : pos.y + size.y - 1 )
: coord_def( coinflip()? pos.x : pos.x + size.x - 1,
pos.y + random2(size.y) );
}
coord_def dgn_region::random_point() const
{
return coord_def( pos.x + random2(size.x), pos.y + random2(size.y) );
}
struct StairConnectivity
{
StairConnectivity()
{
region[0] = region[1] = region[2] = 0;
connected[0] = connected[1] = connected[2] = true;
}
void connect_region(int idx)
{
for (int i = 0; i < 3; i++)
connected[i] |= (region[i] == idx);
}
void read(reader &th)
{
region[0] = unmarshallByte(th);
region[1] = unmarshallByte(th);
region[2] = unmarshallByte(th);
connected[0] = unmarshallByte(th);
connected[1] = unmarshallByte(th);
connected[2] = unmarshallByte(th);
}
void write(writer &th)
{
marshallByte(th, region[0]);
marshallByte(th, region[1]);
marshallByte(th, region[2]);
marshallByte(th, connected[0]);
marshallByte(th, connected[1]);
marshallByte(th, connected[2]);
}
char region[3];
bool connected[3];
};
FixedVector<std::vector<StairConnectivity>, NUM_BRANCHES> connectivity;
void init_level_connectivity()
{
for (int i = 0; i < NUM_BRANCHES; i++)
{
int depth = branches[i].depth > 0 ? branches[i].depth : 0;
connectivity[i].resize(depth);
}
}
void read_level_connectivity(reader &th)
{
for (int i = 0; i < NUM_BRANCHES; i++)
{
unsigned int depth = branches[i].depth > 0 ? branches[i].depth : 0;
unsigned int num_entries = unmarshallLong(th);
connectivity[i].resize(std::max(depth, num_entries));
for (unsigned int e = 0; e < num_entries; e++)
connectivity[i][e].read(th);
}
}
void write_level_connectivity(writer &th)
{
for (int i = 0; i < NUM_BRANCHES; i++)
{
marshallLong(th, connectivity[i].size());
for (unsigned int e = 0; e < connectivity[i].size(); e++)
connectivity[i][e].write(th);
}
}
static bool _fixup_interlevel_connectivity()
{
if (you.level_type != LEVEL_DUNGEON || your_branch().depth == -1)
return (true);
if (branches[you.where_are_you].branch_flags & BFLAG_ISLANDED)
return (true);
StairConnectivity full;
StairConnectivity &prev_con = (player_branch_depth() == 1) ? full :
(connectivity[your_branch().id][player_branch_depth() - 2]);
StairConnectivity &this_con =
(connectivity[your_branch().id][player_branch_depth() - 1]);
FixedVector<coord_def, 3> up_gc;
FixedVector<coord_def, 3> down_gc;
FixedVector<int, 3> up_region;
FixedVector<int, 3> down_region;
FixedVector<bool, 3> has_down;
std::vector<bool> region_connected;
up_region[0] = up_region[1] = up_region[2] = -1;
down_region[0] = down_region[1] = down_region[2] = -1;
has_down[0] = has_down[1] = has_down[2] = false;
memset(travel_point_distance, 0, sizeof(travel_distance_grid_t));
int nzones = 0;
for (int y = 0; y < GYM ; ++y)
for (int x = 0; x < GXM; ++x)
{
coord_def gc(x,y);
if (!map_bounds(x, y)
|| travel_point_distance[x][y]
|| !_is_passable_ignore_vault(gc))
{
continue;
}
_dgn_fill_zone(gc, ++nzones, _dgn_point_record_stub,
_is_passable_ignore_vault, NULL);
}
int max_region = 0;
for (int y = 0; y < GYM ; ++y)
for (int x = 0; x < GXM; ++x)
{
coord_def gc(x,y);
dungeon_feature_type feat = grd(gc);
switch (feat)
{
case DNGN_STONE_STAIRS_DOWN_I:
case DNGN_STONE_STAIRS_DOWN_II:
case DNGN_STONE_STAIRS_DOWN_III:
{
int idx = feat - DNGN_STONE_STAIRS_DOWN_I;
if (down_region[idx] == -1)
{
down_region[idx] = travel_point_distance[x][y];
down_gc[idx] = gc;
max_region = std::max(down_region[idx], max_region);
}
else
{
return (false);
}
break;
}
case DNGN_STONE_STAIRS_UP_I:
case DNGN_STONE_STAIRS_UP_II:
case DNGN_STONE_STAIRS_UP_III:
{
int idx = feat - DNGN_STONE_STAIRS_UP_I;
if (up_region[idx] == -1)
{
up_region[idx] = travel_point_distance[x][y];
up_gc[idx] = gc;
max_region = std::max(up_region[idx], max_region);
}
else
{
return (false);
}
break;
}
default:
break;
}
}
for (int i = 0; i < 3; i++)
if (up_region[i] == -1)
return (false);
region_connected.resize(max_region + 1);
for (unsigned int i = 0; i < region_connected.size(); i++)
region_connected[i] = false;
if (!at_branch_bottom())
{
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
{
if (down_region[j] == up_region[i])
has_down[i] = true;
}
}
bool any_connected = has_down[0] || has_down[1] || has_down[2];
if (!any_connected && !at_branch_bottom())
return (false);
int assign_prev[3] = {-1, -1, -1};
int assign_cur[3] = {-1, -1, -1};
bool minimal_connectivity = false;
for (int i = 0; i < 3 && !minimal_connectivity; i++)
{
if (!prev_con.connected[i])
continue;
for (int j = 0; j < 3; j++)
{
if (!has_down[j] && !at_branch_bottom())
continue;
minimal_connectivity = true;
assign_prev[i] = j;
assign_cur[j] = i;
region_connected[up_region[j]] = true;
break;
}
}
if (!minimal_connectivity)
return (false);
for (int i = 0; i < 3; i++)
{
if (assign_prev[i] != -1 || prev_con.connected[i])
continue;
bool unique_region = true;
for (int j = 0; j < i; j++)
{
if (prev_con.region[j] == prev_con.region[i])
unique_region = false;
}
if (!unique_region)
continue;
for (int j = 0; j < 3; j++)
{
if (assign_cur[j] != -1 || !region_connected[up_region[j]])
continue;
assign_prev[i] = j;
assign_cur[j] = i;
prev_con.connect_region(prev_con.region[i]);
break;
}
if (assign_prev[i] != -1)
continue;
for (int j = 0; j < 3; j++)
{
if (assign_cur[j] != -1 || !has_down[j])
continue;
assign_prev[i] = j;
assign_cur[j] = i;
break;
}
}
for (int i = 0; i < 3; i++)
{
if (!prev_con.connected[i] || assign_prev[i] != -1)
continue;
for (int j = 0; j < 3; j++)
{
if (has_down[j] || assign_cur[j] != -1)
continue;
if (region_connected[up_region[j]])
continue;
assign_prev[i] = j;
assign_cur[j] = i;
region_connected[up_region[j]] = true;
break;
}
}
for (int i = 0; i < 3; i++)
{
if (assign_prev[i] != -1)
continue;
for (int j = 0; j < 3; j++)
{
if (assign_cur[j] != -1)
continue;
assign_prev[i] = j;
assign_cur[j] = i;
if (region_connected[up_region[j]])
prev_con.connect_region(prev_con.region[i]);
else if (prev_con.connected[i])
region_connected[up_region[j]] = true;
break;
}
}
if (at_branch_bottom())
{
for (int i = 0; i < 3; i++)
if (!region_connected[up_region[i]])
return (false);
}
bool stairs_unique = (assign_cur[0] != assign_cur[1]
&& assign_cur[1] != assign_cur[2]);
ASSERT(stairs_unique);
if (!stairs_unique)
return (false);
for (int i = 0; i < 3; i++)
{
grd(up_gc[i]) =
(dungeon_feature_type)(DNGN_STONE_STAIRS_UP_I + assign_cur[i]);
}
for (int i = 0; i < 3; i++)
{
this_con.region[i] = down_region[i];
if (down_region[i] != -1)
this_con.connected[i] = region_connected[down_region[i]];
else
this_con.connected[i] = false;
}
return (true);
}
void vault_placement::reset()
{
altar_count = 0;
}
void vault_placement::apply_grid()
{
if (!size.zero())
{
for (rectangle_iterator ri(pos, pos + size - 1); ri; ++ri)
{
const coord_def &rp(*ri);
const coord_def dp = rp - pos;
const int feat = map.map.glyph(dp);
if (feat == ' ')
continue;
const dungeon_feature_type oldgrid = grd(*ri);
_vault_grid( *this, feat, *ri );
if (!Generating_Level)
{
link_items();
const dungeon_feature_type newgrid = grd(*ri);
grd(*ri) = oldgrid;
dungeon_terrain_changed(*ri, newgrid, true, true);
env.markers.remove_markers_at(*ri, MAT_ANY);
}
env.map(*ri).property |= FPROP_VAULT;
}
map.map.apply_overlays(pos);
}
}
void vault_placement::draw_at(const coord_def &c)
{
pos = c;
apply_grid();
}
void remember_vault_placement(std::string key, vault_placement &place)
{
CrawlHashTable &table = env.properties[key].get_table();
std::string name = make_stringf("%s [%d]", place.map.name.c_str(),
table.size() + 1);
std::string place_str
= make_stringf("(%d,%d) (%d,%d) orient: %d lev: %d alt: %d rune: %d "
"subst: %d",
place.pos.x, place.pos.y, place.size.x, place.size.y,
place.orient, place.level_number, place.altar_count,
place.num_runes, place.rune_subst);
table[name] = place_str;
if (you.level_type == LEVEL_DUNGEON
&& !place.map.has_tag("layout")
&& !place.map.has_tag_suffix("dummy")
&& !place.map.has_tag("no_dump"))
{
const std::string type = place.map.has_tag("extra")
? "extra" : "normal";
_you_vault_list[type].get_vector().push_back(place.map.name);
}
else if (you.level_type == LEVEL_PORTAL_VAULT
&& place.map.orient == MAP_ENCOMPASS
&& !place.map.has_tag("no_dump"))
{
_portal_vault_map_name = place.map.name;
}
}
std::string dump_vault_maps()
{
std::string out = "";
std::vector<level_id> levels = all_dungeon_ids();
CrawlHashTable &vaults = you.props[YOU_DUNGEON_VAULTS_KEY].get_table();
for (unsigned int i = 0; i < levels.size(); i++)
{
level_id &lid = levels[i];
std::string lev = lid.describe();
if (!vaults.exists(lev))
continue;
out += lid.describe() + ":\n";
CrawlHashTable &lists = vaults[lev].get_table();
const char *types[] = {"normal", "extra"};
for (int j = 0; j < 2; j++)
{
if (!lists.exists(types[j]))
continue;
out += " ";
out += types[j];
out += ": ";
CrawlVector &vec = lists[types[j]].get_vector();
for (unsigned int k = 0, size = vec.size(); k < size; k++)
{
out += vec[k].get_string();
if (k < (size - 1))
out += ", ";
}
out += "\n";
}
out += "\n";
}
CrawlVector &portals = you.props[YOU_PORTAL_VAULT_MAPS_KEY].get_vector();
if (!portals.empty())
{
out += "\n";
out += "Portal vault maps: ";
for (unsigned int i = 0, size = portals.size(); i < size; i++)
{
out += portals[i].get_string();
if (i < (size - 1))
out += ", ";
}
out += "\n\n";
}
return (out);
}