enhance vl_terraform API

This commit is contained in:
kno10 2024-10-31 20:14:29 +01:00
parent dfeffc8522
commit e8944f202c
14 changed files with 225 additions and 171 deletions

View file

@ -80,7 +80,7 @@ local cold = {
y_max = water_level - 6, y_max = water_level - 6,
y_offset = -1, y_offset = -1,
flags = "place_center_x, place_center_z, force_placement", flags = "place_center_x, place_center_z, force_placement",
prepare = { foundation = -2, clear = false, mode="water" }, prepare = { foundation = -2, clear = false, surface = "water" },
filenames = { filenames = {
modpath.."/schematics/mcl_structures_ocean_ruins_cold_1.mts", modpath.."/schematics/mcl_structures_ocean_ruins_cold_1.mts",
modpath.."/schematics/mcl_structures_ocean_ruins_cold_2.mts", modpath.."/schematics/mcl_structures_ocean_ruins_cold_2.mts",

View file

@ -82,7 +82,7 @@ vl_structures.register_structure("ocean_temple",{
}, },
flags = "force_placement", flags = "force_placement",
force_placement = true, force_placement = true,
prepare = { tolerance = 8, clear = false, foundation = 3, mode="water" }, prepare = { tolerance = 8, clear = false, foundation = 3, surface = "water" },
biomes = ocean_biomes, biomes = ocean_biomes,
y_max = water_level-4, y_max = water_level-4,
y_min = mcl_vars.mg_overworld_min, y_min = mcl_vars.mg_overworld_min,

View file

@ -77,7 +77,7 @@ vl_structures.register_structure("shipwreck",{
y_max = water_level-5, y_max = water_level-5,
y_offset = function(pr) return pr:next(-3,-1) end, y_offset = function(pr) return pr:next(-3,-1) end,
flags = "place_center_x, place_center_z, force_placement", flags = "place_center_x, place_center_z, force_placement",
prepare = { tolerance = -1, clear = false, foundation = false, mode = "water" }, prepare = { tolerance = 99, clear = false, foundation = false, surface = "water", mode = "min" },
filenames = { filenames = {
--schematics by chmodsayshello --schematics by chmodsayshello
modpath.."/schematics/mcl_structures_shipwreck_full_damaged.mts", modpath.."/schematics/mcl_structures_shipwreck_full_damaged.mts",

View file

@ -48,7 +48,7 @@ vl_structures.register_structure("witch_hut",{
num_spawn_by = 3, num_spawn_by = 3,
flags = "place_center_x, place_center_z, all_surfaces", flags = "place_center_x, place_center_z, all_surfaces",
chunk_probability = 8, chunk_probability = 8,
prepare = { mode="under_air", tolerance=4, clear_bottom=3, padding=0, corners=1, foundation=false }, prepare = { surface = "under_air", tolerance = 4, clear_bottom = 3, padding = 0, corners = 1, foundation = false },
y_max = mcl_vars.mg_overworld_max, y_max = mcl_vars.mg_overworld_max,
y_min = -5, y_min = -5,
y_offset = 0, y_offset = 0,

View file

@ -8,7 +8,7 @@ vl_structures.register_structure("obelisk_sand",{
y_max = mcl_vars.mg_overworld_max, y_max = mcl_vars.mg_overworld_max,
y_min = 1, y_min = 1,
y_offset = -3, y_offset = -3,
prepare = { tolerance=3, padding = 0, clear=false }, prepare = { tolerance = 3, padding = 0, clear = false },
biomes = { "Desert" }, biomes = { "Desert" },
filenames = { filenames = {
modpath.."/schematics/obelisk_sand_1.mts", modpath.."/schematics/obelisk_sand_1.mts",
@ -23,7 +23,7 @@ vl_structures.register_structure("obelisk_light",{
y_max = mcl_vars.mg_overworld_max, y_max = mcl_vars.mg_overworld_max,
y_min = 1, y_min = 1,
y_offset = -2, y_offset = -2,
prepare = { tolerance=2, padding = 0, clear=false }, prepare = { tolerance = 2, padding = 0, clear = false },
biomes = { "Desert" }, biomes = { "Desert" },
filenames = { filenames = {
modpath.."/schematics/obelisk_fire.mts", modpath.."/schematics/obelisk_fire.mts",
@ -42,7 +42,7 @@ vl_structures.register_structure("obelisk_cobble",{
y_max = mcl_vars.mg_overworld_max, y_max = mcl_vars.mg_overworld_max,
y_min = 1, y_min = 1,
y_offset = -2, y_offset = -2,
prepare = { tolerance=2, padding=0, clear=false }, prepare = { tolerance = 2, padding = 0, clear = false },
biomes = { "Plains", "SunflowerPlains", "Forest", "FlowerForest", "BrichForest", "Taiga", "RoofedForest", "MegaTaiga", "MegaSpruceTaiga", }, biomes = { "Plains", "SunflowerPlains", "Forest", "FlowerForest", "BrichForest", "Taiga", "RoofedForest", "MegaTaiga", "MegaSpruceTaiga", },
filenames = { filenames = {
modpath.."/schematics/obelisk_cobble.mts", modpath.."/schematics/obelisk_cobble.mts",

View file

@ -15,8 +15,8 @@ local function parse_prepare(prepare)
end end
-- check "enabled" tolerances -- check "enabled" tolerances
local function tolerance_enabled(tolerance, mode) local function tolerance_enabled(tolerance, surface, mode)
return mode ~= "off" and tolerance and (tolerance == "max" or tolerance == "min" or tolerance >= 0) and true return tolerance ~= "off" and (tolerance or surface or mode) and true
end end
--- Main palcement step, when the area has been emerged --- Main palcement step, when the area has been emerged
@ -45,12 +45,13 @@ local function emerge_schematics(blockpos, action, calls_remaining, param)
-- Step 1: adjust ground to a more level position -- Step 1: adjust ground to a more level position
-- todo: also support checking ground of daughter schematics, but not used by current schematics -- 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 if pos and size and prepare and tolerance_enabled(prepare.tolerance, prepare.surface, prepare.mode) then
pos, surface_mat = vl_terraforming.find_level(pos, size, prepare.tolerance, prepare.mode) pos, surface_mat = vl_terraforming.find_level(pos, size, prepare.tolerance, prepare.surface, prepare.mode)
if not pos then if not pos then
minetest.log("warning", "[vl_structures] Not spawning "..tostring(def.name or param.schematic.name).." at "..minetest.pos_to_string(param.pos).." because ground is too uneven.") minetest.log("warning", "[vl_structures] Not spawning "..tostring(def.name or param.schematic.name).." at "..minetest.pos_to_string(param.pos).." because ground is too uneven.")
return return
end end
pos.y = pos.y + 1 -- above surface
-- obey height restrictions, to not violate nether roof -- obey height restrictions, to not violate nether roof
if def.y_max and pos.y - yoffset > def.y_max then pos.y = def.y_max - yoffset end if def.y_max and pos.y - yoffset > def.y_max then pos.y = def.y_max - yoffset end
if def.y_min and pos.y - yoffset < def.y_min then pos.y = def.y_min - yoffset end if def.y_min and pos.y - yoffset < def.y_min then pos.y = def.y_min - yoffset end

View file

@ -1,4 +1,4 @@
# vl_terraforming # `vl_terraforming` -- Terraforming module
Terraforming module built with VoxeLibre and MineClonia in mind, but also useful for other games. Terraforming module built with VoxeLibre and MineClonia in mind, but also useful for other games.
@ -9,6 +9,8 @@ This module provides the following key functionalities:
- build a baseplate for a building - build a baseplate for a building
- clear the area above a building - clear the area above a building
All methods have a `_vm` version to work with Lua Voxel Manipulators
## Rounded corners support ## Rounded corners support
To get nicer looking baseplates, the code supports rounded corners. To get nicer looking baseplates, the code supports rounded corners.
@ -23,77 +25,91 @@ The ellipse condition $dx^2/a^2+dz^2/b^2 \leq 1$ then yields $dx^2/(0.5 sx^2) +
We use $wx2=2 sx^-2$, $wz2=2 sz^-2$ and then $dx^2 wx2 + dz^2 wz2 \leq 1$. We use $wx2=2 sx^-2$, $wz2=2 sz^-2$ and then $dx^2 wx2 + dz^2 wz2 \leq 1$.
## vl_terraforming.find_ground_vm(vm, pos) ## `vl_terraforming.find_ground(pos)`
Find ground starting at the given position. When in a solid area, moves up; otherwise searches downwards. Find ground starting at the given position. When in a solid area, moves up; otherwise searches downwards.
This will ignore trees, mushrooms, and similar surface decorations. This will ignore trees, mushrooms, and similar surface decorations.
## vl_terraforming.find_under_air_vm(vm, pos) ## `vl_terraforming.find_under_air(pos)`
Find ground or liquid surface, starting at the given position. When in a solid or liquid area, moves up; otherwise searches downwards. Find ground or liquid surface, starting at the given position. When in a solid or liquid area, moves up; otherwise searches downwards.
This will ignore trees, mushrooms, and similar surface decorations. This will ignore trees, mushrooms, and similar surface decorations.
## vl_terraforming.find_liquid_surface_vm(vm, pos) ## `vl_terraforming.find_liquid_surface(pos)`
Find a liquid surface starting at the given position. When in a solid or liquid area, moves up; otherwise searches downwards. Find a liquid surface starting at the given position. When in a solid or liquid area, moves up; otherwise searches downwards.
This will ignore trees, mushrooms, and similar surface decorations. This will ignore trees, mushrooms, and similar surface decorations.
## `vl_terraforming.find_under_water_surface(pos)`
## vl_terraforming.find_level_vm(vm, cpos, size, tolerance, mode) Find a solid surface covered by water starting at the given position. When in a solid area, moves up; otherwise searches downwards.
Find "level" ground for a building, centered at the given position, and of the given size. This will ignore trees, mushrooms, and similar surface decorations.
## `vl_terraforming.find_level(cpos, size, tolerance, surface, mode)`
Find "level" (sufficiently even) ground for a structure, centered at the given position, and of the given size.
For this, five samples are taken: center, top left, top right, bottom left, and bottom right. For this, five samples are taken: center, top left, top right, bottom left, and bottom right.
One of these values may be "extreme", and tolerance specifies the maximum height difference of the remaining four values. One of these values may be "extreme", and tolerance specifies the maximum height difference of the remaining four values.
The (rounded) median of these values is used, unless tolerance is set to "min" or "max". The `surface` can be set to:
- `"solid"` (default, i.e., solid under air)
- `"liquid"` (liquid under air)
- `"under_air"` (both liquid and solid surfaces)
- `"under_water"` (solid under water)
The "mode" can be set to "solid" (default), "liquid" (liquid surfaces only), "under_air" (both liquid and solid surfaces), "under_water" (solid below water). The `mode` can be set to:
- `"median"` (default, use the median height, rounded)
- `"min"` (use the lowest support coordinate)
- `"max"` (use the highest support coordinate)
## vl_terraforming.foundation_vm(vm, px, py, pz, sx, sy, sz, corners, surface_mat, platform_mat, stone_mat, dust_mat, pr) ## `vl_terraforming.foundation(px, py, pz, sx, sy, sz, corners, surface_mat, platform_mat, stone_mat, dust_mat, pr)`
The position (px, py, pz) and the size (sx, sy, sz) give the volume of the main base plate, The position `(px, py, pz)` and the size `(sx, sy, sz)` give the volume of the main base plate,
where sy < 0, so that you can later place the structure at (px, py, pz). where `sy < 0`, so that you can later place the structure at `(px, py, pz)`.
The baseplate will be grown by 1 in the level below, to allow mobs to enter, then randomly fade away below. The baseplate will be grown by 1 in the level below, to allow mobs to enter, then randomly fade away below.
-sy can be used to control a minimum depth. The negative depth `sy` can be used to control a minimum depth.
Corners specifies how much to cut the corners, use 0 for a square baseplate. Corners specifies how much to cut the corners, use 0 for a square baseplate.
The materials specified (as lua nodes, to have param2 support) are used a follows: The materials specified (as lua nodes, to have `param2` coloring support) are used a follows:
- surface_mat for surface nodes - `surface_mat` for surface nodes
- platform_mat below surface nodes - `platform_mat` below surface nodes
- stone_mat randomly used below platform_mat - `stone_mat` randomly used below `platform_mat`
- dust_mat on top of surface nodes (snow cover, optional) - `dust_mat` on top of surface nodes (snow cover, optional)
pr is a PcgRandom random generator `pr` is a PcgRandom random generator
## vl_terraforming.clearance_vm(vm, px, py, pz, sx, sy, sz, corners, surface_mat, dust_mat, pr) ## `vl_terraforming.clearance(px, py, pz, sx, sy, sz, corners, surface_mat, dust_mat, pr)`
The position (px, py, pz) and the size (sx, sy, sz) give the volume overhead to clear. The position `(px, py, pz)` and the size `(sx, sy, sz)` give the volume overhead to clear.
The area will be grown by 1 above, to allow mobs to enter, then randomly fade away as height increases beyond sy. The area will be grown by 1 above, to allow mobs to enter, then randomly fade away as height increases beyond `sy`.
Corners specifies how much to cut the corners, use 0 for a square area. `corners` specifies how much to cut the corners, use 0 for a square area.
The surface_mat will be used to turn nodes into surface nodes when widening the area. `surface_mat` is the node used to turn nodes into surface nodes when widening the area. If set, the `dust_mat` will be sprinkled on top.
`pr` is a PcgRandom random generator
pr is a PcgRandom random generator
## TODO ## TODO
- [ ] make even more configurable
- [ ] add ceiling placement
- [ ] add an API that works on VM buffers - [ ] add an API that works on VM buffers
- [ ] add an API version working on the non-VM API - [ ] benchmark when VM is faster than not using VM (5.9 has some optimizations not yet in VM)
- [ ] benchmark if VM is actually faster than not using VM (5.9 has some optimizations not yet in VM)
- [ ] improve tree removal - [ ] improve tree removal

View file

@ -1,13 +1,17 @@
local AIR = {name = "air"} local AIR = vl_terraforming._AIR
local abs = math.abs local abs = math.abs
local max = math.max local max = math.max
local floor = math.floor local floor = math.floor
local vector_new = vector.new 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 get_node = core.get_node
local swap_node = core.swap_node local swap_node = core.swap_node
local is_air = vl_terraforming._is_air
local immutable = vl_terraforming._immutable
local is_solid_not_tree = vl_terraforming._is_solid_not_tree
local is_tree_not_leaves = vl_terraforming._is_tree_not_leaves
local is_tree_or_leaves = vl_terraforming._is_tree_or_leaves
--- Clear an area for a structure --- 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. -- Rounding: we model an ellipse. At zero rounding, we want the line go through the corner, at sx/2, sz/2.
@ -47,7 +51,7 @@ function vl_terraforming.clearance(px, py, pz, sx, sy, sz, corners, surface_mat,
vec.z = zi vec.z = zi
if xi >= px and xi < px+sx and zi >= pz and zi < pz+sz and dx2+dz2 <= 1 then if xi >= px and xi < px+sx and zi >= pz and zi < pz+sz and dx2+dz2 <= 1 then
vec.y = py vec.y = py
if get_node(vec).name ~= "mcl_core:bedrock" then swap_node(vec, AIR) end if not immutable(get_node(vec)) then swap_node(vec, AIR) end
vec.y = py - 1 vec.y = py - 1
local n = get_node(vec) local n = get_node(vec)
if n and n.name ~= surface_mat.name and is_solid_not_tree(n) then if n and n.name ~= surface_mat.name and is_solid_not_tree(n) then
@ -55,14 +59,13 @@ function vl_terraforming.clearance(px, py, pz, sx, sy, sz, corners, surface_mat,
end end
for yi = py+1,min_clear do -- full height for inner area for yi = py+1,min_clear do -- full height for inner area
vec.y = yi vec.y = yi
if get_node(vec).name ~= "mcl_core:bedrock" then swap_node(vec, AIR) end if not immutable(get_node(vec)) then swap_node(vec, AIR) end
end end
elseif dx21+dz21 <= 1 then elseif dx21+dz21 <= 1 then
-- widen the cave above by 1, to make easier to enter for mobs -- widen the cave above by 1, to make easier to enter for mobs
-- todo: make configurable? -- todo: make configurable?
vec.y = py + 1 vec.y = py + 1
local name = get_node(vec).name if not immutable(get_node(vec)) then
if name ~= "mcl_core:bedrock" then
local mat = AIR local mat = AIR
if dust_mat then if dust_mat then
vec.y = py vec.y = py
@ -73,7 +76,7 @@ function vl_terraforming.clearance(px, py, pz, sx, sy, sz, corners, surface_mat,
end end
for yi = py+2,min_clear-1 do for yi = py+2,min_clear-1 do
vec.y = yi vec.y = yi
if get_node(vec).name ~= "mcl_core:bedrock" then swap_node(vec, AIR) end if not immutable(get_node(vec)) then swap_node(vec, AIR) end
if yi > py+4 then if yi > py+4 then
local p = (yi-py) / (max_clear-py) local p = (yi-py) / (max_clear-py)
--minetest.log(tostring(p).."^2 "..tostring(p*p).." rand: "..pr:next(0,1e9)/1e9) --minetest.log(tostring(p).."^2 "..tostring(p*p).." rand: "..pr:next(0,1e9)/1e9)
@ -88,14 +91,14 @@ function vl_terraforming.clearance(px, py, pz, sx, sy, sz, corners, surface_mat,
swap_node(vec, surface_mat) swap_node(vec, surface_mat)
if dust_mat and yi == py then if dust_mat and yi == py then
vec.y = yi + 1 vec.y = yi + 1
if get_node(vec).name == "air" then swap_node(vec, dust_mat) end if is_air(get_node(vec)) then swap_node(vec, dust_mat) end
end end
else else
if n and n.name ~= surface_mat.name and is_solid_not_tree(n) then if n and n.name ~= surface_mat.name and is_solid_not_tree(n) then
swap_node(vec, surface_mat) swap_node(vec, surface_mat)
if dust_mat then if dust_mat then
vec.y = yi + 1 vec.y = yi + 1
if get_node(vec).name == "air" then swap_node(vec, dust_mat) end if is_air(get_node(vec)) then swap_node(vec, dust_mat) end
end end
end end
break break
@ -118,12 +121,12 @@ function vl_terraforming.clearance(px, py, pz, sx, sy, sz, corners, surface_mat,
if py+4 < sy then if py+4 < sy then
for yi = py+2,py+4 do for yi = py+2,py+4 do
vec = vector_new(xi, yi, zi) vec = vector_new(xi, yi, zi)
if get_node(vec).name ~= "mcl_core:bedrock" then swap_node(vec, v) end if not immutable(get_node(vec)) then swap_node(vec, v) end
end end
end end
for yi = py+1,py-1,-1 do for yi = py+1,py-1,-1 do
local n = get_node(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 if is_tree_not_leaves(n) and not immutable(n) then
swap_node(vector_new(xi, yi, zi), AIR) swap_node(vector_new(xi, yi, zi), AIR)
else else
if n and n.name ~= surface_mat.name and is_solid_not_tree(n) then if n and n.name ~= surface_mat.name and is_solid_not_tree(n) then
@ -147,17 +150,15 @@ function vl_terraforming.clearance(px, py, pz, sx, sy, sz, corners, surface_mat,
local keep_trees = (xi<px or xi>=px+sx) or (zi<pz or zi>=pz+sz) -- TODO make parameter? local keep_trees = (xi<px or xi>=px+sx) or (zi<pz or zi>=pz+sz) -- TODO make parameter?
if dx22+dy2+dz22 <= 1 then if dx22+dy2+dz22 <= 1 then
vec.x, vec.y, vec.z = xi, yi, zi vec.x, vec.y, vec.z = xi, yi, zi
local name = get_node(vec).name local nod = get_node(vec)
-- don't break bedrock or air -- 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 if is_air(nod) or immutable(nod) then goto continue end
local meta = minetest.registered_items[name] local is_tree = is_tree_or_leaves(nod)
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 if keep_trees and is_tree then goto continue end
vec.y = yi-1 vec.y = yi-1
-- do not clear above solid -- do not clear above solid
local name_below = get_node(vec).name local nod_below = get_node(vec)
if name_below ~= "air" and name_below ~= "ignore" and name_below ~= "mcl_core:bedrock" then goto continue end if not is_air(nod_below) and not immutable(nod_below) then goto continue end
-- try to completely remove trees overhead -- try to completely remove trees overhead
-- stop randomly depending on fill, to narrow down the caves -- 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 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

View file

@ -1,10 +1,14 @@
local AIR = {name = "air"} local AIR = vl_terraforming._AIR
local abs = math.abs local abs = math.abs
local max = math.max local max = math.max
local floor = math.floor local floor = math.floor
local vector_new = vector.new local vector_new = vector.new
local is_air = vl_terraforming._is_air
local immutable = vl_terraforming._immutable
local is_solid_not_tree = vl_terraforming._is_solid_not_tree local is_solid_not_tree = vl_terraforming._is_solid_not_tree
local is_tree_not_leaves = vl_terraforming._is_tree_not_leaves local is_tree_not_leaves = vl_terraforming._is_tree_not_leaves
local is_tree_or_leaves = vl_terraforming._is_tree_or_leaves
--- Clear an area for a structure --- Clear an area for a structure
-- --
@ -28,8 +32,8 @@ local is_tree_not_leaves = vl_terraforming._is_tree_not_leaves
-- @param pr PcgRandom: random generator -- @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_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 if sx <= 0 or sy <= 0 or sz <= 0 then return end
local get_node_at = vm.get_node_at local get_node = vm.get_node_at
local set_node_at = vm.set_node_at local swap_node = vm.set_node_at
corners = corners or 0 corners = corners or 0
local wx2, wz2 = max(sx - corners, 1)^-2 * 2, max(sz - corners, 1)^-2 * 2 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 cx, cz = px + sx * 0.5 - 0.5, pz + sz * 0.5 - 0.5
@ -48,33 +52,32 @@ function vl_terraforming.clearance_vm(vm, px, py, pz, sx, sy, sz, corners, surfa
vec.z = zi vec.z = zi
if xi >= px and xi < px+sx and zi >= pz and zi < pz+sz and dx2+dz2 <= 1 then if xi >= px and xi < px+sx and zi >= pz and zi < pz+sz and dx2+dz2 <= 1 then
vec.y = py vec.y = py
if get_node_at(vm, vec).name ~= "mcl_core:bedrock" then set_node_at(vm, vec, AIR) end if not immutable(get_node(vm, vec)) then swap_node(vm, vec, AIR) end
vec.y = py - 1 vec.y = py - 1
local n = get_node_at(vm, vec) local n = get_node(vm, vec)
if n and n.name ~= surface_mat.name and is_solid_not_tree(n) then if n and n.name ~= surface_mat.name and is_solid_not_tree(n) then
set_node_at(vm, vec, surface_mat) swap_node(vm, vec, surface_mat)
end end
for yi = py+1,min_clear do -- full height for inner area for yi = py+1,min_clear do -- full height for inner area
vec.y = yi vec.y = yi
if get_node_at(vm, vec).name ~= "mcl_core:bedrock" then set_node_at(vm, vec, AIR) end if not immutable(get_node(vm, vec)) then swap_node(vm, vec, AIR) end
end end
elseif dx21+dz21 <= 1 then elseif dx21+dz21 <= 1 then
-- widen the cave above by 1, to make easier to enter for mobs -- widen the cave above by 1, to make easier to enter for mobs
-- todo: make configurable? -- todo: make configurable?
vec.y = py + 1 vec.y = py + 1
local name = get_node_at(vm, vec).name if not immutable(get_node(vm, vec)) then
if name ~= "mcl_core:bedrock" then
local mat = AIR local mat = AIR
if dust_mat then if dust_mat then
vec.y = py vec.y = py
if get_node_at(vm, vec).name == surface_mat.name then mat = dust_mat end if get_node(vm, vec).name == surface_mat.name then mat = dust_mat end
vec.y = py + 1 vec.y = py + 1
end end
set_node_at(vm, vec, mat) swap_node(vm, vec, mat)
end end
for yi = py+2,min_clear-1 do for yi = py+2,min_clear-1 do
vec.y = yi vec.y = yi
if get_node_at(vm, vec).name ~= "mcl_core:bedrock" then set_node_at(vm, vec, AIR) end if not immutable(get_node(vm, vec)) then swap_node(vm, vec, AIR) end
if yi > py+4 then if yi > py+4 then
local p = (yi-py) / (max_clear-py) local p = (yi-py) / (max_clear-py)
--minetest.log(tostring(p).."^2 "..tostring(p*p).." rand: "..pr:next(0,1e9)/1e9) --minetest.log(tostring(p).."^2 "..tostring(p*p).." rand: "..pr:next(0,1e9)/1e9)
@ -84,19 +87,19 @@ function vl_terraforming.clearance_vm(vm, px, py, pz, sx, sy, sz, corners, surfa
-- remove some tree parts and fix surfaces down -- remove some tree parts and fix surfaces down
for yi = py,py-1,-1 do for yi = py,py-1,-1 do
vec.y = yi vec.y = yi
local n = get_node_at(vm, vec) local n = get_node(vm, vec)
if is_tree_not_leaves(n) then if is_tree_not_leaves(n) then
set_node_at(vm, vec, surface_mat) swap_node(vm, vec, surface_mat)
if dust_mat and yi == py then if dust_mat and yi == py then
vec.y = yi + 1 vec.y = yi + 1
if get_node_at(vm, vec).name == "air" then set_node_at(vm, vec, dust_mat) end if is_air(get_node(vm, vec)) then swap_node(vm, vec, dust_mat) end
end end
else else
if n and n.name ~= surface_mat.name and is_solid_not_tree(n) then if n and n.name ~= surface_mat.name and is_solid_not_tree(n) then
set_node_at(vm, vec, surface_mat) swap_node(vm, vec, surface_mat)
if dust_mat then if dust_mat then
vec.y = yi + 1 vec.y = yi + 1
if get_node_at(vm, vec).name == "air" then set_node_at(vm, vec, dust_mat) end if is_air(get_node(vm, vec)) then swap_node(vm, vec, dust_mat) end
end end
end end
break break
@ -119,16 +122,16 @@ function vl_terraforming.clearance_vm(vm, px, py, pz, sx, sy, sz, corners, surfa
if py+4 < sy then if py+4 < sy then
for yi = py+2,py+4 do for yi = py+2,py+4 do
vec = vector_new(xi, yi, zi) vec = vector_new(xi, yi, zi)
if get_node_at(vm, vec).name ~= "mcl_core:bedrock" then set_node_at(vm, vec, v) end if not immutable(get_node(vm, vec)) then swap_node(vm, vec, v) end
end end
end end
for yi = py+1,py-1,-1 do for yi = py+1,py-1,-1 do
local n = get_node_at(vm, vector_new(xi, yi, zi)) local n = get_node(vm, vector_new(xi, yi, zi))
if is_tree_bot_leaves(n) and n.name ~= "mcl_core:bedrock" then if is_tree_not_leaves(n) and not immutable(n) then
set_node_at(vm, vector_new(xi, yi, zi), AIR) swap_node(vm, vector_new(xi, yi, zi), AIR)
else else
if n and n.name ~= surface_mat.name and is_solid_not_tree(n) then 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(vm, vector_new(xi, yi, zi), surface_mat)
end end
break break
end end
@ -148,22 +151,20 @@ function vl_terraforming.clearance_vm(vm, px, py, pz, sx, sy, sz, corners, surfa
local keep_trees = (xi<px or xi>=px+sx) or (zi<pz or zi>=pz+sz) -- TODO make parameter? local keep_trees = (xi<px or xi>=px+sx) or (zi<pz or zi>=pz+sz) -- TODO make parameter?
if dx22+dy2+dz22 <= 1 then if dx22+dy2+dz22 <= 1 then
vec.x, vec.y, vec.z = xi, yi, zi vec.x, vec.y, vec.z = xi, yi, zi
local name = get_node_at(vm, vec).name local nod = get_node(vm, vec)
-- don't break bedrock or air -- 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 if is_air(nod) or immutable(nod) then goto continue end
local meta = minetest.registered_items[name] local is_tree = is_tree_or_leaves(nod)
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 if keep_trees and is_tree then goto continue end
vec.y = yi-1 vec.y = yi-1
-- do not clear above solid -- do not clear above solid
local name_below = get_node_at(vm, vec).name local nod_below = get_node(vm, vec)
if name_below ~= "air" and name_below ~= "ignore" and name_below ~= "mcl_core:bedrock" then goto continue end if not is_air(nod_below) and not immutable(nod_below) then goto continue end
-- try to completely remove trees overhead -- try to completely remove trees overhead
-- stop randomly depending on fill, to narrow down the caves -- 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 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 vec.x, vec.y, vec.z = xi, yi, zi
set_node_at(vm, vec, AIR) swap_node(vm, vec, AIR)
active = true active = true
::continue:: ::continue::
end end

View file

@ -1,12 +1,14 @@
local abs = math.abs local abs = math.abs
local max = math.max local max = math.max
local vector_new = vector.new local vector_new = vector.new
local is_solid_not_tree = vl_terraforming._is_solid_not_tree
local make_solid = vl_terraforming._make_solid
local get_node = core.get_node local get_node = core.get_node
local swap_node = core.swap_node local swap_node = core.swap_node
local is_air = vl_terraforming._is_air
local is_solid_not_tree = vl_terraforming._is_solid_not_tree
local immutable = vl_terraforming._immutable
local make_solid = vl_terraforming._make_solid
--- Grow the foundation downwards --- Grow the foundation downwards
-- @param xi number: x coordinate -- @param xi number: x coordinate
-- @param yi number: y coordinate -- @param yi number: y coordinate
@ -34,7 +36,7 @@ local function grow_foundation(xi,yi,zi,pr,surface_mat,platform_mat,stone_mat)
-- TODO: allow controlling the random depth with an additional parameter? -- TODO: allow controlling the random depth with an additional parameter?
if (pr:next(0,1e9)/1e9)^2 > c/9.1 then return false end if (pr:next(0,1e9)/1e9)^2 > c/9.1 then return false end
pos.x, pos.y, pos.z = xi, yi, zi pos.x, pos.y, pos.z = xi, yi, zi
if get_node(pos).name == "mcl_core:bedrock" then return false end if immutable(get_node(pos)) then return false end
swap_node(pos, platform_mat) swap_node(pos, platform_mat)
return true return true
end end
@ -77,11 +79,11 @@ function vl_terraforming.foundation(px, py, pz, sx, sy, sz, corners, surface_mat
pos.z = zi pos.z = zi
if xi >= px and xi < px+sx and zi >= pz and zi < pz+sz and dx2+dz2 <= 1 then if xi >= px and xi < px+sx and zi >= pz and zi < pz+sz and dx2+dz2 <= 1 then
pos.y = py pos.y = py
if get_node(pos).name ~= "mcl_core:bedrock" then if not immutable(get_node(pos)) then
swap_node(pos, surface_mat) swap_node(pos, surface_mat)
if dust_mat then if dust_mat then
pos.y = py + 1 pos.y = py + 1
if get_node(pos).name == "air" then swap_node(pos, dust_mat) end if is_air(get_node(pos)) then swap_node(pos, dust_mat) end
end end
pos.y = py - 1 pos.y = py - 1
make_solid(pos, platform_mat) make_solid(pos, platform_mat)
@ -92,7 +94,7 @@ function vl_terraforming.foundation(px, py, pz, sx, sy, sz, corners, surface_mat
make_solid(pos, surface_mat) make_solid(pos, surface_mat)
if dust_mat then if dust_mat then
pos.y = py pos.y = py
if get_node(pos).name == "air" then swap_node(pos, dust_mat) end if is_air(get_node(pos)) then swap_node(pos, dust_mat) end
end end
end end
end end

View file

@ -2,7 +2,9 @@ local abs = math.abs
local max = math.max local max = math.max
local vector_new = vector.new local vector_new = vector.new
local is_air = vl_terraforming._is_air
local is_solid_not_tree = vl_terraforming._is_solid_not_tree local is_solid_not_tree = vl_terraforming._is_solid_not_tree
local immutable = vl_terraforming._immutable
local make_solid_vm = vl_terraforming._make_solid_vm local make_solid_vm = vl_terraforming._make_solid_vm
--- Grow the foundation downwards --- Grow the foundation downwards
@ -15,11 +17,12 @@ local make_solid_vm = vl_terraforming._make_solid_vm
-- @param platform_mat Node: platform material node -- @param platform_mat Node: platform material node
-- @param stone_mat Node: stone 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 function grow_foundation_vm(vm,xi,yi,zi,pr,surface_mat,platform_mat,stone_mat)
local get_node_at = vm.get_node_at local get_node = vm.get_node_at
local swap_node = vm.set_node_at
local pos, n, c = vector_new(xi,yi,zi), nil, 0 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(vm, pos)) then return false end -- already solid, nothing to do
pos.y = pos.y + 1 pos.y = pos.y + 1
local cur = get_node_at(vm, pos) local cur = get_node(vm, pos)
if not is_solid_not_tree(cur) then return false end -- above is empty, do not fill below 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 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 if pr:next(1,4) == 1 then platform_mat = stone_mat end -- randomly switch to stone sometimes
@ -27,15 +30,15 @@ local function grow_foundation_vm(vm,xi,yi,zi,pr,surface_mat,platform_mat,stone_
for x = xi-1,xi+1 do for x = xi-1,xi+1 do
for z = zi-1,zi+1 do for z = zi-1,zi+1 do
pos.x, pos.z = x, z 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(vm, pos)) then c = c + 1 end
end end
end end
-- stop randomly depending on fill, to narrow down the foundation -- stop randomly depending on fill, to narrow down the foundation
-- TODO: allow controlling the random depth with an additional parameter? -- TODO: allow controlling the random depth with an additional parameter?
if (pr:next(0,1e9)/1e9)^2 > c/9.1 then return false end if (pr:next(0,1e9)/1e9)^2 > c/9.1 then return false end
pos.x, pos.y, pos.z = xi, yi, zi pos.x, pos.y, pos.z = xi, yi, zi
if get_node_at(vm, pos).name == "mcl_core:bedrock" then return false end if immutable(get_node(vm, pos)) then return false end
vm:set_node_at(pos, platform_mat) swap_node(vm, pos, platform_mat)
return true return true
end end
--- Generate a foundation from px,py,pz with size sx,sy,sz (sy < 0) plus some margin --- Generate a foundation from px,py,pz with size sx,sy,sz (sy < 0) plus some margin
@ -63,8 +66,8 @@ end
-- @param pr PcgRandom: random generator -- @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_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 if sx <= 0 or sy >= 0 or sz <= 0 then return end
local get_node_at = vm.get_node_at local get_node = vm.get_node_at
local set_node_at = vm.set_node_at local swap_node = vm.set_node_at
corners = corners or 0 corners = corners or 0
local wx2, wz2 = max(sx - corners, 1)^-2 * 2, max(sz - corners, 1)^-2 * 2 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 cx, cz = px + sx * 0.5 - 0.5, pz + sz * 0.5 - 0.5
@ -80,11 +83,11 @@ function vl_terraforming.foundation_vm(vm, px, py, pz, sx, sy, sz, corners, surf
pos.z = zi pos.z = zi
if xi >= px and xi < px+sx and zi >= pz and zi < pz+sz and dx2+dz2 <= 1 then if xi >= px and xi < px+sx and zi >= pz and zi < pz+sz and dx2+dz2 <= 1 then
pos.y = py pos.y = py
if get_node_at(vm, pos).name ~= "mcl_core:bedrock" then if not immutable(get_node(vm, pos)) then
set_node_at(vm, pos, surface_mat) swap_node(vm, pos, surface_mat)
if dust_mat then if dust_mat then
pos.y = py + 1 pos.y = py + 1
if get_node_at(vm, pos).name == "air" then set_node_at(vm, pos, dust_mat) end if is_air(get_node(vm, pos)) then swap_node(vm, pos, dust_mat) end
end end
pos.y = py - 1 pos.y = py - 1
make_solid_vm(vm, pos, platform_mat) make_solid_vm(vm, pos, platform_mat)
@ -95,7 +98,7 @@ function vl_terraforming.foundation_vm(vm, px, py, pz, sx, sy, sz, corners, surf
make_solid_vm(vm, pos, surface_mat) make_solid_vm(vm, pos, surface_mat)
if dust_mat then if dust_mat then
pos.y = py pos.y = py
if get_node_at(vm, pos).name == "air" then set_node_at(vm, pos, dust_mat) end if is_air(get_node(vm, pos)) then swap_node(vm, pos, dust_mat) end
end end
end end
end end

View file

@ -203,18 +203,19 @@ local find_under_water_surface = vl_terraforming.find_under_water_surface
--- find suitable height for a structure of this size --- find suitable height for a structure of this size
-- @param cpos vector: center -- @param cpos vector: center
-- @param size vector: area size -- @param size vector: area size
-- @param tolerance number or string: maximum height difference allowed, default 8. -- @param tolerance number or string: maximum height difference allowed, default 8,
-- @param mode string: "solid" (default), "liquid_surface", "under_air" -- @param surface string: "solid" (default), "liquid_surface", "under_air"
-- @param mode string: "median" (default), "min" and "max"
-- @return position over surface, surface material (or nil, nil) -- @return position over surface, surface material (or nil, nil)
function vl_terraforming.find_level(cpos, size, tolerance, mode) function vl_terraforming.find_level(cpos, size, tolerance, surface, mode)
local _find_ground = find_ground local _find_ground = find_ground
if mode == "liquid_surface" or mode == "liquid" then _find_ground = find_liquid_surface end if surface == "liquid_surface" or surface == "liquid" then _find_ground = find_liquid_surface end
if mode == "under_water" or mode == "water" then _find_ground = find_under_water_surface end if surface == "under_water" or surface == "water" then _find_ground = find_under_water_surface end
if mode == "under_air" then _find_ground = find_under_air end if surface == "under_air" then _find_ground = find_under_air end
-- begin at center, then top-left and clockwise -- begin at center, then top-left and clockwise
local pos, surface_material = _find_ground(cpos) local pos, surface_material = _find_ground(cpos)
if not pos then if not pos then
-- minetest.log("action", "[vl_terraforming] no ground at starting position "..minetest.pos_to_string(cpos).." mode "..tostring(mode or "default")) -- minetest.log("action", "[vl_terraforming] no ground at starting position "..minetest.pos_to_string(cpos).." surface "..tostring(surface or "default"))
return nil, nil return nil, nil
end end
local ys = { pos.y } local ys = { pos.y }
@ -239,21 +240,19 @@ function vl_terraforming.find_level(cpos, size, tolerance, mode)
table.sort(ys) table.sort(ys)
tolerance = tolerance or 8 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? -- well supported base, not too uneven?
if #ys < 5 or min(ys[#ys-1]-ys[1], ys[#ys]-ys[2]) > tolerance then 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] -- 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) -- .." tolerance "..tostring(#ys > 2 and min(ys[#ys-1]-ys[1], ys[#ys]-ys[2])).." > "..tolerance)
return nil, nil return nil, nil
end 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 if mode == "min" then
return cpos, surface_material pos.y = ys[1]
elseif mode == "max" then
pos.y = ys[#ys]
else -- median except for largest
pos.y = floor(0.5 * (ys[floor(1 + (#ys - 1) * 0.5)] + ys[ceil(1 + (#ys - 1) * 0.5)])) -- rounded
end
return pos, surface_material
end end

View file

@ -11,8 +11,9 @@ local is_solid_not_tree = vl_terraforming._is_solid_not_tree
-- @return position and material of surface -- @return position and material of surface
function vl_terraforming.find_ground_vm(vm, pos) function vl_terraforming.find_ground_vm(vm, pos)
if not pos then return nil, nil end if not pos then return nil, nil end
local get_node = vm.get_node_at
pos = vector_copy(pos) pos = vector_copy(pos)
local cur = vm:get_node_at(pos) local cur = get_node(vm, pos)
if cur.name == "ignore" then if cur.name == "ignore" then
local e1, e2 = vm:get_emerged_area() 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) minetest.log("warning", "find_ground with invalid position (outside of emerged area?) at "..minetest.pos_to_string(pos)
@ -23,7 +24,7 @@ function vl_terraforming.find_ground_vm(vm, pos)
local prev = cur local prev = cur
while true do while true do
pos.y = pos.y + 1 pos.y = pos.y + 1
local cur = vm:get_node_at(pos) local cur = get_node(vm, pos)
if not cur or cur.name == "ignore" then 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)) -- minetest.log("action", "No ground, "..tostring(cur and cur.name).." over "..tostring(prev and prev.name).." at "..minetest.pos_to_string(pos))
return nil return nil
@ -38,7 +39,7 @@ function vl_terraforming.find_ground_vm(vm, pos)
while true do while true do
pos.y = pos.y - 1 pos.y = pos.y - 1
local prev = cur local prev = cur
local cur = vm:get_node_at(pos) local cur = get_node(vm, pos)
if not cur or cur.name == "ignore" then 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)) -- minetest.log("action", "No ground, "..tostring(cur and cur.name).." below "..tostring(prev and prev.name).." at "..minetest.pos_to_string(pos))
return nil return nil
@ -60,8 +61,9 @@ local find_ground_vm = vl_terraforming.find_ground_vm
-- @return position and material of surface -- @return position and material of surface
function vl_terraforming.find_under_air_vm(vm, pos) function vl_terraforming.find_under_air_vm(vm, pos)
if not pos then return nil, nil end if not pos then return nil, nil end
local get_node = vm.get_node_at
pos = vector_copy(pos) pos = vector_copy(pos)
local cur = vm:get_node_at(pos) local cur = get_node(vm, pos)
if cur.name == "ignore" then if cur.name == "ignore" then
local e1, e2 = vm:get_emerged_area() 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) minetest.log("warning", "find_under_air with invalid position (outside of emerged area?) at "..minetest.pos_to_string(pos)
@ -72,7 +74,7 @@ function vl_terraforming.find_under_air_vm(vm, pos)
local prev = cur local prev = cur
while true do while true do
pos.y = pos.y + 1 pos.y = pos.y + 1
local cur = vm:get_node_at(pos) local cur = get_node(vm, pos)
if not cur or cur.name == "ignore" then 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)) -- minetest.log("action", "No ground, "..tostring(cur and cur.name).." over "..tostring(prev and prev.name).." at "..minetest.pos_to_string(pos))
return nil return nil
@ -88,7 +90,7 @@ function vl_terraforming.find_under_air_vm(vm, pos)
while true do while true do
pos.y = pos.y - 1 pos.y = pos.y - 1
local prev = cur local prev = cur
local cur = vm:get_node_at(pos) local cur = get_node(vm, pos)
if not cur or cur.name == "ignore" then 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)) -- minetest.log("action", "No ground, "..tostring(cur and cur.name).." below "..tostring(prev and prev.name).." at "..minetest.pos_to_string(pos))
return nil return nil
@ -108,8 +110,9 @@ local find_under_air_vm = vl_terraforming.find_under_air_vm
-- @return position and material of surface -- @return position and material of surface
function vl_terraforming.find_liquid_surface_vm(vm, pos) function vl_terraforming.find_liquid_surface_vm(vm, pos)
if not pos then return nil, nil end if not pos then return nil, nil end
local get_node = vm.get_node_at
pos = vector_copy(pos) pos = vector_copy(pos)
local cur = vm:get_node_at(pos) local cur = get_node(vm, pos)
if cur.name == "ignore" then if cur.name == "ignore" then
local e1, e2 = vm:get_emerged_area() 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) minetest.log("warning", "find_liquid_surface with invalid position (outside of emerged area?) at "..minetest.pos_to_string(pos)
@ -120,7 +123,7 @@ function vl_terraforming.find_liquid_surface_vm(vm, pos)
local prev = cur local prev = cur
while true do while true do
pos.y = pos.y + 1 pos.y = pos.y + 1
local cur = vm:get_node_at(pos) local cur = get_node(vm, pos)
if not cur or cur.name == "ignore" then 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)) -- minetest.log("action", "No ground, "..tostring(cur and cur.name).." over "..tostring(prev and prev.name).." at "..minetest.pos_to_string(pos))
return nil return nil
@ -136,7 +139,7 @@ function vl_terraforming.find_liquid_surface_vm(vm, pos)
while true do while true do
pos.y = pos.y - 1 pos.y = pos.y - 1
local prev = cur local prev = cur
local cur = vm:get_node_at(pos) local cur = get_node(vm, pos)
if not cur or cur.name == "ignore" then 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)) -- minetest.log("action", "No ground, "..tostring(cur and cur.name).." below "..tostring(prev and prev.name).." at "..minetest.pos_to_string(pos))
return nil return nil
@ -160,8 +163,9 @@ local find_liquid_surface_vm = vl_terraforming.find_liquid_surface_vm
-- @return position and material of surface -- @return position and material of surface
function vl_terraforming.find_under_water_surface_vm(vm, pos) function vl_terraforming.find_under_water_surface_vm(vm, pos)
if not pos then return nil, nil end if not pos then return nil, nil end
local get_node = vm.get_node_at
pos = vector_copy(pos) pos = vector_copy(pos)
local cur = vm:get_node_at(pos) local cur = get_node(vm, pos)
if cur.name == "ignore" then if cur.name == "ignore" then
local e1, e2 = vm:get_emerged_area() 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) minetest.log("warning", "find_under_water_surface with invalid position (outside of emerged area?) at "..minetest.pos_to_string(pos)
@ -172,7 +176,7 @@ function vl_terraforming.find_under_water_surface_vm(vm, pos)
local prev = cur local prev = cur
while true do while true do
pos.y = pos.y + 1 pos.y = pos.y + 1
local cur = vm:get_node_at(pos) local cur = get_node(vm, pos)
if not cur or cur.name == "ignore" then 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)) -- minetest.log("action", "No ground, "..tostring(cur and cur.name).." over "..tostring(prev and prev.name).." at "..minetest.pos_to_string(pos))
return nil return nil
@ -188,7 +192,7 @@ function vl_terraforming.find_under_water_surface_vm(vm, pos)
while true do while true do
pos.y = pos.y - 1 pos.y = pos.y - 1
local prev = cur local prev = cur
local cur = vm:get_node_at(pos) local cur = get_node(vm, pos)
if not cur or cur.name == "ignore" then 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)) -- minetest.log("action", "No ground, "..tostring(cur and cur.name).." below "..tostring(prev and prev.name).." at "..minetest.pos_to_string(pos))
return nil return nil
@ -211,18 +215,19 @@ local find_under_water_surface_vm = vl_terraforming.find_under_water_surface_vm
-- @param vm VoxelManip: to read data -- @param vm VoxelManip: to read data
-- @param cpos vector: center -- @param cpos vector: center
-- @param size vector: area size -- @param size vector: area size
-- @param tolerance number or string: maximum height difference allowed, default 8. -- @param tolerance number or string: maximum height difference allowed, default 8,
-- @param mode string: "solid" (default), "liquid_surface", "under_air" -- @param surface string: "solid" (default), "liquid_surface", "under_air"
-- @param mode string: "median" (default), "min" and "max"
-- @return position over surface, surface material (or nil, nil) -- @return position over surface, surface material (or nil, nil)
function vl_terraforming.find_level_vm(vm, cpos, size, tolerance, mode) function vl_terraforming.find_level_vm(vm, cpos, size, tolerance, surface, mode)
local find_ground = find_ground_vm local _find_ground = find_ground_vm
if mode == "liquid_surface" or mode == "liquid" then find_ground = find_liquid_surface_vm end if surface == "liquid_surface" or surface == "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 surface == "under_water" or surface == "water" then _find_ground = find_under_water_surface_vm end
if mode == "under_air" then find_ground = find_under_air_vm end if surface == "under_air" then _find_ground = find_under_air_vm end
-- begin at center, then top-left and clockwise -- begin at center, then top-left and clockwise
local pos, surface_material = find_ground(vm, cpos) local pos, surface_material = _find_ground(vm, cpos)
if not pos then if not pos then
-- minetest.log("action", "[vl_terraforming] no ground at starting position "..minetest.pos_to_string(cpos).." mode "..tostring(mode or "default")) -- minetest.log("action", "[vl_terraforming] no ground at starting position "..minetest.pos_to_string(cpos).." surface "..tostring(surface or "default"))
return nil, nil return nil, nil
end end
local ys = { pos.y } local ys = { pos.y }
@ -230,38 +235,36 @@ function vl_terraforming.find_level_vm(vm, cpos, size, tolerance, mode)
if size.x == 1 and size.z == 1 then return pos end if size.x == 1 and size.z == 1 then return pos end
-- move to top left corner -- move to top left corner
pos.x, pos.z = pos.x - floor((size.x-1)/2), pos.z - floor((size.z-1)/2) 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(vm, pos)
if pos_c then table.insert(ys, pos_c.y) end if pos_c then table.insert(ys, pos_c.y) end
-- move to top right corner -- move to top right corner
pos.x = pos.x + size.x - 1 pos.x = pos.x + size.x - 1
local pos_c = find_ground(vm, pos) local pos_c = _find_ground(vm, pos)
if pos_c then table.insert(ys, pos_c.y) end if pos_c then table.insert(ys, pos_c.y) end
-- move to bottom right corner -- move to bottom right corner
pos.z = pos.z + size.z - 1 pos.z = pos.z + size.z - 1
local pos_c = find_ground(vm, pos) local pos_c = _find_ground(vm, pos)
if pos_c then table.insert(ys, pos_c.y) end if pos_c then table.insert(ys, pos_c.y) end
-- move to bottom left corner -- move to bottom left corner
pos.x = pos.x - (size.x - 1) pos.x = pos.x - (size.x - 1)
local pos_c = find_ground(vm, pos) local pos_c = _find_ground(vm, pos)
if pos_c then table.insert(ys, pos_c.y) end if pos_c then table.insert(ys, pos_c.y) end
table.sort(ys) table.sort(ys)
tolerance = tolerance or 8 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? -- well supported base, not too uneven?
if #ys < 5 or min(ys[#ys-1]-ys[1], ys[#ys]-ys[2]) > tolerance then 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] -- 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) -- .." tolerance "..tostring(#ys > 2 and min(ys[#ys-1]-ys[1], ys[#ys]-ys[2])).." > "..tolerance)
return nil, nil return nil, nil
end 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 if mode == "min" then
return cpos, surface_material pos.y = ys[1]
elseif mode == "max" then
pos.y = ys[#ys]
else -- median except for largest
pos.y = floor(0.5 * (ys[floor(1 + (#ys - 1) * 0.5)] + ys[ceil(1 + (#ys - 1) * 0.5)])) -- rounded
end
return pos, surface_material
end end

View file

@ -1,6 +1,17 @@
local get_node = core.get_node local get_node = core.get_node
local swap_node = core.swap_node local swap_node = core.swap_node
--- node that is used to place air
vl_terraforming._AIR = {name = "air"}
--- immutable nodes where we have to stop
-- @param node string or Node: node or node name
-- @return true if this must never be changed
function vl_terraforming._immutable(node)
local name = node.name or node
return name == "ignore" or name == "mcl_core:bedrock"
end
--- fairly strict: air, ignore, or no_paths marker --- fairly strict: air, ignore, or no_paths marker
-- @param node string or Node: node or node name -- @param node string or Node: node or node name
-- @return true for air and ignore nodes -- @return true for air and ignore nodes
@ -13,31 +24,48 @@ end
-- @param node LUA node or node name -- @param node LUA node or node name
-- @return truthy when solid but not tree/decoration/fungi -- @return truthy when solid but not tree/decoration/fungi
function vl_terraforming._is_solid_not_tree(node) function vl_terraforming._is_solid_not_tree(node)
local name = node.name or node local name = node.name
if name == "air" or name == "ignore" or name == "mcl_villages:no_paths" or name == "mcl_core:bedrock" then return false end if name == "air" or name == "ignore" or name == "mcl_villages:no_paths" or name == "mcl_core:bedrock" then return false end
if name == "mcl_nether:soul_sand" then return true end -- not "solid". Other exceptions we need? if name == "mcl_nether:soul_sand" then return true end -- not "walkable". Other exceptions we need?
if name == "mcl_nether:nether_wart_block" then return false end -- crimson forest, treat as tree if name == "mcl_crimson:crimson_hyphae" then return false end -- crimson forest, treat as tree
-- is deco_block if name == "mcl_crimson:warped_wart_block" then return false end -- warped forest, treat as tree if name == "mcl_nether:nether_wart_block" then return false end -- crimson forest, treat as leaves
-- is deco_block if name == "mcl_crimson:shroomlight" then return false end -- crimson forest, treat as tree if name == "mcl_crimson:warped_hyphae" then return false end -- warped forest, treat as tree
-- is deco_block if name == "mcl_core:snow" then return false end if name == "mcl_crimson:warped_wart_block" then return false end -- warped forest, treat as leaves
if name == "mcl_crimson:shroomlight" then return false end -- crimson forest, treat as tree
if name == "mcl_core:snow" then return false end
-- is walkable if name == "mcl_core:snowblock" then return true end -- is walkable if name == "mcl_core:snowblock" then return true end
local meta = minetest.registered_items[name] local meta = minetest.registered_items[name]
local groups = meta and meta.groups local groups = meta and meta.groups
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)) return groups and meta.walkable and not ((groups.tree or 0) > 0 or (groups.leaves or 0) > 0 or (groups.plant or 0) > 0 or (groups.huge_mushroom or 0) > 0)
end end
local is_solid_not_tree = vl_terraforming._is_solid_not_tree local is_solid_not_tree = vl_terraforming._is_solid_not_tree
--- check if a node is tree --- check if a node is tree or leaves
-- @param node string or Node: node or node name -- @param node string or Node: node or node name
-- @return true for tree, leaves -- @return true for tree or leaves, also other compostable things
function vl_terraforming._is_tree_or_leaves(node)
local name = node.name or node
if name == "mcl_crimson:crimson_hyphae" then return true end -- crimson forest, treat as tree
if name == "mcl_nether:nether_wart_block" then return true end -- crimson forest, treat as leaves
if name == "mcl_crimson:warped_hyphae" then return true end -- warped forest, treat as tree
if name == "mcl_crimson:warped_wart_block" then return true end -- warped forest, treat as leaves
if name == "mcl_crimson:shroomlight" then return true end -- crimson forest, treat as tree
local meta = minetest.registered_items[node]
local groups = meta and meta.groups
return groups and ((groups.tree or 0) > 0 or (groups.leaves or 0) > 0 or (groups.plant or 0) > 0 or (groups.huge_mushroom or 0) > 0)
end
--- check if a node is tree trunk
-- @param node string or Node: node or node name
-- @return true for tree, but not leaves
function vl_terraforming._is_tree_not_leaves(node) function vl_terraforming._is_tree_not_leaves(node)
local name = node.name or node local name = node.name or node
if name == "air" or name == "ignore" or name == "mcl_villages:no_paths" then return false end if name == "air" or name == "ignore" or name == "mcl_villages:no_paths" then return false end
-- if name == "mcl_nether:nether_wart_block" then return true end -- crimson forest, treat as tree if name == "mcl_crimson:crimson_hyphae" then return true end -- crimson forest, treat as tree
-- if name == "mcl_crimson:warped_wart_block" then return true end -- warped forest, treat as tree if name == "mcl_crimson:warped_hyphae" 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] local meta = minetest.registered_items[name]
return meta and meta.groups and (meta.groups.tree or 0) > 0 local groups = meta and meta.groups
return groups and ((groups.tree or 0) > 0 or (groups.huge_mushroom_stem or 0) > 0)
end end
--- check if a node is liquid --- check if a node is liquid