Anonymous | Login | Signup for a new account | 2019-02-17 20:00 CET |
Main | My View | View Issues | Change Log | Wiki | Tavern | News |
Viewing Issue Simple Details [ Jump to Notes ] [ Wiki ] | [ View Advanced ] [ Issue History ] [ Print ] | ||||||||||||
ID | Category | Severity | Reproducibility | Date Submitted | Last Update | ||||||||
0008917 | [DCSS] Patches | feature | N/A | 2014-08-31 23:49 | 2014-10-11 19:49 | ||||||||
Reporter | infiniplex | View Status | public | ||||||||||
Assigned To | gammafunk | ||||||||||||
Priority | normal | Resolution | done | ||||||||||
Status | resolved | Product Branch | 0.16 ancient branch | ||||||||||
Summary | 0008917: Added castle layout to Elf | ||||||||||||
Description |
2 patches are included: 1. 0001-Improved-LUA-primary_vault_dimensions-function.patch -> this patch modifies the primary_vault_dimensions LUA function to work correctly regardless of vault size -> Previously, the function only could find primary vaults that overlapped the current vault. This meant it did not work for layouts before calling extend_map. -> This patch assumes the first patch from 0008857 has been applied first. (Oops!) 2. 0002-Added-layout_geoelf_castle.patch -> This adds a new geoelf layout. It is very long (a bit leass than 1200 lines), so it gets its own file -> It also adds a bit more code to the geoelf generator. The new code is for making it combine with other layouts -> This patch depends on patch 1 and thus on the first patch from 0008857. Overview of layout_geoelf_castle: -> Only places on Elf:$ -> Builds a castle around the primary vault with walls, towers, and a moat -> Fills the rest of the map with geoelf rooms -> The castle is marked as a vault, so it will not be overwritten My own tests suggest that many of these layouts would look better if the Elf primary vaults were not padded to a square shape with "x"s. Is there a reason for this padding? If not, I will make a patch removing some of it. Comments and/or suggestions welcome, especially from doy (who requested an Elf castle). |
||||||||||||
Additional Information | |||||||||||||
Tags | No tags attached. | ||||||||||||
Attached Files |
![]() From 3350761f7063b1d842958b9a2717f59a5fe32be3 Mon Sep 17 00:00:00 2001 From: infiniplex <infiniplex@hotmail.com> Date: Sun, 31 Aug 2014 13:46:16 -0600 Subject: [PATCH 1/2] Improved LUA primary_vault_dimensions function --- crawl-ref/source/l_dgnbld.cc | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/crawl-ref/source/l_dgnbld.cc b/crawl-ref/source/l_dgnbld.cc index 7114ea4..93feca6 100644 --- a/crawl-ref/source/l_dgnbld.cc +++ b/crawl-ref/source/l_dgnbld.cc @@ -729,7 +729,9 @@ LUAFN(dgn_height) LUAFN(dgn_primary_vault_dimensions) { - LINES(ls, 1, lines); + // we don't need this because this function doesn't use the + // current map + // LINES(ls, 1, lines); static const int NO_PRIMARY_VAULT = 99999; @@ -738,8 +740,8 @@ LUAFN(dgn_primary_vault_dimensions) int y_min = NO_PRIMARY_VAULT; int y_max = -NO_PRIMARY_VAULT; - for (int y = 0; y < lines.height(); y++) - for (int x = 0; x < lines.width(); x++) + for (int y = 0; y < GYM; y++) + for (int x = 0; x < GXM; x++) { if (env.level_map_mask(coord_def(x,y)) & MMT_VAULT) { -- 1.8.1.2 ![]() From d65fdd2fdf7a3a82f4b335e2a5626818ff662410 Mon Sep 17 00:00:00 2001 From: infiniplex <infiniplex@hotmail.com> Date: Sun, 31 Aug 2014 15:04:47 -0600 Subject: [PATCH 2/2] Added layout_geoelf_castle --- crawl-ref/source/dat/des/builder/layout_geoelf.des | 5 +- .../dat/des/builder/layout_geoelf_castle.des | 1182 ++++++++++++++++++++ crawl-ref/source/dat/dlua/layout/geoelf.lua | 130 ++- .../source/dat/dlua/layout/geoelf_directions.lua | 33 +- 4 files changed, 1340 insertions(+), 10 deletions(-) create mode 100644 crawl-ref/source/dat/des/builder/layout_geoelf_castle.des diff --git a/crawl-ref/source/dat/des/builder/layout_geoelf.des b/crawl-ref/source/dat/des/builder/layout_geoelf.des index 274ea12..51310c7 100644 --- a/crawl-ref/source/dat/des/builder/layout_geoelf.des +++ b/crawl-ref/source/dat/des/builder/layout_geoelf.des @@ -2,6 +2,7 @@ # layout_geoelf.des: Layouts for Elf with geometric rooms # These are not the only Elf layouts # These layouts will build around primary vaults correctly. +# There is another geoelf layout in layout_geoelf_castle.des. ############################################################################### # Required file contains overview @@ -122,7 +123,7 @@ TAGS: no_rotate no_vmirror no_hmirror -- block this one (i.e. an NE-SW one) local block_index = nil if (rooms_by_position[y][x - 1] ~= nil) then - old_index = rooms_by_position[y][x - 1] + local old_index = rooms_by_position[y][x - 1] block_index = room_data[old_index].corridor[geoelf.directions.NE] end @@ -256,7 +257,7 @@ TAGS: no_rotate no_vmirror no_hmirror -- block this one (i.e. an E-W one) local block_index = nil if (x >= 1 and rooms_by_position[y - 1][x - 1] ~= nil) then - old_index = rooms_by_position[y - 1][x - 1] + local old_index = rooms_by_position[y - 1][x - 1] block_index = room_data[old_index].corridor[geoelf.directions.E] end diff --git a/crawl-ref/source/dat/des/builder/layout_geoelf_castle.des b/crawl-ref/source/dat/des/builder/layout_geoelf_castle.des new file mode 100644 index 0000000..a23fead --- /dev/null +++ b/crawl-ref/source/dat/des/builder/layout_geoelf_castle.des @@ -0,0 +1,1182 @@ +############################################################################### +# layout_geoelf_castle.des: A special geoelf layout for Elf:$ that builds a +# castle around the primary vault. +# There are other geoelf layouts in layout_geoelf.des. +############################################################################### + +# Required file contains overview of geoelf room placement +: require("dlua/layout/geoelf.lua") + +{{ + -- + -- Constants + -- + -- These have to be global so they can also be used in the + -- veto function. + -- + + -- size of castle + CASTLE_INNER = 4 + CASTLE_HALLWAY = 2 + CASTLE_MOAT = 2 + CASTLE_OPEN = 1 + CASTLE_OVERWRITABLE = 3 + CASTLE_TOTAL = CASTLE_INNER + CASTLE_HALLWAY + CASTLE_MOAT + + CASTLE_OPEN + CASTLE_OVERWRITABLE + CASTLE_TOWER_RADIUS = 3 + CASTLE_TOWER_MOAT_RADIUS = CASTLE_TOWER_RADIUS + CASTLE_MOAT + CASTLE_TOWER_OPEN_RADIUS = CASTLE_TOWER_MOAT_RADIUS + CASTLE_OPEN + CASTLE_TOWER_OVERWRITABLE_RADIUS = CASTLE_MOAT + CASTLE_OPEN + + CASTLE_OVERWRITABLE + 1 + + INNER_WALL_SPACING_MIN = 6 + INNER_WALL_SPACING_MAX = 12 + + GATE_FROM_CORNER_MIN = 8 + GATE_SIZE_MIN = 2 + GATE_SIZE_MAX = 3 + PRINMARY_VAULT_MIX_SIZE = GATE_FROM_CORNER_MIN + GATE_SIZE_MAX + - CASTLE_HALLWAY - CASTLE_INNER + + -- room parameters + ROOMS_OFFSET_MAX = 1 + ROOMS_RADIUS_MIN = 3 + ROOMS_RADIUS_MAX = 5 + ROOMS_RADIUS_EDGE_MIN = 2 + ROOMS_RADIUS_EDGE_MAX = 4 + ROOMS_EXTRA_CORRIDOR_FRACTION = 0.5 + ROOMS_FANCY_ROOM_FRACTION = 0.2 + + -- room placement + ROOMS_BORDER_MIN = ROOMS_OFFSET_MAX + ROOMS_RADIUS_MAX + 1 + ROOMS_FROM_CASTLE = ROOMS_OFFSET_MAX + ROOMS_RADIUS_MAX + 1 + ROOMS_SPACING_MIN = ROOMS_OFFSET_MAX + ROOMS_RADIUS_MAX * 2 + 2 + ROOMS_INTO_CASTLE = ROOMS_SPACING_MIN - ROOMS_FROM_CASTLE + ROOMS_MIN_SPACE = CASTLE_TOTAL + ROOMS_BORDER_MIN + ROOMS_FROM_CASTLE + + -- edge rows of rooms have no individual offset to allow + -- for maximum distance from grid + ROOMS_SPACING_EDGE = math.ceil(ROOMS_SPACING_MIN / 2) + 1 + ROOMS_MIN_SPACE_EDGE = ROOMS_SPACING_EDGE + ROOMS_RADIUS_EDGE_MAX + 2 + + DEBUG_CASTLE_DESIGN = false +}} + + + +############################################################## +# layout_geoelf_castle +# Idea by psyshvl and doy (probably independantly) +# Adapted and coded by infiniplex +# +# This layout builds a castle around a primary vault, and then +# fills up the extra space with geoelf rooms. It will NOT work +# without a primary vault of a suitable size. If there is no +# primary vault, or the primary vault is too big or too small, +# the layout will be vetoed. +# +# The shape of the castle is as follows (not to scale): +# +# ??????????''cc~~".. +# ??????????''cc~~".. ? - primary vault +# ??????????''cc~~".. +# ??????????''cc~~"". ' - inner rooms area +# '''''''''''***~~~". c - outer hallway +# ''''''''''*****~~". ~ - moat +# cccccccccc*****~~". " - open area around castle +# cccc==cccc*****~~". . - open area around castle (overwritable) +# ~~~~::~~~~~***~~~". +# ~~~~::~~~~~~~~~~"". * - tower +# """"""""""~~~~~"".. = - main castle gate +# ........."""""""... : - gate causeway +# ................... +# +# Depending on the position of the primary vault, the castle may +# be: +# -> in the map corner (2 walls, 1 tower) +# -> along a map edge (3 walls, 2 towers) +# -> "floating" in the middle of the map (4 walls, 4 towers) +# +# The castle is not overwritable; it is effectively an extension +# of the primary vault. Stairs are never placed in the castle +# proper. Instead, they are always in overwritable area around +# it or the rooms. +# +# The rooms around the castle are a modified grid. +# -> Rooms that would overlap the castle proper are not added. +# Rooms may (and often do) overlap the "overwritable" area +# outside the castle. +# -> The rooms are lined up just outside the castle. This +# means the spacing between the rows overlapping the castle +# may be larger than between other rows. If this happens, +# diagonal hallways are not added between these rooms. +# -> An extra row half-offset may be added any any or all sides +# of the grid, depending on the available space. +# + +NAME: layout_geoelf_castle +DEPTH: Elf:$ +WEIGHT: 35 +ORIENT: encompass +TAGS: overwritable layout allow_dup unrand layout_type_rooms +TAGS: no_rotate no_vmirror no_hmirror +{{ + local INSIDE_GLYPH = "'" + local WALL_GLYPH = "c" + local DOOR_GLYPH = "=" + local MOAT_GLYPH = "w" + + -- + -- PHASE 1: Resize the map to maximal size. + -- + + local gxm, gym = dgn.max_bounds() + extend_map{width = gxm, height = gym, fill = 'x'} + + + + -- + -- PHASE 2: Choose design and placement of castle and rooms + -- based on primary vault + -- + + -- choose which sides of primary vault have castle and rooms + local p_x_min, p_x_max, p_y_min, p_y_max = primary_vault_dimensions() + if (p_x_min == nil) then + crawl.mpr("layout_geoelf_castle didn't veto for no primary vault") + return true -- no primary vault + end + + local primary_vault = { bounds = {}, border = {} } + primary_vault.bounds[geoelf.directions.W] = p_x_min + primary_vault.bounds[geoelf.directions.E] = p_x_max + primary_vault.bounds[geoelf.directions.N] = p_y_min + primary_vault.bounds[geoelf.directions.S] = p_y_max + primary_vault.border[geoelf.directions.W] = p_x_min - 1 + primary_vault.border[geoelf.directions.E] = gxm - p_x_max - 2 + primary_vault.border[geoelf.directions.N] = p_y_min - 1 + primary_vault.border[geoelf.directions.S] = gym - p_y_max - 2 + + local is_castle = {} + local is_rooms = {} + local gate_best = nil + local gate_best_space = 0 + for i = 0, geoelf.directions.COUNT_STRAIGHT - 1 do + is_castle[i] = (primary_vault.border[i] >= CASTLE_TOTAL) + is_rooms[i] = (primary_vault.border[i] >= ROOMS_MIN_SPACE) + + -- put the castle gate on the side with most space + if (primary_vault.border[i] > gate_best_space) then + gate_best = i + gate_best_space = primary_vault.border[i] + end + end + + if (DEBUG_CASTLE_DESIGN) then + print("") + print("GEOELF CASTLE DEBUG INFO") + print("Primary vault") + print(" Bounds: (" .. primary_vault.bounds[geoelf.directions.W] + .. " and " .. primary_vault.bounds[geoelf.directions.E] + .. ", " .. primary_vault.bounds[geoelf.directions.N] + .. " and " .. primary_vault.bounds[geoelf.directions.S] .. ")") + print(" Borders: (" .. primary_vault.border[geoelf.directions.W] + .. " and " .. primary_vault.border[geoelf.directions.E] + .. ", " .. primary_vault.border[geoelf.directions.N] + .. " and " .. primary_vault.border[geoelf.directions.S] + .. ") / " .. CASTLE_TOTAL .. ", " .. ROOMS_MIN_SPACE) + end + + + + -- + -- PHASE 3: Design the castle + -- + + -- choose the shape of the components + castle = { base = {}, inner = {}, hallway = {}, moat = {}, open = {}, + overwritable = {}, turret = {} } + + for i = 0, geoelf.directions.COUNT_STRAIGHT - 1 do + castle.base[i] = primary_vault.bounds[i] + end + + if (castle.base[geoelf.directions.W] < 1) then + castle.base[geoelf.directions.W] = 1 + end + if (castle.base[geoelf.directions.E] > gxm - 2) then + castle.base[geoelf.directions.E] = gxm - 2 + end + if (castle.base[geoelf.directions.N] < 1) then + castle.base[geoelf.directions.N] = 1 + end + if (castle.base[geoelf.directions.S] > gym - 2) then + castle.base[geoelf.directions.S] = gym - 2 + end + + for i = 0, geoelf.directions.COUNT_STRAIGHT - 1 do + local step_sign = geoelf.directions.STEP_SIGN[i] + + if (is_castle[i]) then + castle.inner[i] = castle.base[i] + CASTLE_INNER * step_sign + castle.hallway[i] = castle.inner[i] + CASTLE_HALLWAY * step_sign + castle.moat[i] = castle.hallway[i] + CASTLE_MOAT * step_sign + castle.open[i] = castle.moat[i] + CASTLE_OPEN * step_sign + castle.overwritable[i] = castle.open[i] + CASTLE_OVERWRITABLE * step_sign + else + castle.inner[i] = castle.base[i] + step_sign + castle.hallway[i] = castle.base[i] + step_sign + castle.moat[i] = castle.base[i] + castle.open[i] = castle.base[i] + castle.overwritable[i] = castle.base[i] + end + end + + if (DEBUG_CASTLE_DESIGN) then + print("Castle Bounds") + print(" Base: (" .. castle.base[geoelf.directions.W] + .. " - " .. castle.base[geoelf.directions.E] + .. ", " .. castle.base[geoelf.directions.N] + .. " - " .. castle.base[geoelf.directions.S] .. ")") + print(" Inner: (" .. castle.inner[geoelf.directions.W] + .. " - " .. castle.inner[geoelf.directions.E] + .. ", " .. castle.inner[geoelf.directions.N] + .. " - " .. castle.inner[geoelf.directions.S] .. ")") + print(" Hallway: (" .. castle.hallway[geoelf.directions.W] + .. " - " .. castle.hallway[geoelf.directions.E] + .. ", " .. castle.hallway[geoelf.directions.N] + .. " - " .. castle.hallway[geoelf.directions.S] .. ")") + print(" Moat: (" .. castle.moat[geoelf.directions.W] + .. " - " .. castle.moat[geoelf.directions.E] + .. ", " .. castle.moat[geoelf.directions.N] + .. " - " .. castle.moat[geoelf.directions.S] .. ")") + print(" Open: (" .. castle.open[geoelf.directions.W] + .. " - " .. castle.open[geoelf.directions.E] + .. ", " .. castle.open[geoelf.directions.N] + .. " - " .. castle.open[geoelf.directions.S] .. ")") + print(" Overwritable: (" .. castle.overwritable[geoelf.directions.W] + .. " - " .. castle.overwritable[geoelf.directions.E] + .. ", " .. castle.overwritable[geoelf.directions.N] + .. " - " .. castle.overwritable[geoelf.directions.S] + .. ")") + end + + -- choose which turrets to add + for i = geoelf.directions.COUNT_STRAIGHT, geoelf.directions.COUNT - 1 do + local component_x = geoelf.directions.COMPONENT_X[i] + local component_y = geoelf.directions.COMPONENT_Y[i] + + castle.turret[i] = {} + castle.turret[i].is_placed = ( is_castle[component_x] + and is_castle[component_y]) + castle.turret[i].x = castle.hallway[component_x] + - geoelf.directions.STEP_SIGN[component_x] + castle.turret[i].y = castle.hallway[component_y] + - geoelf.directions.STEP_SIGN[component_y] + end + + + + -- + -- PHASE 4: Choose which rooms to add to the basic grid + -- outside the castle + -- + + local edge_rooms = {} + + -- spacing of north and south rooms along x axis + local min_x = castle.overwritable[geoelf.directions.W] + ROOMS_INTO_CASTLE + local max_x = castle.overwritable[geoelf.directions.E] - ROOMS_INTO_CASTLE + local range_x = max_x - min_x + edge_rooms.count_x = math.floor(range_x / ROOMS_SPACING_MIN) + 1 + local range_x_min = (edge_rooms.count_x - 1) * ROOMS_SPACING_MIN + edge_rooms.spacing_x = ROOMS_SPACING_MIN + edge_rooms.allow_diagonals_x = true + + if (is_rooms[geoelf.directions.W]) then + if (is_rooms[geoelf.directions.E]) then + -- spread out the rooms to connect on both sides + -- -> this prevents diagonal connections on the side + edge_rooms.min_x = min_x + edge_rooms.spacing_x = range_x / (edge_rooms.count_x - 1) + edge_rooms.allow_diagonals_x = false + else + -- pack the rooms against the west side + edge_rooms.min_x = min_x + end + else + if (is_rooms[geoelf.directions.E]) then + -- pack the rooms against the east side + edge_rooms.min_x = max_x - range_x_min + else + -- center the rooms + local min_x_max = max_x - range_x_min + edge_rooms.min_x = math.floor((min_x + min_x_max) / 2) + end + end + + -- spacing of east and west rooms along x axis + local min_y = castle.overwritable[geoelf.directions.N] + ROOMS_INTO_CASTLE + local max_y = castle.overwritable[geoelf.directions.S] - ROOMS_INTO_CASTLE + local range_y = max_y - min_y + edge_rooms.count_y = math.floor(range_y / ROOMS_SPACING_MIN) + 1 + local range_y_min = (edge_rooms.count_y - 1) * ROOMS_SPACING_MIN + edge_rooms.spacing_y = ROOMS_SPACING_MIN + edge_rooms.allow_diagonals_y = true + + if (is_rooms[geoelf.directions.N]) then + if (is_rooms[geoelf.directions.S]) then + -- spread out the rooms to connect on both sides + -- -> this prevents diagonal connections on the side + edge_rooms.min_y = min_y + edge_rooms.spacing_y = range_y / (edge_rooms.count_y - 1) + edge_rooms.allow_diagonals_y = false + else + -- pack the rooms against the east side + edge_rooms.min_y = min_y + end + else + if (is_rooms[geoelf.directions.S]) then + -- pack the rooms against the west side + edge_rooms.min_y = max_y - range_y_min + else + -- center the rooms + local min_y_max = max_y - range_y_min + edge_rooms.min_y = math.floor((min_y + min_y_max) / 2) + end + end + + -- calculate row positions + edge_rooms.row_base = {} + edge_rooms.row_base[geoelf.directions.W] = + castle.overwritable[geoelf.directions.W] - ROOMS_FROM_CASTLE + edge_rooms.row_base[geoelf.directions.E] = + castle.overwritable[geoelf.directions.E] + ROOMS_FROM_CASTLE + edge_rooms.row_base[geoelf.directions.N] = + castle.overwritable[geoelf.directions.N] - ROOMS_FROM_CASTLE + edge_rooms.row_base[geoelf.directions.S] = + castle.overwritable[geoelf.directions.S] + ROOMS_FROM_CASTLE + + edge_rooms.row_count = {} + for i = 0, geoelf.directions.COUNT_STRAIGHT - 1 do + if (is_rooms[i] == false) then + edge_rooms.row_count[i] = 0 + else + local available_space = primary_vault.border[i] + - (ROOMS_FROM_CASTLE + ROOMS_BORDER_MIN) + 1 + edge_rooms.row_count[i] = math.floor(available_space / ROOMS_SPACING_MIN) + if (edge_rooms.row_count[i] <= 0) then + crawl.mpr("Error in layout_geoelf_castle: 0 rows in direction " .. + i .. " filling " .. ROOMS_SPACING_MIN .. " / " .. + available_space .. " space") + end + end + end + + if (DEBUG_CASTLE_DESIGN) then + print("Rooms by edge") + + print(" Rooms along castle X: " .. edge_rooms.count_x) + print(" Rooms along castle X: " .. edge_rooms.count_x) + print(" Rooms minimum spacing: " .. ROOMS_SPACING_MIN .. + " (straight), " .. ROOMS_SPACING_EDGE .. " (diagonal) + " .. + ROOMS_BORDER_MIN .. " (border)") + + local LETTERS = + { [geoelf.directions.S] = "S", + [geoelf.directions.N] = "N", + [geoelf.directions.E] = "E", + [geoelf.directions.W] = "W" } + + for i = 0, geoelf.directions.COUNT_STRAIGHT - 1 do + local step_sign = geoelf.directions.STEP_SIGN[i] + local line = " " .. LETTERS[i] .. " " .. edge_rooms.row_count[i] .. + (is_rooms[i] and " Y" or " N") .. ": " + for j = 0, edge_rooms.row_count[i] - 1 do + local pos = edge_rooms.row_base[i] + j * ROOMS_SPACING_MIN * step_sign + line = line .. " " .. pos + end + print(line) + end + end + + -- combine all x positions into an array + local room_pos_x = {} + room_pos_x.begin_center = edge_rooms.row_count[geoelf.directions.W] + room_pos_x.begin_east = room_pos_x.begin_center + edge_rooms.count_x + room_pos_x.total = room_pos_x.begin_east + + edge_rooms.row_count[geoelf.directions.E] + room_pos_x.last = room_pos_x.total - 1 + local is_room_diagonals_x = {} + for i = 0, room_pos_x.total - 1 do + is_room_diagonals_x[i] = true + if (i < room_pos_x.begin_center) then + local rows_out = room_pos_x.begin_center - i - 1 + room_pos_x[i] = edge_rooms.row_base[geoelf.directions.W] + - rows_out * ROOMS_SPACING_MIN + elseif (i < room_pos_x.begin_east) then + local rows_along = i - room_pos_x.begin_center + room_pos_x[i] = edge_rooms.min_x + + math.floor(rows_along * edge_rooms.spacing_x) + is_room_diagonals_x[i] = edge_rooms.allow_diagonals_x + else + local rows_out = i - room_pos_x.begin_east + room_pos_x[i] = edge_rooms.row_base[geoelf.directions.E] + + rows_out * ROOMS_SPACING_MIN + end + end + + -- combine all y positions into an array + local room_pos_y = {} + room_pos_y.begin_center = edge_rooms.row_count[geoelf.directions.N] + room_pos_y.begin_south = room_pos_y.begin_center + edge_rooms.count_y + room_pos_y.total = room_pos_y.begin_south + + edge_rooms.row_count[geoelf.directions.S] + room_pos_y.last = room_pos_y.total - 1 + local is_room_diagonals_y = {} + for i = 0, room_pos_y.total - 1 do + is_room_diagonals_y[i] = true + if (i < room_pos_y.begin_center) then + local rows_out = room_pos_y.begin_center - i - 1 + room_pos_y[i] = edge_rooms.row_base[geoelf.directions.N] + - rows_out * ROOMS_SPACING_MIN + elseif (i < room_pos_y.begin_south) then + local rows_along = i - room_pos_y.begin_center + room_pos_y[i] = edge_rooms.min_y + + math.floor(rows_along * edge_rooms.spacing_y) + is_room_diagonals_y[i] = edge_rooms.allow_diagonals_y + else + local rows_out = i - room_pos_y.begin_south + room_pos_y[i] = edge_rooms.row_base[geoelf.directions.S] + + rows_out * ROOMS_SPACING_MIN + end + end + + if (DEBUG_CASTLE_DESIGN) then + print("Room grid positions") + + local line = " X " .. room_pos_x.total .. ":" + for i = 0, room_pos_x.total - 1 do + line = line .. " " .. room_pos_x[i] + if (not is_room_diagonals_x[i]) then + line = line .. " (NODIAG)" + end + end + print(line) + + local line = " Y " .. room_pos_y.total .. ":" + for i = 0, room_pos_y.total - 1 do + line = line .. " " .. room_pos_y[i] + if (not is_room_diagonals_y[i]) then + line = line .. " (NODIAG)" + end + end + print(line) + end + + + + -- + -- PHASE 5: Calculate the edge rows of rooms to add + -- + + local is_edge_row = {} + is_edge_row[geoelf.directions.W] = (room_pos_x[0] >= ROOMS_MIN_SPACE_EDGE) + is_edge_row[geoelf.directions.E] = (gxm - room_pos_x[room_pos_x.last] + >= ROOMS_MIN_SPACE_EDGE) + is_edge_row[geoelf.directions.N] = (room_pos_y[0] >= ROOMS_MIN_SPACE_EDGE) + is_edge_row[geoelf.directions.S] = (gym - room_pos_y[room_pos_y.last] + >= ROOMS_MIN_SPACE_EDGE) + + -- setting these all is easier than working out which ones we need + local edge_row_at = {} + edge_row_at[geoelf.directions.W] = room_pos_x[0] - ROOMS_SPACING_EDGE + edge_row_at[geoelf.directions.E] = room_pos_x[room_pos_x.last] + + ROOMS_SPACING_EDGE + edge_row_at[geoelf.directions.N] = room_pos_y[0] - ROOMS_SPACING_EDGE + edge_row_at[geoelf.directions.S] = room_pos_y[room_pos_y.last] + + ROOMS_SPACING_EDGE + + -- add a corner edge rooms if we have both edge sides + local is_edge_corner = {} + for i = geoelf.directions.COUNT_STRAIGHT, geoelf.directions.COUNT - 1 do + local component_x = geoelf.directions.COMPONENT_X[i] + local component_y = geoelf.directions.COMPONENT_Y[i] + is_edge_corner[i] = is_edge_row[component_x] and + is_edge_row[component_y] and + (edge_rooms.row_count[component_x] >= 1 or + edge_rooms.row_count[component_y] >= 1 ) + end + + if (DEBUG_CASTLE_DESIGN) then + print("Edge rows") + + if (is_edge_row[geoelf.directions.W]) then + print(" W: x = " .. edge_row_at[geoelf.directions.W]) + end + if (is_edge_row[geoelf.directions.E]) then + print(" E: x = " .. edge_row_at[geoelf.directions.E]) + end + if (is_edge_row[geoelf.directions.N]) then + print(" N: y = " .. edge_row_at[geoelf.directions.N]) + end + if (is_edge_row[geoelf.directions.S]) then + print(" S: y = " .. edge_row_at[geoelf.directions.S]) + end + + if (is_edge_corner[geoelf.directions.SE]) then + print(" SE corner") + end + if (is_edge_corner[geoelf.directions.NW]) then + print(" NW corner") + end + if (is_edge_corner[geoelf.directions.SS]) then + print(" SW corner") + end + if (is_edge_corner[geoelf.directions.NE]) then + print(" NE corner") + end + end + + + + -- + -- PHASE 6: Add the main grid of rooms outside the castle + -- + + -- create the arrays + local room_data = { count = 0 } + local corridor_data = { count = 0 } + local grid_rooms_by_position = {} + + -- add rooms for the main grid + for y = 0, room_pos_y.total - 1 do + grid_rooms_by_position[y] = {} + for x = 0, room_pos_x.total - 1 do + local x_pos = room_pos_x[x] + + crawl.random_range(-ROOMS_OFFSET_MAX, ROOMS_OFFSET_MAX) + local y_pos = room_pos_y[y] + + crawl.random_range(-ROOMS_OFFSET_MAX, ROOMS_OFFSET_MAX) + local radius = crawl.random_range(ROOMS_RADIUS_MIN, ROOMS_RADIUS_MAX) + + if (x < room_pos_x.begin_center or + x >= room_pos_x.begin_east or + y < room_pos_y.begin_center or + y >= room_pos_y.begin_south ) then + grid_rooms_by_position[y][x] = geoelf.add_room(room_data, + x_pos, y_pos, radius) + else + grid_rooms_by_position[y][x] = geoelf.add_room_dummy(room_data, + x_pos, y_pos) + end + end + end + + -- add potential corridors + -- -> at least one room must be non-dummy + -- -> only add diagonals if at least one room allows them + -- (we don't need both) + for y = 0, room_pos_y.total - 1 do + for x = 0, room_pos_x.total - 1 do + local index = grid_rooms_by_position[y][x] + local allow_diagonals = is_room_diagonals_x[x] and + is_room_diagonals_y[y] + + -- east-west + if (x >= 1) then + local other_index = grid_rooms_by_position[y][x - 1] + if (not geoelf.is_room_dummy(room_data, index) or + not geoelf.is_room_dummy(room_data, other_index)) then + geoelf.add_corridor(room_data, corridor_data, + other_index, index, + geoelf.directions.E, nil) + end + end + + -- south-north + if (y >= 1) then + local other_index = grid_rooms_by_position[y - 1][x] + if (not geoelf.is_room_dummy(room_data, index) or + not geoelf.is_room_dummy(room_data, other_index)) then + geoelf.add_corridor(room_data, corridor_data, + other_index, index, + geoelf.directions.S, nil) + end + end + + -- southeast-northwest + if (y >= 1 and x >= 1) then + local other_index = grid_rooms_by_position[y - 1][x - 1] + local allow_diagonals_other = is_room_diagonals_x[x - 1] and + is_room_diagonals_y[y - 1] + if (not geoelf.is_room_dummy(room_data, index) or + not geoelf.is_room_dummy(room_data, other_index)) then + if(allow_diagonals or allow_diagonals_other) then + + -- determine if there is another corridor that would + -- block this one (i.e. an NE-SW one) + local block_index = nil + local old_index = grid_rooms_by_position[y][x - 1] + if (not geoelf.is_room_dummy(room_data, old_index)) then + block_index = room_data[old_index].corridor[geoelf.directions.NE] + end + + geoelf.add_corridor(room_data, corridor_data, + other_index, index, + geoelf.directions.SE, block_index) + end + end + end + + -- southwest-northeast + if (y >= 1 and x + 1 < room_pos_x.total) then + local other_index = grid_rooms_by_position[y - 1][x + 1] + local allow_diagonals_other = is_room_diagonals_x[x + 1] and + is_room_diagonals_y[y - 1] + if (not geoelf.is_room_dummy(room_data, index) or + not geoelf.is_room_dummy(room_data, other_index)) then + if(allow_diagonals or allow_diagonals_other) then + geoelf.add_corridor(room_data, corridor_data, + other_index, index, + geoelf.directions.SW, nil) + end + end + end + + -- done this room + end + end + + + + -- + -- PHASE 7: Add edge rows of rooms + -- -> Rooms must be push as far as possible away from + -- the main grid to avoid having too much overlap + -- + + -- corners + local corner_index = {} + + for i = geoelf.directions.COUNT_STRAIGHT, geoelf.directions.COUNT - 1 do + if (is_edge_corner[i]) then + local component_x = geoelf.directions.COMPONENT_X[i] + local component_y = geoelf.directions.COMPONENT_Y[i] + + local radius = crawl.random_range(ROOMS_RADIUS_EDGE_MIN, + ROOMS_RADIUS_EDGE_MAX) + corner_index[i] = geoelf.add_room(room_data, + edge_row_at[component_x], + edge_row_at[component_y], radius) + + local other_index_x = (component_x == geoelf.directions.W) + and 0 or room_pos_x.last + local other_index_y = (component_y == geoelf.directions.N) + and 0 or room_pos_y.last + local other_index = grid_rooms_by_position[other_index_y][other_index_x] + geoelf.add_corridor(room_data, corridor_data, + other_index, corner_index[i], i, nil) + end + end + + -- + -- NOTE: I tried combining these four cases into a single + -- loop, but it ended up longer than the 4 cases, + -- very hard to understand (even by the standards of my + -- convoluted code), and didn't work reliably. + -- ~ infiniplex + -- + + -- The west side (north->south) + if (is_edge_row[geoelf.directions.W]) then + local x_pos = edge_row_at[geoelf.directions.W] + local previous_index = corner_index[geoelf.directions.NW] + for y = 0, room_pos_y.total - 2 do + local other_index_NE = grid_rooms_by_position[y ][0] + local other_index_SE = grid_rooms_by_position[y + 1][0] + if (not geoelf.is_room_dummy(room_data, other_index_NE) and + not geoelf.is_room_dummy(room_data, other_index_SE)) then + local y_pos = math.floor((room_pos_y[y] + room_pos_y[y + 1]) / 2) + crawl.random_range(-ROOMS_OFFSET_MAX, ROOMS_OFFSET_MAX) + local radius = crawl.random_range(ROOMS_RADIUS_EDGE_MIN, + ROOMS_RADIUS_EDGE_MAX) + local new_index = geoelf.add_room(room_data, x_pos, y_pos, radius) + + local allow_diagonals_here = true + if (y >= room_pos_y.begin_center and + y < room_pos_y.begin_south - 1) then + allow_diagonals_here = edge_rooms.allow_diagonals_y + end + local allow_diagonals_NE = is_room_diagonals_y[y] + local allow_diagonals_SE = is_room_diagonals_y[y + 1] + + if (allow_diagonals_here or allow_diagonals_NE) then + geoelf.add_corridor(room_data, corridor_data, + new_index, other_index_NE, + geoelf.directions.NE, nil) + end + if (allow_diagonals_here or allow_diagonals_SE) then + geoelf.add_corridor(room_data, corridor_data, + new_index, other_index_SE, + geoelf.directions.SE, nil) + end + if (previous_index ~= nil) then + geoelf.add_corridor(room_data, corridor_data, + new_index, previous_index, + geoelf.directions.N, nil) + end + + previous_index = new_index + end + end + if (corner_index[geoelf.directions.SW] ~= nil and + previous_index ~= nil) then + geoelf.add_corridor(room_data, corridor_data, + corner_index[geoelf.directions.SW], previous_index, + geoelf.directions.N, nil) + end + end + + -- The east side (north->south) + if (is_edge_row[geoelf.directions.E]) then + local x_pos = edge_row_at[geoelf.directions.E] + local previous_index = corner_index[geoelf.directions.NE] + for y = 0, room_pos_y.total - 2 do + local other_index_NW = grid_rooms_by_position[y ][room_pos_x.last] + local other_index_SW = grid_rooms_by_position[y + 1][room_pos_x.last] + if (not geoelf.is_room_dummy(room_data, other_index_NW) and + not geoelf.is_room_dummy(room_data, other_index_SW)) then + local y_pos = math.floor((room_pos_y[y] + room_pos_y[y + 1]) / 2) + crawl.random_range(-ROOMS_OFFSET_MAX, ROOMS_OFFSET_MAX) + local radius = crawl.random_range(ROOMS_RADIUS_EDGE_MIN, + ROOMS_RADIUS_EDGE_MAX) + local new_index = geoelf.add_room(room_data, x_pos, y_pos, radius) + + local allow_diagonals_here = true + if (y >= room_pos_y.begin_center and + y < room_pos_y.begin_south - 1) then + allow_diagonals_here = edge_rooms.allow_diagonals_y + end + local allow_diagonals_NW = is_room_diagonals_y[y] + local allow_diagonals_SW = is_room_diagonals_y[y + 1] + + if (allow_diagonals_here or allow_diagonals_NW) then + geoelf.add_corridor(room_data, corridor_data, + new_index, other_index_NW, + geoelf.directions.NW, nil) + end + if (allow_diagonals_here or allow_diagonals_SW) then + geoelf.add_corridor(room_data, corridor_data, + new_index, other_index_SW, + geoelf.directions.SW, nil) + end + if (previous_index ~= nil) then + geoelf.add_corridor(room_data, corridor_data, + new_index, previous_index, + geoelf.directions.N, nil) + end + + previous_index = new_index + end + end + if (corner_index[geoelf.directions.SE] ~= nil and + previous_index ~= nil) then + geoelf.add_corridor(room_data, corridor_data, + corner_index[geoelf.directions.SE], previous_index, + geoelf.directions.N, nil) + end + end + + -- The north side (west->east) + if (is_edge_row[geoelf.directions.N]) then + local y_pos = edge_row_at[geoelf.directions.N] + local previous_index = corner_index[geoelf.directions.NW] + for x = 0, room_pos_x.total - 2 do + local other_index_SW = grid_rooms_by_position[0][x] + local other_index_SE = grid_rooms_by_position[0][x + 1] + if (not geoelf.is_room_dummy(room_data, other_index_SW) and + not geoelf.is_room_dummy(room_data, other_index_SE)) then + local x_pos = math.floor((room_pos_x[x] + room_pos_x[x + 1]) / 2) + crawl.random_range(-ROOMS_OFFSET_MAX, ROOMS_OFFSET_MAX) + local radius = crawl.random_range(ROOMS_RADIUS_EDGE_MIN, + ROOMS_RADIUS_EDGE_MAX) + local new_index = geoelf.add_room(room_data, x_pos, y_pos, radius) + + local allow_diagonals_here = true + if (x >= room_pos_x.begin_center and + x < room_pos_x.begin_east - 1) then + allow_diagonals_here = edge_rooms.allow_diagonals_x + end + local allow_diagonals_SW = is_room_diagonals_x[x] + local allow_diagonals_SE = is_room_diagonals_x[x + 1] + + if (allow_diagonals_here or allow_diagonals_SW) then + geoelf.add_corridor(room_data, corridor_data, + new_index, other_index_SW, + geoelf.directions.SW, nil) + end + if (allow_diagonals_here or allow_diagonals_SE) then + geoelf.add_corridor(room_data, corridor_data, + new_index, other_index_SE, + geoelf.directions.SE, nil) + end + if (previous_index ~= nil) then + geoelf.add_corridor(room_data, corridor_data, + new_index, previous_index, + geoelf.directions.W, nil) + end + + previous_index = new_index + end + end + if (corner_index[geoelf.directions.NE] ~= nil and + previous_index ~= nil) then + geoelf.add_corridor(room_data, corridor_data, + corner_index[geoelf.directions.NE], previous_index, + geoelf.directions.W, nil) + end + end + + -- The south side (west->east) + if (is_edge_row[geoelf.directions.S]) then + local y_pos = edge_row_at[geoelf.directions.S] + local previous_index = corner_index[geoelf.directions.SW] + for x = 0, room_pos_x.total - 2 do + local other_index_NW = grid_rooms_by_position[room_pos_y.last][x] + local other_index_NE = grid_rooms_by_position[room_pos_y.last][x + 1] + if (not geoelf.is_room_dummy(room_data, other_index_NW) and + not geoelf.is_room_dummy(room_data, other_index_NE)) then + local x_pos = math.floor((room_pos_x[x] + room_pos_x[x + 1]) / 2) + crawl.random_range(-ROOMS_OFFSET_MAX, ROOMS_OFFSET_MAX) + local radius = crawl.random_range(ROOMS_RADIUS_EDGE_MIN, + ROOMS_RADIUS_EDGE_MAX) + local new_index = geoelf.add_room(room_data, x_pos, y_pos, radius) + + local allow_diagonals_here = true + if (x >= room_pos_x.begin_center and + x < room_pos_x.begin_east - 1) then + allow_diagonals_here = edge_rooms.allow_diagonals_x + end + local allow_diagonals_NW = is_room_diagonals_x[x] + local allow_diagonals_NE = is_room_diagonals_x[x + 1] + + if (allow_diagonals_here or allow_diagonals_NW) then + geoelf.add_corridor(room_data, corridor_data, + new_index, other_index_NW, + geoelf.directions.NW, nil) + end + if (allow_diagonals_here or allow_diagonals_NE) then + geoelf.add_corridor(room_data, corridor_data, + new_index, other_index_NE, + geoelf.directions.NE, nil) + end + if (previous_index ~= nil) then + geoelf.add_corridor(room_data, corridor_data, + new_index, previous_index, + geoelf.directions.W, nil) + end + + previous_index = new_index + end + end + if (corner_index[geoelf.directions.SE] ~= nil and + previous_index ~= nil) then + geoelf.add_corridor(room_data, corridor_data, + corner_index[geoelf.directions.SE], previous_index, + geoelf.directions.W, nil) + end + end + + + + -- + -- PHASE 8: Draw the everything except the castle + -- + + -- draw the open area around the castle + + fill_area { x1 = castle.hallway[geoelf.directions.W] + 1, + x2 = castle.hallway[geoelf.directions.E] - 1, + y1 = castle.overwritable[geoelf.directions.N], + y2 = castle.overwritable[geoelf.directions.S], + fill = geoelf.glyphs.FLOOR } + fill_area { x1 = castle.overwritable[geoelf.directions.W], + x2 = castle.overwritable[geoelf.directions.E], + y1 = castle.hallway[geoelf.directions.N] + 1, + y2 = castle.hallway[geoelf.directions.S] - 1, + fill = geoelf.glyphs.FLOOR } + + for i = geoelf.directions.COUNT_STRAIGHT, geoelf.directions.COUNT - 1 do + if (castle.turret[i].is_placed) then + make_circle { x = castle.turret[i].x, y = castle.turret[i].y, + radius = CASTLE_TOWER_OVERWRITABLE_RADIUS, + fill = geoelf.glyphs.FLOOR } + end + end + + -- run the geoelf generator to add the rooms + -- -> note that this can interact with the castle area + geoelf.generate(_G, room_data, corridor_data, ROOMS_EXTRA_CORRIDOR_FRACTION, + ROOMS_FANCY_ROOM_FRACTION, false, false) + + + + -- + -- PHASE 9: Build the castle + -- -> This much of the map is non-overwritable + -- + + -- draw the non-overwritable open area around the castle + fill_area { x1 = castle.open[geoelf.directions.W], + x2 = castle.open[geoelf.directions.E], + y1 = castle.open[geoelf.directions.N], + y2 = castle.open[geoelf.directions.S], fill = INSIDE_GLYPH } + + for i = geoelf.directions.COUNT_STRAIGHT, geoelf.directions.COUNT - 1 do + if (castle.turret[i].is_placed) then + make_circle { x = castle.turret[i].x, y = castle.turret[i].y, + radius = CASTLE_TOWER_OPEN_RADIUS, fill = INSIDE_GLYPH } + end + end + + -- draw the moat + fill_area { x1 = castle.moat[geoelf.directions.W], + x2 = castle.moat[geoelf.directions.E], + y1 = castle.moat[geoelf.directions.N], + y2 = castle.moat[geoelf.directions.S], fill = MOAT_GLYPH } + + for i = geoelf.directions.COUNT_STRAIGHT, geoelf.directions.COUNT - 1 do + if (castle.turret[i].is_placed) then + make_circle { x = castle.turret[i].x, y = castle.turret[i].y, + radius = CASTLE_TOWER_MOAT_RADIUS, fill = MOAT_GLYPH } + end + end + + -- draw the hallways + fill_area { x1 = castle.hallway[geoelf.directions.W], + x2 = castle.hallway[geoelf.directions.E], + y1 = castle.hallway[geoelf.directions.N], + y2 = castle.hallway[geoelf.directions.S], + border = WALL_GLYPH, fill = INSIDE_GLYPH } + + -- draw the inner rooms + fill_area { x1 = castle.inner[geoelf.directions.W], + x2 = castle.inner[geoelf.directions.E], + y1 = castle.inner[geoelf.directions.N], + y2 = castle.inner[geoelf.directions.S], + border = WALL_GLYPH, fill = INSIDE_GLYPH } + + -- draw horizontal walls + local wall_at = castle.inner[geoelf.directions.W] + local wall_end = castle.inner[geoelf.directions.E] - INNER_WALL_SPACING_MIN + while (wall_at < wall_end) do + fill_area { x1 = wall_at, x2 = wall_at, + y1 = castle.inner[geoelf.directions.N], + y2 = castle.inner[geoelf.directions.S], + fill = WALL_GLYPH } + wall_at = wall_at + crawl.random_range(INNER_WALL_SPACING_MIN, + INNER_WALL_SPACING_MAX) + end + + -- draw vertical walls + wall_at = castle.inner[geoelf.directions.N] + wall_end = castle.inner[geoelf.directions.S] - INNER_WALL_SPACING_MIN + while (wall_at < wall_end) do + fill_area { x1 = castle.inner[geoelf.directions.W], + x2 = castle.inner[geoelf.directions.E], + y1 = wall_at, y2 = wall_at, + fill = WALL_GLYPH } + wall_at = wall_at + crawl.random_range(INNER_WALL_SPACING_MIN, + INNER_WALL_SPACING_MAX) + end + + -- draw the towers + local turret_center = crawl.random_element { + [geoelf.glyphs.TREE] = geoelf.glyphs.PLANTLIKE_OPTIONS[geoelf.glyphs.TREE], + [geoelf.glyphs.BUSH] = geoelf.glyphs.PLANTLIKE_OPTIONS[geoelf.glyphs.BUSH], + [geoelf.glyphs.PLANT] = geoelf.glyphs.PLANTLIKE_OPTIONS[geoelf.glyphs.PLANT], + [geoelf.glyphs.FUNGUS] = geoelf.glyphs.PLANTLIKE_OPTIONS[geoelf.glyphs.FUNGUS], + [geoelf.glyphs.STATUE] = geoelf.glyphs.FEATURE_OPTIONS[geoelf.glyphs.STATUE] * 5, + [geoelf.glyphs.FOUNTAIN] = geoelf.glyphs.FEATURE_OPTIONS[geoelf.glyphs.FOUNTAIN] * 5, + [INSIDE_GLYPH] = 15, + } + + for i = geoelf.directions.COUNT_STRAIGHT, geoelf.directions.COUNT - 1 do + if (castle.turret[i].is_placed) then + make_round_box { x1 = castle.turret[i].x - CASTLE_TOWER_RADIUS, + x2 = castle.turret[i].x + CASTLE_TOWER_RADIUS, + y1 = castle.turret[i].y - CASTLE_TOWER_RADIUS, + y2 = castle.turret[i].y + CASTLE_TOWER_RADIUS, + floor = INSIDE_GLYPH, wall = WALL_GLYPH, + door = DOOR_GLYPH, door_count = 2, + passable = INSIDE_GLYPH } + mapgrd[castle.turret[i].x][castle.turret[i].y] = turret_center + end + end + + -- draw the gate + local gate_size = crawl.random_range(GATE_SIZE_MIN, GATE_SIZE_MAX) + + if (gate_best == geoelf.directions.S or + gate_best == geoelf.directions.N) then + local gate_x_min = castle.hallway[geoelf.directions.W] + + GATE_FROM_CORNER_MIN + local gate_x_max = castle.hallway[geoelf.directions.E] + - GATE_FROM_CORNER_MIN + local gate_x = crawl.random_range(gate_x_min, gate_x_max - gate_size) + + local gate_y + if (gate_best == geoelf.directions.S) then + gate_y = castle.hallway[geoelf.directions.S] + fill_area { x1 = gate_x, x2 = gate_x + gate_size - 1, + y1 = gate_y + 1, y2 = gate_y + 1 + CASTLE_MOAT, + fill = INSIDE_GLYPH } + else + gate_y = castle.hallway[geoelf.directions.N] + fill_area { x1 = gate_x, x2 = gate_x + gate_size - 1, + y1 = gate_y - 1 - CASTLE_MOAT, y2 = gate_y - 1, + fill = INSIDE_GLYPH } + end + fill_area { x1 = gate_x, x2 = gate_x + gate_size - 1, + y1 = gate_y, y2 = gate_y, + fill = DOOR_GLYPH } + else + local gate_y_min = castle.hallway[geoelf.directions.N] + + GATE_FROM_CORNER_MIN + local gate_y_max = castle.hallway[geoelf.directions.S] + - GATE_FROM_CORNER_MIN + local gate_y = crawl.random_range(gate_y_min, gate_y_max - gate_size) + + local gate_x + if (gate_best == geoelf.directions.E) then + gate_x = castle.hallway[geoelf.directions.E] + fill_area { x1 = gate_x + 1, x2 = gate_x + 1 + CASTLE_MOAT, + y1 = gate_y, y2 = gate_y + gate_size - 1, + fill = INSIDE_GLYPH } + else + gate_x = castle.hallway[geoelf.directions.W] + fill_area { x1 = gate_x - 1 - CASTLE_MOAT, x2 = gate_x - 1, + y1 = gate_y, y2 = gate_y + gate_size - 1, + fill = INSIDE_GLYPH } + end + fill_area { x1 = gate_x, x2 = gate_x, + y1 = gate_y, y2 = gate_y + gate_size - 1, + fill = DOOR_GLYPH } + end + + -- fill in the area around the castle + -- -> this area will be overridden + -- -> we need to make sure the map is connected without going + -- into this area + for y = 1, gym - 2 do + for x = 1, gxm - 2 do + if dgn.in_vault(x, y) then + mapgrd[x][y] = WALL_GLYPH + end + end + end + + -- add doors + local inner_area = (castle.inner[geoelf.directions.E] - + castle.inner[geoelf.directions.W] + 1) * + (castle.inner[geoelf.directions.S] - + castle.inner[geoelf.directions.N] + 1) + connect_adjacent_rooms { wall = WALL_GLYPH, floor = INSIDE_GLYPH, + replace = DOOR_GLYPH, + max = inner_area, check_distance = 8, + x1 = castle.inner[geoelf.directions.W] - 1, + x2 = castle.inner[geoelf.directions.E] + 1, + y1 = castle.inner[geoelf.directions.N] - 1, + y2 = castle.inner[geoelf.directions.S] + 1 } + + + + -- + -- PHASE 10: Miscellaneous Cleanup + -- + + -- apply special glyph meanings to castle + -- -> remember that the "=" must be the first glyph for parsing reasons + -- -> the castle cannot be overwritten + kmask(DOOR_GLYPH .. INSIDE_GLYPH .. MOAT_GLYPH .. WALL_GLYPH .. " = vault") + kfeat(DOOR_GLYPH .. " = closed_door") + kfeat(INSIDE_GLYPH .. " = floor") + + -- place the stairs outside the vault area + nsubst(geoelf.glyphs.FLOOR .. " = 1:{ / 1:( / 1:[ / *:" .. + geoelf.glyphs.FLOOR) + + -- Replace all floor cells around primary vault with "."s. + -- This is needed to prevent vetos + -- I think it is related to ensuring the primary vault is + -- connected to the layout + -- TODO: Why does it have to be "."s? Can this be fixed? + for y = 1, gym - 2 do + for x = 1, gxm - 2 do + if dgn.in_vault(x, y) then + for dy = -1, 1 do + for dx = -1, 1 do + if mapgrd[x + dx][y + dy] == INSIDE_GLYPH then + mapgrd[x + dx][y + dy] = "." + end + end + end + end + end + end + + -- Finally, draw the map for debugging reasons + if (DEBUG_CASTLE_DESIGN) then + for y = 0, gym - 1 do + local line = "" + for x = 0, gxm - 1 do + if dgn.in_vault(x, y) then + line = line .. " " + else + line = line .. mapgrd[x][y] + end + end + print(line) + end + print("") + end +}} + +veto {{ + -- Only allow this layout if there is a primary vault with + -- enough space around it for at least one line of rooms + + local gxm, gym = dgn.max_bounds() + local p_x_min, p_x_max, p_y_min, p_y_max = primary_vault_dimensions() + + if (p_x_min == nil) then + if (DEBUG_CASTLE_DESIGN) then + crawl.mpr("layout_geoelf_castle vetoed because no primary vault") + end + return true -- no primary vault + elseif (p_x_max - p_x_min < PRINMARY_VAULT_MIX_SIZE or + p_y_max - p_y_min < PRINMARY_VAULT_MIX_SIZE) then + -- primary vault is too small + if (DEBUG_CASTLE_DESIGN) then + crawl.mpr("layout_geoelf_castle vetoed because primary vault too small") + end + return true + elseif (p_x_min < ROOMS_MIN_SPACE and + p_x_max >= gxm - ROOMS_MIN_SPACE and + p_y_min < ROOMS_MIN_SPACE and + p_y_max >= gym - ROOMS_MIN_SPACE) then + -- not enough room around primary vault on any side + if (DEBUG_CASTLE_DESIGN) then + crawl.mpr("layout_geoelf_castle vetoed because primary vault too big") + end + return true + else + if (DEBUG_CASTLE_DESIGN) then + crawl.mpr("layout_geoelf_castle passed veto test") + end + return false + end +}} +MAP +ENDMAP + diff --git a/crawl-ref/source/dat/dlua/layout/geoelf.lua b/crawl-ref/source/dat/dlua/layout/geoelf.lua index c59813d..c2f12da 100644 --- a/crawl-ref/source/dat/dlua/layout/geoelf.lua +++ b/crawl-ref/source/dat/dlua/layout/geoelf.lua @@ -22,7 +22,7 @@ require("dlua/layout/geoelf_corridors.lua") ---------------------------------------------------------------- -- --- How to use the geoelf layout generator: +-- How to use the geoelf layout generator: -- -- 1. Initialize the room and corridor arrays in the layout. -- They will be needed as parameters to the subsequent @@ -51,6 +51,23 @@ require("dlua/layout/geoelf_corridors.lua") -- plant, fungus, statue, fountain -- (needs special tiles) -- +-- If you need to connect the portion of the map generated +-- using geoelf layout generator ("the geoelf layout") to a +-- portion of the map generated in a different way ("your +-- layout"): +-- +-- 1.-3. As above +-- 4. Use the add_room_dummy function to mark where on your +-- layout you want the geoelf layout to connect to +-- -> Nothing will be displayed for these rooms +-- 5. Add the possible corridors to connect the dummy rooms to +-- the geoelf layout +-- -> Not all corridors will appear, but at least one will +-- connect to each dummy room +-- -> If you connect the dummy rooms to each other, corridors +-- will be added between them as well +-- 6. Call the geoelf.generate function +-- @@ -79,7 +96,8 @@ require("dlua/layout/geoelf_corridors.lua") -- -> radius: The size from room center to edge -- -> zone: used to connect the rooms into a tree -- -> corridor: An array of the corridors indexed using --- direction values +-- direction values +-- -> is_dummy: Whether this room is a plaeholder -- -- The elements in the array of room data are numbered from 0 as -- in C++, not from 1 as recommended in LUA documentation. The @@ -116,6 +134,65 @@ function geoelf.add_room (room_data, center_x, center_y, radius) room_data[index].radius = radius room_data[index].zone = index room_data[index].corridor = {} + room_data[index].is_dummy = false + + for i = 0, geoelf.directions.COUNT - 1 do + room_data[index].corridor[i] = nil + end + + room_data.count = index + 1 + return index +end + +---------------- +-- +-- add_room_dummy +-- +-- Purpose: This function adds a dummy room at the specified +-- position to the list of rooms for the layout. A +-- dummy room is connected into the tree but is never +-- drawn to the layout. Dummy rooms should be used to +-- connect the geoelf room system to parts of a map +-- generate using a different method. +-- Parameter(s): +-- -> room_data: The array of room information. This new room +-- will be added to the array. +-- -> center_x +-- -> center_y: The x/y coordinates for the center of the room. +-- Returns: The index of the room. You will need this to +-- specify possible corridors. +-- +-- The room data structure is the same as for add_room +-- + +function geoelf.add_room_dummy (room_data, center_x, center_y) + if (room_data == nil) then + crawl.mpr("Error: No room data array") + end + if (room_data.count == nil) then + crawl.mpr("Error: Room data array does not have count") + end + if (center_x == nil) then + crawl.mpr("Error: center_x is nil") + end + if (center_y == nil) then + crawl.mpr("Error: center_y is nil") + end + + if (geoelf.debug) then + print("geoelf.add_room_dummy(room_data, " .. center_x .. ", " .. + center_y .. ")") + end + + local index = room_data.count + room_data[index] = {} + + room_data[index].center_x = center_x + room_data[index].center_y = center_y + room_data[index].radius = 1 + room_data[index].zone = index + room_data[index].corridor = {} + room_data[index].is_dummy = true for i = 0, geoelf.directions.COUNT - 1 do room_data[index].corridor[i] = nil @@ -127,6 +204,36 @@ end ---------------- -- +-- is_room_dummy +-- +-- Purpose: To determine if the room with the specified index +-- is a dummy room dummy room. +-- Parameter(s): +-- -> room_data: The array of room information. +-- -> index: The index of the room +-- Returns: Whether the room in room_data with index index is a +-- dummy room. +-- + +function geoelf.is_room_dummy (room_data, index) + if (room_data == nil) then + crawl.mpr("Error: No room data array") + end + if (room_data.count == nil) then + crawl.mpr("Error: Room data array does not have count") + end + if (index == nil) then + crawl.mpr("Error: index is nil") + end + if (index >= room_data.count) then + crawl.mpr("Error: index is larger than room count") + end + + return room_data[index].is_dummy +end + +---------------- +-- -- add_corridor -- -- Purpose: This function adds a possible corridor to the list @@ -271,6 +378,15 @@ function geoelf.generate (e, room_data, corridor_data, if (extra_fraction == nil) then crawl.mpr("Error: extra_fraction is nil") end + if (fancy_room_fraction == nil) then + crawl.mpr("Error: fancy_room_fraction is nil") + end + if (only_trees == nil) then + crawl.mpr("Error: only_trees is nil") + end + if (force_open_room_edge == nil) then + crawl.mpr("Error: force_open_room_edge is nil") + end if (geoelf.debug) then print("geoelf.generate(room_data, corridor_data, " .. extra_fraction .. ")") @@ -301,9 +417,11 @@ function geoelf.generate (e, room_data, corridor_data, -- draw rooms if (geoelf.debug) then print("Drawing rooms:") end for i = 0, room_data.count - 1 do - geoelf.rooms.draw(e, room_data, corridor_data, draw_order[i], - (crawl.random_real() < fancy_room_fraction), - force_open_room_edge) + if (not room_data[draw_order[i]].is_dummy) then + geoelf.rooms.draw(e, room_data, corridor_data, draw_order[i], + (crawl.random_real() < fancy_room_fraction), + force_open_room_edge) + end end -- substitutions @@ -335,8 +453,6 @@ function geoelf.generate (e, room_data, corridor_data, end end - - ---------------- -- -- This is the helper function that turns the doors in glass diff --git a/crawl-ref/source/dat/dlua/layout/geoelf_directions.lua b/crawl-ref/source/dat/dlua/layout/geoelf_directions.lua index 74c4506..6dd8fd2 100644 --- a/crawl-ref/source/dat/dlua/layout/geoelf_directions.lua +++ b/crawl-ref/source/dat/dlua/layout/geoelf_directions.lua @@ -7,7 +7,7 @@ -- direction. ------------------------------------------------------------------------------ -geoelf.directions = {} -- Namespace for direction constants, etc. +geoelf.directions = {} -- Namespace for direction constants, etc. @@ -16,6 +16,7 @@ geoelf.directions = {} -- Namespace for direction constants, etc. -- geoelf.directions.COUNT = 8 +geoelf.directions.COUNT_STRAIGHT = 4 -- -- The possible directions @@ -63,3 +64,33 @@ geoelf.directions.GET_REVERSE = [geoelf.directions.NW] = geoelf.directions.SE, [geoelf.directions.SW] = geoelf.directions.NE, [geoelf.directions.NE] = geoelf.directions.SW } + +-- +-- Moving along any straight directions involve repeatedly +-- either increasing or decreasing the component of the +-- position. This can be seen as either adding 1 or -1. +-- + +geoelf.directions.STEP_SIGN = + { [geoelf.directions.S] = 1, + [geoelf.directions.N] = -1, + [geoelf.directions.E] = 1, + [geoelf.directions.W] = -1 } + +-- +-- Each diagonal direction is a combination of 2 straight +-- directions. +-- + +geoelf.directions.COMPONENT_X = + { [geoelf.directions.SE] = geoelf.directions.E, + [geoelf.directions.NW] = geoelf.directions.W, + [geoelf.directions.SW] = geoelf.directions.W, + [geoelf.directions.NE] = geoelf.directions.E } + +geoelf.directions.COMPONENT_Y = + { [geoelf.directions.SE] = geoelf.directions.S, + [geoelf.directions.NW] = geoelf.directions.N, + [geoelf.directions.SW] = geoelf.directions.S, + [geoelf.directions.NE] = geoelf.directions.N } + -- 1.8.1.2 ![]() ![]() From 2031c660a66b2012cd6374599c0485e92748afb3 Mon Sep 17 00:00:00 2001 From: infiniplex <infiniplex@hotmail.com> Date: Tue, 7 Oct 2014 14:06:44 -0600 Subject: [PATCH] layout-geoelf-castle-v2 --- crawl-ref/source/dat/des/builder/layout_geoelf.des | 5 +- .../dat/des/builder/layout_geoelf_castle.des | 1196 ++++++++++++++++++++ crawl-ref/source/dat/dlua/layout/geoelf.lua | 130 ++- .../source/dat/dlua/layout/geoelf_directions.lua | 32 +- 4 files changed, 1353 insertions(+), 10 deletions(-) create mode 100644 crawl-ref/source/dat/des/builder/layout_geoelf_castle.des diff --git a/crawl-ref/source/dat/des/builder/layout_geoelf.des b/crawl-ref/source/dat/des/builder/layout_geoelf.des index 274ea12..51310c7 100644 --- a/crawl-ref/source/dat/des/builder/layout_geoelf.des +++ b/crawl-ref/source/dat/des/builder/layout_geoelf.des @@ -2,6 +2,7 @@ # layout_geoelf.des: Layouts for Elf with geometric rooms # These are not the only Elf layouts # These layouts will build around primary vaults correctly. +# There is another geoelf layout in layout_geoelf_castle.des. ############################################################################### # Required file contains overview @@ -122,7 +123,7 @@ TAGS: no_rotate no_vmirror no_hmirror -- block this one (i.e. an NE-SW one) local block_index = nil if (rooms_by_position[y][x - 1] ~= nil) then - old_index = rooms_by_position[y][x - 1] + local old_index = rooms_by_position[y][x - 1] block_index = room_data[old_index].corridor[geoelf.directions.NE] end @@ -256,7 +257,7 @@ TAGS: no_rotate no_vmirror no_hmirror -- block this one (i.e. an E-W one) local block_index = nil if (x >= 1 and rooms_by_position[y - 1][x - 1] ~= nil) then - old_index = rooms_by_position[y - 1][x - 1] + local old_index = rooms_by_position[y - 1][x - 1] block_index = room_data[old_index].corridor[geoelf.directions.E] end diff --git a/crawl-ref/source/dat/des/builder/layout_geoelf_castle.des b/crawl-ref/source/dat/des/builder/layout_geoelf_castle.des new file mode 100644 index 0000000..7dc34d5 --- /dev/null +++ b/crawl-ref/source/dat/des/builder/layout_geoelf_castle.des @@ -0,0 +1,1196 @@ +############################################################################### +# layout_geoelf_castle.des: A special geoelf layout for Elf:$ that builds a +# castle around the primary vault. +# There are other geoelf layouts in layout_geoelf.des. +############################################################################### + +# Required file contains overview of geoelf room placement +: require("dlua/layout/geoelf.lua") + +{{ + -- + -- Constants + -- + -- These have to be global so they can also be used in the + -- veto function. + -- + + -- size of castle + CASTLE_INNER = 4 + CASTLE_HALLWAY = 3 + CASTLE_MOAT = 2 + CASTLE_OPEN = 1 + CASTLE_OVERWRITABLE = 3 + CASTLE_TOTAL = CASTLE_INNER + CASTLE_HALLWAY + CASTLE_MOAT + + CASTLE_OPEN + CASTLE_OVERWRITABLE + CASTLE_TURRET_RADIUS = 4 + CASTLE_TURRET_MOAT_RADIUS = CASTLE_TURRET_RADIUS + CASTLE_MOAT + CASTLE_TURRET_OPEN_RADIUS = CASTLE_TURRET_MOAT_RADIUS + CASTLE_OPEN + CASTLE_TURRET_OVERWRITABLE_RADIUS = CASTLE_HALLWAY + CASTLE_MOAT + + CASTLE_OPEN + CASTLE_OVERWRITABLE - 1 + + INNER_WALL_SPACING_MIN = 6 + INNER_WALL_SPACING_MAX = 12 + + GATE_FROM_CORNER_MIN = 8 + GATE_SIZE_MIN = 2 + GATE_SIZE_MAX = 3 + + -- room parameters + ROOMS_OFFSET_MAX = 1 + ROOMS_RADIUS_MIN = 3 + ROOMS_RADIUS_MAX = 5 + ROOMS_RADIUS_EDGE_MIN = 2 + ROOMS_RADIUS_EDGE_MAX = 4 + ROOMS_EXTRA_CORRIDOR_FRACTION = 0.5 + ROOMS_FANCY_ROOM_FRACTION = 0.2 + + -- room placement + ROOMS_BORDER_MIN = ROOMS_OFFSET_MAX + ROOMS_RADIUS_MAX + 1 + ROOMS_FROM_CASTLE = ROOMS_OFFSET_MAX + ROOMS_RADIUS_MAX + 1 + ROOMS_SPACING_MIN = ROOMS_OFFSET_MAX + ROOMS_RADIUS_MAX * 2 + 2 + ROOMS_INTO_CASTLE = ROOMS_SPACING_MIN - ROOMS_FROM_CASTLE + ROOMS_MIN_SPACE = CASTLE_TOTAL + ROOMS_BORDER_MIN + ROOMS_FROM_CASTLE + + -- veto tests + PRIMARY_VAULT_MIN_PADDING = ROOMS_MIN_SPACE + 1 + PRIMARY_VAULT_MIN_SIZE = GATE_FROM_CORNER_MIN + GATE_SIZE_MAX + - CASTLE_HALLWAY - CASTLE_INNER + + -- edge rows of rooms have no individual offset to allow + -- for maximum distance from grid + ROOMS_SPACING_EDGE = math.ceil(ROOMS_SPACING_MIN / 2) + 1 + ROOMS_MIN_SPACE_EDGE = ROOMS_SPACING_EDGE + ROOMS_RADIUS_EDGE_MAX + 2 + + DEBUG_CASTLE_DESIGN = false +}} + + + +############################################################## +# layout_geoelf_castle +# Idea by psyshvl and doy (probably independantly) +# Adapted and coded by infiniplex +# +# This layout builds a castle around a primary vault, and then +# fills up the extra space with geoelf rooms. It will NOT work +# without a primary vault of a suitable size. If there is no +# primary vault, or the primary vault is too big or too small, +# the layout will be vetoed. +# +# The shape of the castle is as follows (not to scale): +# +# ??????????''cc~~".. +# ??????????''cc~~".. ? - primary vault +# ??????????''cc~~".. +# ??????????''cc~~"". ' - inner rooms area +# '''''''''''***~~~". c - outer hallway +# ''''''''''*****~~". ~ - moat +# cccccccccc*****~~". " - open area around castle +# cccc==cccc*****~~". . - open area around castle (overwritable) +# ~~~~::~~~~~***~~~". +# ~~~~::~~~~~~~~~~"". * - turret +# """"""""""~~~~~"".. = - main castle gate +# ........."""""""... : - gate causeway +# ................... +# +# Depending on the position of the primary vault, the castle may +# be: +# -> in the map corner (2 walls, 1 turrets) +# -> along a map edge (3 walls, 2 turrets) +# -> "floating" in the middle of the map (4 walls, 4 turrets) +# +# The castle is not overwritable; it is effectively an extension +# of the primary vault. Stairs are never placed in the castle +# proper. Instead, they are always in overwritable area around +# it or the rooms. +# +# The rooms around the castle are a modified grid. +# -> Rooms that would overlap the castle proper are not added. +# Rooms may (and often do) overlap the "overwritable" area +# outside the castle. +# -> The rooms are lined up just outside the castle. This +# means the spacing between the rows overlapping the castle +# may be larger than between other rows. If this happens, +# diagonal hallways are not added between these rooms. +# -> An extra row half-offset may be added any any or all sides +# of the grid, depending on the available space. +# +# TODO: When geometric pan (WFCD) boxes are finalized, add +# octagon-, square-, and two types of hexagon-shaped +# turrets as alternatives to the round ones. +# + +NAME: layout_geoelf_castle +DEPTH: Elf:$ +WEIGHT: 15 +ORIENT: encompass +TAGS: overwritable layout allow_dup unrand layout_type_rooms +TAGS: no_rotate no_vmirror no_hmirror +{{ + local INSIDE_GLYPH = "'" + local WALL_GLYPH = "c" + local DOOR_GLYPH = "=" + local MOAT_GLYPH = "w" + + -- + -- PHASE 1: Resize the map to maximal size. + -- + + local gxm, gym = dgn.max_bounds() + extend_map{width = gxm, height = gym, fill = 'x'} + + + + -- + -- PHASE 2: Choose design and placement of castle and rooms + -- based on primary vault + -- + + -- choose which sides of primary vault have castle and rooms + local p_x_min, p_x_max, p_y_min, p_y_max = primary_vault_dimensions() + if (p_x_min == nil) then + crawl.mpr("layout_geoelf_castle didn't veto for no primary vault") + return true -- no primary vault + end + + local primary_vault = { bounds = {}, border = {} } + primary_vault.bounds[geoelf.directions.W] = p_x_min + primary_vault.bounds[geoelf.directions.E] = p_x_max + primary_vault.bounds[geoelf.directions.N] = p_y_min + primary_vault.bounds[geoelf.directions.S] = p_y_max + primary_vault.border[geoelf.directions.W] = p_x_min - 1 + primary_vault.border[geoelf.directions.E] = gxm - p_x_max - 2 + primary_vault.border[geoelf.directions.N] = p_y_min - 1 + primary_vault.border[geoelf.directions.S] = gym - p_y_max - 2 + + local is_castle = {} + local is_rooms = {} + local gate_best = nil + local gate_best_space = 0 + for i = 0, geoelf.directions.COUNT_STRAIGHT - 1 do + is_castle[i] = (primary_vault.border[i] >= CASTLE_TOTAL) + is_rooms[i] = (primary_vault.border[i] >= ROOMS_MIN_SPACE) + + -- put the castle gate on the side with most space + if (primary_vault.border[i] > gate_best_space) then + gate_best = i + gate_best_space = primary_vault.border[i] + end + end + + if (DEBUG_CASTLE_DESIGN) then + print("") + print("GEOELF CASTLE DEBUG INFO") + print("Primary vault") + print(" Bounds: (" .. primary_vault.bounds[geoelf.directions.W] + .. " and " .. primary_vault.bounds[geoelf.directions.E] + .. ", " .. primary_vault.bounds[geoelf.directions.N] + .. " and " .. primary_vault.bounds[geoelf.directions.S] .. ")") + print(" Borders: (" .. primary_vault.border[geoelf.directions.W] + .. " and " .. primary_vault.border[geoelf.directions.E] + .. ", " .. primary_vault.border[geoelf.directions.N] + .. " and " .. primary_vault.border[geoelf.directions.S] + .. ") / " .. CASTLE_TOTAL .. ", " .. ROOMS_MIN_SPACE) + end + + + + -- + -- PHASE 3: Design the castle + -- + + -- choose the shape of the components + castle = { base = {}, inner = {}, hallway = {}, moat = {}, open = {}, + overwritable = {}, turret = {} } + + for i = 0, geoelf.directions.COUNT_STRAIGHT - 1 do + castle.base[i] = primary_vault.bounds[i] + end + + if (castle.base[geoelf.directions.W] < 1) then + castle.base[geoelf.directions.W] = 1 + end + if (castle.base[geoelf.directions.E] > gxm - 2) then + castle.base[geoelf.directions.E] = gxm - 2 + end + if (castle.base[geoelf.directions.N] < 1) then + castle.base[geoelf.directions.N] = 1 + end + if (castle.base[geoelf.directions.S] > gym - 2) then + castle.base[geoelf.directions.S] = gym - 2 + end + + for i = 0, geoelf.directions.COUNT_STRAIGHT - 1 do + local step_sign = geoelf.directions.STEP_SIGN[i] + + if (is_castle[i]) then + castle.inner[i] = castle.base[i] + CASTLE_INNER * step_sign + castle.hallway[i] = castle.inner[i] + CASTLE_HALLWAY * step_sign + castle.moat[i] = castle.hallway[i] + CASTLE_MOAT * step_sign + castle.open[i] = castle.moat[i] + CASTLE_OPEN * step_sign + castle.overwritable[i] = castle.open[i] + CASTLE_OVERWRITABLE * step_sign + else + castle.inner[i] = castle.base[i] + step_sign + castle.hallway[i] = castle.base[i] + step_sign + castle.moat[i] = castle.base[i] + castle.open[i] = castle.base[i] + castle.overwritable[i] = castle.base[i] + end + end + + if (DEBUG_CASTLE_DESIGN) then + print("Castle Bounds") + print(" Base: (" .. castle.base[geoelf.directions.W] + .. " - " .. castle.base[geoelf.directions.E] + .. ", " .. castle.base[geoelf.directions.N] + .. " - " .. castle.base[geoelf.directions.S] .. ")") + print(" Inner: (" .. castle.inner[geoelf.directions.W] + .. " - " .. castle.inner[geoelf.directions.E] + .. ", " .. castle.inner[geoelf.directions.N] + .. " - " .. castle.inner[geoelf.directions.S] .. ")") + print(" Hallway: (" .. castle.hallway[geoelf.directions.W] + .. " - " .. castle.hallway[geoelf.directions.E] + .. ", " .. castle.hallway[geoelf.directions.N] + .. " - " .. castle.hallway[geoelf.directions.S] .. ")") + print(" Moat: (" .. castle.moat[geoelf.directions.W] + .. " - " .. castle.moat[geoelf.directions.E] + .. ", " .. castle.moat[geoelf.directions.N] + .. " - " .. castle.moat[geoelf.directions.S] .. ")") + print(" Open: (" .. castle.open[geoelf.directions.W] + .. " - " .. castle.open[geoelf.directions.E] + .. ", " .. castle.open[geoelf.directions.N] + .. " - " .. castle.open[geoelf.directions.S] .. ")") + print(" Overwritable: (" .. castle.overwritable[geoelf.directions.W] + .. " - " .. castle.overwritable[geoelf.directions.E] + .. ", " .. castle.overwritable[geoelf.directions.N] + .. " - " .. castle.overwritable[geoelf.directions.S] + .. ")") + end + + -- choose which turrets to add + for i = geoelf.directions.COUNT_STRAIGHT, geoelf.directions.COUNT - 1 do + local component_x = geoelf.directions.COMPONENT_X[i] + local component_y = geoelf.directions.COMPONENT_Y[i] + + castle.turret[i] = {} + castle.turret[i].is_placed = ( is_castle[component_x] + and is_castle[component_y]) + -- turret position is linked to inner rooms, not hallway + castle.turret[i].x = castle.inner[component_x] + + geoelf.directions.STEP_SIGN[component_x] + castle.turret[i].y = castle.inner[component_y] + + geoelf.directions.STEP_SIGN[component_y] + end + + + + -- + -- PHASE 4: Choose which rooms to add to the basic grid + -- outside the castle + -- + + local edge_rooms = {} + + -- spacing of north and south rooms along x axis + local min_x = castle.overwritable[geoelf.directions.W] + ROOMS_INTO_CASTLE + local max_x = castle.overwritable[geoelf.directions.E] - ROOMS_INTO_CASTLE + local range_x = max_x - min_x + edge_rooms.count_x = math.floor(range_x / ROOMS_SPACING_MIN) + 1 + local range_x_min = (edge_rooms.count_x - 1) * ROOMS_SPACING_MIN + edge_rooms.spacing_x = ROOMS_SPACING_MIN + edge_rooms.allow_diagonals_x = true + + if (is_rooms[geoelf.directions.W]) then + if (is_rooms[geoelf.directions.E]) then + -- spread out the rooms to connect on both sides + -- -> this prevents diagonal connections on the side + edge_rooms.min_x = min_x + edge_rooms.spacing_x = range_x / (edge_rooms.count_x - 1) + edge_rooms.allow_diagonals_x = false + else + -- pack the rooms against the west side + edge_rooms.min_x = min_x + end + else + if (is_rooms[geoelf.directions.E]) then + -- pack the rooms against the east side + edge_rooms.min_x = max_x - range_x_min + else + -- center the rooms + local min_x_max = max_x - range_x_min + edge_rooms.min_x = math.floor((min_x + min_x_max) / 2) + end + end + + -- spacing of east and west rooms along x axis + local min_y = castle.overwritable[geoelf.directions.N] + ROOMS_INTO_CASTLE + local max_y = castle.overwritable[geoelf.directions.S] - ROOMS_INTO_CASTLE + local range_y = max_y - min_y + edge_rooms.count_y = math.floor(range_y / ROOMS_SPACING_MIN) + 1 + local range_y_min = (edge_rooms.count_y - 1) * ROOMS_SPACING_MIN + edge_rooms.spacing_y = ROOMS_SPACING_MIN + edge_rooms.allow_diagonals_y = true + + if (is_rooms[geoelf.directions.N]) then + if (is_rooms[geoelf.directions.S]) then + -- spread out the rooms to connect on both sides + -- -> this prevents diagonal connections on the side + edge_rooms.min_y = min_y + edge_rooms.spacing_y = range_y / (edge_rooms.count_y - 1) + edge_rooms.allow_diagonals_y = false + else + -- pack the rooms against the east side + edge_rooms.min_y = min_y + end + else + if (is_rooms[geoelf.directions.S]) then + -- pack the rooms against the west side + edge_rooms.min_y = max_y - range_y_min + else + -- center the rooms + local min_y_max = max_y - range_y_min + edge_rooms.min_y = math.floor((min_y + min_y_max) / 2) + end + end + + -- calculate row positions + edge_rooms.row_base = {} + edge_rooms.row_base[geoelf.directions.W] = + castle.overwritable[geoelf.directions.W] - ROOMS_FROM_CASTLE + edge_rooms.row_base[geoelf.directions.E] = + castle.overwritable[geoelf.directions.E] + ROOMS_FROM_CASTLE + edge_rooms.row_base[geoelf.directions.N] = + castle.overwritable[geoelf.directions.N] - ROOMS_FROM_CASTLE + edge_rooms.row_base[geoelf.directions.S] = + castle.overwritable[geoelf.directions.S] + ROOMS_FROM_CASTLE + + edge_rooms.row_count = {} + for i = 0, geoelf.directions.COUNT_STRAIGHT - 1 do + if (is_rooms[i] == false) then + edge_rooms.row_count[i] = 0 + else + local available_space = primary_vault.border[i] + - (ROOMS_FROM_CASTLE + ROOMS_BORDER_MIN) + 1 + edge_rooms.row_count[i] = math.floor(available_space / ROOMS_SPACING_MIN) + if (edge_rooms.row_count[i] <= 0) then + crawl.mpr("Error in layout_geoelf_castle: 0 rows in direction " .. + i .. " filling " .. ROOMS_SPACING_MIN .. " / " .. + available_space .. " space") + end + end + end + + if (DEBUG_CASTLE_DESIGN) then + print("Rooms by edge") + + print(" Rooms along castle X: " .. edge_rooms.count_x) + print(" Rooms along castle X: " .. edge_rooms.count_x) + print(" Rooms minimum spacing: " .. ROOMS_SPACING_MIN .. + " (straight), " .. ROOMS_SPACING_EDGE .. " (diagonal) + " .. + ROOMS_BORDER_MIN .. " (border)") + + local LETTERS = + { [geoelf.directions.S] = "S", + [geoelf.directions.N] = "N", + [geoelf.directions.E] = "E", + [geoelf.directions.W] = "W" } + + for i = 0, geoelf.directions.COUNT_STRAIGHT - 1 do + local step_sign = geoelf.directions.STEP_SIGN[i] + local line = " " .. LETTERS[i] .. " " .. edge_rooms.row_count[i] .. + (is_rooms[i] and " Y" or " N") .. ": " + for j = 0, edge_rooms.row_count[i] - 1 do + local pos = edge_rooms.row_base[i] + j * ROOMS_SPACING_MIN * step_sign + line = line .. " " .. pos + end + print(line) + end + end + + -- combine all x positions into an array + local room_pos_x = {} + room_pos_x.begin_center = edge_rooms.row_count[geoelf.directions.W] + room_pos_x.begin_east = room_pos_x.begin_center + edge_rooms.count_x + room_pos_x.total = room_pos_x.begin_east + + edge_rooms.row_count[geoelf.directions.E] + room_pos_x.last = room_pos_x.total - 1 + local is_room_diagonals_x = {} + for i = 0, room_pos_x.total - 1 do + is_room_diagonals_x[i] = true + if (i < room_pos_x.begin_center) then + local rows_out = room_pos_x.begin_center - i - 1 + room_pos_x[i] = edge_rooms.row_base[geoelf.directions.W] + - rows_out * ROOMS_SPACING_MIN + elseif (i < room_pos_x.begin_east) then + local rows_along = i - room_pos_x.begin_center + room_pos_x[i] = edge_rooms.min_x + + math.floor(rows_along * edge_rooms.spacing_x) + is_room_diagonals_x[i] = edge_rooms.allow_diagonals_x + else + local rows_out = i - room_pos_x.begin_east + room_pos_x[i] = edge_rooms.row_base[geoelf.directions.E] + + rows_out * ROOMS_SPACING_MIN + end + end + + -- combine all y positions into an array + local room_pos_y = {} + room_pos_y.begin_center = edge_rooms.row_count[geoelf.directions.N] + room_pos_y.begin_south = room_pos_y.begin_center + edge_rooms.count_y + room_pos_y.total = room_pos_y.begin_south + + edge_rooms.row_count[geoelf.directions.S] + room_pos_y.last = room_pos_y.total - 1 + local is_room_diagonals_y = {} + for i = 0, room_pos_y.total - 1 do + is_room_diagonals_y[i] = true + if (i < room_pos_y.begin_center) then + local rows_out = room_pos_y.begin_center - i - 1 + room_pos_y[i] = edge_rooms.row_base[geoelf.directions.N] + - rows_out * ROOMS_SPACING_MIN + elseif (i < room_pos_y.begin_south) then + local rows_along = i - room_pos_y.begin_center + room_pos_y[i] = edge_rooms.min_y + + math.floor(rows_along * edge_rooms.spacing_y) + is_room_diagonals_y[i] = edge_rooms.allow_diagonals_y + else + local rows_out = i - room_pos_y.begin_south + room_pos_y[i] = edge_rooms.row_base[geoelf.directions.S] + + rows_out * ROOMS_SPACING_MIN + end + end + + if (DEBUG_CASTLE_DESIGN) then + print("Room grid positions") + + local line = " X " .. room_pos_x.total .. ":" + for i = 0, room_pos_x.total - 1 do + line = line .. " " .. room_pos_x[i] + if (not is_room_diagonals_x[i]) then + line = line .. " (NODIAG)" + end + end + print(line) + + local line = " Y " .. room_pos_y.total .. ":" + for i = 0, room_pos_y.total - 1 do + line = line .. " " .. room_pos_y[i] + if (not is_room_diagonals_y[i]) then + line = line .. " (NODIAG)" + end + end + print(line) + end + + + + -- + -- PHASE 5: Calculate the edge rows of rooms to add + -- + + local is_edge_row = {} + is_edge_row[geoelf.directions.W] = (room_pos_x[0] >= ROOMS_MIN_SPACE_EDGE) + is_edge_row[geoelf.directions.E] = (gxm - room_pos_x[room_pos_x.last] + >= ROOMS_MIN_SPACE_EDGE) + is_edge_row[geoelf.directions.N] = (room_pos_y[0] >= ROOMS_MIN_SPACE_EDGE) + is_edge_row[geoelf.directions.S] = (gym - room_pos_y[room_pos_y.last] + >= ROOMS_MIN_SPACE_EDGE) + + -- setting these all is easier than working out which ones we need + local edge_row_at = {} + edge_row_at[geoelf.directions.W] = room_pos_x[0] - ROOMS_SPACING_EDGE + edge_row_at[geoelf.directions.E] = room_pos_x[room_pos_x.last] + + ROOMS_SPACING_EDGE + edge_row_at[geoelf.directions.N] = room_pos_y[0] - ROOMS_SPACING_EDGE + edge_row_at[geoelf.directions.S] = room_pos_y[room_pos_y.last] + + ROOMS_SPACING_EDGE + + -- add a corner edge rooms if we have both edge sides + local is_edge_corner = {} + for i = geoelf.directions.COUNT_STRAIGHT, geoelf.directions.COUNT - 1 do + local component_x = geoelf.directions.COMPONENT_X[i] + local component_y = geoelf.directions.COMPONENT_Y[i] + is_edge_corner[i] = is_edge_row[component_x] and + is_edge_row[component_y] and + (edge_rooms.row_count[component_x] >= 1 or + edge_rooms.row_count[component_y] >= 1 ) + end + + if (DEBUG_CASTLE_DESIGN) then + print("Edge rows") + + if (is_edge_row[geoelf.directions.W]) then + print(" W: x = " .. edge_row_at[geoelf.directions.W]) + end + if (is_edge_row[geoelf.directions.E]) then + print(" E: x = " .. edge_row_at[geoelf.directions.E]) + end + if (is_edge_row[geoelf.directions.N]) then + print(" N: y = " .. edge_row_at[geoelf.directions.N]) + end + if (is_edge_row[geoelf.directions.S]) then + print(" S: y = " .. edge_row_at[geoelf.directions.S]) + end + + if (is_edge_corner[geoelf.directions.SE]) then + print(" SE corner") + end + if (is_edge_corner[geoelf.directions.NW]) then + print(" NW corner") + end + if (is_edge_corner[geoelf.directions.SS]) then + print(" SW corner") + end + if (is_edge_corner[geoelf.directions.NE]) then + print(" NE corner") + end + end + + + + -- + -- PHASE 6: Add the main grid of rooms outside the castle + -- + + -- create the arrays + local room_data = { count = 0 } + local corridor_data = { count = 0 } + local grid_rooms_by_position = {} + + -- add rooms for the main grid + for y = 0, room_pos_y.total - 1 do + grid_rooms_by_position[y] = {} + for x = 0, room_pos_x.total - 1 do + local x_pos = room_pos_x[x] + + crawl.random_range(-ROOMS_OFFSET_MAX, ROOMS_OFFSET_MAX) + local y_pos = room_pos_y[y] + + crawl.random_range(-ROOMS_OFFSET_MAX, ROOMS_OFFSET_MAX) + local radius = crawl.random_range(ROOMS_RADIUS_MIN, ROOMS_RADIUS_MAX) + + if (x < room_pos_x.begin_center or + x >= room_pos_x.begin_east or + y < room_pos_y.begin_center or + y >= room_pos_y.begin_south ) then + grid_rooms_by_position[y][x] = geoelf.add_room(room_data, + x_pos, y_pos, radius) + else + grid_rooms_by_position[y][x] = geoelf.add_room_dummy(room_data, + x_pos, y_pos) + end + end + end + + -- add potential corridors + -- -> at least one room must be non-dummy + -- -> only add diagonals if at least one room allows them + -- (we don't need both) + for y = 0, room_pos_y.total - 1 do + for x = 0, room_pos_x.total - 1 do + local index = grid_rooms_by_position[y][x] + local allow_diagonals = is_room_diagonals_x[x] and + is_room_diagonals_y[y] + + -- east-west + if (x >= 1) then + local other_index = grid_rooms_by_position[y][x - 1] + if (not geoelf.is_room_dummy(room_data, index) or + not geoelf.is_room_dummy(room_data, other_index)) then + geoelf.add_corridor(room_data, corridor_data, + other_index, index, + geoelf.directions.E, nil) + end + end + + -- south-north + if (y >= 1) then + local other_index = grid_rooms_by_position[y - 1][x] + if (not geoelf.is_room_dummy(room_data, index) or + not geoelf.is_room_dummy(room_data, other_index)) then + geoelf.add_corridor(room_data, corridor_data, + other_index, index, + geoelf.directions.S, nil) + end + end + + -- southeast-northwest + if (y >= 1 and x >= 1) then + local other_index = grid_rooms_by_position[y - 1][x - 1] + local allow_diagonals_other = is_room_diagonals_x[x - 1] and + is_room_diagonals_y[y - 1] + if (not geoelf.is_room_dummy(room_data, index) or + not geoelf.is_room_dummy(room_data, other_index)) then + if(allow_diagonals or allow_diagonals_other) then + + -- determine if there is another corridor that would + -- block this one (i.e. an NE-SW one) + local block_index = nil + local old_index = grid_rooms_by_position[y][x - 1] + if (not geoelf.is_room_dummy(room_data, old_index)) then + block_index = room_data[old_index].corridor[geoelf.directions.NE] + end + + geoelf.add_corridor(room_data, corridor_data, + other_index, index, + geoelf.directions.SE, block_index) + end + end + end + + -- southwest-northeast + if (y >= 1 and x + 1 < room_pos_x.total) then + local other_index = grid_rooms_by_position[y - 1][x + 1] + local allow_diagonals_other = is_room_diagonals_x[x + 1] and + is_room_diagonals_y[y - 1] + if (not geoelf.is_room_dummy(room_data, index) or + not geoelf.is_room_dummy(room_data, other_index)) then + if(allow_diagonals or allow_diagonals_other) then + geoelf.add_corridor(room_data, corridor_data, + other_index, index, + geoelf.directions.SW, nil) + end + end + end + + -- done this room + end + end + + + + -- + -- PHASE 7: Add edge rows of rooms + -- -> Rooms must be push as far as possible away from + -- the main grid to avoid having too much overlap + -- + + -- corners + local corner_index = {} + + for i = geoelf.directions.COUNT_STRAIGHT, geoelf.directions.COUNT - 1 do + if (is_edge_corner[i]) then + local component_x = geoelf.directions.COMPONENT_X[i] + local component_y = geoelf.directions.COMPONENT_Y[i] + + local radius = crawl.random_range(ROOMS_RADIUS_EDGE_MIN, + ROOMS_RADIUS_EDGE_MAX) + corner_index[i] = geoelf.add_room(room_data, + edge_row_at[component_x], + edge_row_at[component_y], radius) + + local other_index_x = (component_x == geoelf.directions.W) + and 0 or room_pos_x.last + local other_index_y = (component_y == geoelf.directions.N) + and 0 or room_pos_y.last + local other_index = grid_rooms_by_position[other_index_y][other_index_x] + geoelf.add_corridor(room_data, corridor_data, + other_index, corner_index[i], i, nil) + end + end + + -- + -- NOTE: I tried combining these four cases into a single + -- loop, but it ended up longer than the 4 cases, + -- very hard to understand (even by the standards of my + -- convoluted code), and didn't work reliably. + -- ~ infiniplex + -- + + -- The west side (north->south) + if (is_edge_row[geoelf.directions.W]) then + local x_pos = edge_row_at[geoelf.directions.W] + local previous_index = corner_index[geoelf.directions.NW] + for y = 0, room_pos_y.total - 2 do + local other_index_NE = grid_rooms_by_position[y ][0] + local other_index_SE = grid_rooms_by_position[y + 1][0] + if (not geoelf.is_room_dummy(room_data, other_index_NE) and + not geoelf.is_room_dummy(room_data, other_index_SE)) then + local y_pos = math.floor((room_pos_y[y] + room_pos_y[y + 1]) / 2) + crawl.random_range(-ROOMS_OFFSET_MAX, ROOMS_OFFSET_MAX) + local radius = crawl.random_range(ROOMS_RADIUS_EDGE_MIN, + ROOMS_RADIUS_EDGE_MAX) + local new_index = geoelf.add_room(room_data, x_pos, y_pos, radius) + + local allow_diagonals_here = true + if (y >= room_pos_y.begin_center and + y < room_pos_y.begin_south - 1) then + allow_diagonals_here = edge_rooms.allow_diagonals_y + end + local allow_diagonals_NE = is_room_diagonals_y[y] + local allow_diagonals_SE = is_room_diagonals_y[y + 1] + + if (allow_diagonals_here or allow_diagonals_NE) then + geoelf.add_corridor(room_data, corridor_data, + new_index, other_index_NE, + geoelf.directions.NE, nil) + end + if (allow_diagonals_here or allow_diagonals_SE) then + geoelf.add_corridor(room_data, corridor_data, + new_index, other_index_SE, + geoelf.directions.SE, nil) + end + if (previous_index ~= nil) then + geoelf.add_corridor(room_data, corridor_data, + new_index, previous_index, + geoelf.directions.N, nil) + end + + previous_index = new_index + end + end + if (corner_index[geoelf.directions.SW] ~= nil and + previous_index ~= nil) then + geoelf.add_corridor(room_data, corridor_data, + corner_index[geoelf.directions.SW], previous_index, + geoelf.directions.N, nil) + end + end + + -- The east side (north->south) + if (is_edge_row[geoelf.directions.E]) then + local x_pos = edge_row_at[geoelf.directions.E] + local previous_index = corner_index[geoelf.directions.NE] + for y = 0, room_pos_y.total - 2 do + local other_index_NW = grid_rooms_by_position[y ][room_pos_x.last] + local other_index_SW = grid_rooms_by_position[y + 1][room_pos_x.last] + if (not geoelf.is_room_dummy(room_data, other_index_NW) and + not geoelf.is_room_dummy(room_data, other_index_SW)) then + local y_pos = math.floor((room_pos_y[y] + room_pos_y[y + 1]) / 2) + crawl.random_range(-ROOMS_OFFSET_MAX, ROOMS_OFFSET_MAX) + local radius = crawl.random_range(ROOMS_RADIUS_EDGE_MIN, + ROOMS_RADIUS_EDGE_MAX) + local new_index = geoelf.add_room(room_data, x_pos, y_pos, radius) + + local allow_diagonals_here = true + if (y >= room_pos_y.begin_center and + y < room_pos_y.begin_south - 1) then + allow_diagonals_here = edge_rooms.allow_diagonals_y + end + local allow_diagonals_NW = is_room_diagonals_y[y] + local allow_diagonals_SW = is_room_diagonals_y[y + 1] + + if (allow_diagonals_here or allow_diagonals_NW) then + geoelf.add_corridor(room_data, corridor_data, + new_index, other_index_NW, + geoelf.directions.NW, nil) + end + if (allow_diagonals_here or allow_diagonals_SW) then + geoelf.add_corridor(room_data, corridor_data, + new_index, other_index_SW, + geoelf.directions.SW, nil) + end + if (previous_index ~= nil) then + geoelf.add_corridor(room_data, corridor_data, + new_index, previous_index, + geoelf.directions.N, nil) + end + + previous_index = new_index + end + end + if (corner_index[geoelf.directions.SE] ~= nil and + previous_index ~= nil) then + geoelf.add_corridor(room_data, corridor_data, + corner_index[geoelf.directions.SE], previous_index, + geoelf.directions.N, nil) + end + end + + -- The north side (west->east) + if (is_edge_row[geoelf.directions.N]) then + local y_pos = edge_row_at[geoelf.directions.N] + local previous_index = corner_index[geoelf.directions.NW] + for x = 0, room_pos_x.total - 2 do + local other_index_SW = grid_rooms_by_position[0][x] + local other_index_SE = grid_rooms_by_position[0][x + 1] + if (not geoelf.is_room_dummy(room_data, other_index_SW) and + not geoelf.is_room_dummy(room_data, other_index_SE)) then + local x_pos = math.floor((room_pos_x[x] + room_pos_x[x + 1]) / 2) + crawl.random_range(-ROOMS_OFFSET_MAX, ROOMS_OFFSET_MAX) + local radius = crawl.random_range(ROOMS_RADIUS_EDGE_MIN, + ROOMS_RADIUS_EDGE_MAX) + local new_index = geoelf.add_room(room_data, x_pos, y_pos, radius) + + local allow_diagonals_here = true + if (x >= room_pos_x.begin_center and + x < room_pos_x.begin_east - 1) then + allow_diagonals_here = edge_rooms.allow_diagonals_x + end + local allow_diagonals_SW = is_room_diagonals_x[x] + local allow_diagonals_SE = is_room_diagonals_x[x + 1] + + if (allow_diagonals_here or allow_diagonals_SW) then + geoelf.add_corridor(room_data, corridor_data, + new_index, other_index_SW, + geoelf.directions.SW, nil) + end + if (allow_diagonals_here or allow_diagonals_SE) then + geoelf.add_corridor(room_data, corridor_data, + new_index, other_index_SE, + geoelf.directions.SE, nil) + end + if (previous_index ~= nil) then + geoelf.add_corridor(room_data, corridor_data, + new_index, previous_index, + geoelf.directions.W, nil) + end + + previous_index = new_index + end + end + if (corner_index[geoelf.directions.NE] ~= nil and + previous_index ~= nil) then + geoelf.add_corridor(room_data, corridor_data, + corner_index[geoelf.directions.NE], previous_index, + geoelf.directions.W, nil) + end + end + + -- The south side (west->east) + if (is_edge_row[geoelf.directions.S]) then + local y_pos = edge_row_at[geoelf.directions.S] + local previous_index = corner_index[geoelf.directions.SW] + for x = 0, room_pos_x.total - 2 do + local other_index_NW = grid_rooms_by_position[room_pos_y.last][x] + local other_index_NE = grid_rooms_by_position[room_pos_y.last][x + 1] + if (not geoelf.is_room_dummy(room_data, other_index_NW) and + not geoelf.is_room_dummy(room_data, other_index_NE)) then + local x_pos = math.floor((room_pos_x[x] + room_pos_x[x + 1]) / 2) + crawl.random_range(-ROOMS_OFFSET_MAX, ROOMS_OFFSET_MAX) + local radius = crawl.random_range(ROOMS_RADIUS_EDGE_MIN, + ROOMS_RADIUS_EDGE_MAX) + local new_index = geoelf.add_room(room_data, x_pos, y_pos, radius) + + local allow_diagonals_here = true + if (x >= room_pos_x.begin_center and + x < room_pos_x.begin_east - 1) then + allow_diagonals_here = edge_rooms.allow_diagonals_x + end + local allow_diagonals_NW = is_room_diagonals_x[x] + local allow_diagonals_NE = is_room_diagonals_x[x + 1] + + if (allow_diagonals_here or allow_diagonals_NW) then + geoelf.add_corridor(room_data, corridor_data, + new_index, other_index_NW, + geoelf.directions.NW, nil) + end + if (allow_diagonals_here or allow_diagonals_NE) then + geoelf.add_corridor(room_data, corridor_data, + new_index, other_index_NE, + geoelf.directions.NE, nil) + end + if (previous_index ~= nil) then + geoelf.add_corridor(room_data, corridor_data, + new_index, previous_index, + geoelf.directions.W, nil) + end + + previous_index = new_index + end + end + if (corner_index[geoelf.directions.SE] ~= nil and + previous_index ~= nil) then + geoelf.add_corridor(room_data, corridor_data, + corner_index[geoelf.directions.SE], previous_index, + geoelf.directions.W, nil) + end + end + + + + -- + -- PHASE 8: Draw the everything except the castle + -- + + -- draw the open area around the castle + + fill_area { x1 = castle.hallway[geoelf.directions.W] + 1, + x2 = castle.hallway[geoelf.directions.E] - 1, + y1 = castle.overwritable[geoelf.directions.N], + y2 = castle.overwritable[geoelf.directions.S], + fill = geoelf.glyphs.FLOOR } + fill_area { x1 = castle.overwritable[geoelf.directions.W], + x2 = castle.overwritable[geoelf.directions.E], + y1 = castle.hallway[geoelf.directions.N] + 1, + y2 = castle.hallway[geoelf.directions.S] - 1, + fill = geoelf.glyphs.FLOOR } + + for i = geoelf.directions.COUNT_STRAIGHT, geoelf.directions.COUNT - 1 do + if (castle.turret[i].is_placed) then + make_circle { x = castle.turret[i].x, y = castle.turret[i].y, + radius = CASTLE_TURRET_OVERWRITABLE_RADIUS, + fill = geoelf.glyphs.FLOOR } + end + end + + -- run the geoelf generator to add the rooms + -- -> note that this can interact with the castle area + geoelf.generate(_G, room_data, corridor_data, ROOMS_EXTRA_CORRIDOR_FRACTION, + ROOMS_FANCY_ROOM_FRACTION, false, false) + + + + -- + -- PHASE 9: Build the castle + -- -> This much of the map is non-overwritable + -- + + -- draw the non-overwritable open area around the castle + fill_area { x1 = castle.open[geoelf.directions.W], + x2 = castle.open[geoelf.directions.E], + y1 = castle.open[geoelf.directions.N], + y2 = castle.open[geoelf.directions.S], fill = INSIDE_GLYPH } + + for i = geoelf.directions.COUNT_STRAIGHT, geoelf.directions.COUNT - 1 do + if (castle.turret[i].is_placed) then + make_circle { x = castle.turret[i].x, y = castle.turret[i].y, + radius = CASTLE_TURRET_OPEN_RADIUS, fill = INSIDE_GLYPH } + end + end + + -- draw the moat + fill_area { x1 = castle.moat[geoelf.directions.W], + x2 = castle.moat[geoelf.directions.E], + y1 = castle.moat[geoelf.directions.N], + y2 = castle.moat[geoelf.directions.S], fill = MOAT_GLYPH } + + for i = geoelf.directions.COUNT_STRAIGHT, geoelf.directions.COUNT - 1 do + if (castle.turret[i].is_placed) then + make_circle { x = castle.turret[i].x, y = castle.turret[i].y, + radius = CASTLE_TURRET_MOAT_RADIUS, fill = MOAT_GLYPH } + end + end + + -- draw the hallways + fill_area { x1 = castle.hallway[geoelf.directions.W], + x2 = castle.hallway[geoelf.directions.E], + y1 = castle.hallway[geoelf.directions.N], + y2 = castle.hallway[geoelf.directions.S], + border = WALL_GLYPH, fill = INSIDE_GLYPH } + + -- draw the inner rooms + fill_area { x1 = castle.inner[geoelf.directions.W], + x2 = castle.inner[geoelf.directions.E], + y1 = castle.inner[geoelf.directions.N], + y2 = castle.inner[geoelf.directions.S], + border = WALL_GLYPH, fill = INSIDE_GLYPH } + + -- draw horizontal walls + local wall_at = castle.inner[geoelf.directions.W] + local wall_end = castle.inner[geoelf.directions.E] - INNER_WALL_SPACING_MIN + while (wall_at < wall_end) do + fill_area { x1 = wall_at, x2 = wall_at, + y1 = castle.inner[geoelf.directions.N], + y2 = castle.inner[geoelf.directions.S], + fill = WALL_GLYPH } + wall_at = wall_at + crawl.random_range(INNER_WALL_SPACING_MIN, + INNER_WALL_SPACING_MAX) + end + + -- draw vertical walls + wall_at = castle.inner[geoelf.directions.N] + wall_end = castle.inner[geoelf.directions.S] - INNER_WALL_SPACING_MIN + while (wall_at < wall_end) do + fill_area { x1 = castle.inner[geoelf.directions.W], + x2 = castle.inner[geoelf.directions.E], + y1 = wall_at, y2 = wall_at, + fill = WALL_GLYPH } + wall_at = wall_at + crawl.random_range(INNER_WALL_SPACING_MIN, + INNER_WALL_SPACING_MAX) + end + + -- draw the turrets + local turret_center = crawl.random_element { + [geoelf.glyphs.TREE] = geoelf.glyphs.PLANTLIKE_OPTIONS[geoelf.glyphs.TREE], + [geoelf.glyphs.BUSH] = geoelf.glyphs.PLANTLIKE_OPTIONS[geoelf.glyphs.BUSH], + [geoelf.glyphs.PLANT] = geoelf.glyphs.PLANTLIKE_OPTIONS[geoelf.glyphs.PLANT], + [geoelf.glyphs.FUNGUS] = geoelf.glyphs.PLANTLIKE_OPTIONS[geoelf.glyphs.FUNGUS], + [geoelf.glyphs.STATUE] = geoelf.glyphs.FEATURE_OPTIONS[geoelf.glyphs.STATUE] * 5, + [geoelf.glyphs.FOUNTAIN] = geoelf.glyphs.FEATURE_OPTIONS[geoelf.glyphs.FOUNTAIN] * 5, + [INSIDE_GLYPH] = 15, + } + + for i = geoelf.directions.COUNT_STRAIGHT, geoelf.directions.COUNT - 1 do + if (castle.turret[i].is_placed) then + make_round_box { x1 = castle.turret[i].x - CASTLE_TURRET_RADIUS, + x2 = castle.turret[i].x + CASTLE_TURRET_RADIUS, + y1 = castle.turret[i].y - CASTLE_TURRET_RADIUS, + y2 = castle.turret[i].y + CASTLE_TURRET_RADIUS, + floor = INSIDE_GLYPH, wall = WALL_GLYPH, + door = DOOR_GLYPH, + door_count = crawl.random_range(2, 4), + passable = INSIDE_GLYPH, veto_gates = false } + mapgrd[castle.turret[i].x][castle.turret[i].y] = turret_center + end + end + + -- draw the gate + local gate_size = crawl.random_range(GATE_SIZE_MIN, GATE_SIZE_MAX) + + if (gate_best == geoelf.directions.S or + gate_best == geoelf.directions.N) then + local gate_x_min = castle.hallway[geoelf.directions.W] + + GATE_FROM_CORNER_MIN + local gate_x_max = castle.hallway[geoelf.directions.E] + - GATE_FROM_CORNER_MIN + local gate_x = crawl.random_range(gate_x_min, gate_x_max - gate_size) + + local gate_y + if (gate_best == geoelf.directions.S) then + gate_y = castle.hallway[geoelf.directions.S] + fill_area { x1 = gate_x, x2 = gate_x + gate_size - 1, + y1 = gate_y + 1, y2 = gate_y + 1 + CASTLE_MOAT, + fill = INSIDE_GLYPH } + else + gate_y = castle.hallway[geoelf.directions.N] + fill_area { x1 = gate_x, x2 = gate_x + gate_size - 1, + y1 = gate_y - 1 - CASTLE_MOAT, y2 = gate_y - 1, + fill = INSIDE_GLYPH } + end + fill_area { x1 = gate_x, x2 = gate_x + gate_size - 1, + y1 = gate_y, y2 = gate_y, + fill = DOOR_GLYPH } + else + local gate_y_min = castle.hallway[geoelf.directions.N] + + GATE_FROM_CORNER_MIN + local gate_y_max = castle.hallway[geoelf.directions.S] + - GATE_FROM_CORNER_MIN + local gate_y = crawl.random_range(gate_y_min, gate_y_max - gate_size) + + local gate_x + if (gate_best == geoelf.directions.E) then + gate_x = castle.hallway[geoelf.directions.E] + fill_area { x1 = gate_x + 1, x2 = gate_x + 1 + CASTLE_MOAT, + y1 = gate_y, y2 = gate_y + gate_size - 1, + fill = INSIDE_GLYPH } + else + gate_x = castle.hallway[geoelf.directions.W] + fill_area { x1 = gate_x - 1 - CASTLE_MOAT, x2 = gate_x - 1, + y1 = gate_y, y2 = gate_y + gate_size - 1, + fill = INSIDE_GLYPH } + end + fill_area { x1 = gate_x, x2 = gate_x, + y1 = gate_y, y2 = gate_y + gate_size - 1, + fill = DOOR_GLYPH } + end + + -- fill in the area around the castle + -- -> this area will be overridden + -- -> we need to make sure the map is connected without going + -- into this area + for y = 1, gym - 2 do + for x = 1, gxm - 2 do + if dgn.in_vault(x, y) then + mapgrd[x][y] = WALL_GLYPH + end + end + end + + -- add doors + local inner_area = (castle.inner[geoelf.directions.E] - + castle.inner[geoelf.directions.W] + 1) * + (castle.inner[geoelf.directions.S] - + castle.inner[geoelf.directions.N] + 1) + connect_adjacent_rooms { wall = WALL_GLYPH, floor = INSIDE_GLYPH, + replace = DOOR_GLYPH, + max = inner_area, check_distance = 8, + x1 = castle.inner[geoelf.directions.W] - 1, + x2 = castle.inner[geoelf.directions.E] + 1, + y1 = castle.inner[geoelf.directions.N] - 1, + y2 = castle.inner[geoelf.directions.S] + 1 } + + + + -- + -- PHASE 10: Miscellaneous Cleanup + -- + + -- apply special glyph meanings to castle + -- -> remember that the "=" must be the first glyph for parsing reasons + -- -> the castle cannot be overwritten + kmask(DOOR_GLYPH .. INSIDE_GLYPH .. MOAT_GLYPH .. WALL_GLYPH .. " = vault") + kfeat(DOOR_GLYPH .. " = closed_door") + kfeat(INSIDE_GLYPH .. " = floor") + + -- place the stairs outside the vault area + nsubst(geoelf.glyphs.FLOOR .. " = 1:{ / 1:( / 1:[ / *:" .. + geoelf.glyphs.FLOOR) + + -- Replace all floor cells around primary vault with "."s. + -- This is needed to prevent vetos + -- I think it is related to ensuring the primary vault is + -- connected to the layout + -- TODO: Why does it have to be "."s? Can this be fixed? + for y = 1, gym - 2 do + for x = 1, gxm - 2 do + if dgn.in_vault(x, y) then + for dy = -1, 1 do + for dx = -1, 1 do + if mapgrd[x + dx][y + dy] == INSIDE_GLYPH then + mapgrd[x + dx][y + dy] = "." + end + end + end + end + end + end + + -- Finally, draw the map for debugging reasons + if (DEBUG_CASTLE_DESIGN) then + for y = 0, gym - 1 do + local line = "" + for x = 0, gxm - 1 do + if dgn.in_vault(x, y) then + line = line .. " " + else + line = line .. mapgrd[x][y] + end + end + print(line) + end + print("") + end +}} + +veto {{ + -- Only allow this layout if there is a primary vault with + -- enough space around it for at least one line of rooms + + local gxm, gym = dgn.max_bounds() + local p_x_min, p_x_max, p_y_min, p_y_max = primary_vault_dimensions() + + if (p_x_min == nil) then + if (DEBUG_CASTLE_DESIGN) then + crawl.mpr("layout_geoelf_castle vetoed because no primary vault") + end + return true -- no primary vault + elseif (p_x_max - p_x_min < PRIMARY_VAULT_MIN_SIZE or + p_y_max - p_y_min < PRIMARY_VAULT_MIN_SIZE) then + -- primary vault is too small + if (DEBUG_CASTLE_DESIGN) then + crawl.mpr("layout_geoelf_castle vetoed because primary vault too small") + end + return true + elseif (p_x_min < PRIMARY_VAULT_MIN_PADDING and + p_x_max > gxm - 1 - PRIMARY_VAULT_MIN_PADDING and + p_y_min < PRIMARY_VAULT_MIN_PADDING and + p_y_max > gym - 1 - PRIMARY_VAULT_MIN_PADDING) then + -- not enough room around primary vault on any side + if (DEBUG_CASTLE_DESIGN) then + crawl.mpr("layout_geoelf_castle vetoed because primary vault too big") + end + return true + else + if (DEBUG_CASTLE_DESIGN) then + crawl.mpr("layout_geoelf_castle passed veto test") + print("Bounds: " .. p_x_min .. " - " .. p_x_max .. + ", " .. p_y_min .. " - " .. p_y_max) + print("Required: " .. PRIMARY_VAULT_MIN_PADDING .. + " - " .. (gxm - PRIMARY_VAULT_MIN_PADDING) .. + ", " .. PRIMARY_VAULT_MIN_PADDING .. + " - " .. (gym - PRIMARY_VAULT_MIN_PADDING)) + end + return false + end +}} +MAP +ENDMAP diff --git a/crawl-ref/source/dat/dlua/layout/geoelf.lua b/crawl-ref/source/dat/dlua/layout/geoelf.lua index c59813d..c2f12da 100644 --- a/crawl-ref/source/dat/dlua/layout/geoelf.lua +++ b/crawl-ref/source/dat/dlua/layout/geoelf.lua @@ -22,7 +22,7 @@ require("dlua/layout/geoelf_corridors.lua") ---------------------------------------------------------------- -- --- How to use the geoelf layout generator: +-- How to use the geoelf layout generator: -- -- 1. Initialize the room and corridor arrays in the layout. -- They will be needed as parameters to the subsequent @@ -51,6 +51,23 @@ require("dlua/layout/geoelf_corridors.lua") -- plant, fungus, statue, fountain -- (needs special tiles) -- +-- If you need to connect the portion of the map generated +-- using geoelf layout generator ("the geoelf layout") to a +-- portion of the map generated in a different way ("your +-- layout"): +-- +-- 1.-3. As above +-- 4. Use the add_room_dummy function to mark where on your +-- layout you want the geoelf layout to connect to +-- -> Nothing will be displayed for these rooms +-- 5. Add the possible corridors to connect the dummy rooms to +-- the geoelf layout +-- -> Not all corridors will appear, but at least one will +-- connect to each dummy room +-- -> If you connect the dummy rooms to each other, corridors +-- will be added between them as well +-- 6. Call the geoelf.generate function +-- @@ -79,7 +96,8 @@ require("dlua/layout/geoelf_corridors.lua") -- -> radius: The size from room center to edge -- -> zone: used to connect the rooms into a tree -- -> corridor: An array of the corridors indexed using --- direction values +-- direction values +-- -> is_dummy: Whether this room is a plaeholder -- -- The elements in the array of room data are numbered from 0 as -- in C++, not from 1 as recommended in LUA documentation. The @@ -116,6 +134,65 @@ function geoelf.add_room (room_data, center_x, center_y, radius) room_data[index].radius = radius room_data[index].zone = index room_data[index].corridor = {} + room_data[index].is_dummy = false + + for i = 0, geoelf.directions.COUNT - 1 do + room_data[index].corridor[i] = nil + end + + room_data.count = index + 1 + return index +end + +---------------- +-- +-- add_room_dummy +-- +-- Purpose: This function adds a dummy room at the specified +-- position to the list of rooms for the layout. A +-- dummy room is connected into the tree but is never +-- drawn to the layout. Dummy rooms should be used to +-- connect the geoelf room system to parts of a map +-- generate using a different method. +-- Parameter(s): +-- -> room_data: The array of room information. This new room +-- will be added to the array. +-- -> center_x +-- -> center_y: The x/y coordinates for the center of the room. +-- Returns: The index of the room. You will need this to +-- specify possible corridors. +-- +-- The room data structure is the same as for add_room +-- + +function geoelf.add_room_dummy (room_data, center_x, center_y) + if (room_data == nil) then + crawl.mpr("Error: No room data array") + end + if (room_data.count == nil) then + crawl.mpr("Error: Room data array does not have count") + end + if (center_x == nil) then + crawl.mpr("Error: center_x is nil") + end + if (center_y == nil) then + crawl.mpr("Error: center_y is nil") + end + + if (geoelf.debug) then + print("geoelf.add_room_dummy(room_data, " .. center_x .. ", " .. + center_y .. ")") + end + + local index = room_data.count + room_data[index] = {} + + room_data[index].center_x = center_x + room_data[index].center_y = center_y + room_data[index].radius = 1 + room_data[index].zone = index + room_data[index].corridor = {} + room_data[index].is_dummy = true for i = 0, geoelf.directions.COUNT - 1 do room_data[index].corridor[i] = nil @@ -127,6 +204,36 @@ end ---------------- -- +-- is_room_dummy +-- +-- Purpose: To determine if the room with the specified index +-- is a dummy room dummy room. +-- Parameter(s): +-- -> room_data: The array of room information. +-- -> index: The index of the room +-- Returns: Whether the room in room_data with index index is a +-- dummy room. +-- + +function geoelf.is_room_dummy (room_data, index) + if (room_data == nil) then + crawl.mpr("Error: No room data array") + end + if (room_data.count == nil) then + crawl.mpr("Error: Room data array does not have count") + end + if (index == nil) then + crawl.mpr("Error: index is nil") + end + if (index >= room_data.count) then + crawl.mpr("Error: index is larger than room count") + end + + return room_data[index].is_dummy +end + +---------------- +-- -- add_corridor -- -- Purpose: This function adds a possible corridor to the list @@ -271,6 +378,15 @@ function geoelf.generate (e, room_data, corridor_data, if (extra_fraction == nil) then crawl.mpr("Error: extra_fraction is nil") end + if (fancy_room_fraction == nil) then + crawl.mpr("Error: fancy_room_fraction is nil") + end + if (only_trees == nil) then + crawl.mpr("Error: only_trees is nil") + end + if (force_open_room_edge == nil) then + crawl.mpr("Error: force_open_room_edge is nil") + end if (geoelf.debug) then print("geoelf.generate(room_data, corridor_data, " .. extra_fraction .. ")") @@ -301,9 +417,11 @@ function geoelf.generate (e, room_data, corridor_data, -- draw rooms if (geoelf.debug) then print("Drawing rooms:") end for i = 0, room_data.count - 1 do - geoelf.rooms.draw(e, room_data, corridor_data, draw_order[i], - (crawl.random_real() < fancy_room_fraction), - force_open_room_edge) + if (not room_data[draw_order[i]].is_dummy) then + geoelf.rooms.draw(e, room_data, corridor_data, draw_order[i], + (crawl.random_real() < fancy_room_fraction), + force_open_room_edge) + end end -- substitutions @@ -335,8 +453,6 @@ function geoelf.generate (e, room_data, corridor_data, end end - - ---------------- -- -- This is the helper function that turns the doors in glass diff --git a/crawl-ref/source/dat/dlua/layout/geoelf_directions.lua b/crawl-ref/source/dat/dlua/layout/geoelf_directions.lua index 74c4506..35b9482 100644 --- a/crawl-ref/source/dat/dlua/layout/geoelf_directions.lua +++ b/crawl-ref/source/dat/dlua/layout/geoelf_directions.lua @@ -7,7 +7,7 @@ -- direction. ------------------------------------------------------------------------------ -geoelf.directions = {} -- Namespace for direction constants, etc. +geoelf.directions = {} -- Namespace for direction constants, etc. @@ -16,6 +16,7 @@ geoelf.directions = {} -- Namespace for direction constants, etc. -- geoelf.directions.COUNT = 8 +geoelf.directions.COUNT_STRAIGHT = 4 -- -- The possible directions @@ -63,3 +64,32 @@ geoelf.directions.GET_REVERSE = [geoelf.directions.NW] = geoelf.directions.SE, [geoelf.directions.SW] = geoelf.directions.NE, [geoelf.directions.NE] = geoelf.directions.SW } + +-- +-- Moving along any straight directions involve repeatedly +-- either increasing or decreasing the component of the +-- position. This can be seen as either adding 1 or -1. +-- + +geoelf.directions.STEP_SIGN = + { [geoelf.directions.S] = 1, + [geoelf.directions.N] = -1, + [geoelf.directions.E] = 1, + [geoelf.directions.W] = -1 } + +-- +-- Each diagonal direction is a combination of 2 straight +-- directions. +-- + +geoelf.directions.COMPONENT_X = + { [geoelf.directions.SE] = geoelf.directions.E, + [geoelf.directions.NW] = geoelf.directions.W, + [geoelf.directions.SW] = geoelf.directions.W, + [geoelf.directions.NE] = geoelf.directions.E } + +geoelf.directions.COMPONENT_Y = + { [geoelf.directions.SE] = geoelf.directions.S, + [geoelf.directions.NW] = geoelf.directions.N, + [geoelf.directions.SW] = geoelf.directions.S, + [geoelf.directions.NE] = geoelf.directions.N } -- 1.8.1.2 ![]() |
||||||||||||
|
![]() |
|
(0027135) infiniplex (reporter) 2014-08-31 23:52 |
The image shows 2 instances of the layout with each primary vault (except grunt_elf_hall_spiral, which is too big and thus triggers a veto). I also tested it with a very small dummy Elf:$ vault, and the layout can correctly place rooms on all sides. |
(0027139) infiniplex (reporter) 2014-09-02 05:21 edited on: 2014-10-05 01:53 |
Changes from IRC (collected so I don't forget): 1. Make the outer hallway 2 wide 2. The weight should not be too high. 1/5 of the time was suggested as a maximum. gammafunk says 15, then 20. 3. It is kind of homogenizing 4. It overwhelms the map 5. Stone walls are dangerous, but should stay provisionally. |
(0027453) infiniplex (reporter) 2014-10-07 22:25 edited on: 2014-10-07 22:27 |
Here is the revised geoelf castle. Changes: 1. Outer hallways is now 2 cells wide, turrets are bigger. 2. Weight is now 15 instead of 35. 3. Fixed two whitespace errors. 4. The things in the corners are consistently referred to as turrets instead of sometime turrets and sometimes towers. 5. Fixed a rare bug where the complete castle could appear without any rooms around it. It still requires the first patch on issue 0008857, unfortunately. |
(0027466) gammafunk (administrator) 2014-10-11 19:49 |
Pushed in a batch ending with 0.16-a0-1211-ge393dcd, thanks! |
Mantis 1.1.8[^] Copyright © 2000 - 2009 Mantis Group |