From 80478e12524f0a0d046e42805023cdbaa3b222ea Mon Sep 17 00:00:00 2001 From: kno10 Date: Tue, 29 Oct 2024 19:41:33 +0100 Subject: [PATCH] vl_terraforming: add non-vm version --- mods/MAPGEN/vl_terraforming/clearance.lua | 47 ++- mods/MAPGEN/vl_terraforming/clearance_vm.lua | 174 ++++++++++++ mods/MAPGEN/vl_terraforming/foundation.lua | 37 ++- mods/MAPGEN/vl_terraforming/foundation_vm.lua | 117 ++++++++ mods/MAPGEN/vl_terraforming/init.lua | 3 + mods/MAPGEN/vl_terraforming/level.lua | 78 +++-- mods/MAPGEN/vl_terraforming/level_vm.lua | 267 ++++++++++++++++++ mods/MAPGEN/vl_terraforming/util.lua | 22 +- 8 files changed, 655 insertions(+), 90 deletions(-) create mode 100644 mods/MAPGEN/vl_terraforming/clearance_vm.lua create mode 100644 mods/MAPGEN/vl_terraforming/foundation_vm.lua create mode 100644 mods/MAPGEN/vl_terraforming/level_vm.lua diff --git a/mods/MAPGEN/vl_terraforming/clearance.lua b/mods/MAPGEN/vl_terraforming/clearance.lua index fed999842..b6cf7ce44 100644 --- a/mods/MAPGEN/vl_terraforming/clearance.lua +++ b/mods/MAPGEN/vl_terraforming/clearance.lua @@ -5,6 +5,8 @@ local floor = math.floor local vector_new = vector.new local is_solid_not_tree = vl_terraforming._is_solid_not_tree local is_tree_not_leaves = vl_terraforming._is_tree_not_leaves +local get_node = core.get_node +local swap_node = core.swap_node --- Clear an area for a structure -- @@ -15,7 +17,6 @@ local is_tree_not_leaves = vl_terraforming._is_tree_not_leaves -- The ellipse condition dx^2/a^2+dz^2/b^2 <= 1 then yields dx^2/(sx^2*0.5) + dz^2/(sz^2*0.5) <= 1 -- We use wx2=sx^-2*2, wz2=sz^-2*2 and then dx^2*wx2+dz^2*wz2 <= 1 -- --- @param vm VoxelManip: Lua voxel manipulator -- @param px number: lowest x -- @param py number: lowest y -- @param pz number: lowest z @@ -26,10 +27,8 @@ local is_tree_not_leaves = vl_terraforming._is_tree_not_leaves -- @param surface_mat Node: surface node material -- @param dust_mat Node: surface dust material -- @param pr PcgRandom: random generator -function vl_terraforming.clearance_vm(vm, px, py, pz, sx, sy, sz, corners, surface_mat, dust_mat, pr) +function vl_terraforming.clearance(px, py, pz, sx, sy, sz, corners, surface_mat, dust_mat, pr) if sx <= 0 or sy <= 0 or sz <= 0 then return end - local get_node_at = vm.get_node_at - local set_node_at = vm.set_node_at corners = corners or 0 local wx2, wz2 = max(sx - corners, 1)^-2 * 2, max(sz - corners, 1)^-2 * 2 local cx, cz = px + sx * 0.5 - 0.5, pz + sz * 0.5 - 0.5 @@ -48,33 +47,33 @@ function vl_terraforming.clearance_vm(vm, px, py, pz, sx, sy, sz, corners, surfa vec.z = zi if xi >= px and xi < px+sx and zi >= pz and zi < pz+sz and dx2+dz2 <= 1 then vec.y = py - if vm:get_node_at(vec).name ~= "mcl_core:bedrock" then set_node_at(vm, vec, AIR) end + if get_node(vec).name ~= "mcl_core:bedrock" then swap_node(vec, AIR) end vec.y = py - 1 - local n = get_node_at(vm, vec) + local n = get_node(vec) if n and n.name ~= surface_mat.name and is_solid_not_tree(n) then - set_node_at(vm, vec, surface_mat) + swap_node(vec, surface_mat) end for yi = py+1,min_clear do -- full height for inner area vec.y = yi - if vm:get_node_at(vec).name ~= "mcl_core:bedrock" then set_node_at(vm, vec, AIR) end + if get_node(vec).name ~= "mcl_core:bedrock" then swap_node(vec, AIR) end end elseif dx21+dz21 <= 1 then -- widen the cave above by 1, to make easier to enter for mobs -- todo: make configurable? vec.y = py + 1 - local name = vm:get_node_at(vec).name + local name = get_node(vec).name if name ~= "mcl_core:bedrock" then local mat = AIR if dust_mat then vec.y = py - if vm:get_node_at(vec).name == surface_mat.name then mat = dust_mat end + if get_node(vec).name == surface_mat.name then mat = dust_mat end vec.y = py + 1 end - set_node_at(vm, vec, mat) + swap_node(vec, mat) end for yi = py+2,min_clear-1 do vec.y = yi - if vm:get_node_at(vec).name ~= "mcl_core:bedrock" then set_node_at(vm, vec, AIR) end + if get_node(vec).name ~= "mcl_core:bedrock" then swap_node(vec, AIR) end if yi > py+4 then local p = (yi-py) / (max_clear-py) --minetest.log(tostring(p).."^2 "..tostring(p*p).." rand: "..pr:next(0,1e9)/1e9) @@ -84,19 +83,19 @@ function vl_terraforming.clearance_vm(vm, px, py, pz, sx, sy, sz, corners, surfa -- remove some tree parts and fix surfaces down for yi = py,py-1,-1 do vec.y = yi - local n = get_node_at(vm, vec) + local n = get_node(vec) if is_tree_not_leaves(n) then - set_node_at(vm, vec, surface_mat) + swap_node(vec, surface_mat) if dust_mat and yi == py then vec.y = yi + 1 - if vm:get_node_at(vec).name == "air" then set_node_at(vm, vec, dust_mat) end + if get_node(vec).name == "air" then swap_node(vec, dust_mat) end end else if n and n.name ~= surface_mat.name and is_solid_not_tree(n) then - set_node_at(vm, vec, surface_mat) + swap_node(vec, surface_mat) if dust_mat then vec.y = yi + 1 - if vm:get_node_at(vec).name == "air" then set_node_at(vm, vec, dust_mat) end + if get_node(vec).name == "air" then swap_node(vec, dust_mat) end end end break @@ -119,16 +118,16 @@ function vl_terraforming.clearance_vm(vm, px, py, pz, sx, sy, sz, corners, surfa if py+4 < sy then for yi = py+2,py+4 do vec = vector_new(xi, yi, zi) - if vm:get_node_at(vec).name ~= "mcl_core:bedrock" then set_node_at(vm, vec, v) end + if get_node(vec).name ~= "mcl_core:bedrock" then swap_node(vec, v) end end end for yi = py+1,py-1,-1 do - local n = get_node_at(vm, vector_new(xi, yi, zi)) + local n = get_node(vector_new(xi, yi, zi)) if is_tree_bot_leaves(n) and n.name ~= "mcl_core:bedrock" then - set_node_at(vm, vector_new(xi, yi, zi), AIR) + swap_node(vector_new(xi, yi, zi), AIR) else if n and n.name ~= surface_mat.name and is_solid_not_tree(n) then - set_node_at(vm, vector_new(xi, yi, zi), surface_mat) + swap_node(vector_new(xi, yi, zi), surface_mat) end break end @@ -148,7 +147,7 @@ function vl_terraforming.clearance_vm(vm, px, py, pz, sx, sy, sz, corners, surfa local keep_trees = (xi=px+sx) or (zi=pz+sz) -- TODO make parameter? if dx22+dy2+dz22 <= 1 then vec.x, vec.y, vec.z = xi, yi, zi - local name = get_node_at(vm, vec).name + local name = get_node(vec).name -- don't break bedrock or air if name == "air" or name == "ignore" or name == "mcl_core:bedrock" or name == "mcl_villages:no_paths" then goto continue end local meta = minetest.registered_items[name] @@ -157,13 +156,13 @@ function vl_terraforming.clearance_vm(vm, px, py, pz, sx, sy, sz, corners, surfa if keep_trees and is_tree then goto continue end vec.y = yi-1 -- do not clear above solid - local name_below = get_node_at(vm, vec).name + local name_below = get_node(vec).name if name_below ~= "air" and name_below ~= "ignore" and name_below ~= "mcl_core:bedrock" then goto continue end -- try to completely remove trees overhead -- stop randomly depending on fill, to narrow down the caves if not keep_trees and not is_tree and (pr:next(0,1e9)/1e9)^0.5 > 1-(dx22+dy2+dz22-0.1) then goto continue end vec.x, vec.y, vec.z = xi, yi, zi - set_node_at(vm, vec, AIR) + swap_node(vec, AIR) active = true ::continue:: end diff --git a/mods/MAPGEN/vl_terraforming/clearance_vm.lua b/mods/MAPGEN/vl_terraforming/clearance_vm.lua new file mode 100644 index 000000000..4fe5ea9aa --- /dev/null +++ b/mods/MAPGEN/vl_terraforming/clearance_vm.lua @@ -0,0 +1,174 @@ +local AIR = {name = "air"} +local abs = math.abs +local max = math.max +local floor = math.floor +local vector_new = vector.new +local is_solid_not_tree = vl_terraforming._is_solid_not_tree +local is_tree_not_leaves = vl_terraforming._is_tree_not_leaves + +--- Clear an area for a structure +-- +-- Rounding: we model an ellipse. At zero rounding, we want the line go through the corner, at sx/2, sz/2. +-- For this, we need to make ellipse sized 2a=sqrt(2)*sx, 2b=sqrt(2)*sz, +-- Which yields a = sx/sqrt(2), b=sz/sqrt(2) and a^2=sx^2*0.5, b^2=sz^2*0.5 +-- To get corners, we decrease a and b by approx. corners each +-- The ellipse condition dx^2/a^2+dz^2/b^2 <= 1 then yields dx^2/(sx^2*0.5) + dz^2/(sz^2*0.5) <= 1 +-- We use wx2=sx^-2*2, wz2=sz^-2*2 and then dx^2*wx2+dz^2*wz2 <= 1 +-- +-- @param vm VoxelManip: Lua voxel manipulator +-- @param px number: lowest x +-- @param py number: lowest y +-- @param pz number: lowest z +-- @param sx number: x width +-- @param sy number: y height +-- @param sz number: z depth +-- @param corners number: corner rounding +-- @param surface_mat Node: surface node material +-- @param dust_mat Node: surface dust material +-- @param pr PcgRandom: random generator +function vl_terraforming.clearance_vm(vm, px, py, pz, sx, sy, sz, corners, surface_mat, dust_mat, pr) + if sx <= 0 or sy <= 0 or sz <= 0 then return end + local get_node_at = vm.get_node_at + local set_node_at = vm.set_node_at + corners = corners or 0 + local wx2, wz2 = max(sx - corners, 1)^-2 * 2, max(sz - corners, 1)^-2 * 2 + local cx, cz = px + sx * 0.5 - 0.5, pz + sz * 0.5 - 0.5 + local min_clear, max_clear = py+sy, py+floor(sy*1.5+2) -- todo: make more parameterizable, but adds another parameter + -- excavate the needed volume and some headroom + local vec = vector_new(0, 0, 0) -- single vector, to avoid allocations -- performance! + for xi = px-1,px+sx do + local dx = abs(cx-xi) + local dx2 = max(dx+0.51,0)^2*wx2 + local dx21 = max(dx-0.49,0)^2*wx2 + vec.x = xi + for zi = pz-1,pz+sz do + local dz = abs(cz-zi) + local dz2 = max(dz+0.51,0)^2*wz2 + local dz21 = max(dz-0.49,0)^2*wz2 + vec.z = zi + if xi >= px and xi < px+sx and zi >= pz and zi < pz+sz and dx2+dz2 <= 1 then + vec.y = py + if get_node_at(vm, vec).name ~= "mcl_core:bedrock" then set_node_at(vm, vec, AIR) end + vec.y = py - 1 + local n = get_node_at(vm, vec) + if n and n.name ~= surface_mat.name and is_solid_not_tree(n) then + set_node_at(vm, vec, surface_mat) + end + for yi = py+1,min_clear do -- full height for inner area + vec.y = yi + if get_node_at(vm, vec).name ~= "mcl_core:bedrock" then set_node_at(vm, vec, AIR) end + end + elseif dx21+dz21 <= 1 then + -- widen the cave above by 1, to make easier to enter for mobs + -- todo: make configurable? + vec.y = py + 1 + local name = get_node_at(vm, vec).name + if name ~= "mcl_core:bedrock" then + local mat = AIR + if dust_mat then + vec.y = py + if get_node_at(vm, vec).name == surface_mat.name then mat = dust_mat end + vec.y = py + 1 + end + set_node_at(vm, vec, mat) + end + for yi = py+2,min_clear-1 do + vec.y = yi + if get_node_at(vm, vec).name ~= "mcl_core:bedrock" then set_node_at(vm, vec, AIR) end + if yi > py+4 then + local p = (yi-py) / (max_clear-py) + --minetest.log(tostring(p).."^2 "..tostring(p*p).." rand: "..pr:next(0,1e9)/1e9) + if (pr:next(0,1e9)/1e9) < p then break end + end + end + -- remove some tree parts and fix surfaces down + for yi = py,py-1,-1 do + vec.y = yi + local n = get_node_at(vm, vec) + if is_tree_not_leaves(n) then + set_node_at(vm, vec, surface_mat) + if dust_mat and yi == py then + vec.y = yi + 1 + if get_node_at(vm, vec).name == "air" then set_node_at(vm, vec, dust_mat) end + end + else + if n and n.name ~= surface_mat.name and is_solid_not_tree(n) then + set_node_at(vm, vec, surface_mat) + if dust_mat then + vec.y = yi + 1 + if get_node_at(vm, vec).name == "air" then set_node_at(vm, vec, dust_mat) end + end + end + break + end + end + end + end + end + -- some extra gaps for entry + -- todo: make optional instead of hard-coded 25% + -- todo: only really useful if there is space at px-3,py+3 to px-3,py+5 + --[[ + for xi = px-2,px+sx+1 do + local dx21 = max(abs(cx-xi)-0.49,0)^2*wx2 + local dx22 = max(abs(cx-xi)-1.49,0)^2*wx2 + for zi = pz-2,pz+sz+1 do + local dz21 = max(abs(cz-zi)-0.49,0)^2*wz2 + local dz22 = max(abs(cz-zi)-1.49,0)^2*wz2 + if dx21+dz21 > 1 and dx22+dz22 <= 1 and pr:next(1,4) == 1 then + if py+4 < sy then + for yi = py+2,py+4 do + vec = vector_new(xi, yi, zi) + if get_node_at(vm, vec).name ~= "mcl_core:bedrock" then set_node_at(vm, vec, v) end + end + end + for yi = py+1,py-1,-1 do + local n = get_node_at(vm, vector_new(xi, yi, zi)) + if is_tree_bot_leaves(n) and n.name ~= "mcl_core:bedrock" then + set_node_at(vm, vector_new(xi, yi, zi), AIR) + else + if n and n.name ~= surface_mat.name and is_solid_not_tree(n) then + set_node_at(vm, vector_new(xi, yi, zi), surface_mat) + end + break + end + end + end + end + end + ]]-- + -- cave some additional area overhead, try to make it interesting though + for yi = min_clear+1,max_clear do + local dy2 = max(yi-min_clear-1,0)^2*0.05 + local active = false + for xi = px-2,px+sx+1 do + local dx22 = max(abs(cx-xi)-1.49,0)^2*wx2 + for zi = pz-2,pz+sz+1 do + local dz22 = max(abs(cz-zi)-1.49,0)^2*wz2 + local keep_trees = (xi=px+sx) or (zi=pz+sz) -- TODO make parameter? + if dx22+dy2+dz22 <= 1 then + vec.x, vec.y, vec.z = xi, yi, zi + local name = get_node_at(vm, vec).name + -- don't break bedrock or air + if name == "air" or name == "ignore" or name == "mcl_core:bedrock" or name == "mcl_villages:no_paths" then goto continue end + local meta = minetest.registered_items[name] + local groups = meta and meta.groups + local is_tree = groups.leaves or groups.tree or (groups.compostability or 0 > 50) + if keep_trees and is_tree then goto continue end + vec.y = yi-1 + -- do not clear above solid + local name_below = get_node_at(vm, vec).name + if name_below ~= "air" and name_below ~= "ignore" and name_below ~= "mcl_core:bedrock" then goto continue end + -- try to completely remove trees overhead + -- stop randomly depending on fill, to narrow down the caves + if not keep_trees and not is_tree and (pr:next(0,1e9)/1e9)^0.5 > 1-(dx22+dy2+dz22-0.1) then goto continue end + vec.x, vec.y, vec.z = xi, yi, zi + set_node_at(vm, vec, AIR) + active = true + ::continue:: + end + end + end + if not active then break end + end +end diff --git a/mods/MAPGEN/vl_terraforming/foundation.lua b/mods/MAPGEN/vl_terraforming/foundation.lua index 74e313c03..812e97ddc 100644 --- a/mods/MAPGEN/vl_terraforming/foundation.lua +++ b/mods/MAPGEN/vl_terraforming/foundation.lua @@ -3,10 +3,11 @@ local max = math.max local vector_new = vector.new local is_solid_not_tree = vl_terraforming._is_solid_not_tree -local make_solid_vm = vl_terraforming._make_solid_vm +local make_solid = vl_terraforming._make_solid +local get_node = core.get_node +local swap_node = core.swap_node --- Grow the foundation downwards --- @param vm VoxelManip: Lua Voxel Manipulator -- @param xi number: x coordinate -- @param yi number: y coordinate -- @param zi number: z coordinate @@ -14,12 +15,11 @@ local make_solid_vm = vl_terraforming._make_solid_vm -- @param surface_mat Node: surface material node -- @param platform_mat Node: platform material node -- @param stone_mat Node: stone material node -local function grow_foundation_vm(vm,xi,yi,zi,pr,surface_mat,platform_mat,stone_mat) - local get_node_at = vm.get_node_at +local function grow_foundation(xi,yi,zi,pr,surface_mat,platform_mat,stone_mat) local pos, n, c = vector_new(xi,yi,zi), nil, 0 - if is_solid_not_tree(get_node_at(vm, pos)) then return false end -- already solid, nothing to do + if is_solid_not_tree(get_node(pos)) then return false end -- already solid, nothing to do pos.y = pos.y + 1 - local cur = get_node_at(vm, pos) + local cur = get_node(pos) if not is_solid_not_tree(cur) then return false end -- above is empty, do not fill below if cur and cur.name and cur.name ~= surface_mat.name then platform_mat = cur end if pr:next(1,4) == 1 then platform_mat = stone_mat end -- randomly switch to stone sometimes @@ -27,15 +27,15 @@ local function grow_foundation_vm(vm,xi,yi,zi,pr,surface_mat,platform_mat,stone_ for x = xi-1,xi+1 do for z = zi-1,zi+1 do pos.x, pos.z = x, z - if is_solid_not_tree(get_node_at(vm, pos)) then c = c + 1 end + if is_solid_not_tree(get_node(pos)) then c = c + 1 end end end -- stop randomly depending on fill, to narrow down the foundation -- TODO: allow controlling the random depth with an additional parameter? if (pr:next(0,1e9)/1e9)^2 > c/9.1 then return false end pos.x, pos.y, pos.z = xi, yi, zi - if get_node_at(vm, pos).name == "mcl_core:bedrock" then return false end - vm:set_node_at(pos, platform_mat) + if get_node(pos).name == "mcl_core:bedrock" then return false end + swap_node(pos, platform_mat) return true end --- Generate a foundation from px,py,pz with size sx,sy,sz (sy < 0) plus some margin @@ -48,7 +48,6 @@ end -- The ellipse condition dx^2/a^2+dz^2/b^2 <= 1 then yields dx^2/(sx^2*0.5) + dz^2/(sz^2*0.5) <= 1 -- We use wx2=sx^-2*2, wz2=sz^-2*2 and then dx^2*wx2+dz^2*wz2 <= 1 -- --- @param vm VoxelManip: Lua Voxel Manipulator -- @param px number: lowest x -- @param py number: lowest y -- @param pz number: lowest z @@ -61,10 +60,8 @@ end -- @param stone_mat Node: stone material node -- @param dust_mat Node: dust material, optional -- @param pr PcgRandom: random generator -function vl_terraforming.foundation_vm(vm, px, py, pz, sx, sy, sz, corners, surface_mat, platform_mat, stone_mat, dust_mat, pr) +function vl_terraforming.foundation(px, py, pz, sx, sy, sz, corners, surface_mat, platform_mat, stone_mat, dust_mat, pr) if sx <= 0 or sy >= 0 or sz <= 0 then return end - local get_node_at = vm.get_node_at - local set_node_at = vm.set_node_at corners = corners or 0 local wx2, wz2 = max(sx - corners, 1)^-2 * 2, max(sz - corners, 1)^-2 * 2 local cx, cz = px + sx * 0.5 - 0.5, pz + sz * 0.5 - 0.5 @@ -80,22 +77,22 @@ function vl_terraforming.foundation_vm(vm, px, py, pz, sx, sy, sz, corners, surf pos.z = zi if xi >= px and xi < px+sx and zi >= pz and zi < pz+sz and dx2+dz2 <= 1 then pos.y = py - if get_node_at(vm, pos).name ~= "mcl_core:bedrock" then - set_node_at(vm, pos, surface_mat) + if get_node(pos).name ~= "mcl_core:bedrock" then + swap_node(pos, surface_mat) if dust_mat then pos.y = py + 1 - if get_node_at(vm, pos).name == "air" then set_node_at(vm, pos, dust_mat) end + if get_node(pos).name == "air" then swap_node(pos, dust_mat) end end pos.y = py - 1 - make_solid_vm(vm, pos, platform_mat) + make_solid(pos, platform_mat) end elseif dx21+dz21 <= 1 then -- and pr:next(1,4) < 4 then -- TODO: make randomness configurable. -- slightly widen the baseplate below, to make easier to enter for mobs pos.y = py - 1 - make_solid_vm(vm, pos, surface_mat) + make_solid(pos, surface_mat) if dust_mat then pos.y = py - if get_node_at(vm, pos).name == "air" then set_node_at(vm, pos, dust_mat) end + if get_node(pos).name == "air" then swap_node(pos, dust_mat) end end end end @@ -108,7 +105,7 @@ function vl_terraforming.foundation_vm(vm, px, py, pz, sx, sy, sz, corners, surf local dx22 = max(abs(cx-xi)-1.49,0)^2*wx2 for zi = pz-1,pz+sz do local dz22 = max(abs(cz-zi)-1.49,0)^2*wz2 - if dx22+dy2+dz22 <= 1 and grow_foundation_vm(vm,xi,yi,zi,pr,surface_mat,platform_mat,stone_mat) then active = true end + if dx22+dy2+dz22 <= 1 and grow_foundation(xi,yi,zi,pr,surface_mat,platform_mat,stone_mat) then active = true end end end if not active and yi < py + sy then break end diff --git a/mods/MAPGEN/vl_terraforming/foundation_vm.lua b/mods/MAPGEN/vl_terraforming/foundation_vm.lua new file mode 100644 index 000000000..74e313c03 --- /dev/null +++ b/mods/MAPGEN/vl_terraforming/foundation_vm.lua @@ -0,0 +1,117 @@ +local abs = math.abs +local max = math.max +local vector_new = vector.new + +local is_solid_not_tree = vl_terraforming._is_solid_not_tree +local make_solid_vm = vl_terraforming._make_solid_vm + +--- Grow the foundation downwards +-- @param vm VoxelManip: Lua Voxel Manipulator +-- @param xi number: x coordinate +-- @param yi number: y coordinate +-- @param zi number: z coordinate +-- @param pr PcgRandom: random generator +-- @param surface_mat Node: surface material node +-- @param platform_mat Node: platform material node +-- @param stone_mat Node: stone material node +local function grow_foundation_vm(vm,xi,yi,zi,pr,surface_mat,platform_mat,stone_mat) + local get_node_at = vm.get_node_at + local pos, n, c = vector_new(xi,yi,zi), nil, 0 + if is_solid_not_tree(get_node_at(vm, pos)) then return false end -- already solid, nothing to do + pos.y = pos.y + 1 + local cur = get_node_at(vm, pos) + if not is_solid_not_tree(cur) then return false end -- above is empty, do not fill below + if cur and cur.name and cur.name ~= surface_mat.name then platform_mat = cur end + if pr:next(1,4) == 1 then platform_mat = stone_mat end -- randomly switch to stone sometimes + -- count solid nodes above otherwise + for x = xi-1,xi+1 do + for z = zi-1,zi+1 do + pos.x, pos.z = x, z + if is_solid_not_tree(get_node_at(vm, pos)) then c = c + 1 end + end + end + -- stop randomly depending on fill, to narrow down the foundation + -- TODO: allow controlling the random depth with an additional parameter? + if (pr:next(0,1e9)/1e9)^2 > c/9.1 then return false end + pos.x, pos.y, pos.z = xi, yi, zi + if get_node_at(vm, pos).name == "mcl_core:bedrock" then return false end + vm:set_node_at(pos, platform_mat) + return true +end +--- Generate a foundation from px,py,pz with size sx,sy,sz (sy < 0) plus some margin +-- TODO: add support for dust_mat (snow) +-- +-- Rounding: we model an ellipse. At zero rounding, we want the line go through the corner, at sx/2, sz/2. +-- For this, we need to make ellipse sized 2a=sqrt(2)*sx, 2b=sqrt(2)*sz, +-- Which yields a = sx/sqrt(2), b=sz/sqrt(2) and a^2=sx^2*0.5, b^2=sz^2*0.5 +-- To get corners, we decrease a and b by approx. corners each +-- The ellipse condition dx^2/a^2+dz^2/b^2 <= 1 then yields dx^2/(sx^2*0.5) + dz^2/(sz^2*0.5) <= 1 +-- We use wx2=sx^-2*2, wz2=sz^-2*2 and then dx^2*wx2+dz^2*wz2 <= 1 +-- +-- @param vm VoxelManip: Lua Voxel Manipulator +-- @param px number: lowest x +-- @param py number: lowest y +-- @param pz number: lowest z +-- @param sx number: x width +-- @param sy number: y height +-- @param sz number: z depth +-- @param corners number: Corner rounding +-- @param surface_mat Node: surface material node +-- @param platform_mat Node: platform material node +-- @param stone_mat Node: stone material node +-- @param dust_mat Node: dust material, optional +-- @param pr PcgRandom: random generator +function vl_terraforming.foundation_vm(vm, px, py, pz, sx, sy, sz, corners, surface_mat, platform_mat, stone_mat, dust_mat, pr) + if sx <= 0 or sy >= 0 or sz <= 0 then return end + local get_node_at = vm.get_node_at + local set_node_at = vm.set_node_at + corners = corners or 0 + local wx2, wz2 = max(sx - corners, 1)^-2 * 2, max(sz - corners, 1)^-2 * 2 + local cx, cz = px + sx * 0.5 - 0.5, pz + sz * 0.5 - 0.5 + -- generate a baseplate (2 layers, lower is wider + local pos = vector_new(px, py, pz) + for xi = px-1,px+sx do + local dx2 = max(abs(cx-xi)+0.51,0)^2*wx2 + local dx21 = max(abs(cx-xi)-0.49,0)^2*wx2 + pos.x = xi + for zi = pz-1,pz+sz do + local dz2 = max(abs(cz-zi)+0.51,0)^2*wz2 + local dz21 = max(abs(cz-zi)-0.49,0)^2*wz2 + pos.z = zi + if xi >= px and xi < px+sx and zi >= pz and zi < pz+sz and dx2+dz2 <= 1 then + pos.y = py + if get_node_at(vm, pos).name ~= "mcl_core:bedrock" then + set_node_at(vm, pos, surface_mat) + if dust_mat then + pos.y = py + 1 + if get_node_at(vm, pos).name == "air" then set_node_at(vm, pos, dust_mat) end + end + pos.y = py - 1 + make_solid_vm(vm, pos, platform_mat) + end + elseif dx21+dz21 <= 1 then -- and pr:next(1,4) < 4 then -- TODO: make randomness configurable. + -- slightly widen the baseplate below, to make easier to enter for mobs + pos.y = py - 1 + make_solid_vm(vm, pos, surface_mat) + if dust_mat then + pos.y = py + if get_node_at(vm, pos).name == "air" then set_node_at(vm, pos, dust_mat) end + end + end + end + end + -- construct additional baseplate below, also try to make it interesting + for yi = py-2,py-20,-1 do + local dy2 = max(0,py-2-yi)^2*0.10 + local active = false + for xi = px-1,px+sx do + local dx22 = max(abs(cx-xi)-1.49,0)^2*wx2 + for zi = pz-1,pz+sz do + local dz22 = max(abs(cz-zi)-1.49,0)^2*wz2 + if dx22+dy2+dz22 <= 1 and grow_foundation_vm(vm,xi,yi,zi,pr,surface_mat,platform_mat,stone_mat) then active = true end + end + end + if not active and yi < py + sy then break end + end + -- TODO: add back additional steps for easier entering, optional, and less regular? +end diff --git a/mods/MAPGEN/vl_terraforming/init.lua b/mods/MAPGEN/vl_terraforming/init.lua index 326403c52..c1832065a 100644 --- a/mods/MAPGEN/vl_terraforming/init.lua +++ b/mods/MAPGEN/vl_terraforming/init.lua @@ -4,5 +4,8 @@ vl_terraforming = {} dofile(modpath.."/util.lua") dofile(modpath.."/clearance.lua") +dofile(modpath.."/clearance_vm.lua") dofile(modpath.."/foundation.lua") +dofile(modpath.."/foundation_vm.lua") dofile(modpath.."/level.lua") +dofile(modpath.."/level_vm.lua") diff --git a/mods/MAPGEN/vl_terraforming/level.lua b/mods/MAPGEN/vl_terraforming/level.lua index 643c1ab37..ecc632b8f 100644 --- a/mods/MAPGEN/vl_terraforming/level.lua +++ b/mods/MAPGEN/vl_terraforming/level.lua @@ -4,26 +4,25 @@ local ceil = math.ceil local vector_copy = vector.copy local is_liquid = vl_terraforming._is_liquid local is_solid_not_tree = vl_terraforming._is_solid_not_tree +local get_node = core.get_node --- Find ground below a given position --- @param vm VoxelManip: buffer -- @param pos vector: Start position -- @return position and material of surface -function vl_terraforming.find_ground_vm(vm, pos) +function vl_terraforming.find_ground(pos) if not pos then return nil, nil end pos = vector_copy(pos) - local cur = vm:get_node_at(pos) + local cur = get_node(pos) if cur.name == "ignore" then - local e1, e2 = vm:get_emerged_area() minetest.log("warning", "find_ground with invalid position (outside of emerged area?) at "..minetest.pos_to_string(pos) - ..": "..tostring(cur and cur.name).." area: "..minetest.pos_to_string(e1).." "..minetest.pos_to_string(e2)) + ..": "..tostring(cur and cur.name)) return nil end if is_solid_not_tree(cur) then -- find up local prev = cur while true do pos.y = pos.y + 1 - local cur = vm:get_node_at(pos) + local cur = get_node(pos) if not cur or cur.name == "ignore" then -- minetest.log("action", "No ground, "..tostring(cur and cur.name).." over "..tostring(prev and prev.name).." at "..minetest.pos_to_string(pos)) return nil @@ -38,7 +37,7 @@ function vl_terraforming.find_ground_vm(vm, pos) while true do pos.y = pos.y - 1 local prev = cur - local cur = vm:get_node_at(pos) + local cur = get_node(pos) if not cur or cur.name == "ignore" then -- minetest.log("action", "No ground, "..tostring(cur and cur.name).." below "..tostring(prev and prev.name).." at "..minetest.pos_to_string(pos)) return nil @@ -52,27 +51,25 @@ function vl_terraforming.find_ground_vm(vm, pos) end end end -local find_ground_vm = vl_terraforming.find_ground_vm +local find_ground = vl_terraforming.find_ground --- Find ground or liquid surface for a given position --- @param vm VoxelManip: buffer -- @param pos vector: Start position -- @return position and material of surface -function vl_terraforming.find_under_air_vm(vm, pos) +function vl_terraforming.find_under_air(pos) if not pos then return nil, nil end pos = vector_copy(pos) - local cur = vm:get_node_at(pos) + local cur = get_node(pos) if cur.name == "ignore" then - local e1, e2 = vm:get_emerged_area() minetest.log("warning", "find_under_air with invalid position (outside of emerged area?) at "..minetest.pos_to_string(pos) - ..": "..tostring(cur and cur.name).." area: "..minetest.pos_to_string(e1).." "..minetest.pos_to_string(e2)) + ..": "..tostring(cur and cur.name)) return nil end if is_solid_not_tree(cur) or is_liquid(cur) then -- find up local prev = cur while true do pos.y = pos.y + 1 - local cur = vm:get_node_at(pos) + local cur = get_node(pos) if not cur or cur.name == "ignore" then -- minetest.log("action", "No ground, "..tostring(cur and cur.name).." over "..tostring(prev and prev.name).." at "..minetest.pos_to_string(pos)) return nil @@ -88,7 +85,7 @@ function vl_terraforming.find_under_air_vm(vm, pos) while true do pos.y = pos.y - 1 local prev = cur - local cur = vm:get_node_at(pos) + local cur = get_node(pos) if not cur or cur.name == "ignore" then -- minetest.log("action", "No ground, "..tostring(cur and cur.name).." below "..tostring(prev and prev.name).." at "..minetest.pos_to_string(pos)) return nil @@ -100,27 +97,25 @@ function vl_terraforming.find_under_air_vm(vm, pos) end end end -local find_under_air_vm = vl_terraforming.find_under_air_vm +local find_under_air = vl_terraforming.find_under_air --- Find liquid surface for a given position --- @param vm VoxelManip: buffer -- @param pos vector: Start position -- @return position and material of surface -function vl_terraforming.find_liquid_surface_vm(vm, pos) +function vl_terraforming.find_liquid_surface(pos) if not pos then return nil, nil end pos = vector_copy(pos) - local cur = vm:get_node_at(pos) + local cur = get_node(pos) if cur.name == "ignore" then - local e1, e2 = vm:get_emerged_area() minetest.log("warning", "find_liquid_surface with invalid position (outside of emerged area?) at "..minetest.pos_to_string(pos) - ..": "..tostring(cur and cur.name).." area: "..minetest.pos_to_string(e1).." "..minetest.pos_to_string(e2)) + ..": "..tostring(cur and cur.name)) return nil end if is_liquid(cur) then -- find up local prev = cur while true do pos.y = pos.y + 1 - local cur = vm:get_node_at(pos) + local cur = get_node(pos) if not cur or cur.name == "ignore" then -- minetest.log("action", "No ground, "..tostring(cur and cur.name).." over "..tostring(prev and prev.name).." at "..minetest.pos_to_string(pos)) return nil @@ -136,7 +131,7 @@ function vl_terraforming.find_liquid_surface_vm(vm, pos) while true do pos.y = pos.y - 1 local prev = cur - local cur = vm:get_node_at(pos) + local cur = get_node(pos) if not cur or cur.name == "ignore" then -- minetest.log("action", "No ground, "..tostring(cur and cur.name).." below "..tostring(prev and prev.name).." at "..minetest.pos_to_string(pos)) return nil @@ -152,27 +147,25 @@ function vl_terraforming.find_liquid_surface_vm(vm, pos) end end end -local find_liquid_surface_vm = vl_terraforming.find_liquid_surface_vm +local find_liquid_surface = vl_terraforming.find_liquid_surface --- Find under water surface for a given position --- @param vm VoxelManip: buffer -- @param pos vector: Start position -- @return position and material of surface -function vl_terraforming.find_under_water_surface_vm(vm, pos) +function vl_terraforming.find_under_water_surface(pos) if not pos then return nil, nil end pos = vector_copy(pos) - local cur = vm:get_node_at(pos) + local cur = get_node(pos) if cur.name == "ignore" then - local e1, e2 = vm:get_emerged_area() minetest.log("warning", "find_under_water_surface with invalid position (outside of emerged area?) at "..minetest.pos_to_string(pos) - ..": "..tostring(cur and cur.name).." area: "..minetest.pos_to_string(e1).." "..minetest.pos_to_string(e2)) + ..": "..tostring(cur and cur.name)) return nil end if is_solid_not_tree(cur) then -- find up local prev = cur while true do pos.y = pos.y + 1 - local cur = vm:get_node_at(pos) + local cur = get_node(pos) if not cur or cur.name == "ignore" then -- minetest.log("action", "No ground, "..tostring(cur and cur.name).." over "..tostring(prev and prev.name).." at "..minetest.pos_to_string(pos)) return nil @@ -188,7 +181,7 @@ function vl_terraforming.find_under_water_surface_vm(vm, pos) while true do pos.y = pos.y - 1 local prev = cur - local cur = vm:get_node_at(pos) + local cur = get_node(pos) if not cur or cur.name == "ignore" then -- minetest.log("action", "No ground, "..tostring(cur and cur.name).." below "..tostring(prev and prev.name).." at "..minetest.pos_to_string(pos)) return nil @@ -205,22 +198,21 @@ function vl_terraforming.find_under_water_surface_vm(vm, pos) end end end -local find_under_water_surface_vm = vl_terraforming.find_under_water_surface_vm +local find_under_water_surface = vl_terraforming.find_under_water_surface --- find suitable height for a structure of this size --- @param vm VoxelManip: to read data -- @param cpos vector: center -- @param size vector: area size -- @param tolerance number or string: maximum height difference allowed, default 8. -- @param mode string: "solid" (default), "liquid_surface", "under_air" -- @return position over surface, surface material (or nil, nil) -function vl_terraforming.find_level_vm(vm, cpos, size, tolerance, mode) - local find_ground = find_ground_vm - if mode == "liquid_surface" or mode == "liquid" then find_ground = find_liquid_surface_vm end - if mode == "under_water" or mode == "water" then find_ground = find_under_water_surface_vm end - if mode == "under_air" then find_ground = find_under_air_vm end +function vl_terraforming.find_level(cpos, size, tolerance, mode) + local _find_ground = find_ground + if mode == "liquid_surface" or mode == "liquid" then _find_ground = find_liquid_surface end + if mode == "under_water" or mode == "water" then _find_ground = find_under_water_surface end + if mode == "under_air" then _find_ground = find_under_air end -- begin at center, then top-left and clockwise - local pos, surface_material = find_ground(vm, cpos) + local pos, surface_material = _find_ground(cpos) if not pos then -- minetest.log("action", "[vl_terraforming] no ground at starting position "..minetest.pos_to_string(cpos).." mode "..tostring(mode or "default")) return nil, nil @@ -230,19 +222,19 @@ function vl_terraforming.find_level_vm(vm, cpos, size, tolerance, mode) if size.x == 1 and size.z == 1 then return pos end -- move to top left corner pos.x, pos.z = pos.x - floor((size.x-1)/2), pos.z - floor((size.z-1)/2) - local pos_c = find_ground(vm, pos) + local pos_c = _find_ground(pos) if pos_c then table.insert(ys, pos_c.y) end -- move to top right corner pos.x = pos.x + size.x - 1 - local pos_c = find_ground(vm, pos) + local pos_c = _find_ground(pos) if pos_c then table.insert(ys, pos_c.y) end -- move to bottom right corner pos.z = pos.z + size.z - 1 - local pos_c = find_ground(vm, pos) + local pos_c = _find_ground(pos) if pos_c then table.insert(ys, pos_c.y) end -- move to bottom left corner pos.x = pos.x - (size.x - 1) - local pos_c = find_ground(vm, pos) + local pos_c = _find_ground(pos) if pos_c then table.insert(ys, pos_c.y) end table.sort(ys) diff --git a/mods/MAPGEN/vl_terraforming/level_vm.lua b/mods/MAPGEN/vl_terraforming/level_vm.lua new file mode 100644 index 000000000..643c1ab37 --- /dev/null +++ b/mods/MAPGEN/vl_terraforming/level_vm.lua @@ -0,0 +1,267 @@ +local min = math.min +local floor = math.floor +local ceil = math.ceil +local vector_copy = vector.copy +local is_liquid = vl_terraforming._is_liquid +local is_solid_not_tree = vl_terraforming._is_solid_not_tree + +--- Find ground below a given position +-- @param vm VoxelManip: buffer +-- @param pos vector: Start position +-- @return position and material of surface +function vl_terraforming.find_ground_vm(vm, pos) + if not pos then return nil, nil end + pos = vector_copy(pos) + local cur = vm:get_node_at(pos) + if cur.name == "ignore" then + local e1, e2 = vm:get_emerged_area() + minetest.log("warning", "find_ground with invalid position (outside of emerged area?) at "..minetest.pos_to_string(pos) + ..": "..tostring(cur and cur.name).." area: "..minetest.pos_to_string(e1).." "..minetest.pos_to_string(e2)) + return nil + end + if is_solid_not_tree(cur) then -- find up + local prev = cur + while true do + pos.y = pos.y + 1 + local cur = vm:get_node_at(pos) + if not cur or cur.name == "ignore" then + -- minetest.log("action", "No ground, "..tostring(cur and cur.name).." over "..tostring(prev and prev.name).." at "..minetest.pos_to_string(pos)) + return nil + end + if not is_solid_not_tree(cur) then + pos.y = pos.y - 1 + return pos, prev + end + prev = cur + end + else -- find down + while true do + pos.y = pos.y - 1 + local prev = cur + local cur = vm:get_node_at(pos) + if not cur or cur.name == "ignore" then + -- minetest.log("action", "No ground, "..tostring(cur and cur.name).." below "..tostring(prev and prev.name).." at "..minetest.pos_to_string(pos)) + return nil + end + if is_liquid(cur) then + return nil + end + if is_solid_not_tree(cur) then + return pos, cur + end + end + end +end +local find_ground_vm = vl_terraforming.find_ground_vm + +--- Find ground or liquid surface for a given position +-- @param vm VoxelManip: buffer +-- @param pos vector: Start position +-- @return position and material of surface +function vl_terraforming.find_under_air_vm(vm, pos) + if not pos then return nil, nil end + pos = vector_copy(pos) + local cur = vm:get_node_at(pos) + if cur.name == "ignore" then + local e1, e2 = vm:get_emerged_area() + minetest.log("warning", "find_under_air with invalid position (outside of emerged area?) at "..minetest.pos_to_string(pos) + ..": "..tostring(cur and cur.name).." area: "..minetest.pos_to_string(e1).." "..minetest.pos_to_string(e2)) + return nil + end + if is_solid_not_tree(cur) or is_liquid(cur) then -- find up + local prev = cur + while true do + pos.y = pos.y + 1 + local cur = vm:get_node_at(pos) + if not cur or cur.name == "ignore" then + -- minetest.log("action", "No ground, "..tostring(cur and cur.name).." over "..tostring(prev and prev.name).." at "..minetest.pos_to_string(pos)) + return nil + end + if not is_solid_not_tree(cur) and not is_liquid(cur) then + pos.y = pos.y - 1 + -- minetest.log("action", "Found surface: "..minetest.pos_to_string(pos).." "..tostring(prev and prev.name).." under "..tostring(cur and cur.name)) + return pos, prev + end + prev = cur + end + else -- find down + while true do + pos.y = pos.y - 1 + local prev = cur + local cur = vm:get_node_at(pos) + if not cur or cur.name == "ignore" then + -- minetest.log("action", "No ground, "..tostring(cur and cur.name).." below "..tostring(prev and prev.name).." at "..minetest.pos_to_string(pos)) + return nil + end + if is_solid_not_tree(cur) or is_liquid(cur) then + -- minetest.log("action", "Found surface: "..minetest.pos_to_string(pos).." "..(cur and cur.name).." over "..(prev and prev.name)) + return pos, cur + end + end + end +end +local find_under_air_vm = vl_terraforming.find_under_air_vm + +--- Find liquid surface for a given position +-- @param vm VoxelManip: buffer +-- @param pos vector: Start position +-- @return position and material of surface +function vl_terraforming.find_liquid_surface_vm(vm, pos) + if not pos then return nil, nil end + pos = vector_copy(pos) + local cur = vm:get_node_at(pos) + if cur.name == "ignore" then + local e1, e2 = vm:get_emerged_area() + minetest.log("warning", "find_liquid_surface with invalid position (outside of emerged area?) at "..minetest.pos_to_string(pos) + ..": "..tostring(cur and cur.name).." area: "..minetest.pos_to_string(e1).." "..minetest.pos_to_string(e2)) + return nil + end + if is_liquid(cur) then -- find up + local prev = cur + while true do + pos.y = pos.y + 1 + local cur = vm:get_node_at(pos) + if not cur or cur.name == "ignore" then + -- minetest.log("action", "No ground, "..tostring(cur and cur.name).." over "..tostring(prev and prev.name).." at "..minetest.pos_to_string(pos)) + return nil + end + if not is_liquid(cur) then + pos.y = pos.y - 1 + -- minetest.log("action", "Found surface: "..minetest.pos_to_string(pos).." "..tostring(prev and prev.name).." under "..tostring(cur and cur.name)) + return pos, prev + end + prev = cur + end + else -- find down + while true do + pos.y = pos.y - 1 + local prev = cur + local cur = vm:get_node_at(pos) + if not cur or cur.name == "ignore" then + -- minetest.log("action", "No ground, "..tostring(cur and cur.name).." below "..tostring(prev and prev.name).." at "..minetest.pos_to_string(pos)) + return nil + end + if is_solid_not_tree(cur) then + -- minetest.log("action", "No ground, "..tostring(cur and cur.name).." below "..tostring(prev and prev.name).." at "..minetest.pos_to_string(pos)) + return nil + end + if is_liquid(cur) then + -- minetest.log("action", "Found surface: "..minetest.pos_to_string(pos).." "..(cur and cur.name).." over "..(prev and prev.name)) + return pos, cur + end + end + end +end +local find_liquid_surface_vm = vl_terraforming.find_liquid_surface_vm + +--- Find under water surface for a given position +-- @param vm VoxelManip: buffer +-- @param pos vector: Start position +-- @return position and material of surface +function vl_terraforming.find_under_water_surface_vm(vm, pos) + if not pos then return nil, nil end + pos = vector_copy(pos) + local cur = vm:get_node_at(pos) + if cur.name == "ignore" then + local e1, e2 = vm:get_emerged_area() + minetest.log("warning", "find_under_water_surface with invalid position (outside of emerged area?) at "..minetest.pos_to_string(pos) + ..": "..tostring(cur and cur.name).." area: "..minetest.pos_to_string(e1).." "..minetest.pos_to_string(e2)) + return nil + end + if is_solid_not_tree(cur) then -- find up + local prev = cur + while true do + pos.y = pos.y + 1 + local cur = vm:get_node_at(pos) + if not cur or cur.name == "ignore" then + -- minetest.log("action", "No ground, "..tostring(cur and cur.name).." over "..tostring(prev and prev.name).." at "..minetest.pos_to_string(pos)) + return nil + end + if is_liquid(cur) then + pos.y = pos.y - 1 + -- minetest.log("action", "Found surface: "..minetest.pos_to_string(pos).." "..tostring(prev and prev.name).." under "..tostring(cur and cur.name)) + return pos, prev + end + prev = cur + end + else -- find down + while true do + pos.y = pos.y - 1 + local prev = cur + local cur = vm:get_node_at(pos) + if not cur or cur.name == "ignore" then + -- minetest.log("action", "No ground, "..tostring(cur and cur.name).." below "..tostring(prev and prev.name).." at "..minetest.pos_to_string(pos)) + return nil + end + if is_solid_not_tree(cur) then + if is_liquid(prev) then + -- minetest.log("action", "Found surface: "..minetest.pos_to_string(pos).." "..(cur and cur.name).." over "..(prev and prev.name)) + return pos, cur + else + -- minetest.log("action", "No ground, "..tostring(cur and cur.name).." below "..tostring(prev and prev.name).." at "..minetest.pos_to_string(pos)) + return nil, nil + end + end + end + end +end +local find_under_water_surface_vm = vl_terraforming.find_under_water_surface_vm + +--- find suitable height for a structure of this size +-- @param vm VoxelManip: to read data +-- @param cpos vector: center +-- @param size vector: area size +-- @param tolerance number or string: maximum height difference allowed, default 8. +-- @param mode string: "solid" (default), "liquid_surface", "under_air" +-- @return position over surface, surface material (or nil, nil) +function vl_terraforming.find_level_vm(vm, cpos, size, tolerance, mode) + local find_ground = find_ground_vm + if mode == "liquid_surface" or mode == "liquid" then find_ground = find_liquid_surface_vm end + if mode == "under_water" or mode == "water" then find_ground = find_under_water_surface_vm end + if mode == "under_air" then find_ground = find_under_air_vm end + -- begin at center, then top-left and clockwise + local pos, surface_material = find_ground(vm, cpos) + if not pos then + -- minetest.log("action", "[vl_terraforming] no ground at starting position "..minetest.pos_to_string(cpos).." mode "..tostring(mode or "default")) + return nil, nil + end + local ys = { pos.y } + pos.y = pos.y + 1 -- position above surface + if size.x == 1 and size.z == 1 then return pos end + -- move to top left corner + pos.x, pos.z = pos.x - floor((size.x-1)/2), pos.z - floor((size.z-1)/2) + local pos_c = find_ground(vm, pos) + if pos_c then table.insert(ys, pos_c.y) end + -- move to top right corner + pos.x = pos.x + size.x - 1 + local pos_c = find_ground(vm, pos) + if pos_c then table.insert(ys, pos_c.y) end + -- move to bottom right corner + pos.z = pos.z + size.z - 1 + local pos_c = find_ground(vm, pos) + if pos_c then table.insert(ys, pos_c.y) end + -- move to bottom left corner + pos.x = pos.x - (size.x - 1) + local pos_c = find_ground(vm, pos) + if pos_c then table.insert(ys, pos_c.y) end + table.sort(ys) + + tolerance = tolerance or 8 + if tolerance == "min" then + cpos.y = ys[1] + 1 + return cpos, surface_material + end + if tolerance == "max" then + cpos.y = ys[#ys] + 1 + return cpos, surface_material + end + -- well supported base, not too uneven? + if #ys < 5 or min(ys[#ys-1]-ys[1], ys[#ys]-ys[2]) > tolerance then + -- minetest.log("action", "[vl_terraforming] ground too uneven: "..#ys.." positions: "..({dump(ys):gsub("[\n\t ]+", " ")})[1] + -- .." tolerance "..tostring(#ys > 2 and min(ys[#ys-1]-ys[1], ys[#ys]-ys[2])).." > "..tolerance) + return nil, nil + end + cpos.y = floor(0.5 * (ys[floor(1 + (#ys - 1) * 0.5)] + ys[ceil(1 + (#ys - 1) * 0.5)]) + 1) -- median except for largest, rounded, over surface + return cpos, surface_material +end + diff --git a/mods/MAPGEN/vl_terraforming/util.lua b/mods/MAPGEN/vl_terraforming/util.lua index 58715e020..87b233758 100644 --- a/mods/MAPGEN/vl_terraforming/util.lua +++ b/mods/MAPGEN/vl_terraforming/util.lua @@ -1,3 +1,6 @@ +local get_node = core.get_node +local swap_node = core.swap_node + --- fairly strict: air, ignore, or no_paths marker -- @param node string or Node: node or node name -- @return true for air and ignore nodes @@ -20,7 +23,7 @@ function vl_terraforming._is_solid_not_tree(node) -- is walkable if name == "mcl_core:snowblock" then return true end local meta = minetest.registered_items[name] local groups = meta and meta.groups - return meta and meta.walkable and not (groups and (groups.deco_block or groups.tree or groups.leaves or groups.plant)) + return meta and meta.walkable and not (groups and ((groups.deco_block or 0) > 0 or (groups.tree or 0) > 0 or (groups.leaves or 0) > 0 or (groups.plant or 0) > 0)) end local is_solid_not_tree = vl_terraforming._is_solid_not_tree @@ -34,7 +37,7 @@ function vl_terraforming._is_tree_not_leaves(node) -- if name == "mcl_crimson:warped_wart_block" then return true end -- warped forest, treat as tree -- if name == "mcl_crimson:shroomlight" then return true end -- crimson forest, treat as tree local meta = minetest.registered_items[name] - return meta and meta.groups and meta.groups.tree + return meta and meta.groups and (meta.groups.tree or 0) > 0 end --- check if a node is liquid @@ -45,7 +48,20 @@ function vl_terraforming._is_liquid(node) if name == "air" or name == "ignore" or name == "mcl_villages:no_paths" then return false end local meta = minetest.registered_items[name] local groups = meta and meta.groups - return groups and groups.liquid + return groups and (groups.liquid or 0) > 0 +end + +--- replace a non-solid node, optionally also "additional" +-- @param pos position +-- @param with replacement Lua node (not just name) +-- @param always additional node to awlays replace even when solid +function vl_terraforming._make_solid(pos, with, always) + local cur = get_node(pos) + if cur.name == "ignore" or cur.name == "mcl_core:bedrock" then return end + if cur.name == always or not is_solid_not_tree(cur) then + swap_node(pos, with) + return true + end end --- replace a non-solid node, optionally also "additional"