summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--crawl-ref/source/dat/clua/loadmaps.lua5
-rw-r--r--crawl-ref/source/dat/layout.des303
-rw-r--r--crawl-ref/source/dungeon.cc64
-rw-r--r--crawl-ref/source/maps.cc2
4 files changed, 327 insertions, 47 deletions
diff --git a/crawl-ref/source/dat/clua/loadmaps.lua b/crawl-ref/source/dat/clua/loadmaps.lua
index c462002101..ae1bb54600 100644
--- a/crawl-ref/source/dat/clua/loadmaps.lua
+++ b/crawl-ref/source/dat/clua/loadmaps.lua
@@ -8,8 +8,9 @@
local des_files = {
"altar.des", "bazaar.des", "entry.des", "elf.des", "float.des", "hells.des",
- "hive.des", "lab.des", "lair.des", "large.des", "mini.des", "orc.des",
- "pan.des", "portal.des", "temple.des", "vaults.des", "crypt.des", "zot.des"
+ "hive.des", "lab.des", "lair.des", "large.des", "layout.des", "mini.des",
+ "orc.des", "pan.des", "portal.des", "temple.des", "vaults.des", "crypt.des",
+ "zot.des"
}
for _, file in ipairs(des_files) do
diff --git a/crawl-ref/source/dat/layout.des b/crawl-ref/source/dat/layout.des
new file mode 100644
index 0000000000..0727074e79
--- /dev/null
+++ b/crawl-ref/source/dat/layout.des
@@ -0,0 +1,303 @@
+###############################################################################
+# layout.des: All large layout vaults go here. These are defined by having
+# both ORIENT: encompass and TAGS: layout. These are not true
+# vaults, in that the dungeon builder can add other vaults on
+# top of them.
+#
+###############################################################################
+
+lua {{
+ function fill_area(x1, y1, x2, y2, feature)
+ for x = x1, x2 do
+ for y = y1, y2 do
+ dgn.grid(x, y, feature)
+ end
+ end
+ end
+
+ function octa_room(x1, y1, x2, y2, oblique, floor)
+ local ob_temp = oblique
+
+ local rock_wall = dgn.feature_number("rock_wall")
+ local shallow_water = dgn.feature_number("shallow_water")
+
+ for x = x1, x2 do
+ for y = y1 + ob_temp, y2 - ob_temp do
+ if dgn.grid(x, y) == rock_wall then
+ dgn.grid(x, y, floor)
+ end
+ if dgn.grid(x, y) == floor and floor == shallow_water then
+ dgn.grid(x, y, shallow_water)
+ end
+ end
+
+ if x > x2 - oblique then
+ ob_temp = ob_temp + 1
+ elseif ob_temp > 0 then
+ ob_temp = ob_temp - 1
+ end
+ end
+ end
+
+ function pillars(center_x, center_y, num, flip_x, big_radius,
+ pillar_radius, pillar_routine, fill)
+ for i = 0, num - 1 do
+ local angle = i * 2 * math.pi / num
+ local x = math.floor(math.cos(angle) * big_radius * flip_x + 0.5)
+ local y = math.floor(math.sin(angle) * big_radius + 0.5)
+
+ pillar_routine(center_x + x, center_y + y, pillar_radius, fill)
+ end
+ end
+
+ -- begin pillar functions
+ function pillar_square(center_x, center_y, radius, fill)
+ for x = -radius, radius do
+ for y = -radius, radius do
+ dgn.grid(center_x + x, center_y + y, fill)
+ end
+ end
+ end
+
+ function pillar_rounded_square(center_x, center_y, radius, fill)
+ for x = -radius, radius do
+ for y = -radius, radius do
+ if math.abs(x) ~= radius or math.abs(y) ~= radius then
+ dgn.grid(center_x + x, center_y + y, fill)
+ end
+ end
+ end
+ end
+
+ function pillar_circle(center_x, center_y, radius, fill)
+ for x = -radius, radius do
+ for y = -radius, radius do
+ if x * x + y * y < radius * radius then
+ dgn.grid(center_x + x, center_y + y, fill)
+ end
+ end
+ end
+ end
+ -- end pillar functions
+
+ function select_from_weighted_table(tb)
+ local value = nil
+ local rollsize = 0
+ for temp_val, temp_weight in pairs (tb) do
+ if rollsize == 0 then
+ value = temp_val
+ rollsize = temp_weight
+ else
+ rollsize = rollsize + temp_weight
+ if crawl.random2(rollsize) < temp_weight then
+ value = temp_val
+ end
+ end
+ end
+
+ return value
+ end
+
+ -- Randomly selects a grid with feature 'search' and sets it to 'replace'
+ function replace_random(search, replace)
+ local gxm, gym = dgn.max_bounds()
+
+ local feature = nil, x, y
+ while feature ~= search do
+ x = crawl.random2(gxm)
+ y = crawl.random2(gym)
+ feature = dgn.grid(x, y)
+ end
+
+ dgn.grid(x, y, replace)
+ end
+
+ function valid(x, y)
+ local gxm, gym = dgn.max_bounds()
+ return (x >= 0 and y >= 0 and x < gxm and y < gym)
+ end
+
+ -- Walks from x, y along dx,dy until it finds 'search'
+ function replace_first(x, y, dx, dy, search, replace)
+
+ local feature = dgn.grid(x, y)
+
+ while feature ~= search and valid(x,y) do
+ x = x + dx
+ y = y + dy
+ feature = dgn.grid(x, y)
+ end
+
+ if valid(x, y) then
+ dgn.grid(x, y, replace)
+ return true
+ else
+ return false
+ end
+ end
+}}
+
+##############################################################
+# layout_cross
+#
+# This replaces dungeon.cc:_plan_2(). It creates a large cross
+# of varying width.
+#
+NAME: layout_cross
+ORIENT: encompass
+TAGS: layout allow_dup
+
+{{
+ local width = 5 - crawl.random2(5)
+ local height = 5 - crawl.random2(5)
+
+ local gxm, gym = dgn.max_bounds()
+
+ local wall = dgn.feature_number("rock_wall")
+ local floor = dgn.feature_number("floor")
+
+ -- Include a small possibility of adding windows around the cross.
+ -- This layout can get used with spotty_level, so don't make this
+ -- chance too large as lava/water prevents that from happening.
+ local window = crawl.one_chance_in(20)
+
+ if window or crawl.one_chance_in(20) then
+ if crawl.coinflip() then
+ wall = dgn.feature_number("lava")
+ else
+ wall = dgn.feature_number("deep_water")
+ end
+ end
+
+ -- fill with rock
+ fill_area(0, 0, gxm-1, gym-1, wall)
+
+ -- create window
+ if window then
+ local clear = dgn.feature_number("clear_rock_wall")
+ fill_area(10, gym/2 - height - 1, gxm - 10, gym/2 - height - 1, clear)
+ fill_area(10, gym/2 + height + 1, gxm - 10, gym/2 + height + 1, clear)
+ fill_area(gxm/2 - width - 1, 10, gxm/2 - width - 1, gym - 10, clear)
+ fill_area(gxm/2 + width + 1, 10, gxm/2 + width + 1, gym - 10, clear)
+ end
+
+ -- create a cross
+ fill_area(10, gym/2 - height, gxm - 10, gym/2 + height, floor)
+ fill_area(gxm/2 - width, 10, gxm/2 + width, gym - 10, floor)
+
+ -- TODO enne - add lua function for spotty()
+}}
+MAP
+.
+ENDMAP
+
+##############################################################
+# layout_big_octagon
+#
+# This replaces dungeon.cc:_plan_6(). It has an octagonal
+# room with some number of pillars in the middle. The stairs
+# are generally all grouped together.
+#
+
+NAME: layout_big_octagon
+ORIENT: encompass
+TAGS: layout allow_dup
+
+{{
+ local gxm, gym = dgn.max_bounds()
+ local wall = dgn.feature_number("rock_wall")
+ local floor = dgn.feature_number("floor")
+
+ local oblique = 10 + crawl.random2(20)
+
+ -- Step 1: Create octagon
+ fill_area(0, 0, gxm - 1, gym - 1, wall)
+ octa_room(10, 10, gxm - 10, gym - 10, oblique, floor)
+
+ -- Step 2: Add pillars
+
+ -- pillar types and relative weights
+ local pillar_fill = {
+ ["rock_wall"] = 15,
+ ["green_crystal_wall"] = 5,
+ ["metal_wall"] = 4,
+ ["clear_rock_wall"] = 3,
+ ["deep_water"] = 2,
+ ["lava"] = 1,
+ }
+ local fill = dgn.feature_number(select_from_weighted_table(pillar_fill))
+
+ -- Potential pillar drawing routines
+ local pillar_func = {pillar_circle, pillar_square, pillar_rounded_square}
+
+ -- Pillar size params
+ -- NOTE: Be careful about tweaking the ranges here. Pillars that are
+ -- too large, close, or large in number can entirely surround the center.
+ local type = crawl.random2(#pillar_func) + 1
+ local num = 3 + crawl.random2(9)
+ local pillar_radius = 1 + crawl.random2(3)
+ local circle_radius = 2 + crawl.random2(6) + pillar_radius * 2 + num / 2
+
+ -- beautification hack: no "circle" pillars of radius 1
+ if type == 1 and pillar_radius == 1 then
+ fill = dgn.feature_number("stone_arch")
+ end
+
+ -- Finally, make the pillars
+ pillars(gxm/2, gym/2, num, 1, circle_radius, pillar_radius,
+ pillar_func[type], fill)
+
+ -- Step 3: Create stairs
+
+ -- Potential stair locations
+ -- 0) random
+ -- 1) inside
+ -- 2) up
+ -- 3) right
+ -- 4) down
+ -- 5) left
+
+ local up_loc = crawl.random2(6)
+ local down_loc = crawl.random2(6)
+ while up_loc == down_loc do
+ down_loc = crawl.random2(6)
+ end
+
+ local up_stairs = {
+ dgn.feature_number("stone_stairs_up_i"),
+ dgn.feature_number("stone_stairs_up_ii"),
+ dgn.feature_number("stone_stairs_up_iii"),
+ }
+ local down_stairs = {
+ dgn.feature_number("stone_stairs_down_i"),
+ dgn.feature_number("stone_stairs_down_ii"),
+ dgn.feature_number("stone_stairs_down_iii"),
+ }
+ local full_stair_set = {[up_loc] = up_stairs, [down_loc] = down_stairs}
+
+ for loc, stair_list in pairs (full_stair_set) do
+ for i = 1, #stair_list do
+ local st = stair_list[i]
+ local ret = true
+
+ if loc == 0 then
+ replace_random(floor, stair_list[i])
+ elseif loc == 1 then
+ dgn.grid(gxm/2 + i - 2, gym/2 + 1 - math.abs(i - 2), st)
+ elseif loc == 2 then
+ ret = replace_first(gxm/2 + i - 2, 0, 0, 1, floor, st)
+ elseif loc == 3 then
+ ret = replace_first(gxm - 1, gym/2 + i - 2, -1, 0, floor, st)
+ elseif loc == 4 then
+ ret = replace_first(gxm/2 + i - 2, gym - 1, 0, -1, floor, st)
+ elseif loc == 5 then
+ ret = replace_first(0, gym/2 + i - 2, 1, 0, floor, st)
+ end
+
+ assert(ret)
+ end
+ end
+}}
+MAP
+.
+ENDMAP
diff --git a/crawl-ref/source/dungeon.cc b/crawl-ref/source/dungeon.cc
index 443a62e419..0e74a08826 100644
--- a/crawl-ref/source/dungeon.cc
+++ b/crawl-ref/source/dungeon.cc
@@ -169,7 +169,7 @@ static void _spotty_level(bool seeded, int iterations, bool boxy);
static void _bigger_room();
static void _plan_main(int level_number, int force_plan);
static char _plan_1();
-static char _plan_2();
+static char _plan_2(int level_number);
static char _plan_3();
static char _plan_4(char forbid_x1, char forbid_y1, char forbid_x2,
char forbid_y2, dungeon_feature_type force_wall);
@@ -639,7 +639,9 @@ static void _register_place(const vault_placement &place)
{
_dgn_register_vault(place.map);
- _mask_vault(place, MMT_VAULT | MMT_NO_DOOR);
+ 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);
@@ -4273,9 +4275,11 @@ static bool _build_vaults(int level_number, int force_vault, int rune_subst,
mapgen_report_map_use(place.map);
#endif
+ bool is_layout = place.map.has_tag("layout");
+
// If the map takes the whole screen or we were only requested to
// build the vault, our work is done.
- if (gluggy == MAP_ENCOMPASS || build_only)
+ if (gluggy == MAP_ENCOMPASS && !is_layout || build_only)
return (true);
// Does this level require Dis treatment (metal wallification)?
@@ -4299,7 +4303,7 @@ static bool _build_vaults(int level_number, int force_vault, int rune_subst,
{
_plan_4(v1x, v1y, v2x, v2y, DNGN_METAL_WALL);
}
- else
+ else if (!is_layout)
{
dgn_region_list excluded_regions;
excluded_regions.push_back( dgn_region::absolute(v1x, v1y, v2x, v2y) );
@@ -5767,7 +5771,7 @@ static void _plan_main(int level_number, int force_plan)
force_plan = 1 + random2(12);
do_stairs = ((force_plan == 1) ? _plan_1() :
- (force_plan == 2) ? _plan_2() :
+ (force_plan == 2) ? _plan_2(level_number) :
(force_plan == 3) ? _plan_3() :
(force_plan == 4) ? _plan_4(0, 0, 0, 0, NUM_FEATURES) :
(force_plan == 5) ? (one_chance_in(9) ? _plan_5()
@@ -5864,19 +5868,17 @@ static char _plan_1()
return (one_chance_in(5) ? 3 : 2);
} // end plan_1()
-// just a cross:
-static char _plan_2()
+static char _plan_2(int level_number)
{
dgn_Build_Method = "plan_2";
- char width2 = (5 - random2(5)); // value range of [1,5] {dlb}
+ const int vault = random_map_for_tag("layout", false, true);
+ ASSERT(vault != -1);
- _replace_area(10, (35 - width2), (GXM - 10), (35 + width2),
- DNGN_ROCK_WALL, DNGN_FLOOR);
- _replace_area((40 - width2), 10, (40 + width2), (GYM - 10),
- DNGN_ROCK_WALL, DNGN_FLOOR);
+ bool success = _build_vaults(level_number, vault);
+ _ensure_vault_placed(success);
- return (one_chance_in(4) ? 2 : 3);
+ return (one_chance_in(4) ? 0 : 1);
} // end plan_2()
static char _plan_3()
@@ -6100,39 +6102,11 @@ static char _plan_6(int level_number)
{
dgn_Build_Method = "plan_6";
- spec_room sr;
+ const int vault = random_map_for_tag("layout", false, true);
+ ASSERT(vault != -1);
- // circle of standing stones (well, kind of)
- sr.x1 = 10;
- sr.x2 = (GXM - 10);
- sr.y1 = 10;
- sr.y2 = (GYM - 10);
-
- _octa_room(sr, 14, DNGN_FLOOR);
-
- _replace_area(23, 23, 26, 26, DNGN_FLOOR, DNGN_STONE_WALL);
- _replace_area(23, 47, 26, 50, DNGN_FLOOR, DNGN_STONE_WALL);
- _replace_area(55, 23, 58, 26, DNGN_FLOOR, DNGN_STONE_WALL);
- _replace_area(55, 47, 58, 50, DNGN_FLOOR, DNGN_STONE_WALL);
- _replace_area(39, 20, 43, 23, DNGN_FLOOR, DNGN_STONE_WALL);
- _replace_area(39, 50, 43, 53, DNGN_FLOOR, DNGN_STONE_WALL);
- _replace_area(20, 30, 23, 33, DNGN_FLOOR, DNGN_STONE_WALL);
- _replace_area(20, 40, 23, 43, DNGN_FLOOR, DNGN_STONE_WALL);
- _replace_area(58, 30, 61, 33, DNGN_FLOOR, DNGN_STONE_WALL);
- _replace_area(58, 40, 61, 43, DNGN_FLOOR, DNGN_STONE_WALL);
-
- grd[35][32] = DNGN_STONE_WALL;
- grd[46][32] = DNGN_STONE_WALL;
- grd[35][40] = DNGN_STONE_WALL;
- grd[46][40] = DNGN_STONE_WALL;
-
- grd[69][34] = DNGN_STONE_STAIRS_DOWN_I;
- grd[69][35] = DNGN_STONE_STAIRS_DOWN_II;
- grd[69][36] = DNGN_STONE_STAIRS_DOWN_III;
-
- grd[10][34] = DNGN_STONE_STAIRS_UP_I;
- grd[10][35] = DNGN_STONE_STAIRS_UP_II;
- grd[10][36] = DNGN_STONE_STAIRS_UP_III;
+ bool success = _build_vaults(level_number, vault);
+ _ensure_vault_placed(success);
// This "back door" is often one of the easier ways to get out of
// pandemonium... the easiest is to use the banish spell.
diff --git a/crawl-ref/source/maps.cc b/crawl-ref/source/maps.cc
index 665c8e22ad..044ae2a47f 100644
--- a/crawl-ref/source/maps.cc
+++ b/crawl-ref/source/maps.cc
@@ -361,6 +361,7 @@ int random_map_for_place(const level_id &place, bool want_minivault)
// We also accept tagged levels here.
if (vdefs[i].place == place
&& vdefs[i].is_minivault() == want_minivault
+ && !vdefs[i].has_tag("layout")
&& vault_unforbidden(vdefs[i]))
{
rollsize += vdefs[i].chance;
@@ -398,6 +399,7 @@ int random_map_in_depth(const level_id &place, bool want_minivault)
&& !vdefs[i].has_tag("pan")
&& !vdefs[i].has_tag("unrand")
&& !vdefs[i].has_tag("bazaar")
+ && !vdefs[i].has_tag("layout")
&& vault_unforbidden(vdefs[i]))
{
rollsize += vdefs[i].chance;