File: hyper_fort.lua

package info (click to toggle)
crawl 2%3A0.33.1-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 95,264 kB
  • sloc: cpp: 358,145; ansic: 27,203; javascript: 9,491; python: 8,359; perl: 3,327; java: 2,667; xml: 2,191; makefile: 1,830; sh: 611; objc: 250; cs: 15; sed: 9; lisp: 3
file content (122 lines) | stat: -rw-r--r-- 6,738 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
------------------------------------------------------------------------------
-- hyper_fort.lua:
--
-- Decided to keep this separate as well; it might be used from a lot of
-- places.
--
-- Builds interesting fort structures with crennelations and internal
-- architecture.
------------------------------------------------------------------------------

crawl_require("dlua/hyper_city.lua")

hyper.fort = {}

-- Forts with crennelations. This vault can be as small as 5x5 but will look much better at larger sizes.
-- TODO: * Figure out how to create lava/deep water moats and paint drawbridges from the doors.
--       * Or, fill the whole level with lava/deep water and ensure the forts are connected up
--       * Make forts connect up with cloisters
--       * Another option should be to fill the fort with solid wall and allow rooms to be carved within
--       * Add interesting decor spots in the center of crennelations if they're large enough
-- First versions of forts were built by a drunken architect but they looked awesome in a different way, e.g.:
-- TODO: Integrate slightly broken designs into shape_fort and enable on a parameter occasionally.
--       * Corner crennelations were half size so they were offset / only on certain sizes
--       * One corner was missing (could randomly omit corners)
--       * Side crennelations sometimes bigger on one side than the other and missing from one axis
--       * Side crennelations being clustered in the middle, not going all the way along the wall (started with pos too big) - could also randomly omit them
-- Other ideas for ruination:
--       * Randomly scatter sections of wall to look like they've been smashed down in a battle or crumbled over time
function hyper.fort.shape_fort(params)
  -- Figure out how size of corner crennelations
  local shortest_axis = math.min(params.size.x,params.size.y)
  local corners_min = math.max(2, crawl.div_rand_round(shortest_axis, 8))
  local corners_max = math.max(corners_min, crawl.div_rand_round(shortest_axis, 5))
  local corners = crawl.random_range(corners_min,corners_max)
  -- Pad the map by half the corner size
  local padding = crawl.div_rand_round(corners,2)

  -- Paint floor with padding around the edge
  local paint = {
    -- Generate a border of exit and fill the middle with floor. The exits will get painted over by the crennelations. This ensures
    -- doors and attachments only happen in between the crennelations.
    -- TODO: Something seems broken with exit analysis
    { type = "floor", corner1 = { x = padding, y = padding }, corner2 = { x = room.size.x - 1 - padding, y = room.size.y - 1 - padding }, exit = true },
    -- Finally leave a border and paint an open area in the middle for inner geometry placement
    { type = "floor", corner1 = { x = padding + 1, y = padding + 1 }, corner2 = { x = room.size.x - 2 - padding, y = room.size.y - 2 - padding }, open = true },
    -- { type = "floor", corner1 = { x = padding + 2, y = padding + 2 }, corner2 = { x = room.size.x - 3 - padding, y = room.size.y - 3 - padding }, open = true },
  }

  -- Paint corner crennelations
  for i, n in ipairs(vector.diagonals) do
    local cx = ((n.x + 1) / 2) * (params.size.x - corners)
    local cy = ((n.y + 1) / 2) * (params.size.y - corners)
    table.insert(paint, { type = "floor", corner1 = { x = cx, y = cy }, corner2 = { x = cx + corners - 1, y = cy + corners - 1 } })
  end

  -- Size of remaining crennelations
  -- TODO: It's looking pretty good already but we could add more variation in this randomisation. Also vary the sides more (as long as opposite
  --       sides are symmetrical (apart from 'drunk' version) which the algorithm nicely supports)
  local cren = math.min(crawl.random_range(crawl.div_rand_round(padding,2), padding), math.max(1,crawl.div_rand_round(shortest_axis - 2 * corners, 5)))
  if cren == 0 then return paint end

  local cren_depth = math.max(1,crawl.div_rand_round(cren,2))
  local cren_gap = crawl.random_range(math.max(1,crawl.div_rand_round(cren_depth,2)),math.min(cren_depth*3,math.floor((shortest_axis - 2 * corners) / 3)))

  -- Deal with each side separately. Calculate how many cells we have remaining on each side.
  local sides = {
    { x = 1, y = 0, rem = room.size.x - 2 * corners - (crawl.random_range(0,3)) },
    { x = 0, y = 1, rem = room.size.y - 2 * corners - (crawl.random_range(0,3)) },  -- The random(0,3) will cause a gap to be left in the middle of up to 3 squares
  }

  for i,side in ipairs(sides) do
    local rem = side.rem
    local pos = corners
    -- Work from the outside and see how many crennelations we can add
    while (rem > 0) do
      -- Subtract gap on both sides
      rem = rem - 2 * cren_gap
      pos = pos + cren_gap
      if rem >= (2 * cren) then
        -- Draw symmetrical crennelations on each side (we'll end up drawing the center ones twice over but it doesn't hugely matter)
        for n = 0, 1, 1 do
          for m = 0, 1, 1 do
            -- Don't worry about the math here, it all makes sense. Actually it would have been simpler to just insert four paint lines but oh well.
            local spos = (1-m) * pos + m * ( (side.x * room.size.x) + (side.y * room.size.y) - pos - cren )
            local npos = (1-n) * (padding - cren_depth) + n * ( (side.x * room.size.y) + (side.y * room.size.x) - padding )
            c1 = {
              x = side.x * (spos) + (1 - side.x) * (npos),
              y = side.y * (spos) + (1 - side.y) * (npos)
            }
            table.insert(paint, { type = "floor", corner1 = c1,
              corner2 = { x = c1.x + (side.x * cren) + (side.y * cren_depth) - 1, y = c1.y + (side.y * cren) + (side.x * cren_depth) - 1 } })
          end
        end
      end
      rem = rem - 2 * cren
      pos = pos + cren
    end
  end

  -- This is new. Since we know exactly where we should build internal stuff right now, let's do it. This is more controlled than letting
  -- random placement do the job later.
  -- TODO: There should be a consistent mechanism to achieve this across different rooms - from outside being able to set subdivision
  -- params / methods, placement strategies, as well as generator weights. And not to mention that any child rooms could themselves
  -- spawn a child build process.

  -- So -- rather than even trying to do this here, instead we start off with an area

  -- local zones = hyper.city.omnigrid_
  -- table.insert(paint, { type = "build", passes = passes })

  return paint

end

-- Room function for the fort.
function hyper.fort.room_fort(room,options,gen)
  -- TODO: Need more options. Map gen.fort_options onto the params table.
  return hyper.fort.shape_fort{
    size = room.size
    -- entrances = gen.entrances
  }
end