628 lines
21 KiB
Lua
628 lines
21 KiB
Lua
local S = settlements.S
|
|
|
|
local c_air = minetest.get_content_id("air")
|
|
|
|
local default_path_material = "default:gravel"
|
|
local default_deep_platform = "default:stone"
|
|
local default_shallow_platform = "default:dirt"
|
|
|
|
local surface_mats = settlements.surface_materials
|
|
|
|
local settlement_waypoint_def = {
|
|
default_name = S("a settlement"),
|
|
default_color = 0xFFFFFF,
|
|
discovery_volume_radius = tonumber(minetest.settings:get("settlements_discovery_range")) or 30,
|
|
}
|
|
if minetest.settings:get_bool("settlements_hud_requires_item", true) then
|
|
local item_required = minetest.settings:get("settlements_hud_item_required")
|
|
if item_required == nil or item_required == "" then
|
|
item_required = "map:mapping_kit"
|
|
end
|
|
settlement_waypoint_def.visibility_requires_item = item_required
|
|
end
|
|
if minetest.settings:get_bool("settlements_show_in_hud", true) then
|
|
settlement_waypoint_def.visibility_volume_radius = tonumber(minetest.settings:get("settlements_visibility_range")) or 600
|
|
settlement_waypoint_def.on_discovery = named_waypoints.default_discovery_popup
|
|
end
|
|
named_waypoints.register_named_waypoints("settlements", settlement_waypoint_def)
|
|
|
|
local buildable_to_set
|
|
local buildable_to = function(c_node)
|
|
if buildable_to_set then return buildable_to_set[c_node] end
|
|
buildable_to_set = {}
|
|
for k, v in pairs(minetest.registered_nodes) do
|
|
if v.buildable_to then
|
|
buildable_to_set[minetest.get_content_id(k)] = true
|
|
end
|
|
end
|
|
|
|
-- TODO: some way to discriminate between registered_settlements? For now, apply ignore_materials universally.
|
|
for _, def in pairs(settlements.registered_settlements) do
|
|
if def.ignore_surface_materials then
|
|
for _, ignore_material in ipairs(def.ignore_surface_materials) do
|
|
buildable_to_set[minetest.get_content_id(ignore_material)] = true
|
|
end
|
|
end
|
|
end
|
|
|
|
return buildable_to_set[c_node]
|
|
end
|
|
|
|
-- function to fill empty space below baseplate when building on a hill
|
|
local function ground(pos, data, va, c_shallow, c_deep) -- role model: Wendelsteinkircherl, Brannenburg
|
|
local p2 = vector.new(pos)
|
|
local cnt = 0
|
|
local mat = c_shallow
|
|
p2.y = p2.y-1
|
|
local depth = math.random(2,4)
|
|
while true do
|
|
cnt = cnt+1
|
|
if cnt > 20 then break end
|
|
if cnt > depth then mat = c_deep end
|
|
local vi = va:index(p2.x, p2.y, p2.z)
|
|
if not buildable_to(data[vi]) then break end -- stop when we hit solid ground
|
|
data[vi] = mat
|
|
p2.y = p2.y-1
|
|
end
|
|
end
|
|
|
|
-- for displacing building schematic positions so that they're more centered
|
|
local function get_corner_pos(center_pos, schematic, rotation)
|
|
local pos = center_pos
|
|
local size = vector.new(schematic.size)
|
|
size.y = 0
|
|
if rotation == "90" or rotation == "270" then
|
|
local tempz = size.z
|
|
size.z = size.x
|
|
size.x = tempz
|
|
end
|
|
local corner1 = vector.subtract(pos, vector.floor(vector.divide(size, 2)))
|
|
local corner2 = vector.add(schematic.size, corner1)
|
|
return corner1, corner2
|
|
end
|
|
|
|
local group_ids = {}
|
|
local is_in_group = function(c_id, groupname)
|
|
local grouplist = group_ids[groupname]
|
|
if grouplist then
|
|
return grouplist[c_id]
|
|
end
|
|
grouplist = {}
|
|
for name, def in pairs(minetest.registered_nodes) do
|
|
if minetest.get_item_group(name, groupname) > 0 then
|
|
grouplist[minetest.get_content_id(name)] = true
|
|
end
|
|
end
|
|
group_ids[groupname] = grouplist
|
|
return grouplist[c_id]
|
|
end
|
|
|
|
-- function clear space above baseplate
|
|
local function terraform(data, va, settlement_info)
|
|
local c_air = minetest.get_content_id(settlement_info.def.platform_air or "air")
|
|
local c_shallow = minetest.get_content_id(settlement_info.def.platform_shallow or default_shallow_platform)
|
|
local c_deep = minetest.get_content_id(settlement_info.def.platform_deep or default_deep_platform)
|
|
local fheight
|
|
local fwidth
|
|
local fdepth
|
|
|
|
for _, built_house in ipairs(settlement_info) do
|
|
local schematic_data = built_house.schematic_info
|
|
|
|
local replace_air = schematic_data.platform_clear_above
|
|
local build_platform = schematic_data.platform_build_below
|
|
if replace_air == nil then
|
|
replace_air = true
|
|
end
|
|
if build_platform == nil then
|
|
build_platform = true
|
|
end
|
|
|
|
local skip_group_above = schematic_data.platform_ignore_group_above
|
|
if skip_group_above then
|
|
skip_group_above = skip_group_above:gsub("^group:", "")
|
|
end
|
|
|
|
local size = schematic_data.schematic.size
|
|
local pos = built_house.build_pos_min
|
|
if built_house.rotation == "0" or built_house.rotation == "180"
|
|
then
|
|
fwidth = size.x
|
|
fdepth = size.z
|
|
else
|
|
fwidth = size.z
|
|
fdepth = size.x
|
|
end
|
|
fheight = size.y
|
|
if replace_air then-- remove trees and leaves above
|
|
fheight = fheight * 3
|
|
end
|
|
--
|
|
-- now that every info is available -> create platform and clear space above
|
|
--
|
|
for zi = 0,fdepth-1 do
|
|
for yi = 0,fheight do
|
|
for xi = 0,fwidth-1 do
|
|
if yi == 0 and build_platform then
|
|
local p = {x=pos.x+xi, y=pos.y, z=pos.z+zi}
|
|
ground(p, data, va, c_shallow, c_deep)
|
|
elseif replace_air then
|
|
local p = vector.new(pos.x+xi, pos.y+yi, pos.z+zi)
|
|
local vi = va:indexp(p)
|
|
if not (skip_group_above and is_in_group(data[vi], skip_group_above)) then
|
|
data[vi] = c_air
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-------------------------------------------------------------------------------
|
|
-- function to find surface block y coordinate
|
|
-------------------------------------------------------------------------------
|
|
local function find_surface(pos, data, va, altitude_min, altitude_max)
|
|
if not va:containsp(pos) then return nil end
|
|
|
|
local y = pos.y
|
|
|
|
-- starting point for looking for surface
|
|
local previous_vi = va:indexp(pos)
|
|
local previous_node = data[previous_vi]
|
|
local itter -- count up or down
|
|
if buildable_to(previous_node) then
|
|
itter = -1 -- going down
|
|
else
|
|
itter = 1 -- going up
|
|
end
|
|
for cnt = 0, 100 do
|
|
local next_vi = previous_vi + va.ystride * itter
|
|
y = y + itter
|
|
if (altitude_min and altitude_min > y) or (altitude_max and altitude_max < y) then
|
|
-- an altitude range was specified and we're outside it
|
|
return nil
|
|
end
|
|
if not va:containsi(next_vi) then return nil end
|
|
local next_node = data[next_vi]
|
|
if buildable_to(previous_node) ~= buildable_to(next_node) then
|
|
--we transitioned through what may be a surface. Test if it was the right material.
|
|
local above_node, below_node, above_vi, below_vi
|
|
if itter > 0 then
|
|
-- going up
|
|
above_node, below_node = next_node, previous_node
|
|
above_vi, below_vi = next_vi, previous_vi
|
|
else
|
|
above_node, below_node = previous_node, next_node
|
|
above_vi, below_vi = previous_vi, next_vi
|
|
end
|
|
if surface_mats[below_node] then
|
|
return va:position(below_vi), below_node
|
|
else
|
|
return nil
|
|
end
|
|
end
|
|
previous_vi = next_vi
|
|
previous_node = next_node
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local function shallowCopy(original)
|
|
local copy = {}
|
|
for key, value in pairs(original) do
|
|
copy[key] = value
|
|
end
|
|
return copy
|
|
end
|
|
|
|
-- randomize table
|
|
local function shuffle(tbl)
|
|
local ret = shallowCopy(tbl)
|
|
local size = #ret
|
|
for i = size, 1, -1 do
|
|
local rand = math.random(size)
|
|
ret[i], ret[rand] = ret[rand], ret[i]
|
|
end
|
|
return ret
|
|
end
|
|
|
|
-- If the building fits into the areastore without overlapping existing buildings,
|
|
-- add it to the areastore and return true. Otherwise return false.
|
|
local function insert_into_area(building, areastore)
|
|
local buffer = building.schematic_info.buffer or 0
|
|
local edge1 = vector.new(building.build_pos_min)
|
|
edge1 = vector.subtract(edge1, buffer)
|
|
edge1.y = 0
|
|
local edge2 = vector.new(building.build_pos_max)
|
|
edge2 = vector.add(edge2, buffer)
|
|
edge2.y = 1
|
|
|
|
local result = areastore:get_areas_in_area(edge1, edge2, true)
|
|
if next(result) then
|
|
return false
|
|
end
|
|
areastore:insert_area(edge1, edge2, "")
|
|
return true
|
|
end
|
|
|
|
local possible_rotations = {"0", "90", "180", "270"}
|
|
|
|
-------------------------------------------------------------------------------
|
|
-- everything necessary to pick a fitting next building
|
|
-------------------------------------------------------------------------------
|
|
local function pick_next_building(pos_surface, surface_material, count_buildings, settlement_info, settlement_def, areastore)
|
|
local number_of_buildings = settlement_info.number_of_buildings
|
|
local randomized_schematic_table = shuffle(settlement_def.schematics)
|
|
-- pick schematic
|
|
local size = #randomized_schematic_table
|
|
for i = size, 1, -1 do
|
|
-- already enough buildings of that type?
|
|
local current_schematic = randomized_schematic_table[i]
|
|
local current_schematic_name = current_schematic.name
|
|
count_buildings[current_schematic_name] = count_buildings[current_schematic_name] or 0
|
|
if count_buildings[current_schematic_name] < current_schematic.max_num*number_of_buildings then
|
|
local rotation = possible_rotations[math.random(#possible_rotations)]
|
|
local corner1, corner2 = get_corner_pos(pos_surface, current_schematic.schematic, rotation)
|
|
local building_info = {
|
|
center_pos = pos_surface,
|
|
build_pos_min = corner1,
|
|
build_pos_max = corner2,
|
|
schematic_info = current_schematic,
|
|
rotation = rotation,
|
|
surface_mat = surface_material,
|
|
}
|
|
if insert_into_area(building_info, areastore) then
|
|
count_buildings[current_schematic.name] = count_buildings[current_schematic.name] +1
|
|
return building_info
|
|
end
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local function select_replacements(source)
|
|
local destination = {}
|
|
if source then
|
|
for original, replacement in pairs(source) do
|
|
if type(replacement) == "table" then
|
|
replacement = replacement[math.random(1, #replacement)]
|
|
end
|
|
destination[original] = replacement
|
|
end
|
|
end
|
|
return destination
|
|
end
|
|
|
|
-------------------------------------------------------------------------------
|
|
-- fill settlement_info with LVM
|
|
--------------------------------------------------------------------------------
|
|
local function create_site_plan(minp, maxp, data, va, existing_settlement_name)
|
|
-- find center of chunk
|
|
local center = vector.floor({
|
|
x=maxp.x-(maxp.x - minp.x)/2,
|
|
y=maxp.y,
|
|
z=maxp.z-(maxp.z - minp.z)/2,
|
|
})
|
|
-- find center_surface of chunk
|
|
local center_surface_pos, surface_material = find_surface(center, data, va)
|
|
if not center_surface_pos then
|
|
return nil
|
|
end
|
|
|
|
-- get a list of all the settlement defs that can be made on this surface mat
|
|
local material_defs = surface_mats[surface_material]
|
|
local registered_settlements = {}
|
|
-- cull out any that have altitude min/max set outside the range of the chunk
|
|
for _, def in ipairs(material_defs) do
|
|
if (not def.altitude_min or def.altitude_min < maxp.y) and
|
|
(not def.altitude_max or def.altitude_max > minp.y) then
|
|
table.insert(registered_settlements, def)
|
|
end
|
|
end
|
|
-- Nothing to pick from
|
|
if #registered_settlements == 0 then
|
|
return nil
|
|
end
|
|
|
|
-- pick one at random
|
|
local settlement_def = registered_settlements[math.random(1, #registered_settlements)]
|
|
|
|
-- Get a name for the settlement.
|
|
local name = existing_settlement_name or settlement_def.generate_name(center)
|
|
|
|
local min_number = settlement_def.building_count_min or 5
|
|
local max_number = settlement_def.building_count_max or 25
|
|
|
|
local settlement_info = {}
|
|
settlement_info.def = settlement_def
|
|
settlement_info.name = name
|
|
local number_of_buildings = math.random(min_number, max_number)
|
|
settlement_info.number_of_buildings = number_of_buildings
|
|
local areastore = AreaStore() -- An efficient structure for storing building footprints and testing for overlaps
|
|
settlement_info.areastore = areastore
|
|
areastore:reserve(number_of_buildings)
|
|
|
|
settlement_info.replacements = select_replacements(settlement_def.replacements)
|
|
settlement_info.replacements_optional = select_replacements(settlement_def.replacements_optional)
|
|
|
|
local count_buildings = {}
|
|
|
|
-- first building is selected from the central_schematics list, or randomly from schematics if that isn't defined.
|
|
local central_list = settlement_def.central_schematics or settlement_def.schematics
|
|
|
|
local townhall = central_list[math.random(#central_list)]
|
|
local rotation = possible_rotations[math.random(#possible_rotations)]
|
|
-- add to settlement info table
|
|
local number_built = 1
|
|
local corner1, corner2 = get_corner_pos(center_surface_pos, townhall.schematic, rotation)
|
|
local center_building = {
|
|
center_pos = center_surface_pos,
|
|
build_pos_min = corner1,
|
|
build_pos_max = corner2,
|
|
schematic_info = townhall,
|
|
rotation = rotation,
|
|
surface_mat = surface_material,
|
|
}
|
|
settlement_info[number_built] = center_building
|
|
|
|
insert_into_area(center_building, areastore)
|
|
|
|
-- now some buildings around in a circle, radius = size of town center
|
|
local x, z = center_surface_pos.x, center_surface_pos.z
|
|
local r = math.max(townhall.schematic.size.x, townhall.schematic.size.z) + (townhall.buffer or 0)
|
|
-- draw circles around center and increase radius by math.random(2,5)
|
|
for circle = 1,20 do
|
|
if number_built < number_of_buildings then
|
|
-- set position on imaginary circle
|
|
for angle_step = 0, 360, 15 do
|
|
local angle = angle_step * math.pi / 180
|
|
local ptx, ptz = x + r * math.cos( angle ), z + r * math.sin( angle )
|
|
ptx = math.floor(ptx + 0.5) -- round
|
|
ptz = math.floor(ptz + 0.5)
|
|
local pos1 = { x=ptx, y=center_surface_pos.y, z=ptz}
|
|
local pos_surface, surface_material = find_surface(pos1, data, va, settlement_def.altitude_min, settlement_def.altitude_max)
|
|
if pos_surface then
|
|
local building_info = pick_next_building(pos_surface, surface_material, count_buildings, settlement_info, settlement_def, areastore)
|
|
if building_info then
|
|
number_built = number_built + 1
|
|
settlement_info[number_built] = building_info
|
|
local name_built = building_info.schematic_info.name
|
|
--building_counts[name_built] = (building_counts[name_built] or 0) + 1
|
|
if number_of_buildings == number_built then
|
|
break
|
|
end
|
|
end
|
|
else
|
|
break
|
|
end
|
|
end
|
|
r = r + math.random(2,5)
|
|
else
|
|
break
|
|
end
|
|
end
|
|
|
|
if number_built <= 1 then
|
|
return nil
|
|
end
|
|
|
|
if not existing_settlement_name then
|
|
local waypoint_pos = vector.add(center_surface_pos, {x=0,y=2,z=0})
|
|
named_waypoints.add_waypoint("settlements", waypoint_pos, {name=name, settlement_type=settlement_def.name})
|
|
end
|
|
|
|
return settlement_info
|
|
end
|
|
|
|
local function initialize_nodes(settlement_info)
|
|
for i, built_house in ipairs(settlement_info) do
|
|
local pmin = built_house.build_pos_min
|
|
local pmax = built_house.build_pos_max
|
|
for yi = pmin.y, pmax.y do
|
|
for xi = pmin.x, pmax.x do
|
|
for zi = pmin.z, pmax.z do
|
|
local pos = {x=xi, y=yi, z=zi}
|
|
local node = minetest.get_node(pos)
|
|
local node_def = minetest.registered_nodes[node.name]
|
|
if node_def.on_construct then
|
|
-- if the node has an on_construct defined, call it.
|
|
node_def.on_construct(pos)
|
|
end
|
|
if built_house.schematic_info.initialize_node then
|
|
-- Hook for specialized initialization.
|
|
built_house.schematic_info.initialize_node(pos, node, node_def, settlement_info)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- generate paths between buildings
|
|
local function paths(data, va, settlement_info)
|
|
local c_path_material = minetest.get_content_id(settlement_info.def.path_material or default_path_material)
|
|
local starting_point
|
|
local end_point
|
|
local distance
|
|
starting_point = settlement_info[1].center_pos
|
|
for i,built_house in ipairs(settlement_info) do
|
|
|
|
end_point = built_house.center_pos
|
|
if starting_point ~= end_point
|
|
then
|
|
-- loop until end_point is reached (distance == 0)
|
|
while true do
|
|
|
|
-- define surrounding pos to starting_point
|
|
local north_p = {x=starting_point.x+1, y=starting_point.y, z=starting_point.z}
|
|
local south_p = {x=starting_point.x-1, y=starting_point.y, z=starting_point.z}
|
|
local west_p = {x=starting_point.x, y=starting_point.y, z=starting_point.z+1}
|
|
local east_p = {x=starting_point.x, y=starting_point.y, z=starting_point.z-1}
|
|
-- measure distance to end_point
|
|
local dist_north_p_to_end = math.sqrt(
|
|
((north_p.x - end_point.x)*(north_p.x - end_point.x))+
|
|
((north_p.z - end_point.z)*(north_p.z - end_point.z))
|
|
)
|
|
local dist_south_p_to_end = math.sqrt(
|
|
((south_p.x - end_point.x)*(south_p.x - end_point.x))+
|
|
((south_p.z - end_point.z)*(south_p.z - end_point.z))
|
|
)
|
|
local dist_west_p_to_end = math.sqrt(
|
|
((west_p.x - end_point.x)*(west_p.x - end_point.x))+
|
|
((west_p.z - end_point.z)*(west_p.z - end_point.z))
|
|
)
|
|
local dist_east_p_to_end = math.sqrt(
|
|
((east_p.x - end_point.x)*(east_p.x - end_point.x))+
|
|
((east_p.z - end_point.z)*(east_p.z - end_point.z))
|
|
)
|
|
-- evaluate which pos is closer to the end_point
|
|
if dist_north_p_to_end <= dist_south_p_to_end and
|
|
dist_north_p_to_end <= dist_west_p_to_end and
|
|
dist_north_p_to_end <= dist_east_p_to_end
|
|
then
|
|
starting_point = north_p
|
|
distance = dist_north_p_to_end
|
|
|
|
elseif dist_south_p_to_end <= dist_north_p_to_end and
|
|
dist_south_p_to_end <= dist_west_p_to_end and
|
|
dist_south_p_to_end <= dist_east_p_to_end
|
|
then
|
|
starting_point = south_p
|
|
distance = dist_south_p_to_end
|
|
|
|
elseif dist_west_p_to_end <= dist_north_p_to_end and
|
|
dist_west_p_to_end <= dist_south_p_to_end and
|
|
dist_west_p_to_end <= dist_east_p_to_end
|
|
then
|
|
starting_point = west_p
|
|
distance = dist_west_p_to_end
|
|
|
|
elseif dist_east_p_to_end <= dist_north_p_to_end and
|
|
dist_east_p_to_end <= dist_south_p_to_end and
|
|
dist_east_p_to_end <= dist_west_p_to_end
|
|
then
|
|
starting_point = east_p
|
|
distance = dist_east_p_to_end
|
|
end
|
|
-- find surface of new starting point
|
|
local surface_point, surface_mat = find_surface(starting_point, data, va)
|
|
-- replace surface node with path material
|
|
if surface_point
|
|
then
|
|
local vi = va:index(surface_point.x, surface_point.y, surface_point.z)
|
|
data[vi] = c_path_material
|
|
|
|
-- don't set y coordinate, surface might be too low or high
|
|
starting_point.x = surface_point.x
|
|
starting_point.z = surface_point.z
|
|
end
|
|
if distance <= 1 or
|
|
starting_point == end_point
|
|
then
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function settlements.place_building(vm, built_house, settlement_info)
|
|
local building_all_info = built_house.schematic_info
|
|
|
|
local pos = built_house.build_pos_min
|
|
pos.y = pos.y + (building_all_info.height_adjust or 0)
|
|
local rotation = built_house.rotation
|
|
-- get building node material for better integration to surrounding
|
|
local platform_material = built_house.surface_mat
|
|
local platform_material_name = minetest.get_name_from_content_id(platform_material)
|
|
|
|
local building_schematic = building_all_info.schematic
|
|
local replacements = {}
|
|
if settlement_info.replacements then
|
|
for target, repl in pairs(settlement_info.replacements) do
|
|
replacements[target] = repl
|
|
end
|
|
end
|
|
if building_all_info.replace_nodes_optional and settlement_info.replacements_optional then
|
|
for target, repl in pairs(settlement_info.replacements_optional) do
|
|
replacements[target] = repl
|
|
end
|
|
end
|
|
if settlement_info.def.replace_with_surface_material then
|
|
replacements[settlement_info.def.replace_with_surface_material] = platform_material_name
|
|
end
|
|
|
|
local force_place = building_all_info.force_place
|
|
if force_place == nil then
|
|
force_place = true
|
|
end
|
|
|
|
minetest.place_schematic_on_vmanip(
|
|
vm,
|
|
pos,
|
|
building_schematic,
|
|
rotation,
|
|
replacements,
|
|
force_place)
|
|
end
|
|
|
|
local trigger_timer_for_group = function(minp, maxp, nodenames)
|
|
if not nodenames then
|
|
return
|
|
end
|
|
local targets = minetest.find_nodes_in_area(minp, maxp, nodenames)
|
|
for _, pos in ipairs(targets) do
|
|
minetest.get_node_timer(pos):start(math.random(20, 120) / 10)
|
|
end
|
|
end
|
|
|
|
settlements.generate_settlement_vm = function(vm, va, minp, maxp, existing_settlement_name)
|
|
local data = {} -- normally this buffer would be outside the method to avoid
|
|
-- garbage collecting it between calls and overwhelming LUA's memory management.
|
|
-- But settlements should only need to generate on rare occasions so let's try letting
|
|
-- LUA garbage-collect it to free up the memory in between times.
|
|
vm:get_data(data)
|
|
|
|
local settlement_info = create_site_plan(minp, maxp, data, va, existing_settlement_name)
|
|
if not settlement_info
|
|
then
|
|
return false
|
|
end
|
|
|
|
-- prepare terrain
|
|
terraform(data, va, settlement_info)
|
|
|
|
--build paths between buildings
|
|
if settlement_info.def.path_material then
|
|
paths(data, va, settlement_info)
|
|
end
|
|
|
|
vm:set_data(data)
|
|
|
|
-- place schematics
|
|
for _, built_house in ipairs(settlement_info) do
|
|
settlements.place_building(vm, built_house, settlement_info)
|
|
end
|
|
vm:calc_lighting()
|
|
vm:update_liquids()
|
|
vm:write_to_map()
|
|
|
|
-- evaluate settlement_info and initialize furnaces and chests
|
|
initialize_nodes(settlement_info)
|
|
trigger_timer_for_group(minp, maxp, settlement_info.def.trigger_timers_for_nodes)
|
|
return true
|
|
end
|
|
|
|
-- try to build a settlement outside of map generation
|
|
settlements.generate_settlement = function(minp, maxp)
|
|
local vm = minetest.get_voxel_manip()
|
|
local emin, emax = vm:read_from_map(minp, maxp) -- add borders to simulate mapgen overgeneration
|
|
local va = VoxelArea:new{
|
|
MinEdge = emin,
|
|
MaxEdge = emax
|
|
}
|
|
|
|
return settlements.generate_settlement_vm(vm, va, minp, maxp)
|
|
end
|