Compare commits

...

3 Commits

Author SHA1 Message Date
kno10 e477247485 igloo less slope, more snow, more rare 2024-10-20 21:58:38 +02:00
kno10 068469b1e9 height difference for villages 2024-10-20 21:58:37 +02:00
kno10 16cebc361e better paths, better village layouts 2024-10-20 21:00:12 +02:00
9 changed files with 211 additions and 134 deletions

View File

@ -102,11 +102,11 @@ end
vl_structures.register_structure("igloo",{
filenames = { modpath.."/schematics/mcl_structures_igloo_top.mts" },
place_on = {"mcl_core:snowblock","mcl_core:snow","group:grass_block_snow"},
chunk_probability = 7,
prepare = { padding = 1, corners = 1, foundation = -6, clear_top=-1 },
chunk_probability = 15,
prepare = { tolerance = 3, padding = 1, corners = 1, foundation = -6, clear_top = -1 },
y_max = mcl_vars.mg_overworld_max,
y_min = 0,
y_offset = -2,
y_offset = -1,
biomes = { "ColdTaiga", "IcePlainsSpikes", "IcePlains" },
after_place = igloo_callback,
loot = {

View File

@ -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

View File

@ -1,11 +1,16 @@
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 max_height_difference = 40 -- at distance 40. In the center, half as much
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,23 +40,33 @@ 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
if #settlement == 0 then
center_surface, y = cpos, math.min(maxp.y, pos.y + mcl_villages.max_height_difference * 0.5 + 1)
center_surface, y = cpos, math.min(maxp.y, pos.y + max_height_difference + 1)
end
-- limit height differences to town center, but gradually allow more
if math.abs(pos.y - center_surface.y) <= mcl_villages.max_height_difference * (0.25 + math.min(r/40,0.5)) then
if math.abs(pos.y - center_surface.y) <= max_height_difference * (0.5 + r/80) then
local minp = vector.offset(pos, -math.floor((size.x-1)/2), building.yadjust, -math.floor((size.z-1)/2))
building.minp = minp
building.maxp = vector.offset(minp, size.x, size.y, size.z)
@ -149,15 +164,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 +212,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 +248,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,
})

View File

@ -1,6 +1,3 @@
-- maximum allowed difference in height for building a settlement
mcl_villages.max_height_difference = 56
-- legacy type in old schematics
minetest.register_alias("mcl_villages:stonebrickcarved", "mcl_core:stonebrickcarved")

View File

@ -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({

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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