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 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
|
------------------------------------------------------------------------------
-- hyper_city.lua:
--
-- City layouts get their own fle now. There are two main variants:
-- * Hyper City. Builds an initial space - could be an open level, could
-- have some geometry, maybe a street lyout, could even be an existing layout.
-- Random buildings are then placed wherever they'll fit in the space or are
-- carved into walls or joined onto each other.
-- * Grid City. Creates a zones grid (by one of two methods of subdivision)
-- and then places buildings specifically within those zones.
------------------------------------------------------------------------------
crawl_require("dlua/v_main.lua")
hyper.city = {}
hyper.city.default_strategy = hyper.place.strategy_inner_or_carve
function hyper.city.room_types(pass,options)
return {
{ weight = 10, paint_callback = floor_vault, room_transform = hyper.rooms.add_walls,size_padding = 3,min_size = 3 },
-- Filler to be used with very tiny rooms and occasionally with bigger areas
{ weight = 1, paint_callback = wall_vault, room_transform = hyper.rooms.add_walls,size_padding = 1 },
{ weight = 10, paint_callback = wall_vault, room_transform = hyper.rooms.add_walls,size_padding = 1 },
{ weight = 1, paint_callback = wall_vault, room_transform = hyper.rooms.add_walls,size_padding = 1 },
{ weight = 1, paint_callback = wall_vault, room_transform = hyper.rooms.add_walls,size_padding = 1 },
{ weight = 1, paint_callback = wall_vault, room_transform = hyper.rooms.add_walls,size_padding = 1 },
}
end
function hyper.city.grid_rooms(room,options,gen)
local grid = gen.grid_callback(room,options,gen)
local paint = {
{ type = "floor", corner1 = { x = 0, y = 0 }, corner2 = { x = room.size.x-1, y = room.size.y-1 } }
}
local passes = {}
for i,zone in ipairs(grid.zones) do
table.insert(passes,{ pass = "Zone"..i, strategy = hyper.place.strategy_primary, max_rooms = 1, bounds = zone.bounds, generators = gen.room_generators })
end
table.insert(paint,{ type = "build", passes = passes })
end
-- Creates a house by subdividing the rooms and then connecting them up
function hyper.city.house_vault(room,options,gen)
local params = hyper.merge_options(hyper.layout.omnigrid_defaults(),{
jitter = false,
minimum_size = 3,
subdivide_level_multiplier = 0.90,
guaranteed_divides = 3
})
local zonemap = hyper.layout.omnigrid_subdivide(0,0,room.size.x-1,room.size.y-1,params)
-- Fill with wall to start with
local paint = {
{ type = "wall", corner1 = { x = 0, y = 0 }, corner2 = { x = room.size.x-1, y = room.size.y-1 } }
}
-- Paint the zones with floor
util.append(paint,hyper.layout.zonemap_paint(zonemap,{
feature = "floor",
outer_padding = false,
corridor_width = 1
}))
-- TODO: Genericise the following process more. We should be able to supply custom decorators
-- to draw the connection (and this would allow for e.g. spotty connector for zones that don't
-- already overlap).
-- In fact, this could entirely replace the existing way that carvable walls are decorated.
-- Run two passes of "connect" - the first joins zones together with floor to create bigger room shapes
local plots = util.append(hyper.layout.zonemap_connect(zonemap,{
min_zones = 4,
connect_borders = function(borders)
return function(wall,border)
-- TODO: Can make a bit of internal architecture by patterning the connections
return "floor"
end
end
}),
-- The second connects all the remaining zones together with doors until we only have 1 zone
hyper.layout.zonemap_connect(zonemap,{
max_zones = 1,
connect_borders = function(borders)
-- How many doors to make? (Usually 1)
local numconnects = 1
if crawl.coinflip() then
numconnects = numconnects + crawl.random2(3)
end
local bcons = {}
for n=1,numconnects,1 do
local id = crawl.random2(#borders)+1
if bcons[id] == nil then bcons[id] = {} end
local wallnum = crawl.random2(#borders.walls)+1
bcons[id][wallnum] = true
end
return function(wall,border)
if bcons[border.id] ~= nil and bcons[border.id][wall.id] == true then
return "closed_door"
else
return nil
end
end
end
}))
table.insert(paint, { type = "plot", coords = plots })
room.zonemap = zonemap -- TODO: Probably isn't useful to us in this state
return paint
end
function hyper.layout.zonemap_connect(zonemap,params)
-- TODO: zone connection needs to be in a separate function
local bail = false
local function mergezones(a,b)
-- Always take the lower id, it'll simplify table manipulation
if b.id<a.id then a,b=b,a end
local plots = {}
-- Update foreign borders
local connected = {}
for i,bor in ipairs(b.borders) do
if bor.b == a then
-- Lose borders with the one we're merging to (but remember them for paint func)
table.insert(connected,bor)
util.remove(a.borders,bor.inverse) -- TODO: slow
else
-- Update other borders to point to us
bor.a = a
bor.inverse.b = a
-- TODO: It's possible that the border needs to merge with an existing one if they are congruent lines
table.insert(a.borders,bor)
end
end
local fdeco = params.connect_borders(connected)
-- TODO: Passing in the callback probably obfuscates things here. Should just return a list of connected
-- internals with the result then handle them any way we wanted? Or are there other uses for having it here?
for i,bor in ipairs(connected) do
for w,wall in ipairs(bor.walls) do
local feat = fdeco(wall,bor)
table.insert(plots, { feature = feat, x = wall.x, y = wall.y })
end
end
util.append(a.connects,b.connects)
util.append(a.borders,b.borders)
a.size = a.size + b.size
-- Remove the old zone
table.remove(zonemap.zones,b.id)
-- Adjust ids of zones to the right
for n = b.id, #(zonemap.zones), 1 do
zonemap.zones[n].id = n
end
-- Adjust count
zonemap.count = zonemap.count - 1
return plots
end
-- Weight function for picking the next zone to join up
-- TODO: Not really sure how these
local function fweightpick(zone)
return math.ceil(100 / zone.size)
end
-- Weight function for picking which zone to connect it to
local function fweightconnect(border)
return math.ceil(100 / zone.to.size)
end
local plots = {}
while (zonemap.count > 1 and not bail) do
-- Pick which zone to link up
local picked = util.random_weighted_from(fweightpick,zonemap.zones)
local connect = util.random_weighted_from(fweightconnect,picked.borders)
util.append(plots,mergezones(picked,connect))
if zonemap.count == params.min_zones then
bail = true
elseif zonemap.count <= params.max_zones
-- TODO: Not sure of a good way to handle this proability. Could always just keep going until min_zones is met and let
-- the caller randomise this. Or could get increasingly likely the closer we are to min_zones.
and crawl.random2(101) < 10 then
end
end
return plots
end
|