mirror of
https://git.minetest.land/VoxeLibre/VoxeLibre.git
synced 2024-11-16 16:11:06 +01:00
better paths, better village layouts
This commit is contained in:
parent
34a8ebf023
commit
182bb9c0d6
7 changed files with 205 additions and 126 deletions
|
@ -47,7 +47,7 @@ local function load_schema(name, mts)
|
|||
return { name = name, size = schematic.size, schem_lua = schem_lua }
|
||||
end
|
||||
|
||||
local all_optional = { "yadjust", "no_ground_turnip", "no_clearance" }
|
||||
local all_optional = { "yadjust", "no_ground_turnip", "no_clearance", "rotation_offset" }
|
||||
|
||||
local function set_all_optional(record, data)
|
||||
for _, field in ipairs(all_optional) do
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
local min_jobs = tonumber(minetest.settings:get("mcl_villages_min_jobs")) or 1
|
||||
local max_jobs = tonumber(minetest.settings:get("mcl_villages_max_jobs")) or 12
|
||||
local placement_priority = minetest.settings:get("mcl_villages_placement_priority") or "random"
|
||||
local min_jobs = tonumber(minetest.settings:get("vl_villages_min_jobs")) or 2
|
||||
local max_jobs = tonumber(minetest.settings:get("vl_villages_max_jobs")) or 14
|
||||
local placement_priority = minetest.settings:get("vl_villages_placement_priority") or "houses" -- houses is safer for villagers at night
|
||||
|
||||
local S = minetest.get_translator(minetest.get_current_modname())
|
||||
|
||||
local function add_building(settlement, building, count_buildings)
|
||||
table.insert(settlement, building)
|
||||
if placement_priority == "jobs" then
|
||||
table.insert(settlement, building)
|
||||
else
|
||||
table.insert(settlement, 1, building) -- insert "backwards" - todo: add table.reverse
|
||||
end
|
||||
count_buildings[building.name] = (count_buildings[building.name] or 0) + 1
|
||||
count_buildings.num_jobs = count_buildings.num_jobs + (building.num_jobs or 0)
|
||||
count_buildings.num_beds = count_buildings.num_beds + (building.num_beds or 0)
|
||||
|
@ -35,15 +39,25 @@ local function layout_town(vm, minp, maxp, pr, input_settlement)
|
|||
local building = table.copy(input_settlement[#settlement + 1])
|
||||
local size = vector.copy(building.size)
|
||||
--local rotation = possible_rotations[pr:next(1, #possible_rotations)]
|
||||
local rotation = math.floor(math.atan2(center.z-cpos.z, center.x-cpos.x) / math.pi * 2+6.5)%4
|
||||
rotation = possible_rotations[1+rotation]
|
||||
-- instead of random rotations, rotating doors to the center makes the village
|
||||
-- more defensive and hence safer for the poor villagers, even though less random
|
||||
-- case distinction is simpler and faster than trigonometry here:
|
||||
local rotation = building.rotation_offset or 0
|
||||
if math.abs(cpos.z-center.z) > math.abs(cpos.x-center.x) then
|
||||
rotation = rotation + (cpos.z <= center.z and 0 or 2) -- zero indexed for modulo below
|
||||
else
|
||||
rotation = rotation + (cpos.x <= center.x and 1 or 3) -- zero indexed for modulo below
|
||||
end
|
||||
rotation = possible_rotations[rotation % 4 + 1]
|
||||
--minetest.log("action", building.name.." at "..minetest.pos_to_string(cpos).." rotation: "..rotation.." to "..minetest.pos_to_string(center).." "..minetest.pos_to_string(center-cpos))
|
||||
if rotation == "90" or rotation == "270" then size.x, size.z = size.z, size.x end
|
||||
local tlpos = vector.offset(cpos, -math.floor((size.x-1)/2), 0, -math.floor((size.z-1)/2))
|
||||
|
||||
-- ensure we have 3 space for terraforming, and avoid problems with VoxelManip
|
||||
if tlpos.x - 3 >= minp.x and tlpos.x + size.x + 3 <= maxp.x
|
||||
and tlpos.z + 3 >= minp.z and tlpos.z + size.y + 3 <= maxp.z then
|
||||
local pos, surface_material = vl_terraforming.find_level_vm(vm, cpos, size)
|
||||
local pos, surface_material = vl_terraforming.find_level_vm(vm, cpos, size, 6)
|
||||
if pos and pos.y + size.y > maxp.y then pos = nil end
|
||||
-- check distance to other buildings. Note that we still want to add baseplates etc.
|
||||
if pos and mcl_villages.surface_mat[surface_material.name] and mcl_villages.check_distance(settlement, cpos, size.x, size.z, mindist) then
|
||||
-- use town bell as new reference point for placement height
|
||||
|
@ -149,15 +163,11 @@ function mcl_villages.create_site_plan(vm, minp, maxp, pr)
|
|||
table.insert(settlement, pr:next(1, #settlement), cur_schem)
|
||||
end
|
||||
|
||||
if placement_priority == "jobs" then
|
||||
-- keep ordered as is
|
||||
elseif placement_priority == "houses" then
|
||||
table.reverse(settlement)
|
||||
else
|
||||
if placement_priority == "randomg" then
|
||||
settlement = mcl_villages.shuffle(settlement, pr)
|
||||
end
|
||||
|
||||
table.insert(settlement, 1, bell_info)
|
||||
|
||||
return layout_town(vm, minp, maxp, pr, settlement)
|
||||
end
|
||||
|
||||
|
@ -201,19 +211,20 @@ function mcl_villages.place_schematics(vm, settlement, blockseed, pr)
|
|||
local schematic = loadstring(schem_lua)()
|
||||
|
||||
-- the foundation and air space for the building was already built before
|
||||
-- minetest.log("debug", "placing schematics for "..building.name.." at "..minetest.pos_to_string(minp).." on "..surface_material)
|
||||
-- minetest.log("action", "placing schematics for "..building.name.." at "..minetest.pos_to_string(minp).." on "..surface_material.name)
|
||||
minetest.place_schematic_on_vmanip(vm, minp, schematic, rotation, nil, true, { place_center_x = false, place_center_y = false, place_center_z = false })
|
||||
mcl_villages.store_path_ends(vm, minp, maxp, cpos, blockseed, bell_pos)
|
||||
mcl_villages.increase_no_paths(vm, minp, maxp) -- help the path finder
|
||||
end
|
||||
|
||||
local minp, maxp = vm:get_emerged_area() -- safe area for further processing
|
||||
vm:write_to_map(true) -- for path finder and light
|
||||
|
||||
-- Path planning and placement
|
||||
mcl_villages.paths(blockseed, minetest.get_biome_name(minetest.get_biome_data(bell_pos).biome))
|
||||
mcl_villages.paths(blockseed, minetest.get_biome_name(minetest.get_biome_data(bell_pos).biome), minp, maxp)
|
||||
mcl_villages.clean_no_paths(minp, maxp)
|
||||
-- Clean up paths and initialize nodes
|
||||
for i, building in ipairs(settlement) do
|
||||
mcl_villages.clean_no_paths(building.minp, building.maxp)
|
||||
init_nodes(building.minp, building.maxp, pr)
|
||||
end
|
||||
|
||||
|
@ -236,8 +247,9 @@ minetest.register_node("mcl_villages:village_block", {
|
|||
-- e.g. spawning mobs, running minetest.find_path
|
||||
on_timer = function(pos, _)
|
||||
local meta = minetest.get_meta(pos)
|
||||
minetest.swap_node(pos, { name = meta:get_string("node_type") })
|
||||
mcl_villages.post_process_village(meta:get_string("blockseed"))
|
||||
-- not swap_node, to clear metadata afterwards
|
||||
minetest.set_node(pos, { name = meta:get_string("node_type") })
|
||||
return false
|
||||
end,
|
||||
})
|
||||
|
|
|
@ -118,6 +118,7 @@ mcl_villages.register_well({
|
|||
name = "well",
|
||||
mts = schem_path .. "new_villages/well.mts",
|
||||
yadjust = -1,
|
||||
rotation_offset = 3, -- lamp is east
|
||||
})
|
||||
|
||||
for i = 1, 6 do
|
||||
|
@ -160,6 +161,7 @@ mcl_villages.register_building({
|
|||
min_jobs = 2,
|
||||
max_jobs = 99,
|
||||
yadjust = 1,
|
||||
rotation_offset = 1, -- entrance is west
|
||||
})
|
||||
|
||||
mcl_villages.register_building({
|
||||
|
@ -175,6 +177,7 @@ mcl_villages.register_building({
|
|||
mts = schem_path .. "new_villages/blacksmith.mts",
|
||||
num_others = 8,
|
||||
yadjust = 1,
|
||||
rotation_offset = 1, -- entrance is west
|
||||
})
|
||||
|
||||
mcl_villages.register_building({
|
||||
|
@ -182,6 +185,7 @@ mcl_villages.register_building({
|
|||
mts = schem_path .. "new_villages/butcher.mts",
|
||||
num_others = 8,
|
||||
yadjust = 1,
|
||||
rotation_offset = 0, -- entrance is north
|
||||
})
|
||||
|
||||
mcl_villages.register_building({
|
||||
|
@ -189,6 +193,7 @@ mcl_villages.register_building({
|
|||
mts = schem_path .. "new_villages/farm.mts",
|
||||
num_others = 3,
|
||||
yadjust = 1,
|
||||
rotation_offset = 3, -- composters are east
|
||||
})
|
||||
|
||||
mcl_villages.register_building({
|
||||
|
@ -196,6 +201,7 @@ mcl_villages.register_building({
|
|||
mts = schem_path .. "new_villages/fishery.mts",
|
||||
num_others = 8,
|
||||
yadjust = -2,
|
||||
rotation_offset = 2, -- entrances are east and west
|
||||
})
|
||||
|
||||
mcl_villages.register_building({
|
||||
|
@ -205,6 +211,7 @@ mcl_villages.register_building({
|
|||
num_others = 8,
|
||||
max_jobs = 6,
|
||||
yadjust = 0,
|
||||
rotation_offset = 1, -- entrance is west
|
||||
})
|
||||
|
||||
mcl_villages.register_building({
|
||||
|
@ -214,6 +221,7 @@ mcl_villages.register_building({
|
|||
num_others = 8,
|
||||
min_jobs = 7,
|
||||
yadjust = 1,
|
||||
rotation_offset = 3, -- entrance is east
|
||||
})
|
||||
|
||||
mcl_villages.register_building({
|
||||
|
@ -233,6 +241,7 @@ mcl_villages.register_building({
|
|||
min_jobs = 1,
|
||||
max_jobs = 11,
|
||||
yadjust = 0,
|
||||
rotation_offset = 1, -- entrance is west
|
||||
})
|
||||
|
||||
mcl_villages.register_building({
|
||||
|
@ -240,6 +249,7 @@ mcl_villages.register_building({
|
|||
mts = schem_path .. "new_villages/cartographer.mts",
|
||||
num_others = 15,
|
||||
yadjust = 1,
|
||||
rotation_offset = 1, -- entrance is west
|
||||
})
|
||||
|
||||
mcl_villages.register_building({
|
||||
|
@ -247,6 +257,7 @@ mcl_villages.register_building({
|
|||
mts = schem_path .. "new_villages/mason.mts",
|
||||
num_others = 8,
|
||||
yadjust = 1,
|
||||
rotation_offset = 1, -- entrance is west
|
||||
})
|
||||
|
||||
mcl_villages.register_building({
|
||||
|
@ -261,6 +272,7 @@ mcl_villages.register_building({
|
|||
mts = schem_path .. "new_villages/leather_worker.mts",
|
||||
num_others = 8,
|
||||
yadjust = 1,
|
||||
rotation_offset = 1, -- entrance is west
|
||||
})
|
||||
|
||||
mcl_villages.register_building({
|
||||
|
@ -268,6 +280,7 @@ mcl_villages.register_building({
|
|||
mts = schem_path .. "new_villages/toolsmith.mts",
|
||||
num_others = 8,
|
||||
yadjust = 1,
|
||||
rotation_offset = 1, -- entrance is west
|
||||
})
|
||||
|
||||
mcl_villages.register_building({
|
||||
|
@ -275,6 +288,7 @@ mcl_villages.register_building({
|
|||
mts = schem_path .. "new_villages/weaponsmith.mts",
|
||||
num_others = 8,
|
||||
yadjust = 1,
|
||||
rotation_offset = 0, -- entrance is north
|
||||
})
|
||||
|
||||
mcl_villages.register_building({
|
||||
|
@ -295,6 +309,7 @@ mcl_villages.register_building({
|
|||
min_jobs = 8,
|
||||
max_jobs = 99,
|
||||
yadjust = 0,
|
||||
rotation_offset = 2, -- entrance is west, but tower is south
|
||||
})
|
||||
|
||||
mcl_villages.register_building({
|
||||
|
@ -312,6 +327,7 @@ mcl_villages.register_building({
|
|||
mts = schem_path .. "new_villages/farm_small_1.mts",
|
||||
num_others = 3,
|
||||
yadjust = 1,
|
||||
rotation_offset = 3, -- composters are south west?
|
||||
})
|
||||
|
||||
mcl_villages.register_building({
|
||||
|
@ -319,6 +335,7 @@ mcl_villages.register_building({
|
|||
mts = schem_path .. "new_villages/farm_small_2.mts",
|
||||
num_others = 3,
|
||||
yadjust = 1,
|
||||
rotation_offset = 3, -- composters are south west?
|
||||
})
|
||||
|
||||
mcl_villages.register_building({
|
||||
|
@ -326,6 +343,7 @@ mcl_villages.register_building({
|
|||
mts = schem_path .. "new_villages/farm_large_1.mts",
|
||||
num_others = 6,
|
||||
yadjust = 1,
|
||||
rotation_offset = 3, -- composters are east
|
||||
})
|
||||
|
||||
mcl_villages.register_crop({
|
||||
|
|
|
@ -46,13 +46,8 @@ function mcl_villages.store_path_ends(vm, minp, maxp, pos, blockseed, bell_pos)
|
|||
-- We store by distance because we create paths far away from the bell first
|
||||
local dist = vector.distance(bell_pos, pos)
|
||||
local id = "block_" .. blockseed -- cannot use integers as keys
|
||||
local tab = path_ends[id]
|
||||
if not tab then
|
||||
tab = {}
|
||||
path_ends[id] = tab
|
||||
end
|
||||
if tab[dist] == nil then tab[dist] = {} end
|
||||
-- TODO: use LVM data instead of nodes? But we only process a subset of the area
|
||||
-- TODO: benchmark best way
|
||||
local tab = {}
|
||||
local v = vector.zero()
|
||||
for zi = minp.z, maxp.z do
|
||||
v.z = zi
|
||||
|
@ -62,12 +57,14 @@ function mcl_villages.store_path_ends(vm, minp, maxp, pos, blockseed, bell_pos)
|
|||
v.x = xi
|
||||
local n = vm:get_node_at(v)
|
||||
if n and n.name == "mcl_villages:path_endpoint" then
|
||||
table.insert(tab[dist], minetest.pos_to_string(v))
|
||||
table.insert(tab, vector.copy(v))
|
||||
vm:set_node_at(v, { name = "air" })
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if not path_ends[id] then path_ends[id] = {} end
|
||||
table.insert(path_ends[id], {dist, minetest.pos_to_string(pos), tab})
|
||||
end
|
||||
|
||||
local function place_lamp(pos, pr)
|
||||
|
@ -84,74 +81,103 @@ local function place_lamp(pos, pr)
|
|||
end
|
||||
|
||||
-- TODO: port this to lvm.
|
||||
local function smooth_path(path)
|
||||
-- Smooth out bumps in path or stairs can look naf
|
||||
for pass = 1, 3 do
|
||||
local function smooth_path(path, passes, minp, maxp)
|
||||
-- bridge over water/laver
|
||||
for i = 2, #path - 1 do
|
||||
local prev_y = path[i - 1].y
|
||||
local y = path[i].y
|
||||
local next_y = path[i + 1].y
|
||||
local bump = minetest.get_node(path[i]).name
|
||||
|
||||
-- TODO: also replace bamboo underneath with dirt here?
|
||||
if minetest.get_item_group(bump, "water") ~= 0 then
|
||||
-- ignore in this pass
|
||||
elseif y >= next_y + 2 and y <= prev_y then
|
||||
minetest.swap_node(vector.offset(path[i], 0, -1, 0), { name = "air" })
|
||||
path[i].y = path[i].y - 1
|
||||
elseif y <= next_y - 2 and y >= prev_y then
|
||||
minetest.swap_node(path[i], { name = "mcl_core:dirt" })
|
||||
path[i].y = path[i].y + 1
|
||||
elseif y >= prev_y + 2 and y <= next_y then
|
||||
minetest.swap_node(vector.offset(path[i], 0, -1, 0), { name = "air" })
|
||||
path[i].y = path[i].y - 1
|
||||
elseif y <= prev_y - 2 and y >= prev_y then
|
||||
minetest.swap_node(path[i], { name = "mcl_core:dirt" })
|
||||
path[i].y = path[i].y + 1
|
||||
elseif y < prev_y and y < next_y then
|
||||
-- Fill in dip to flatten path
|
||||
minetest.swap_node(path[i], { name = "mcl_core:dirt" })
|
||||
path[i].y = path[i].y + 1
|
||||
elseif y > prev_y and y > next_y then
|
||||
-- Remove peak to flatten path
|
||||
minetest.swap_node(vector.offset(path[i], 0, -1, 0), { name = "air" })
|
||||
path[i].y = path[i].y - 1
|
||||
while true do
|
||||
local cur = path[i]
|
||||
local node = minetest.get_node(cur).name
|
||||
if node == "air" and vector.in_area(cur, minp, maxp) then
|
||||
local under = minetest.get_node(vector.offset(path[i], 0, -1, 0)).name
|
||||
local udef = minetest.registered_nodes[under]
|
||||
-- do not build paths over leaves
|
||||
if udef and udef.groups.leaves then
|
||||
minetest.swap_node(path[i], {name="mcl_villages:no_paths"})
|
||||
return -- bad path
|
||||
end
|
||||
break
|
||||
else
|
||||
local ndef = minetest.registered_nodes[node]
|
||||
if not ndef then break end -- ignore
|
||||
if (ndef.groups.water or 0) > 0 or (ndef.groups.lava or 0) > 0 then
|
||||
cur.y = cur.y + 1
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Smooth out bumps in path to reduce weird stairs
|
||||
local any_changed = false
|
||||
for pass = 1, passes do
|
||||
local changed = false
|
||||
for i = 2, #path - 1 do
|
||||
local prev_y = path[i - 1].y
|
||||
local y = path[i].y
|
||||
local next_y = path[i + 1].y
|
||||
local bump = minetest.get_node(path[i]).name
|
||||
local bdef = minetest.registered_nodes[bump]
|
||||
|
||||
-- TODO: also replace bamboo underneath with dirt here?
|
||||
if bdef and ((bdef.groups.water or 0) > 0 or (bdef.groups.lava or 0) > 0) then
|
||||
-- ignore in this pass
|
||||
elseif (y > next_y + 1 and y <= prev_y) -- large step
|
||||
or (y > prev_y + 1 and y <= next_y) -- large step
|
||||
or (y > prev_y and y > next_y) then
|
||||
-- Remove peaks to flatten path
|
||||
path[i].y = math.max(prev_y, next_y)
|
||||
minetest.swap_node(path[i], { name = "air" })
|
||||
changed = true
|
||||
elseif (y < next_y - 1 and y >= prev_y) -- large step
|
||||
or (y < prev_y - 1 and y >= next_y) -- large step
|
||||
or (y < prev_y and y < next_y) then
|
||||
-- Fill in dips to flatten path
|
||||
path[i].y = math.min(prev_y, next_y) - 1 -- to replace below first
|
||||
minetest.swap_node(path[i], { name = "mcl_core:dirt" }) -- todo: use sand/sandstone in desert?, use slabs?
|
||||
path[i].y = path[i].y + 1 -- above dirt
|
||||
changed = true
|
||||
end
|
||||
end
|
||||
if changed then any_changed = true else break end
|
||||
end
|
||||
-- by delaying this, we allow making bridges over deep dips:
|
||||
--[[
|
||||
if any_changed then
|
||||
-- we may not yet have filled a gap
|
||||
for i = 2, #path - 1 do
|
||||
local below = vector.offset(path[y], 0, -1, 0)
|
||||
local bdef = minetest.registered_nodes[minetest.get_node(path[i]).name]
|
||||
if bdef and not bdef.walkable then
|
||||
minetest.swap_node(path[i], { name = "mcl_core:dirt" }) -- todo: use sand/sandstone in desert?, use slabs?
|
||||
end
|
||||
end
|
||||
end]]
|
||||
return path
|
||||
end
|
||||
|
||||
-- TODO: port this to lvm.
|
||||
local function place_path(path, pr, stair, slab)
|
||||
-- Smooth out bumps in path or stairs can look naf
|
||||
-- find water/lava below
|
||||
for i = 2, #path - 1 do
|
||||
local prev_y = path[i - 1].y
|
||||
local y = path[i].y
|
||||
local next_y = path[i + 1].y
|
||||
local bump = minetest.get_node(path[i]).name
|
||||
local bdef = minetest.registered_nodes[bump]
|
||||
|
||||
if minetest.get_item_group(bump, "water") ~= 0 then
|
||||
if bdef and ((bdef.groups.water or 0) > 0 or (bdef.groups.lava or 0) > 0) then
|
||||
-- Find air
|
||||
local up_pos = vector.copy(path[i])
|
||||
while true do
|
||||
up_pos.y = up_pos.y + 1
|
||||
local up_node = minetest.get_node(up_pos).name
|
||||
if minetest.get_item_group(up_node, "water") == 0 then
|
||||
local udef = minetest.registered_nodes[up_node]
|
||||
if udef and (udef.groups.water or 0) == 0 and (udef.groups.lava or 0) == 0 then
|
||||
minetest.swap_node(up_pos, { name = "air" })
|
||||
path[i] = up_pos
|
||||
break
|
||||
end
|
||||
elseif not udef then break end -- ignore node encountered
|
||||
end
|
||||
elseif y < prev_y and y < next_y then
|
||||
-- Fill in dip to flatten path
|
||||
-- TODO: do not break other path/stairs
|
||||
minetest.swap_node(path[i], { name = "mcl_core:dirt" })
|
||||
path[i] = vector.offset(path[i], 0, 1, 0)
|
||||
elseif y > prev_y and y > next_y then
|
||||
-- TODO: do not break other path/stairs
|
||||
-- Remove peak to flatten path
|
||||
minetest.swap_node(vector.offset(path[i], 0, -1, 0), { name = "air" })
|
||||
path[i].y = path[i].y - 1
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -208,6 +234,8 @@ local function place_path(path, pr, stair, slab)
|
|||
if not done then
|
||||
if groups.water then
|
||||
minetest.add_node(under_pos, { name = slab })
|
||||
elseif groups.lava then
|
||||
minetest.add_node(under_pos, { name = "mcl_stairs:slab_stone" })
|
||||
elseif groups.sand then
|
||||
minetest.swap_node(under_pos, { name = "mcl_core:sandstonesmooth2" })
|
||||
elseif groups.soil and not groups.dirtifies_below_solid then
|
||||
|
@ -269,7 +297,7 @@ end
|
|||
|
||||
-- Work out which end points should be connected
|
||||
-- works from the outside of the village in
|
||||
function mcl_villages.paths(blockseed, biome_name)
|
||||
function mcl_villages.paths(blockseed, biome_name, minp, maxp)
|
||||
local pr = PcgRandom(blockseed)
|
||||
local pathends = path_ends["block_" .. blockseed]
|
||||
if pathends == nil then
|
||||
|
@ -279,62 +307,66 @@ function mcl_villages.paths(blockseed, biome_name)
|
|||
|
||||
-- Stair and slab style of the village
|
||||
local stair, slab = get_biome_stair_slab(biome_name)
|
||||
-- Keep track of connections
|
||||
local connected = {}
|
||||
|
||||
-- get a list of reverse sorted keys, which are distances
|
||||
local dist_keys = {}
|
||||
for k in pairs(pathends) do table.insert(dist_keys, k) end
|
||||
table.sort(dist_keys, function(a, b) return a > b end)
|
||||
--minetest.log("Planning paths with "..#dist_keys.." nodes")
|
||||
|
||||
for i, from in ipairs(dist_keys) do
|
||||
table.sort(pathends, function(a, b) return a[1] > b[1] end)
|
||||
--minetest.log("action", "path ends: "..dump(pathends,""))
|
||||
-- find ways to connect
|
||||
local connected, to_place = {}, {}
|
||||
for _, tmp in ipairs(pathends) do
|
||||
local from, from_eps = tmp[2], tmp[3]
|
||||
-- ep == end_point
|
||||
for _, from_ep in ipairs(pathends[from]) do
|
||||
local from_ep_pos = minetest.string_to_pos(from_ep)
|
||||
local closest_pos, closest_bld, best = nil, nil, 10000000
|
||||
|
||||
-- Most buildings only do other buildings that are closer to the bell
|
||||
-- for the bell do any end points that don't have paths near them
|
||||
local j = from == 0 and 1 or (i + 1)
|
||||
for j = j, #dist_keys do
|
||||
local to = dist_keys[j]
|
||||
if from ~= to and connected[from .. "-" .. to] == nil and connected[to .. "-" .. from] == nil then
|
||||
for _, to_ep in ipairs(pathends[to]) do
|
||||
local to_ep_pos = minetest.string_to_pos(to_ep)
|
||||
for _, from_ep_pos in ipairs(from_eps) do
|
||||
-- TODO: add back some logic as before that ensures some longer paths, too?
|
||||
local cand = {}
|
||||
for _, tmp in ipairs(pathends) do
|
||||
local to, to_eps = tmp[2], tmp[3]
|
||||
if from ~= to and not connected[from .. "-" .. to] and not connected[to .. "-" .. from] then
|
||||
for _, to_ep_pos in ipairs(to_eps) do
|
||||
local dist = vector.distance(from_ep_pos, to_ep_pos)
|
||||
if dist < best then
|
||||
best = dist
|
||||
closest_pos = to_ep_pos
|
||||
closest_bld = to
|
||||
end
|
||||
table.insert(cand, {dist, from, from_ep_pos, to, to_ep_pos})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if closest_pos then
|
||||
local path = minetest.find_path(from_ep_pos, closest_pos, 64, 2, 2)
|
||||
if path then smooth_path(path) end
|
||||
if not path then
|
||||
path = minetest.find_path(from_ep_pos, closest_pos, 64, 3, 3)
|
||||
if path then smooth_path(path) end
|
||||
end
|
||||
path = minetest.find_path(from_ep_pos, closest_pos, 64, 1, 1)
|
||||
if path and #path > 0 then
|
||||
place_path(path, pr, stair, slab)
|
||||
connected[from .. "-" .. closest_bld] = 1
|
||||
else
|
||||
minetest.log("warning",
|
||||
string.format(
|
||||
"[mcl_villages] No good path from %s to %s, distance %d",
|
||||
minetest.pos_to_string(from_ep_pos),
|
||||
minetest.pos_to_string(closest_pos),
|
||||
vector.distance(from_ep_pos, closest_pos)
|
||||
)
|
||||
)
|
||||
table.sort(cand, function(a,b) return a[1] < b[1] end)
|
||||
--minetest.log("action", "candidates: "..dump(cand,""))
|
||||
for _, pair in ipairs(cand) do
|
||||
local dist, from, from_ep_pos, to, to_ep_pos = unpack(pair)
|
||||
local path = minetest.find_path(from_ep_pos, to_ep_pos, 10, 4, 4)
|
||||
if path then smooth_path(path, 3, minp, maxp) end
|
||||
path = minetest.find_path(from_ep_pos, to_ep_pos, 10, 2, 2)
|
||||
if path then smooth_path(path, 1, minp, maxp) end
|
||||
path = minetest.find_path(from_ep_pos, to_ep_pos, 12, 1, 1)
|
||||
if path then
|
||||
--minetest.log("path "..from.." to "..to.." len "..tostring(#path))
|
||||
path = smooth_path(path, 1, minp, maxp)
|
||||
if path then
|
||||
connected[from .. "-" .. to] = 1
|
||||
table.insert(to_place, pair)
|
||||
goto continue -- add only one path per building
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
::continue::
|
||||
end
|
||||
|
||||
--minetest.log("action", "to_place: "..dump(to_place,""))
|
||||
-- now lay the actual paths
|
||||
for _, cand in ipairs(to_place) do
|
||||
local dist, from, from_ep_pos, to, to_ep_pos = unpack(cand)
|
||||
local path = minetest.find_path(from_ep_pos, to_ep_pos, 12, 1, 1)
|
||||
if path then
|
||||
path = place_path(path, pr, stair, slab)
|
||||
else
|
||||
minetest.log("warning",
|
||||
string.format(
|
||||
"[mcl_villages] No good path from %s to %s, distance %d",
|
||||
minetest.pos_to_string(from_ep_pos),
|
||||
minetest.pos_to_string(to_ep_pos),
|
||||
dist
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
path_ends["block_" .. blockseed] = nil
|
||||
|
|
|
@ -168,7 +168,7 @@ function vl_structures.register_structure(name,def)
|
|||
for _, pos in ipairs(t) do
|
||||
local pr = PcgRandom(minetest.hash_node_position(pos) + worldseed + RANDOM_SEED_OFFSET)
|
||||
if def.chunk_probability == nil or pr:next(0, 1e9) * 1e-9 * def.chunk_probability <= structure_boost then
|
||||
vl_structures.place_structure(vector_offset(pos, 0, 1, 0), def, pr, blockseed)
|
||||
vl_structures.place_structure(pos, def, pr, blockseed)
|
||||
if def.chunk_probability ~= nil then break end -- allow only one per gennotify, e.g., on multiple surfaces
|
||||
end
|
||||
end
|
||||
|
|
|
@ -26,6 +26,7 @@ local function emerge_schematics(blockpos, action, calls_remaining, param)
|
|||
local startmain = os.clock()
|
||||
local pos, size, yoffset, def, pr = param.pos, param.size, param.yoffset or 0, param.def, param.pr
|
||||
local prepare, surface_mat = parse_prepare(param.prepare or def.prepare), param.surface_mat
|
||||
local dust_mat = nil
|
||||
|
||||
-- Step 0: pick random daughter schematics + rotations
|
||||
local daughters = {}
|
||||
|
@ -38,6 +39,10 @@ local function emerge_schematics(blockpos, action, calls_remaining, param)
|
|||
table.insert(daughters, {d, ds, rotation})
|
||||
end
|
||||
|
||||
-- hack to get dust nodes more often, in case the mapgen messed with biomes
|
||||
local n = vm:get_node_at(vector_offset(param.opos, 0, 1, 0))
|
||||
if n.name == "mcl_core:snow" then dust_mat = n end
|
||||
|
||||
-- Step 1: adjust ground to a more level position
|
||||
-- todo: also support checking ground of daughter schematics, but not used by current schematics
|
||||
if pos and size and prepare and tolerance_enabled(prepare.tolerance, prepare.mode) then
|
||||
|
@ -59,11 +64,13 @@ local function emerge_schematics(blockpos, action, calls_remaining, param)
|
|||
if prepare and (prepare.clear or prepare.foundation) then
|
||||
local prepare_start = os.clock()
|
||||
-- Get materials from biome (TODO: make this a function + table?):
|
||||
local b = mg_name ~= "v6" and minetest.registered_biomes[minetest.get_biome_name(minetest.get_biome_data(pos).biome)]
|
||||
-- use original position to not get a different biome than expected for the structure
|
||||
local b = mg_name ~= "v6" and minetest.registered_biomes[minetest.get_biome_name(minetest.get_biome_data(vector_offset(param.pos,0,1,0)).biome)]
|
||||
--minetest.log("action", tostring(def.name or param.schematic.name).." in biome: "..dump(b,""):gsub("\n",""))
|
||||
local node_top = b and b.node_top and { name = b.node_top } or surface_mat or vl_structures.DEFAULT_SURFACE
|
||||
local node_filler = b and b.node_filler and { name = b.node_filler } or vl_structures.DEFAULT_FILLER
|
||||
local node_stone = b and b.node_stone and { name = b.node_stone } or vl_structures.DEFAULT_STONE
|
||||
local node_dust = b and b.node_dust and { name = b.node_dust } or vl_structures.DEFAULT_DUST
|
||||
local node_dust = b and b.node_dust and { name = b.node_dust } or dust_mat or vl_structures.DEFAULT_DUST
|
||||
if node_top.name == "mcl_core:dirt_with_grass" and b then node_top.param2 = b._mcl_grass_palette_index end
|
||||
|
||||
-- Step 2a: clear overhead area
|
||||
|
@ -101,7 +108,7 @@ local function emerge_schematics(blockpos, action, calls_remaining, param)
|
|||
|
||||
-- Step 2b: baseplate underneath
|
||||
if prepare.foundation then
|
||||
-- minetest.log("action", "[vl_structures] fill foundation "..minetest.pos_to_string(gp).." with "..tostring(node_top.name).." "..tostring(node_filler.name))
|
||||
-- minetest.log("action", "[vl_structures] "..tostring(def.name or param.schematic.name).." fill foundation "..minetest.pos_to_string(gp).." with "..tostring(node_top.name).." "..tostring(node_filler.name).." "..tostring(node_dust and node_dust.name))
|
||||
local depth = (type(prepare.foundation) == "number" and prepare.foundation) or vl_structures.DEFAULT_PREPARE.foundation
|
||||
vl_terraforming.foundation_vm(vm, gp.x, gp.y - 1, gp.z,
|
||||
size.x + padding * 2, depth, size.z + padding * 2,
|
||||
|
@ -135,6 +142,7 @@ local function emerge_schematics(blockpos, action, calls_remaining, param)
|
|||
-- todo: allow after_place callbacks for daughter schematics?
|
||||
end
|
||||
local endmain = os.clock()
|
||||
-- TODO: step 4: sprinkle extra dust on top.
|
||||
vm:write_to_map(true)
|
||||
-- Note: deliberately pos, p1 and p2 from the parent, as these are calls to the parent script
|
||||
if def.loot then vl_structures.fill_chests(pmin,pmax,def.loot,pr) end
|
||||
|
@ -173,7 +181,7 @@ vl_structures.place_schematic = function(pos, yoffset, schematic, rotation, def,
|
|||
-- if logging and not def.terrain_feature then minetest.log("action", "[vl_structures] "..def.name.." needs emerge "..minetest.pos_to_string(emin).."-"..minetest.pos_to_string(emax)) end
|
||||
minetest.emerge_area(emin, emax, emerge_schematics, { name = def.name,
|
||||
emin=emin, emax=emax, def=def, schematic=schematic,
|
||||
pos=ppos, yoffset=yoffset, size=size, rotation=rotation,
|
||||
pos=ppos, opos=pos, yoffset=yoffset, size=size, rotation=rotation,
|
||||
pr=pr
|
||||
})
|
||||
end
|
||||
|
|
|
@ -48,8 +48,17 @@ mcl_disabled_events (Disabled events) string
|
|||
# Structure frequency multiplier, keep this less than 3 usually
|
||||
vl_structures_boost (Structure frequency multiplier) float 1.0 0.0 10.0
|
||||
|
||||
# Village frequency multiplier, keep this less than 3 usually
|
||||
vl_villages_boost (Village frequency multiplier) float 1.0 0.0 10.0
|
||||
# Minimum number of job sites for villages to plan (generation may have less)
|
||||
vl_villages_min_jobs (Small village job sites) int 2 0 20
|
||||
|
||||
# Maximum number of job sites for villages to plan (generation may have less)
|
||||
vl_villages_max_jobs (Large villages job sites) int 14 0 20
|
||||
|
||||
# Placement strategy for buildings: jobs, houses, or random
|
||||
vl_villages_placement_priority (Village building placement) enum houses houses,jobs,random
|
||||
|
||||
# Village frequency multiplier, keep this less than 3 usually to avoid excessive map emerges
|
||||
vl_villages_boost (Village frequency multiplier) float 1.0 0.0 5.0
|
||||
|
||||
[Players]
|
||||
# If enabled, players respawn at the bed they last lay on instead of normal
|
||||
|
|
Loading…
Reference in a new issue