1###############################################################################
2# layout_geoelf.des: Layouts for Elf with geometric rooms
3#                    These are not the only Elf layouts
4#                    These layouts will build around primary vaults correctly.
5#                    There is another geoelf layout in layout_geoelf_castle.des.
6###############################################################################
7
8# Required file contains overview
9: crawl_require("dlua/layout/geoelf.lua")
10
11##############################################################
12# layout_geoelf_grid
13#
14# A grid of rooms, inspired by layout_roguey.  There are no
15#  missing rooms because they mess up the corridors between
16#  rooms and can disconnect the map (or connect it through a
17#  primary vault and have the connection be overwritten).
18#
19# These 3 layouts could be combined into 1 big layout, but it
20# would be as long as the three combined, so it would just make
21# them harder to read.
22#
23#  O-O-O-O-O
24#  |X|X|X|X|      O-O-O-O
25#  O-O-O-O-O      |X|X|X|
26#  |X|X|X|X|      O-O-O-O
27#  O-O-O-O-O      |X|X|X|
28#  |X|X|X|X|  OR  O-O-O-O
29#  O-O-O-O-O      |X|X|X|
30#  |X|X|X|X|      O-O-O-O
31#  O-O-O-O-O      |X|X|X|
32#  |X|X|X|X|      O-O-O-O
33#  O-O-O-O-O      16 Rooms
34#   25 Rooms
35#
36NAME:   layout_geoelf_grid
37DEPTH:  Elf
38WEIGHT: 10
39ORIENT: encompass
40TAGS:   overwritable layout allow_dup unrand layout_type_rooms
41TAGS:   no_rotate no_vmirror no_hmirror
42{{
43  local gxm, gym = dgn.max_bounds()
44  extend_map{width = gxm, height = gym, fill = 'x'}
45
46  local depth_fraction = you.depth_fraction()
47
48  -- some paramters
49  local rooms_across = 5
50  local room_spacing = 13
51  local offset_max = 1
52  local radius_min = math.floor(2 + depth_fraction + crawl.random_real())
53  local radius_max = math.floor(4 + depth_fraction + crawl.random_real())
54  local extra_corridor_fraction = 0.7 - depth_fraction * 0.2
55  local fancy_room_fraction = 0.1 + depth_fraction * 0.1
56
57  if (depth_fraction * 2 + crawl.random_real() * 4 >= 3) then
58    rooms_across = 4
59    room_spacing = crawl.random_range(13, 17)
60    offset_max = math.floor(1 + depth_fraction + crawl.random_real())
61    radius_min = math.floor(offset_max + 1 + crawl.random_real())
62    radius_max = math.floor(4 + depth_fraction + crawl.random_real() * 2)
63    fancy_room_fraction = 0.15 + depth_fraction * 0.1
64  end
65
66  -- create the arrays
67  local room_data         = { count = 0 }
68  local corridor_data     = { count = 0 }
69  local rooms_by_position = {}
70
71  -- choose the room positions
72  for y = 0, rooms_across - 1 do
73    rooms_by_position[y] = {}
74    for x = 0, rooms_across - 1 do
75      local x_pos = gxm / 2 +
76                    math.floor(room_spacing * (x - (rooms_across - 1) / 2)) +
77                    crawl.random_range(-offset_max, offset_max)
78      local y_pos = gym / 2 +
79                    math.floor(room_spacing * (y - (rooms_across - 1) / 2)) +
80                    crawl.random_range(-offset_max, offset_max)
81      local radius = crawl.random_range(radius_min, radius_max)
82
83      -- we are only actually checking for vaults
84      if (not find_in_area {x1 = x_pos - radius, y1 = y_pos - radius,
85                            x2 = x_pos + radius, y2 = y_pos + radius,
86                            find = "", find_vault = true }) then
87        rooms_by_position[y][x] = geoelf.add_room(room_data,
88                                                  x_pos, y_pos, radius)
89      else
90        rooms_by_position[y][x] = nil
91      end
92    end
93  end
94
95  -- add potential corridors
96  for y = 0, rooms_across - 1 do
97    for x = 0, rooms_across - 1 do
98      if (rooms_by_position[y][x] ~= nil) then
99        local index = rooms_by_position[y][x]
100
101        -- east-west
102        if (x >= 1 and rooms_by_position[y][x - 1] ~= nil) then
103          local other_index = rooms_by_position[y][x - 1]
104          geoelf.add_corridor(room_data, corridor_data,
105                              other_index, index,
106                              geoelf.directions.E, nil)
107        end
108
109        -- south-north
110        if (y >= 1 and rooms_by_position[y - 1][x] ~= nil) then
111          local other_index = rooms_by_position[y - 1][x]
112          geoelf.add_corridor(room_data, corridor_data,
113                              other_index, index,
114                              geoelf.directions.S, nil)
115        end
116
117        -- southeast-northwest
118        if (x >= 1 and y >= 1 and
119            rooms_by_position[y - 1][x - 1] ~= nil) then
120          local other_index = rooms_by_position[y - 1][x - 1]
121
122          -- determine if there is another corridor that would
123          --  block this one (i.e. an NE-SW one)
124          local block_index = nil
125          if (rooms_by_position[y][x - 1] ~= nil) then
126            local old_index = rooms_by_position[y][x - 1]
127            block_index = room_data[old_index].corridor[geoelf.directions.NE]
128          end
129
130          geoelf.add_corridor(room_data, corridor_data,
131                              other_index, index,
132                              geoelf.directions.SE, block_index)
133        end
134
135        -- southwest-northeast
136        if (x + 1 < rooms_across and y >= 1 and
137            rooms_by_position[y - 1][x + 1] ~= nil) then
138          local other_index = rooms_by_position[y - 1][x + 1]
139          geoelf.add_corridor(room_data, corridor_data,
140                              other_index, index,
141                              geoelf.directions.SW, nil)
142        end
143      end
144    end
145  end
146
147  -- generate the layout
148  geoelf.generate(_G, room_data, corridor_data, extra_corridor_fraction,
149                  fancy_room_fraction, false, false)
150}}
151MAP
152ENDMAP
153
154##############################################################
155# layout_geoelf_diagonals
156#
157# A grid of rooms, with most connection along the diagonals.
158#
159#    O---O---O
160#   /|\ /|\ /|\         O---O---O
161#  O-+-O-+-O-+-O       /|\ /|\ /|
162#  |\|/|\|/|\|/|      O-+-O-+-O |
163#  | O-+-O-+-O |      |\|/|\|/|\|
164#  |/|\|/|\|/|\|      | O-+-O-+-O  Note that the grid actually is 7x7
165#  O-+-O-+-O-+-O  OR  |/|\|/|\|/|  (or 6x6), but only half the cells
166#  |\|/|\|/|\|/|      O-+-O-+-O |  are used (in a checkerboard pattern).
167#  | O-+-O-+-O |      |\|/|\|/|\|
168#  |/|\|/|\|/|\|      | O-+-O-+-O
169#  O-+-O-+-O-+-O      |/ \|/ \|/
170#   \|/ \|/ \|/       O---O---O
171#    O---O---O         18 Rooms
172#     24 Rooms
173#
174NAME:   layout_geoelf_diagonals
175DEPTH:  Elf
176WEIGHT: 10
177ORIENT: encompass
178TAGS:   overwritable layout allow_dup unrand layout_type_rooms
179TAGS:   no_rotate no_vmirror no_hmirror
180{{
181  local gxm, gym = dgn.max_bounds()
182  extend_map{width = gxm, height = gym, fill = 'x'}
183
184  local depth_fraction = you.depth_fraction()
185
186  -- some paramters
187  local rooms_across = 7
188  local room_spacing = 9
189  local offset_max = 1
190  local radius_min = 2
191  local radius_max = math.floor(4 + depth_fraction + crawl.random_real())
192  local extra_corridor_fraction = 0.7 - depth_fraction * 0.2
193  local fancy_room_fraction = 0.1 + depth_fraction * 0.1
194
195  if (depth_fraction * 2 + crawl.random_real() * 4 >= 3) then
196    rooms_across = 6
197    room_spacing = crawl.random_range(9, 11)
198    radius_min = math.floor(2 + depth_fraction + crawl.random_real())
199    radius_max = math.floor(4 + depth_fraction + crawl.random_real() * 2)
200    fancy_room_fraction = 0.15 + depth_fraction * 0.1
201  end
202
203  -- create the arrays
204  local room_data         = { count = 0 }
205  local corridor_data     = { count = 0 }
206  local rooms_by_position = {}
207
208  -- choose the room positions
209  for y = 0, rooms_across - 1 do
210    rooms_by_position[y] = {}
211    for x = 0, rooms_across - 1 do    -- checkerboard test
212      if ((x + y) % 2 == 1) then
213        local x_pos = gxm / 2 +
214                      math.floor(room_spacing * (x - (rooms_across - 1) / 2)) +
215                      crawl.random_range(-offset_max, offset_max)
216        local y_pos = gym / 2 +
217                      math.floor(room_spacing * (y - (rooms_across - 1) / 2)) +
218                      crawl.random_range(-offset_max, offset_max)
219        local radius = crawl.random_range(radius_min, radius_max)
220
221        -- we are only actually checking for vaults
222        if (not find_in_area {x1 = x_pos - radius, y1 = y_pos - radius,
223                              x2 = x_pos + radius, y2 = y_pos + radius,
224                              find = "", find_vault = true }) then
225          rooms_by_position[y][x] = geoelf.add_room(room_data,
226                                                    x_pos, y_pos, radius)
227        else
228          rooms_by_position[y][x] = nil
229        end
230      else
231        rooms_by_position[y][x] = nil
232      end
233    end
234  end
235
236  -- add potential corridors
237  for y = 0, rooms_across - 1 do
238    for x = 0, rooms_across - 1 do
239      if (rooms_by_position[y][x] ~= nil) then
240        -- we have impliclitly passed the checkerboard test if we gwt here
241
242        local index = rooms_by_position[y][x]
243
244        -- east-west
245        if (x >= 2 and rooms_by_position[y][x - 2] ~= nil) then
246          local other_index = rooms_by_position[y][x - 2]
247          geoelf.add_corridor(room_data, corridor_data,
248                              other_index, index,
249                              geoelf.directions.E, nil)
250        end
251
252        -- south-north
253        if (y >= 2 and rooms_by_position[y - 2][x] ~= nil) then
254          local other_index = rooms_by_position[y - 2][x]
255
256          -- determine if there is another corridor that would
257          --  block this one (i.e. an E-W one)
258          local block_index = nil
259          if (x >= 1 and rooms_by_position[y - 1][x - 1] ~= nil) then
260            local old_index = rooms_by_position[y - 1][x - 1]
261            block_index = room_data[old_index].corridor[geoelf.directions.E]
262          end
263
264          geoelf.add_corridor(room_data, corridor_data,
265                              other_index, index,
266                              geoelf.directions.S, block_index)
267        end
268
269        -- southeast-northwest
270        if (x >= 1 and y >= 1 and
271            rooms_by_position[y - 1][x - 1] ~= nil) then
272          local other_index = rooms_by_position[y - 1][x - 1]
273          geoelf.add_corridor(room_data, corridor_data,
274                              other_index, index,
275                              geoelf.directions.SE, nil)
276        end
277
278        -- southwest-northeast
279        if (x + 1 < rooms_across and y >= 1 and
280            rooms_by_position[y - 1][x + 1] ~= nil) then
281          local other_index = rooms_by_position[y - 1][x + 1]
282          geoelf.add_corridor(room_data, corridor_data,
283                              other_index, index,
284                              geoelf.directions.SW, nil)
285        end
286      end
287    end
288  end
289
290  -- generate the layout
291  geoelf.generate(_G, room_data, corridor_data, extra_corridor_fraction,
292                  fancy_room_fraction, false, false)
293}}
294MAP
295ENDMAP
296
297##############################################################
298# layout_geoelf_octagon
299#
300# A large central room, with 2 rings of other rooms around it.
301#  There is no simple formula for this one, so the code is a bit
302#  confusing.  Basically the rooms all start in an array.
303#
304#      O--O--O               O
305#     /  /|\  \             /|\
306#    O--O-+-O--O        O--O-+-O--O         O---O
307#   /|\/|\|/|\/|\       |\/|\|/|\/|        /|\ /|\
308#  O |/\| O |/\| O      |/\| O |/\|       / | O | \
309#  | O--O | O--O |      O--O | O--O      O--O | O--O
310#  |/|\  \|/  /|\|     /|\  \|/  /|\     |\  \|/  /|
311#  O-+-O--O--O-+-O    O-+-O--O--O-+-O    | O--O--O |
312#  |\|/  /|\  \|/|     \|/  /|\  \|/     |/  /|\  \|
313#  | O--O | O--O |      O--O | O--O      O--O | O--O
314#  O |\/| O |\/| O      |\/| O |\/|       \ | O | /
315#   \|/\|/|\|/\|/       |/\|/|\|/\|        \|/ \|/
316#    O--O-+-O--O        O--O-+-O--O         O---O
317#     \  \|/  /             \|/            17 Rooms
318#      O--O--O               O
319#      33 Rooms           25 Rooms
320#
321#
322#      29--21-28
323#     /   /|\   \
324#    22-13-+-12-2O
325#   / |\/|\|/|\/| \
326#  3O |/\| 5 |/\| 27
327#  |  14-6 | 4--11 |
328#  | /|\  \|/  /| \|    Room Indexes
329#  23-+-7--O--3-+-19
330#  | \|/  /|\  \| /|    Smaller versions are missing higher indexes
331#  |  15-8 | 2--1O |
332#  31 |\/| 1 |\/| 26
333#   \ |/\|/|\|/\| /
334#    24-16-+-9--18
335#     \   \|/   /
336#      32--17-25
337#
338NAME:   layout_geoelf_octagon
339DEPTH:  Elf
340WEIGHT: 15
341ORIENT: encompass
342TAGS:   overwritable layout allow_dup unrand layout_type_rooms
343TAGS:   no_rotate no_vmirror no_hmirror
344{{
345  local gxm, gym = dgn.max_bounds()
346  extend_map{width = gxm, height = gym, fill = 'x'}
347
348  -- some paramters
349  local depth_fraction = you.depth_fraction()
350  local place_rings = crawl.random_range(2, 4)
351  local extra_corridor_fraction = 0.7 - depth_fraction * 0.2
352  local fancy_room_fraction = (0.025 + depth_fraction * 0.025) *
353                              (7 - place_rings)
354
355  -- positions are from layout center
356  local ROOMS_TO_PLACE = {
357      -- center room
358      [0]  = { x =  0,  y =  0,  radius_min = 3, radius_max = 5, offset = 1 },
359      -- ring 1
360      [1]  = { x =  0,  y =  12, radius_min = 2, radius_max = 4, offset = 0 },
361      [2]  = { x =  8,  y =  8,  radius_min = 2, radius_max = 4, offset = 0 },
362      [3]  = { x =  12, y =  0,  radius_min = 2, radius_max = 4, offset = 0 },
363      [4]  = { x =  8,  y = -8,  radius_min = 2, radius_max = 4, offset = 0 },
364      [5]  = { x =  0,  y = -12, radius_min = 2, radius_max = 4, offset = 0 },
365      [6]  = { x = -8,  y = -8,  radius_min = 2, radius_max = 4, offset = 0 },
366      [7]  = { x = -12, y =  0,  radius_min = 2, radius_max = 4, offset = 0 },
367      [8]  = { x = -8,  y =  8,  radius_min = 2, radius_max = 4, offset = 0 },
368      -- ring 2
369      [9]  = { x =  8,  y =  20, radius_min = 3, radius_max = 5, offset = 1 },
370      [10] = { x =  20, y =  8,  radius_min = 3, radius_max = 5, offset = 1 },
371      [11] = { x =  20, y = -8,  radius_min = 3, radius_max = 5, offset = 1 },
372      [12] = { x =  8,  y = -20, radius_min = 3, radius_max = 5, offset = 1 },
373      [13] = { x = -8,  y = -20, radius_min = 3, radius_max = 5, offset = 1 },
374      [14] = { x = -20, y = -8,  radius_min = 3, radius_max = 5, offset = 1 },
375      [15] = { x = -20, y =  8,  radius_min = 3, radius_max = 5, offset = 1 },
376      [16] = { x = -8,  y =  20, radius_min = 3, radius_max = 5, offset = 1 },
377      -- ring 3
378      [17] = { x =  0,  y =  28, radius_min = 2, radius_max = 4, offset = 1 },
379      [18] = { x =  20, y =  20, radius_min = 2, radius_max = 4, offset = 1 },
380      [19] = { x =  28, y =   0, radius_min = 2, radius_max = 4, offset = 1 },
381      [20] = { x =  20, y = -20, radius_min = 2, radius_max = 4, offset = 1 },
382      [21] = { x =  0,  y = -28, radius_min = 2, radius_max = 4, offset = 1 },
383      [22] = { x = -20, y = -20, radius_min = 2, radius_max = 4, offset = 1 },
384      [23] = { x = -28, y =   0, radius_min = 2, radius_max = 4, offset = 1 },
385      [24] = { x = -20, y =  20, radius_min = 2, radius_max = 4, offset = 1 },
386      -- ring 4
387      [25] = { x =  12, y =  28, radius_min = 2, radius_max = 3, offset = 1 },
388      [26] = { x =  28, y =  12, radius_min = 2, radius_max = 3, offset = 1 },
389      [27] = { x =  28, y = -12, radius_min = 2, radius_max = 3, offset = 1 },
390      [28] = { x =  12, y = -28, radius_min = 2, radius_max = 3, offset = 1 },
391      [29] = { x = -12, y = -28, radius_min = 2, radius_max = 3, offset = 1 },
392      [30] = { x = -28, y = -12, radius_min = 2, radius_max = 3, offset = 1 },
393      [31] = { x = -28, y =  12, radius_min = 2, radius_max = 3, offset = 1 },
394      [32] = { x = -12, y =  28, radius_min = 2, radius_max = 3, offset = 1 },
395    }
396
397  local DIRECTIONS_IN_ORDER = {
398      [0] = geoelf.directions.S,
399      [1] = geoelf.directions.SE,
400      [2] = geoelf.directions.E,
401      [3] = geoelf.directions.NE,
402      [4] = geoelf.directions.N,
403      [5] = geoelf.directions.NW,
404      [6] = geoelf.directions.W,
405      [7] = geoelf.directions.SW,
406    }
407
408  -- create the arrays
409  local room_data         = { count = 0 }
410  local corridor_data     = { count = 0 }
411  local rooms_by_position = {}
412
413  -- add rooms from array
414  for i = 0, place_rings * 8 do
415    local offset_max = ROOMS_TO_PLACE[i].offset
416    local x_pos
417    local y_pos
418    local radius
419    if (place_rings == 2) then
420      -- if we are only placing two rings of rooms, we have the
421      --  space to make everything bigger
422      local ROOM_POSITION_LOOKUP =
423        { [  0] = 0,
424          [  8] = 10,  [ -8] = -10,
425          [ 12] = 14,  [-12] = -14,
426          [ 20] = 24,  [-20] = -24,
427          [ 28] = 34,  [-28] = -34,
428        }
429      x_pos = gxm / 2 + ROOM_POSITION_LOOKUP[ROOMS_TO_PLACE[i].x] +
430              crawl.random_range(-offset_max, offset_max)
431      y_pos = gym / 2 + ROOM_POSITION_LOOKUP[ROOMS_TO_PLACE[i].y] +
432              crawl.random_range(-offset_max, offset_max)
433      radius = crawl.random_range(ROOMS_TO_PLACE[i].radius_min,
434                                  ROOMS_TO_PLACE[i].radius_max + 1)
435    else
436      x_pos = gxm / 2 + ROOMS_TO_PLACE[i].x +
437              crawl.random_range(-offset_max, offset_max)
438      y_pos = gym / 2 + ROOMS_TO_PLACE[i].y +
439              crawl.random_range(-offset_max, offset_max)
440      radius = crawl.random_range(ROOMS_TO_PLACE[i].radius_min,
441                                  ROOMS_TO_PLACE[i].radius_max)
442    end
443
444    -- we are only actually checking for vaults
445    if (not find_in_area {x1 = x_pos - radius, y1 = y_pos - radius,
446                          x2 = x_pos + radius, y2 = y_pos + radius,
447                          find = "", find_vault = true }) then
448      rooms_by_position[i] = geoelf.add_room(room_data, x_pos, y_pos, radius)
449    else
450      rooms_by_position[i] = nil
451    end
452  end
453
454  -- add corridors
455  --  -> the same pattern is repeated 8 times, so we use a loop
456  for i = 0, 7 do
457    local index_ring_1  = 1  + i
458    local index_ring_2  = 9  + i
459    local index_ring_2B = 9  + (i + 7) % 8
460    local index_ring_3  = 17 + i
461    local index_ring_4  = 25 + i
462    local index_ring_4B = 25 + (i + 7) % 8
463
464    -- connect center to ring 1
465    if (rooms_by_position[0] ~= nil and
466        rooms_by_position[index_ring_1] ~= nil) then
467      geoelf.add_corridor(room_data, corridor_data,
468                          rooms_by_position[0],
469                          rooms_by_position[index_ring_1],
470                          DIRECTIONS_IN_ORDER[i], nil)
471    end
472
473    -- connect ring 1 to ring 2
474    if (rooms_by_position[index_ring_1] ~= nil and
475        rooms_by_position[index_ring_2] ~= nil) then
476      geoelf.add_corridor(room_data, corridor_data,
477                          rooms_by_position[index_ring_1],
478                          rooms_by_position[index_ring_2],
479                          DIRECTIONS_IN_ORDER[(i + 1) % 8], nil)
480    end
481    if (rooms_by_position[index_ring_1] ~= nil and
482        rooms_by_position[index_ring_2B] ~= nil) then
483      geoelf.add_corridor(room_data, corridor_data,
484                          rooms_by_position[index_ring_1],
485                          rooms_by_position[index_ring_2B],
486                          DIRECTIONS_IN_ORDER[(i + 7) % 8], nil)
487    end
488
489    -- connect ring 2 in a loop
490    local c4  -- we will need this later
491    if (rooms_by_position[index_ring_2B] ~= nil and
492        rooms_by_position[index_ring_2] ~= nil) then
493      c4 = geoelf.add_corridor(room_data, corridor_data,
494                               rooms_by_position[index_ring_2B],
495                               rooms_by_position[index_ring_2],
496                               DIRECTIONS_IN_ORDER[(i + 2) % 8], nil)
497    else
498      c4 = nil
499    end
500
501    if (place_rings >= 3) then
502      -- connect ring 1 to ring 3
503      if (rooms_by_position[index_ring_1] ~= nil and
504          rooms_by_position[index_ring_3] ~= nil) then
505        geoelf.add_corridor(room_data, corridor_data,
506                            rooms_by_position[index_ring_1],
507                            rooms_by_position[index_ring_3],
508                            DIRECTIONS_IN_ORDER[i], c4)
509      end
510
511      -- connect ring 2 to ring 3
512      if (rooms_by_position[index_ring_2B] ~= nil and
513          rooms_by_position[index_ring_3] ~= nil) then
514        geoelf.add_corridor(room_data, corridor_data,
515                            rooms_by_position[index_ring_2B],
516                            rooms_by_position[index_ring_3],
517                            DIRECTIONS_IN_ORDER[(i + 1) % 8], nil)
518      end
519      if (rooms_by_position[index_ring_2] ~= nil and
520          rooms_by_position[index_ring_3] ~= nil) then
521        geoelf.add_corridor(room_data, corridor_data,
522                            rooms_by_position[index_ring_2],
523                            rooms_by_position[index_ring_3],
524                            DIRECTIONS_IN_ORDER[(i + 7) % 8], nil)
525      end
526
527      if (place_rings >= 4) then
528        -- connect ring 3 to ring 4
529        if (rooms_by_position[index_ring_3] ~= nil and
530            rooms_by_position[index_ring_4] ~= nil) then
531          geoelf.add_corridor(room_data, corridor_data,
532                              rooms_by_position[index_ring_3],
533                              rooms_by_position[index_ring_4],
534                              DIRECTIONS_IN_ORDER[(i + 2) % 8], nil)
535        end
536        if (rooms_by_position[index_ring_3] ~= nil and
537            rooms_by_position[index_ring_4B] ~= nil) then
538          geoelf.add_corridor(room_data, corridor_data,
539                              rooms_by_position[index_ring_3],
540                              rooms_by_position[index_ring_4B],
541                              DIRECTIONS_IN_ORDER[(i + 6) % 8], nil)
542        end
543      end
544    end
545  end
546
547  -- generate the layout
548  geoelf.generate(_G, room_data, corridor_data, extra_corridor_fraction,
549                  fancy_room_fraction, false, false)
550}}
551MAP
552ENDMAP
553