Compare commits

...

2 Commits

Author SHA1 Message Date
kno10 e8944f202c enhance vl_terraform API 2024-10-31 20:14:29 +01:00
kno10 dfeffc8522 reorg terrain features with biomes 2024-10-31 19:20:38 +01:00
53 changed files with 750 additions and 697 deletions

View File

@ -220,16 +220,16 @@ local function sort_decorations()
for _, key in ipairs(keys) do for _, key in ipairs(keys) do
local def = map[key] local def = map[key]
if def.name and minetest.get_decoration_id(def.name) then if def.name and minetest.get_decoration_id(def.name) then
minetest.log("warning", "Decoration ID not unique: "..def.name) minetest.log("warning", "Decoration ID not unique: "..tostring(def.name or key))
end end
local deco_id = minetest.register_decoration(def) local deco_id = minetest.register_decoration(def)
if not deco_id then if not deco_id then
error("Failed to register decoration "..tostring(def.name).." - name not unique?") error("Failed to register decoration "..tostring(def.name or key).." - name not unique or schematic not found?")
end end
if def.name then if def.name then
deco_id = minetest.get_decoration_id(def.name) deco_id = minetest.get_decoration_id(def.name)
if not deco_id then if not deco_id then
error("Failed to register decoration "..tostring(def.name).." - name not unique?") error("Failed to register decoration "..tostring(def.name or key).." - name not unique?")
end end
if def.gen_callback then if def.gen_callback then
minetest.set_gen_notify({decoration = true}, {deco_id}) minetest.set_gen_notify({decoration = true}, {deco_id})

View File

@ -31,8 +31,6 @@ dofile(modpath.."/desert_temple.lua")
dofile(modpath.."/desert_well.lua") dofile(modpath.."/desert_well.lua")
dofile(modpath.."/end_city.lua") dofile(modpath.."/end_city.lua")
dofile(modpath.."/end_spawn.lua") dofile(modpath.."/end_spawn.lua")
dofile(modpath.."/fossil.lua")
dofile(modpath.."/geode.lua")
dofile(modpath.."/igloo.lua") dofile(modpath.."/igloo.lua")
dofile(modpath.."/jungle_temple.lua") dofile(modpath.."/jungle_temple.lua")
dofile(modpath.."/ocean_ruins.lua") dofile(modpath.."/ocean_ruins.lua")
@ -43,24 +41,3 @@ dofile(modpath.."/shipwrecks.lua")
dofile(modpath.."/witch_hut.lua") dofile(modpath.."/witch_hut.lua")
dofile(modpath.."/woodland_mansion.lua") dofile(modpath.."/woodland_mansion.lua")
vl_structures.register_structure("boulder",{
-- as they have no place_on, they will not be spawned by this mechanism. this is just for /spawnstruct
filenames = {
-- small boulder 3x as likely
modpath.."/schematics/mcl_structures_boulder_small.mts",
modpath.."/schematics/mcl_structures_boulder_small.mts",
modpath.."/schematics/mcl_structures_boulder_small.mts",
modpath.."/schematics/mcl_structures_boulder.mts",
},
})
vl_structures.register_structure("ice_spike_small",{
-- as they have no place_on, they will not be spawned by this mechanism. this is just for /spawnstruct
filenames = { modpath.."/schematics/mcl_structures_ice_spike_small.mts" },
})
vl_structures.register_structure("ice_spike_large",{
-- as they have no place_on, they will not be spawned by this mechanism. this is just for /spawnstruct
filenames = { modpath.."/schematics/mcl_structures_ice_spike_large.mts" },
})

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

@ -6,23 +6,21 @@ local peaceful = minetest.settings:get_bool("only_peaceful_mobs", false)
local function spawn_witch(p1,p2) local function spawn_witch(p1,p2)
local c = minetest.find_node_near(p1,15,{"mcl_cauldrons:cauldron"}) local c = minetest.find_node_near(p1,15,{"mcl_cauldrons:cauldron"})
if c then if not c then return end
local nn = minetest.find_nodes_in_area_under_air(vector.new(p1.x,c.y-1,p1.z),vector.new(p2.x,c.y-1,p2.z),{"mcl_core:sprucewood"}) local nn = minetest.find_nodes_in_area_under_air(vector.new(p1.x,c.y-1,p1.z),vector.new(p2.x,c.y-1,p2.z),{"mcl_core:sprucewood"})
local witch local witchobj = not peaceful and minetest.add_entity(vector.offset(nn[math.random(#nn)],0,1,0),"mobs_mc:witch")
if not peaceful then if witchobj then
witch = minetest.add_entity(vector.offset(nn[math.random(#nn)],0,1,0),"mobs_mc:witch"):get_luaentity() local witch = witchobj:get_luaentity()
witch._home = c witch._home = c
witch.can_despawn = false witch.can_despawn = false
end end
local catobject = minetest.add_entity(vector.offset(nn[math.random(#nn)],0,1,0),"mobs_mc:cat") local catobj = minetest.add_entity(vector.offset(nn[math.random(#nn)],0,1,0),"mobs_mc:cat")
if catobject and catobject:get_pos() then if catobj then
local cat=catobject:get_luaentity() local cat=catobj:get_luaentity()
cat.object:set_properties({textures = {"mobs_mc_cat_black.png"}}) cat.object:set_properties({textures = {"mobs_mc_cat_black.png"}})
cat.owner = "!witch!" --so it's not claimable by player cat.owner = "!witch!" --so it's not claimable by player
cat._home = c cat._home = c
cat.can_despawn = false cat.can_despawn = false
end
return
end end
end end
@ -30,7 +28,6 @@ local function hut_placement_callback(pos,def,pr,p1,p2)
-- p1.y is the bottom slice only, not a typo, we look for the hut legs -- p1.y is the bottom slice only, not a typo, we look for the hut legs
local legs = minetest.find_nodes_in_area(p1,vector.new(p2.x,p1.y,p2.z), "mcl_core:tree") local legs = minetest.find_nodes_in_area(p1,vector.new(p2.x,p1.y,p2.z), "mcl_core:tree")
local tree = {} local tree = {}
-- TODO: port leg generation to VoxelManip?
for _,leg in pairs(legs) do for _,leg in pairs(legs) do
while true do while true do
local name = minetest.get_node(vector.offset(leg,0,-1,0)).name local name = minetest.get_node(vector.offset(leg,0,-1,0)).name
@ -51,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

@ -1,361 +0,0 @@
local adjacents = {
vector.new(1,0,0),
vector.new(1,0,1),
vector.new(1,0,-1),
vector.new(-1,0,0),
vector.new(-1,0,1),
vector.new(-1,0,-1),
vector.new(0,0,1),
vector.new(0,0,-1),
vector.new(0,-1,0)
}
local function airtower(pos,tbl,h)
for i=1,h do
table.insert(tbl,vector.offset(pos,0,i,0))
end
end
local function makelake(pos,size,liquid,placein,border,pr,noair)
local p1, p2 = vector.offset(pos,-size,-1,-size), vector.offset(pos,size,-1,size)
local e1, e2 = vector.offset(pos,-size,-2,-size), vector.offset(pos,size,15,size)
minetest.emerge_area(e1, e2, function(_, _, calls_remaining)
if calls_remaining ~= 0 then return end
local nn = minetest.find_nodes_in_area(p1,p2,placein)
if not nn[1] then return end
table.sort(nn,function(a, b)
return vector.distance(pos, a) < vector.distance(pos, b)
end)
local y = pos.y - 1
local lq, air = {}, {}
local r = pr:next(1,#nn)
for i=1,r do
airtower(nn[i],air,20)
table.insert(lq,nn[i])
end
minetest.bulk_swap_node(lq,{name=liquid})
minetest.bulk_swap_node(air,{name="air"})
air = {}
local br = {}
for k,v in pairs(lq) do
for kk,vv in pairs(adjacents) do
local pp = vector.add(v,vv)
local an = minetest.get_node(pp)
if not border then
if minetest.get_item_group(an.name,"solid") > 0 then
border = an.name
elseif minetest.get_item_group(minetest.get_node(nn[1]).name,"solid") > 0 then
border = minetest.get_node_or_nil(nn[1]).name
else
border = "mcl_core:stone"
end
if border == nil or border == "mcl_core:dirt" then border = "mcl_core:dirt_with_grass" end
end
if not noair and an.name ~= liquid then
table.insert(br,pp)
--[[ no need to have air above border:
local un = minetest.get_node(vector.offset(pp,0,1,0))
if un.name ~= liquid then
airtower(pp,air,20)
end]]--
end
end
end
minetest.bulk_swap_node(br,{name=border})
minetest.bulk_swap_node(air,{name="air"})
return true
end)
return true
end
local mushrooms = {"mcl_mushrooms:mushroom_brown","mcl_mushrooms:mushroom_red"}
local function place_fallen_tree(pos,def,pr)
local tree = minetest.find_node_near(pos,15,{"group:tree"})
if not tree then return end
tree = minetest.get_node(tree).name
local minlen, maxlen = 3, 9
local vrate, mrate = 120, 160
local len = pr:next(minlen,maxlen)
local dir = pr:next(0,3)
local dx, dy, dz, param2, w1, w2
if dir == 0 then
dx, dy, dz, param2, w1, w2 = 1, 0, 0, 12, 5, 4
elseif dir == 1 then
dx, dy, dz, param2, w1, w2 = -1, 0, 0, 12, 4, 5
elseif dir == 2 then
dx, dy, dz, param2, w1, w2 = 0, 0, 1, 6, 3, 2
else -- if dir == 3 then
dx, dy, dz, param2, w1, w2 = 0, 0, -1, 6, 2, 3
end
-- TODO: port this to voxel manipulators
-- ensure we have room for the tree
local minsupport, maxsupport = 99, 1
for i = 1,len do
-- check below
local n = minetest.get_node(vector.offset(pos, dx * i, -1, dz * i)).name
local nd = minetest.registered_nodes[n]
if n ~= "air" and nd.groups and nd.groups.solid and i > 2 then
if i < minsupport then minsupport = i end
maxsupport = i
end
-- check space
local n = minetest.get_node(vector.offset(pos, dx * i, 0, dz * i)).name
local nd = minetest.registered_nodes[n]
if n ~= "air" and nd.groups and not nd.groups.plant then
if i < minlen or pr:next(1,maxsupport) == 1 then return end
len = i
break
end
end
if maxsupport - minsupport < minlen then return end
len = math.min(len, maxsupport - 1)
if len < minlen then return end
-- place the tree
minetest.swap_node(pos, {name = tree, param2 = 0})
-- some are hollow:
if vl_hollow_logs.logs and pr:next(1,20) == 1 then
local nam = string.sub(tree, string.find(tree, ":") + 1)
nam = "vl_hollow_logs:"..nam.."_hollow"
if minetest.registered_nodes[nam] then tree = nam end
end
for i = 2,len do
minetest.swap_node(vector.offset(pos, dx * i, 0, dz * i), {name = tree, param2 = param2})
if pr:next(0,255) < vrate then
local side = vector.offset(pos, dx * i + dz, 0, dz * i + dx)
local n = minetest.get_node(side).name
if n == "air" then
minetest.swap_node(side, {name="mcl_core:vine", param2=w1})
end
end
if pr:next(0,255) < vrate then
local side = vector.offset(pos, dx * i - dz, 0, dz * i - dx)
local n = minetest.get_node(side).name
if n == "air" then
minetest.swap_node(side, {name="mcl_core:vine", param2=w2})
end
end
if pr:next(0,255) < mrate then
local top = vector.offset(pos, dx * i, 1, dz * i)
local n = minetest.get_node(top).name
if n == "air" then
minetest.swap_node(top, {name = mushrooms[pr:next(1,#mushrooms)], param2 = 12})
end
end
end
end
vl_structures.register_structure("fallen_tree",{
priority = 1100, -- after regular trees
place_on = {"group:grass_block"},
terrain_feature = true,
noise_params = {
offset = 0.00018,
scale = 0.01011,
spread = {x = 250, y = 250, z = 250},
seed = 24533,
octaves = 3,
persist = 0.66
},
flags = "place_center_x, place_center_z",
solid_ground = true,
y_max = mcl_vars.mg_overworld_max,
y_min = minetest.get_mapgen_setting("water_level"),
place_func = place_fallen_tree
})
vl_structures.register_structure("lavapool",{
place_on = {"group:sand", "group:dirt", "group:stone"},
terrain_feature = true,
noise_params = {
offset = 0,
scale = 0.0000022,
spread = {x = 250, y = 250, z = 250},
seed = 78375213,
octaves = 3,
persist = 0.001,
flags = "absvalue",
},
flags = "place_center_x, place_center_z, all_floors",
y_max = mcl_vars.mg_overworld_max,
y_min = minetest.get_mapgen_setting("water_level"),
place_func = function(pos, _, pr)
return makelake(pos,5,"mcl_core:lava_source",{"group:material_stone", "group:sand", "group:dirt"},"mcl_core:stone",pr)
end
})
vl_structures.register_structure("water_lake",{
place_on = {"group:dirt","group:stone"},
terrain_feature = true,
noise_params = {
offset = 0,
scale = 0.000032,
spread = {x = 250, y = 250, z = 250},
seed = 756641353,
octaves = 3,
persist = 0.001,
flags = "absvalue",
},
flags = "place_center_x, place_center_z, all_floors",
y_max = mcl_vars.mg_overworld_max,
y_min = minetest.get_mapgen_setting("water_level"),
place_func = function(pos, _, pr)
return makelake(pos,5,"mcl_core:water_source",{"group:material_stone", "group:sand", "group:dirt","group:grass_block"},"mcl_core:dirt_with_grass",pr)
end
})
vl_structures.register_structure("water_lake_mangrove_swamp",{
place_on = {"mcl_mud:mud"},
biomes = { "MangroveSwamp" },
terrain_feature = true,
noise_params = {
offset = 0,
scale = 0.0032,
spread = {x = 250, y = 250, z = 250},
seed = 6343241353,
octaves = 3,
persist = 0.001,
flags = "absvalue",
},
flags = "place_center_x, place_center_z, all_floors",
y_max = mcl_vars.mg_overworld_max,
y_min = minetest.get_mapgen_setting("water_level"),
place_func = function(pos, _, pr)
return makelake(pos,3,"mcl_core:water_source",{"group:material_stone", "group:sand", "group:dirt","group:grass_block","mcl_mud:mud"},"mcl_mud:mud",pr,true)
end
})
vl_structures.register_structure("basalt_column",{
place_on = {"mcl_blackstone:blackstone","mcl_blackstone:basalt"},
terrain_feature = true,
spawn_by = {"air"},
num_spawn_by = 2,
noise_params = {
offset = 0,
scale = 0.003,
spread = {x = 250, y = 250, z = 250},
seed = 72235213,
octaves = 5,
persist = 0.3,
flags = "absvalue",
},
flags = "all_floors",
y_max = mcl_vars.mg_nether_max - 20,
y_min = mcl_vars.mg_lava_nether_max + 1,
biomes = { "BasaltDelta" },
place_func = function(pos, _, pr)
local nn = minetest.find_nodes_in_area(vector.offset(pos,-5,-1,-5),vector.offset(pos,5,-1,5),{"air","mcl_blackstone:basalt","mcl_blackstone:blackstone"})
table.sort(nn,function(a, b)
return vector.distance(vector.new(pos.x,0,pos.z), a) < vector.distance(vector.new(pos.x,0,pos.z), b)
end)
if #nn < 1 then return false end
local basalt = {}
local magma = {}
for i=1,pr:next(1,#nn) do
if minetest.get_node(vector.offset(nn[i],0,-1,0)).name ~= "air" then
local dst=vector.distance(pos,nn[i])
local r = pr:next(1,14)-dst
for ii=0,r do
if pr:next(1,25) == 1 then
table.insert(magma,vector.new(nn[i].x,nn[i].y + ii,nn[i].z))
else
table.insert(basalt,vector.new(nn[i].x,nn[i].y + ii,nn[i].z))
end
end
end
end
minetest.bulk_swap_node(magma,{name="mcl_nether:magma"})
minetest.bulk_swap_node(basalt,{name="mcl_blackstone:basalt"})
return true
end
})
vl_structures.register_structure("basalt_pillar",{
place_on = {"mcl_blackstone:blackstone","mcl_blackstone:basalt"},
terrain_feature = true,
noise_params = {
offset = 0,
scale = 0.001,
spread = {x = 250, y = 250, z = 250},
seed = 7113,
octaves = 5,
persist = 0.1,
flags = "absvalue",
},
flags = "all_floors",
y_max = mcl_vars.mg_nether_max-40,
y_min = mcl_vars.mg_lava_nether_max + 1,
biomes = { "BasaltDelta" },
place_func = function(pos, _, pr)
local nn = minetest.find_nodes_in_area(vector.offset(pos,-2,-1,-2),vector.offset(pos,2,-1,2),{"air","mcl_blackstone:basalt","mcl_blackstone:blackstone"})
table.sort(nn,function(a, b)
return vector.distance(vector.new(pos.x,0,pos.z), a) < vector.distance(vector.new(pos.x,0,pos.z), b)
end)
if #nn < 1 then return false end
local basalt = {}
local magma = {}
for i=1,pr:next(1,#nn) do
if minetest.get_node(vector.offset(nn[i],0,-1,0)).name ~= "air" then
local dst=vector.distance(pos,nn[i])
for ii=0,pr:next(19,35)-dst do
if pr:next(1,20) == 1 then
table.insert(magma,vector.new(nn[i].x,nn[i].y + ii,nn[i].z))
else
table.insert(basalt,vector.new(nn[i].x,nn[i].y + ii,nn[i].z))
end
end
end
end
minetest.bulk_swap_node(basalt,{name="mcl_blackstone:basalt"})
minetest.bulk_swap_node(magma,{name="mcl_nether:magma"})
return true
end
})
vl_structures.register_structure("lavadelta",{
place_on = {"mcl_blackstone:blackstone","mcl_blackstone:basalt"},
spawn_by = {"mcl_blackstone:basalt","mcl_blackstone:blackstone"},
num_spawn_by = 2,
terrain_feature = true,
noise_params = {
offset = 0,
scale = 0.005,
spread = {x = 250, y = 250, z = 250},
seed = 78375213,
octaves = 5,
persist = 0.1,
flags = "absvalue",
},
flags = "all_floors",
y_max = mcl_vars.mg_nether_max,
y_min = mcl_vars.mg_lava_nether_max + 1,
biomes = { "BasaltDelta" },
place_func = function(pos, _, pr)
local nn = minetest.find_nodes_in_area_under_air(vector.offset(pos,-10,-1,-10),vector.offset(pos,10,-2,10),{"mcl_blackstone:basalt","mcl_blackstone:blackstone","mcl_nether:netherrack"})
table.sort(nn,function(a, b)
return vector.distance(vector.new(pos.x,0,pos.z), a) < vector.distance(vector.new(pos.x,0,pos.z), b)
end)
if #nn < 1 then return false end
local lava = {}
for i=1,pr:next(1,#nn) do
table.insert(lava,nn[i])
end
minetest.bulk_swap_node(lava,{name="mcl_nether:nether_lava_source"})
local basalt = {}
local magma = {}
for _,v in pairs(lava) do
for _,vv in pairs(adjacents) do
local p = vector.add(v,vv)
if minetest.get_node(p).name ~= "mcl_nether:nether_lava_source" then
table.insert(basalt,p)
end
end
if math.random(3) == 1 then
table.insert(magma,v)
end
end
minetest.bulk_swap_node(basalt,{name="mcl_blackstone:basalt"})
minetest.bulk_swap_node(magma,{name="mcl_nether:magma"})
return true
end
})

View File

@ -1,3 +0,0 @@
name = mcl_terrain_features
author = cora
depends = mcl_init, mcl_structures

View File

@ -0,0 +1,59 @@
-- boulders, in MegaTaiga and MegaSpruceTaiga
local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)
-- Mossy cobblestone boulder (3x3)
mcl_mapgen_core.register_decoration({
deco_type = "schematic",
place_on = {"mcl_core:podzol", "mcl_core:dirt", "mcl_core:coarse_dirt"},
terrain_feature = true,
sidelen = 80,
noise_params = {
offset = 0.00015,
scale = 0.001,
spread = vector.new(300, 300, 300),
seed = 775703,
octaves = 4,
persist = 0.63,
},
biomes = { "MegaTaiga", "MegaSpruceTaiga" },
y_min = 1,
y_max = vl_biomes.overworld_max,
schematic = modpath .. "/schematics/mcl_structures_boulder.mts",
flags = "place_center_x, place_center_z",
rotation = "random",
})
-- Small mossy cobblestone boulder (2x2)
mcl_mapgen_core.register_decoration({
deco_type = "schematic",
place_on = {"mcl_core:podzol", "mcl_core:dirt", "mcl_core:coarse_dirt"},
terrain_feature = true,
sidelen = 80,
noise_params = {
offset = 0.001,
scale = 0.001,
spread = vector.new(300, 300, 300),
seed = 775704,
octaves = 4,
persist = 0.63,
},
biomes = { "MegaTaiga", "MegaSpruceTaiga" },
y_min = 1,
y_max = vl_biomes.overworld_max,
schematic = modpath .. "/schematics/mcl_structures_boulder_small.mts",
flags = "place_center_x, place_center_z",
rotation = "random",
})
vl_structures.register_structure("boulder", {
-- as they have no place_on, they will not be spawned by this mechanism. this is just for /spawnstruct
filenames = {
-- small boulder 3x as likely
modpath.."/schematics/mcl_structures_boulder_small.mts",
modpath.."/schematics/mcl_structures_boulder_small.mts",
modpath.."/schematics/mcl_structures_boulder_small.mts",
modpath.."/schematics/mcl_structures_boulder.mts",
},
})

View File

@ -1,5 +1,7 @@
-- TODO: use priorities, and move this to the module where coral blocks are defined? -- TODO: move this to the mcl_ocean module?
local mod_mcl_structures = minetest.get_modpath("mcl_structures") local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)
local coral_min = vl_biomes.OCEAN_MIN local coral_min = vl_biomes.OCEAN_MIN
local coral_max = -10 local coral_max = -10
local warm_oceans = table.copy(vl_biomes.by_water_temp.warm) local warm_oceans = table.copy(vl_biomes.by_water_temp.warm)
@ -21,12 +23,13 @@ for _, c in ipairs({ "brain", "horn", "bubble", "tube", "fire" }) do
mcl_mapgen_core.register_decoration({ mcl_mapgen_core.register_decoration({
deco_type = "schematic", deco_type = "schematic",
place_on = {"group:sand", "mcl_core:gravel", "mcl_mud:mud"}, place_on = {"group:sand", "mcl_core:gravel", "mcl_mud:mud"},
terrain_feature = true,
sidelen = 80, sidelen = 80,
noise_params = noise, noise_params = noise,
biomes = warm_oceans, biomes = warm_oceans,
y_min = coral_min, y_min = coral_min,
y_max = coral_max, y_max = coral_max,
schematic = mod_mcl_structures .. "/schematics/mcl_structures_coral_" .. c .. "_1.mts", schematic = modpath .. "/schematics/mcl_structures_coral_" .. c .. "_1.mts",
rotation = "random", rotation = "random",
flags = "all_floors,force_placement", flags = "all_floors,force_placement",
spawn_by = "mcl_core:water_source", spawn_by = "mcl_core:water_source",
@ -36,12 +39,13 @@ for _, c in ipairs({ "brain", "horn", "bubble", "tube", "fire" }) do
mcl_mapgen_core.register_decoration({ mcl_mapgen_core.register_decoration({
deco_type = "schematic", deco_type = "schematic",
place_on = {"group:sand", "mcl_core:gravel", "mcl_mud:mud"}, place_on = {"group:sand", "mcl_core:gravel", "mcl_mud:mud"},
terrain_feature = true,
noise_params = noise, noise_params = noise,
sidelen = 80, sidelen = 80,
biomes = warm_oceans, biomes = warm_oceans,
y_min = coral_min, y_min = coral_min,
y_max = coral_max, y_max = coral_max,
schematic = mod_mcl_structures .. "/schematics/mcl_structures_coral_" .. c .. "_2.mts", schematic = modpath .. "/schematics/mcl_structures_coral_" .. c .. "_2.mts",
rotation = "random", rotation = "random",
flags = "all_floors,force_placement", flags = "all_floors,force_placement",
spawn_by = "mcl_core:water_source", spawn_by = "mcl_core:water_source",
@ -52,6 +56,7 @@ for _, c in ipairs({ "brain", "horn", "bubble", "tube", "fire" }) do
mcl_mapgen_core.register_decoration({ mcl_mapgen_core.register_decoration({
deco_type = "simple", deco_type = "simple",
place_on = {"mcl_ocean:" .. c .. "_coral_block"}, place_on = {"mcl_ocean:" .. c .. "_coral_block"},
terrain_feature = true,
sidelen = 16, sidelen = 16,
fill_ratio = 3, fill_ratio = 3,
y_min = coral_min, y_min = coral_min,
@ -69,6 +74,7 @@ end
mcl_mapgen_core.register_decoration({ mcl_mapgen_core.register_decoration({
deco_type = "simple", deco_type = "simple",
place_on = {"group:sand", "mcl_core:gravel", "mcl_mud:mud"}, place_on = {"group:sand", "mcl_core:gravel", "mcl_mud:mud"},
terrain_feature = true,
sidelen = 16, sidelen = 16,
noise_params = { noise_params = {
offset = -0.0085, offset = -0.0085,
@ -93,6 +99,7 @@ mcl_mapgen_core.register_decoration({
mcl_mapgen_core.register_decoration({ mcl_mapgen_core.register_decoration({
deco_type = "simple", deco_type = "simple",
place_on = {"mcl_ocean:dead_brain_coral_block"}, place_on = {"mcl_ocean:dead_brain_coral_block"},
terrain_feature = true,
sidelen = 16, sidelen = 16,
fill_ratio = 3, fill_ratio = 3,
y_min = coral_min, y_min = coral_min,
@ -107,6 +114,7 @@ mcl_mapgen_core.register_decoration({
mcl_mapgen_core.register_decoration({ mcl_mapgen_core.register_decoration({
deco_type = "simple", deco_type = "simple",
place_on = {"mcl_ocean:dead_brain_coral_block"}, place_on = {"mcl_ocean:dead_brain_coral_block"},
terrain_feature = true,
sidelen = 16, sidelen = 16,
fill_ratio = 3, fill_ratio = 3,
y_min = coral_min, y_min = coral_min,
@ -121,6 +129,7 @@ mcl_mapgen_core.register_decoration({
mcl_mapgen_core.register_decoration({ mcl_mapgen_core.register_decoration({
deco_type = "simple", deco_type = "simple",
place_on = {"mcl_ocean:dead_brain_coral_block"}, place_on = {"mcl_ocean:dead_brain_coral_block"},
terrain_feature = true,
sidelen = 16, sidelen = 16,
fill_ratio = 2, fill_ratio = 2,
y_min = coral_min, y_min = coral_min,
@ -135,6 +144,7 @@ mcl_mapgen_core.register_decoration({
mcl_mapgen_core.register_decoration({ mcl_mapgen_core.register_decoration({
deco_type = "simple", deco_type = "simple",
place_on = {"mcl_ocean:dead_brain_coral_block"}, place_on = {"mcl_ocean:dead_brain_coral_block"},
terrain_feature = true,
sidelen = 16, sidelen = 16,
fill_ratio = 2, fill_ratio = 2,
y_min = coral_min, y_min = coral_min,
@ -150,12 +160,13 @@ mcl_mapgen_core.register_decoration({
mcl_mapgen_core.register_decoration({ mcl_mapgen_core.register_decoration({
deco_type = "schematic", deco_type = "schematic",
place_on = {"group:sand", "mcl_core:gravel"}, place_on = {"group:sand", "mcl_core:gravel"},
terrain_feature = true,
fill_ratio = 0.0001, fill_ratio = 0.0001,
sidelen = 80, sidelen = 80,
biomes = warm_oceans, biomes = warm_oceans,
y_min = coral_min, y_min = coral_min,
y_max = coral_max, y_max = coral_max,
schematic = mod_mcl_structures .. "/schematics/coral_cora.mts", schematic = modpath .. "/schematics/coral_cora.mts",
rotation = "random", rotation = "random",
flags = "all_floors,force_placement", flags = "all_floors,force_placement",
spawn_by = "mcl_core:water_source", spawn_by = "mcl_core:water_source",

View File

@ -0,0 +1,100 @@
-- TODO: allow longer logs in MegaTaiga?
local mg_name = minetest.get_mapgen_setting("mg_name")
local mushrooms = {"mcl_mushrooms:mushroom_brown","mcl_mushrooms:mushroom_red"}
vl_structures.register_structure("fallen_tree",{
priority = 1100, -- after regular trees
place_on = {"group:grass_block"},
terrain_feature = true,
noise_params = {
offset = 0.00018,
scale = 0.01011,
spread = {x = 250, y = 250, z = 250},
seed = 24533,
octaves = 3,
persist = 0.66
},
flags = "place_center_x, place_center_z",
solid_ground = true,
y_max = mcl_vars.mg_overworld_max,
y_min = minetest.get_mapgen_setting("water_level"),
place_func = function(pos,def,pr)
local tree = minetest.find_node_near(pos,15,{"group:tree"})
if not tree then return end
tree = minetest.get_node(tree).name
local minlen, maxlen = 3, 9
local vrate, mrate = 120, 160
local len = pr:next(minlen,maxlen)
local dir = pr:next(0,3)
local dx, dy, dz, param2, w1, w2
if dir == 0 then
dx, dy, dz, param2, w1, w2 = 1, 0, 0, 12, 5, 4
elseif dir == 1 then
dx, dy, dz, param2, w1, w2 = -1, 0, 0, 12, 4, 5
elseif dir == 2 then
dx, dy, dz, param2, w1, w2 = 0, 0, 1, 6, 3, 2
else -- if dir == 3 then
dx, dy, dz, param2, w1, w2 = 0, 0, -1, 6, 2, 3
end
-- ensure we have room for the tree
local minsupport, maxsupport = 99, 1
for i = 1,len do
-- check below
local n = minetest.get_node(vector.offset(pos, dx * i, -1, dz * i)).name
local nd = minetest.registered_nodes[n]
if n ~= "air" and nd.groups and nd.groups.solid and i > 2 then
if i < minsupport then minsupport = i end
maxsupport = i
end
-- check space
local n = minetest.get_node(vector.offset(pos, dx * i, 0, dz * i)).name
local nd = minetest.registered_nodes[n]
if n ~= "air" and nd.groups and not nd.groups.plant then
if i < minlen or pr:next(1, maxsupport) == 1 then return end
len = i
break
end
end
if maxsupport - minsupport < minlen then return end
-- get the foliage palette for vines:
local biome = mg_name ~= "v6" and minetest.registered_biomes[minetest.get_biome_name(minetest.get_biome_data(pos).biome)]
if biome and biome._mcl_foliage_palette_index then
w1 = biome._mcl_foliage_palette_index * 8 + w1
w2 = biome._mcl_foliage_palette_index * 8 + w2
end
len = math.min(len, maxsupport - 1)
if len < minlen then return end
-- place the upright tree trunk
minetest.swap_node(pos, { name = tree, param2 = 0 })
-- some are hollow:
if vl_hollow_logs.logs and pr:next(1,20) == 1 then
local nam = string.sub(tree, string.find(tree, ":") + 1)
nam = "vl_hollow_logs:"..nam.."_hollow"
if minetest.registered_nodes[nam] then tree = nam end
end
for i = 2,len do
minetest.swap_node(vector.offset(pos, dx * i, 0, dz * i), { name = tree, param2 = param2 })
-- add some vines
if pr:next(0,255) < vrate then
local side = vector.offset(pos, dx * i + dz, 0, dz * i + dx)
if minetest.get_node(side).name == "air" then
minetest.swap_node(side, { name = "mcl_core:vine", param2 = w1 })
end
end
if pr:next(0,255) < vrate then
local side = vector.offset(pos, dx * i - dz, 0, dz * i - dx)
if minetest.get_node(side).name == "air" then
minetest.swap_node(side, { name = "mcl_core:vine", param2 = w2 })
end
end
-- add some mushrooms
if pr:next(0,255) < mrate then
local top = vector.offset(pos, dx * i, 1, dz * i)
if minetest.get_node(top).name == "air" then
minetest.swap_node(top, { name = mushrooms[pr:next(1,#mushrooms)], param2 = 12 })
end
end
end
end
})

View File

@ -1,10 +1,12 @@
local modname = minetest.get_current_modname() local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname) local modpath = minetest.get_modpath(modname)
vl_structures.register_structure("fossil",{ vl_structures.register_structure("fossil", {
place_on = {"group:material_stone","group:sand"}, place_on = { "group:material_stone", "group:sand" },
flags = "place_center_x, place_center_z", flags = "place_center_x, place_center_z",
prepare = false, prepare = false,
priority = 900, -- actually a terrain feature,
terrain_feature = false, -- but add them to /locate nevertheless
chunk_probability = 15, -- was 25, FIXME: needs rebalancing chunk_probability = 15, -- was 25, FIXME: needs rebalancing
y_offset = function(pr) return pr:next(-32,-16) end, y_offset = function(pr) return pr:next(-32,-16) end,
y_max = 15, y_max = 15,

View File

@ -75,12 +75,12 @@ local function makegeode(pos, _, pr)
return true return true
end end
vl_structures.register_structure("geode",{ vl_structures.register_structure("geode", {
place_on = {"group:material_stone"}, place_on = { "group:material_stone" },
noise_params = { noise_params = {
offset = 0, offset = 0,
scale = 0.00022, scale = 0.00022,
spread = {x = 250, y = 250, z = 250}, spread = vector.new(250, 250, 250),
seed = 7894353, seed = 7894353,
octaves = 3, octaves = 3,
persist = 0.001, persist = 0.001,

View File

@ -0,0 +1,142 @@
-- TODO: overall, these pools tend to be very circular, can we make them more interesting?
-- TODO: use the terraforming from vl_terraforming instead of the airtower?
local mg_name = minetest.get_mapgen_setting("mg_name")
local adjacents = {
vector.new(1,0,0),
vector.new(1,0,1),
vector.new(1,0,-1),
vector.new(-1,0,0),
vector.new(-1,0,1),
vector.new(-1,0,-1),
vector.new(0,0,1),
vector.new(0,0,-1),
vector.new(0,-1,0)
}
local function makelake(pos, size, liquid, placein, border, pr, noair)
local p1, p2 = vector.offset(pos,-size,-1,-size), vector.offset(pos,size,-1,size)
local e1, e2 = vector.offset(pos,-size,-2,-size), vector.offset(pos,size,15,size)
minetest.emerge_area(e1, e2, function(_, _, calls_remaining)
if calls_remaining ~= 0 then return end
local nn = minetest.find_nodes_in_area(p1, p2, placein)
if not nn[1] then return end
table.sort(nn, function(a, b)
return vector.distance(pos, a) < vector.distance(pos, b)
end)
local y = pos.y - 1
local lq, air = {}, {}
local r = pr:next(1,#nn)
for i=1,r do
for j = 1, 20 do
table.insert(air, vector.offset(nn[i], 0, j, 0))
end
table.insert(lq, nn[i])
end
minetest.bulk_swap_node(lq, { name = liquid })
minetest.bulk_swap_node(air, { name = "air" })
air = {}
local br = {}
for k,v in pairs(lq) do
for kk,vv in pairs(adjacents) do
local pp = vector.add(v,vv)
local an = minetest.get_node(pp)
-- if not border and minetest.get_item_group(an.name, "solid") > 0 then border = an end
if not noair and an.name ~= liquid then
table.insert(br,pp)
end
end
end
--[[ unused:
if not border then
if minetest.get_item_group(minetest.get_node(nn[1]).name, "solid") > 0 then
border = minetest.get_node_or_nil(nn[1])
else
border = { name = "mcl_core:stone" }
end
end
if border == nil or border.name == "mcl_core:dirt" then
local biome = mg_name ~= "v6" and minetest.registered_biomes[minetest.get_biome_name(minetest.get_biome_data(nn[1]).biome)]
local p2 = biome and biome._mcl_grass_palette_index and biome._mcl_grass_palette_index or nil
border = { name = "mcl_core:dirt_with_grass", param2 = p2 }
end ]]
if border.name == "mcl_core:dirt_with_grass" and not border.param2 then
local biome = mg_name ~= "v6" and minetest.registered_biomes[minetest.get_biome_name(minetest.get_biome_data(nn[1]).biome)]
local p2 = biome and biome._mcl_grass_palette_index and biome._mcl_grass_palette_index or nil
border = { name = "mcl_core:dirt_with_grass", param2 = p2 }
end
minetest.bulk_swap_node(br, border)
minetest.bulk_swap_node(air, { name = "air" })
return true
end)
return true
end
vl_structures.register_structure("lavapool", {
place_on = { "group:sand", "group:dirt", "group:stone" },
terrain_feature = true,
noise_params = {
offset = 0,
scale = 0.0000022,
spread = vector.new(250, 250, 250),
seed = 78375213,
octaves = 3,
persist = 0.001,
flags = "absvalue",
},
flags = "place_center_x, place_center_z, all_floors",
y_max = mcl_vars.mg_overworld_max,
y_min = minetest.get_mapgen_setting("water_level"),
place_func = function(pos, _, pr)
return makelake(pos, 5, "mcl_core:lava_source",
{ "group:material_stone", "group:sand", "group:dirt" },
{ name = "mcl_core:stone" }, pr)
end
})
vl_structures.register_structure("water_lake", {
place_on = { "group:dirt", "group:stone" },
terrain_feature = true,
noise_params = {
offset = 0,
scale = 0.000032,
spread = vector.new(250, 250, 250),
seed = 756641353,
octaves = 3,
persist = 0.001,
flags = "absvalue",
},
flags = "place_center_x, place_center_z, all_floors",
y_max = mcl_vars.mg_overworld_max,
y_min = minetest.get_mapgen_setting("water_level"),
place_func = function(pos, _, pr)
return makelake(pos, 5, "mcl_core:water_source",
{ "group:material_stone", "group:sand", "group:dirt", "group:grass_block"},
{ name = "mcl_core:dirt_with_grass" }, pr)
end
})
vl_structures.register_structure("water_lake_mangrove_swamp", {
place_on = { "mcl_mud:mud" },
biomes = { "MangroveSwamp" },
terrain_feature = true,
noise_params = {
offset = 0,
scale = 0.0032,
spread = vector.new(250, 250, 250),
seed = 6343241353,
octaves = 3,
persist = 0.001,
flags = "absvalue",
},
flags = "place_center_x, place_center_z, all_floors",
y_max = mcl_vars.mg_overworld_max,
y_min = minetest.get_mapgen_setting("water_level"),
place_func = function(pos, _, pr)
return makelake(pos, 3, "mcl_core:water_source",
{ "group:material_stone", "group:sand", "group:dirt", "group:grass_block", "mcl_mud:mud"},
{ name = "mcl_mud:mud" }, pr, true)
end
})

View File

@ -324,11 +324,16 @@ elseif mg_name ~= "v6" then
-- Additional decorations -- Additional decorations
dofile(modpath.."/deco/bamboo.lua") dofile(modpath.."/deco/bamboo.lua")
dofile(modpath.."/deco/boulder.lua")
dofile(modpath.."/deco/cacti.lua") dofile(modpath.."/deco/cacti.lua")
dofile(modpath.."/deco/corals.lua") dofile(modpath.."/deco/corals.lua")
dofile(modpath.."/deco/fallentree.lua")
dofile(modpath.."/deco/fern.lua") dofile(modpath.."/deco/fern.lua")
dofile(modpath.."/deco/flowers.lua") dofile(modpath.."/deco/flowers.lua")
dofile(modpath.."/deco/fossil.lua")
dofile(modpath.."/deco/geode.lua")
dofile(modpath.."/deco/kelp.lua") dofile(modpath.."/deco/kelp.lua")
dofile(modpath.."/deco/lakes.lua")
dofile(modpath.."/deco/mushrooms.lua") dofile(modpath.."/deco/mushrooms.lua")
dofile(modpath.."/deco/pumpkin.lua") dofile(modpath.."/deco/pumpkin.lua")
dofile(modpath.."/deco/reeds.lua") dofile(modpath.."/deco/reeds.lua")

View File

@ -133,3 +133,146 @@ mcl_mapgen_core.register_decoration({
y_max = vl_biomes.nether_max - 5, y_max = vl_biomes.nether_max - 5,
flags = "all_floors, force_placement", flags = "all_floors, force_placement",
}) })
local adjacents = {
vector.new(1,0,0),
vector.new(1,0,1),
vector.new(1,0,-1),
vector.new(-1,0,0),
vector.new(-1,0,1),
vector.new(-1,0,-1),
vector.new(0,0,1),
vector.new(0,0,-1),
vector.new(0,-1,0)
}
vl_structures.register_structure("basalt_column",{
place_on = { "mcl_blackstone:blackstone", "mcl_blackstone:basalt" },
terrain_feature = true,
spawn_by = { "air" },
num_spawn_by = 2,
noise_params = {
offset = 0,
scale = 0.003,
spread = vector.new(250, 250, 250),
seed = 72235213,
octaves = 5,
persist = 0.3,
flags = "absvalue",
},
flags = "all_floors",
y_max = mcl_vars.mg_nether_max - 20,
y_min = mcl_vars.mg_lava_nether_max + 1,
biomes = { "BasaltDelta" },
place_func = function(pos, _, pr)
local nn = minetest.find_nodes_in_area(vector.offset(pos,-5,-1,-5), vector.offset(pos,5,-1,5),
{ "air", "mcl_blackstone:basalt", "mcl_blackstone:blackstone" })
table.sort(nn, function(a, b)
return vector.distance(pos, a) < vector.distance(pos, b)
end)
if #nn < 1 then return false end
local basalt, magma = {}, {}
for i = 1, pr:next(1,#nn) do
if minetest.get_node(vector.offset(nn[i], 0, -1, 0)).name ~= "air" then
local dst = vector.distance(pos, nn[i])
for ii = 0, pr:next(1,14) - dst do
if pr:next(1,25) == 1 then
table.insert(magma, vector.offset(nn[i], 0, ii, 0))
else
table.insert(basalt, vector.offset(nn[i], 0, ii, 0))
end
end
end
end
minetest.bulk_swap_node(magma, { name = "mcl_nether:magma" })
minetest.bulk_swap_node(basalt, { name = "mcl_blackstone:basalt" })
return true
end
})
vl_structures.register_structure("basalt_pillar",{
place_on = { "mcl_blackstone:blackstone", "mcl_blackstone:basalt" },
terrain_feature = true,
noise_params = {
offset = 0,
scale = 0.001,
spread = vector.new(250, 250, 250),
seed = 7113,
octaves = 5,
persist = 0.1,
flags = "absvalue",
},
flags = "all_floors",
y_max = mcl_vars.mg_nether_max - 40,
y_min = mcl_vars.mg_lava_nether_max + 1,
biomes = { "BasaltDelta" },
place_func = function(pos, _, pr)
local nn = minetest.find_nodes_in_area(vector.offset(pos,-2,-1,-2), vector.offset(pos,2,-1,2),
{ "air", "mcl_blackstone:basalt", "mcl_blackstone:blackstone" })
table.sort(nn, function(a, b)
return vector.distance(pos, a) < vector.distance(pos, b)
end)
if #nn < 1 then return false end
local basalt, magma = {}, {}
for i = 1, pr:next(1,#nn) do
if minetest.get_node(vector.offset(nn[i], 0, -1, 0)).name ~= "air" then
local dst = vector.distance(pos, nn[i])
for ii = 0, pr:next(19,35) - dst do
if pr:next(1,20) == 1 then
table.insert(magma, vector.offset(nn[i], 0, ii, 0))
else
table.insert(basalt, vector.offset(nn[i], 0, ii, 0))
end
end
end
end
minetest.bulk_swap_node(basalt, { name = "mcl_blackstone:basalt" })
minetest.bulk_swap_node(magma, { name = "mcl_nether:magma" })
return true
end
})
vl_structures.register_structure("lavadelta",{
place_on = { "mcl_blackstone:blackstone", "mcl_blackstone:basalt" },
spawn_by = { "mcl_blackstone:basalt", "mcl_blackstone:blackstone" },
num_spawn_by = 2,
terrain_feature = true,
noise_params = {
offset = 0,
scale = 0.005,
spread = vector.new(250, 250, 250),
seed = 78375213,
octaves = 5,
persist = 0.1,
flags = "absvalue",
},
flags = "all_floors",
y_max = mcl_vars.mg_nether_max,
y_min = mcl_vars.mg_lava_nether_max + 1,
biomes = { "BasaltDelta" },
place_func = function(pos, _, pr)
local nn = minetest.find_nodes_in_area_under_air(vector.offset(pos,-10,-1,-10), vector.offset(pos,10,-2,10),
{ "mcl_blackstone:basalt", "mcl_blackstone:blackstone", "mcl_nether:netherrack" })
table.sort(nn, function(a, b)
return vector.distance(pos, a) < vector.distance(pos, b)
end)
if #nn < 1 then return false end
local lava = {}
for i=1, pr:next(1,#nn) do table.insert(lava,nn[i]) end
minetest.bulk_swap_node(lava, { name = "mcl_nether:nether_lava_source" })
local basalt, magma = {}, {}
for _, v in pairs(lava) do
for _, vv in pairs(adjacents) do
local p = vector.add(v, vv)
if minetest.get_node(p).name ~= "mcl_nether:nether_lava_source" then
table.insert(basalt,p)
end
end
if math.random(3) == 1 then
table.insert(magma,v)
end
end
minetest.bulk_swap_node(basalt, { name = "mcl_blackstone:basalt" })
minetest.bulk_swap_node(magma, { name = "mcl_nether:magma" })
return true
end
})

View File

@ -1,4 +1,5 @@
local mod_mcl_structures = minetest.get_modpath("mcl_structures") local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)
-- Ice Plains Spikes (rare) aka Ice Spikes -- Ice Plains Spikes (rare) aka Ice Spikes
vl_biomes.register_biome({ vl_biomes.register_biome({
@ -36,6 +37,7 @@ vl_biomes.register_biome({
mcl_mapgen_core.register_decoration({ mcl_mapgen_core.register_decoration({
deco_type = "schematic", deco_type = "schematic",
place_on = {"mcl_core:snowblock", "mcl_core:snow", "group:grass_block_snow"}, place_on = {"mcl_core:snowblock", "mcl_core:snow", "group:grass_block_snow"},
terrain_feature = true,
sidelen = 80, sidelen = 80,
noise_params = { noise_params = {
offset = 0.00040, offset = 0.00040,
@ -48,7 +50,7 @@ mcl_mapgen_core.register_decoration({
biomes = {"IcePlainsSpikes"}, biomes = {"IcePlainsSpikes"},
y_min = 4, y_min = 4,
y_max = vl_biomes.overworld_max, y_max = vl_biomes.overworld_max,
schematic = mod_mcl_structures .. "/schematics/mcl_structures_ice_spike_large.mts", schematic = modpath .. "/schematics/mcl_structures_ice_spike_large.mts",
rotation = "random", rotation = "random",
flags = "place_center_x, place_center_z", flags = "place_center_x, place_center_z",
}) })
@ -57,6 +59,7 @@ mcl_mapgen_core.register_decoration({
mcl_mapgen_core.register_decoration({ mcl_mapgen_core.register_decoration({
deco_type = "schematic", deco_type = "schematic",
place_on = {"mcl_core:snowblock", "mcl_core:snow", "group:grass_block_snow"}, place_on = {"mcl_core:snowblock", "mcl_core:snow", "group:grass_block_snow"},
terrain_feature = true,
sidelen = 80, sidelen = 80,
noise_params = { noise_params = {
offset = 0.005, offset = 0.005,
@ -69,7 +72,18 @@ mcl_mapgen_core.register_decoration({
biomes = {"IcePlainsSpikes"}, biomes = {"IcePlainsSpikes"},
y_min = 4, y_min = 4,
y_max = vl_biomes.overworld_max, y_max = vl_biomes.overworld_max,
schematic = mod_mcl_structures .. "/schematics/mcl_structures_ice_spike_small.mts", schematic = modpath .. "/schematics/mcl_structures_ice_spike_small.mts",
rotation = "random", rotation = "random",
flags = "place_center_x, place_center_z", flags = "place_center_x, place_center_z",
}) })
vl_structures.register_structure("ice_spike_small", {
-- as they have no place_on, they will not be spawned by this mechanism. this is just for /spawnstruct
filenames = { modpath.."/schematics/mcl_structures_ice_spike_small.mts" },
})
vl_structures.register_structure("ice_spike_large", {
-- as they have no place_on, they will not be spawned by this mechanism. this is just for /spawnstruct
filenames = { modpath.."/schematics/mcl_structures_ice_spike_large.mts" },
})

View File

@ -1,5 +1,3 @@
local mod_mcl_structures = minetest.get_modpath("mcl_structures")
-- Mega Pine Taiga aka Old Growth Pine Taiga -- Mega Pine Taiga aka Old Growth Pine Taiga
vl_biomes.register_biome({ vl_biomes.register_biome({
name = "MegaTaiga", name = "MegaTaiga",
@ -41,47 +39,6 @@ minetest.register_ore({
noise_params = {offset = 0, scale = 15, spread = vector.new(130, 130, 130), seed = 24, octaves = 3, persist = 0.70}, noise_params = {offset = 0, scale = 15, spread = vector.new(130, 130, 130), seed = 24, octaves = 3, persist = 0.70},
biomes = {"MegaTaiga"}, biomes = {"MegaTaiga"},
}) })
-- Mossy cobblestone boulder (3x3)
mcl_mapgen_core.register_decoration({
deco_type = "schematic",
place_on = {"mcl_core:podzol", "mcl_core:dirt", "mcl_core:coarse_dirt"},
sidelen = 80,
noise_params = {
offset = 0.00015,
scale = 0.001,
spread = vector.new(300, 300, 300),
seed = 775703,
octaves = 4,
persist = 0.63,
},
biomes = {"MegaTaiga"},
y_min = 1,
y_max = vl_biomes.overworld_max,
schematic = mod_mcl_structures .. "/schematics/mcl_structures_boulder.mts",
flags = "place_center_x, place_center_z",
rotation = "random",
})
-- Small mossy cobblestone boulder (2x2)
mcl_mapgen_core.register_decoration({
deco_type = "schematic",
place_on = {"mcl_core:podzol", "mcl_core:dirt", "mcl_core:coarse_dirt"},
sidelen = 80,
noise_params = {
offset = 0.001,
scale = 0.001,
spread = vector.new(300, 300, 300),
seed = 775704,
octaves = 4,
persist = 0.63,
},
biomes = {"MegaTaiga"},
y_min = 1,
y_max = vl_biomes.overworld_max,
schematic = mod_mcl_structures .. "/schematics/mcl_structures_boulder_small.mts",
flags = "place_center_x, place_center_z",
rotation = "random",
})
-- Huge spruce -- Huge spruce
vl_biomes.register_spruce_decoration(3000, 0.0008, "mcl_core_spruce_huge_up_1.mts", {"MegaTaiga"}) vl_biomes.register_spruce_decoration(3000, 0.0008, "mcl_core_spruce_huge_up_1.mts", {"MegaTaiga"})

View File

@ -1,5 +1,3 @@
local mod_mcl_structures = minetest.get_modpath("mcl_structures")
-- Mega Spruce Taiga aka Old Growth Spruce Taiga -- Mega Spruce Taiga aka Old Growth Spruce Taiga
vl_biomes.register_biome({ vl_biomes.register_biome({
name = "MegaSpruceTaiga", name = "MegaSpruceTaiga",
@ -26,47 +24,6 @@ vl_biomes.register_biome({
depth_filler = 3, depth_filler = 3,
}, },
}) })
-- Mossy cobblestone boulder (3x3)
mcl_mapgen_core.register_decoration({
deco_type = "schematic",
place_on = {"mcl_core:podzol", "mcl_core:dirt", "mcl_core:coarse_dirt"},
sidelen = 80,
noise_params = {
offset = 0.00015,
scale = 0.001,
spread = vector.new(300, 300, 300),
seed = 775703,
octaves = 4,
persist = 0.63,
},
biomes = {"MegaSpruceTaiga"},
y_min = 1,
y_max = vl_biomes.overworld_max,
schematic = mod_mcl_structures .. "/schematics/mcl_structures_boulder.mts",
flags = "place_center_x, place_center_z",
rotation = "random",
})
-- Small mossy cobblestone boulder (2x2)
mcl_mapgen_core.register_decoration({
deco_type = "schematic",
place_on = {"mcl_core:podzol", "mcl_core:dirt", "mcl_core:coarse_dirt"},
sidelen = 80,
noise_params = {
offset = 0.001,
scale = 0.001,
spread = vector.new(300, 300, 300),
seed = 775704,
octaves = 4,
persist = 0.63,
},
biomes = {"MegaSpruceTaiga"},
y_min = 1,
y_max = vl_biomes.overworld_max,
schematic = mod_mcl_structures .. "/schematics/mcl_structures_boulder_small.mts",
flags = "place_center_x, place_center_z",
rotation = "random",
})
-- Huge spruce -- Huge spruce
vl_biomes.register_spruce_decoration(3000, 0.0030, "mcl_core_spruce_huge_1.mts", {"MegaSpruceTaiga"}) vl_biomes.register_spruce_decoration(3000, 0.0030, "mcl_core_spruce_huge_1.mts", {"MegaSpruceTaiga"})

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

@ -6,31 +6,30 @@ local peaceful = minetest.settings:get_bool("only_peaceful_mobs", false)
local function spawn_witch(pos,def,pr,p1,p2) local function spawn_witch(pos,def,pr,p1,p2)
local c = minetest.find_node_near(p1,15,{"mcl_cauldrons:cauldron"}) local c = minetest.find_node_near(p1,15,{"mcl_cauldrons:cauldron"})
if c then if not c then return end
local nn = minetest.find_nodes_in_area_under_air(vector.new(p1.x,c.y-1,p1.z),vector.new(p2.x,c.y-1,p2.z),{"group:stone"}) local nn = minetest.find_nodes_in_area_under_air(vector.new(p1.x,c.y-1,p1.z),vector.new(p2.x,c.y-1,p2.z),{"group:stone"})
local witch if #nn == 0 then return end
if not peaceful then local witchobj = not peaceful and minetest.add_entity(vector.offset(nn[math.random(#nn)],0,1,0),"mobs_mc:witch")
witch = minetest.add_entity(vector.offset(nn[math.random(#nn)],0,1,0),"mobs_mc:witch"):get_luaentity() if witchobj then
witch._home = c local witch = witchobj:get_luaentity()
witch.can_despawn = false witch._home = c
end witch.can_despawn = false
local catobject = minetest.add_entity(vector.offset(nn[math.random(#nn)],0,1,0),"mobs_mc:cat") end
if catobject and catobject:get_pos() then local catobj = minetest.add_entity(vector.offset(nn[math.random(#nn)],0,1,0),"mobs_mc:cat")
local cat=catobject:get_luaentity() if catobj then
cat.object:set_properties({textures = {"mobs_mc_cat_black.png"}}) local cat = catobj:get_luaentity()
cat.owner = "!witch!" --so it's not claimable by player cat.object:set_properties({textures = {"mobs_mc_cat_black.png"}})
cat._home = c cat.owner = "!witch!" --so it's not claimable by player
cat.can_despawn = false cat._home = c
end cat.can_despawn = false
return
end end
end end
vl_structures.register_structure("witches_circle",{ vl_structures.register_structure("witches_circle",{
place_on = {"group:grass_block", "group:dirt", "mclx_core:river_water_source"}, place_on = {"group:grass_block", "group:dirt", "mclx_core:river_water_source"},
flags = "place_center_x, place_center_z, all_surfaces", flags = "place_center_x, place_center_z, all_surfaces",
chunk_probability = 14, chunk_probability = 20,
prepare = { tolerance=4, clear_bottom=1, clear_top=-1, padding=0, corners=3, foundation=-2 }, prepare = { tolerance = 3, clear_bottom = 1, clear_top = 0, padding = 0, corners = 1, foundation = -2 },
y_max = mcl_vars.mg_overworld_max, y_max = mcl_vars.mg_overworld_max,
y_min = 1, y_min = 1,
y_offset = -1, y_offset = -1,

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