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
local def = map[key]
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
local deco_id = minetest.register_decoration(def)
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
if def.name then
deco_id = minetest.get_decoration_id(def.name)
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
if def.gen_callback then
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.."/end_city.lua")
dofile(modpath.."/end_spawn.lua")
dofile(modpath.."/fossil.lua")
dofile(modpath.."/geode.lua")
dofile(modpath.."/igloo.lua")
dofile(modpath.."/jungle_temple.lua")
dofile(modpath.."/ocean_ruins.lua")
@ -43,24 +41,3 @@ dofile(modpath.."/shipwrecks.lua")
dofile(modpath.."/witch_hut.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_offset = -1,
flags = "place_center_x, place_center_z, force_placement",
prepare = { foundation = -2, clear = false, mode="water" },
prepare = { foundation = -2, clear = false, surface = "water" },
filenames = {
modpath.."/schematics/mcl_structures_ocean_ruins_cold_1.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",
force_placement = true,
prepare = { tolerance = 8, clear = false, foundation = 3, mode="water" },
prepare = { tolerance = 8, clear = false, foundation = 3, surface = "water" },
biomes = ocean_biomes,
y_max = water_level-4,
y_min = mcl_vars.mg_overworld_min,

View File

@ -77,7 +77,7 @@ vl_structures.register_structure("shipwreck",{
y_max = water_level-5,
y_offset = function(pr) return pr:next(-3,-1) end,
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 = {
--schematics by chmodsayshello
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 c = minetest.find_node_near(p1,15,{"mcl_cauldrons:cauldron"})
if c then
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
if not peaceful then
witch = minetest.add_entity(vector.offset(nn[math.random(#nn)],0,1,0),"mobs_mc:witch"):get_luaentity()
witch._home = c
witch.can_despawn = false
end
local catobject = minetest.add_entity(vector.offset(nn[math.random(#nn)],0,1,0),"mobs_mc:cat")
if catobject and catobject:get_pos() then
local cat=catobject:get_luaentity()
cat.object:set_properties({textures = {"mobs_mc_cat_black.png"}})
cat.owner = "!witch!" --so it's not claimable by player
cat._home = c
cat.can_despawn = false
end
return
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 witchobj = not peaceful and minetest.add_entity(vector.offset(nn[math.random(#nn)],0,1,0),"mobs_mc:witch")
if witchobj then
local witch = witchobj:get_luaentity()
witch._home = c
witch.can_despawn = false
end
local catobj = minetest.add_entity(vector.offset(nn[math.random(#nn)],0,1,0),"mobs_mc:cat")
if catobj then
local cat=catobj:get_luaentity()
cat.object:set_properties({textures = {"mobs_mc_cat_black.png"}})
cat.owner = "!witch!" --so it's not claimable by player
cat._home = c
cat.can_despawn = false
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
local legs = minetest.find_nodes_in_area(p1,vector.new(p2.x,p1.y,p2.z), "mcl_core:tree")
local tree = {}
-- TODO: port leg generation to VoxelManip?
for _,leg in pairs(legs) do
while true do
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,
flags = "place_center_x, place_center_z, all_surfaces",
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_min = -5,
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?
local mod_mcl_structures = minetest.get_modpath("mcl_structures")
-- TODO: move this to the mcl_ocean module?
local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)
local coral_min = vl_biomes.OCEAN_MIN
local coral_max = -10
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({
deco_type = "schematic",
place_on = {"group:sand", "mcl_core:gravel", "mcl_mud:mud"},
terrain_feature = true,
sidelen = 80,
noise_params = noise,
biomes = warm_oceans,
y_min = coral_min,
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",
flags = "all_floors,force_placement",
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({
deco_type = "schematic",
place_on = {"group:sand", "mcl_core:gravel", "mcl_mud:mud"},
terrain_feature = true,
noise_params = noise,
sidelen = 80,
biomes = warm_oceans,
y_min = coral_min,
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",
flags = "all_floors,force_placement",
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({
deco_type = "simple",
place_on = {"mcl_ocean:" .. c .. "_coral_block"},
terrain_feature = true,
sidelen = 16,
fill_ratio = 3,
y_min = coral_min,
@ -69,6 +74,7 @@ end
mcl_mapgen_core.register_decoration({
deco_type = "simple",
place_on = {"group:sand", "mcl_core:gravel", "mcl_mud:mud"},
terrain_feature = true,
sidelen = 16,
noise_params = {
offset = -0.0085,
@ -93,6 +99,7 @@ mcl_mapgen_core.register_decoration({
mcl_mapgen_core.register_decoration({
deco_type = "simple",
place_on = {"mcl_ocean:dead_brain_coral_block"},
terrain_feature = true,
sidelen = 16,
fill_ratio = 3,
y_min = coral_min,
@ -107,6 +114,7 @@ mcl_mapgen_core.register_decoration({
mcl_mapgen_core.register_decoration({
deco_type = "simple",
place_on = {"mcl_ocean:dead_brain_coral_block"},
terrain_feature = true,
sidelen = 16,
fill_ratio = 3,
y_min = coral_min,
@ -121,6 +129,7 @@ mcl_mapgen_core.register_decoration({
mcl_mapgen_core.register_decoration({
deco_type = "simple",
place_on = {"mcl_ocean:dead_brain_coral_block"},
terrain_feature = true,
sidelen = 16,
fill_ratio = 2,
y_min = coral_min,
@ -135,6 +144,7 @@ mcl_mapgen_core.register_decoration({
mcl_mapgen_core.register_decoration({
deco_type = "simple",
place_on = {"mcl_ocean:dead_brain_coral_block"},
terrain_feature = true,
sidelen = 16,
fill_ratio = 2,
y_min = coral_min,
@ -150,12 +160,13 @@ mcl_mapgen_core.register_decoration({
mcl_mapgen_core.register_decoration({
deco_type = "schematic",
place_on = {"group:sand", "mcl_core:gravel"},
terrain_feature = true,
fill_ratio = 0.0001,
sidelen = 80,
biomes = warm_oceans,
y_min = coral_min,
y_max = coral_max,
schematic = mod_mcl_structures .. "/schematics/coral_cora.mts",
schematic = modpath .. "/schematics/coral_cora.mts",
rotation = "random",
flags = "all_floors,force_placement",
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 modpath = minetest.get_modpath(modname)
vl_structures.register_structure("fossil",{
place_on = {"group:material_stone","group:sand"},
vl_structures.register_structure("fossil", {
place_on = { "group:material_stone", "group:sand" },
flags = "place_center_x, place_center_z",
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
y_offset = function(pr) return pr:next(-32,-16) end,
y_max = 15,

View File

@ -75,12 +75,12 @@ local function makegeode(pos, _, pr)
return true
end
vl_structures.register_structure("geode",{
place_on = {"group:material_stone"},
vl_structures.register_structure("geode", {
place_on = { "group:material_stone" },
noise_params = {
offset = 0,
scale = 0.00022,
spread = {x = 250, y = 250, z = 250},
spread = vector.new(250, 250, 250),
seed = 7894353,
octaves = 3,
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
dofile(modpath.."/deco/bamboo.lua")
dofile(modpath.."/deco/boulder.lua")
dofile(modpath.."/deco/cacti.lua")
dofile(modpath.."/deco/corals.lua")
dofile(modpath.."/deco/fallentree.lua")
dofile(modpath.."/deco/fern.lua")
dofile(modpath.."/deco/flowers.lua")
dofile(modpath.."/deco/fossil.lua")
dofile(modpath.."/deco/geode.lua")
dofile(modpath.."/deco/kelp.lua")
dofile(modpath.."/deco/lakes.lua")
dofile(modpath.."/deco/mushrooms.lua")
dofile(modpath.."/deco/pumpkin.lua")
dofile(modpath.."/deco/reeds.lua")

View File

@ -133,3 +133,146 @@ mcl_mapgen_core.register_decoration({
y_max = vl_biomes.nether_max - 5,
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
vl_biomes.register_biome({
@ -36,6 +37,7 @@ vl_biomes.register_biome({
mcl_mapgen_core.register_decoration({
deco_type = "schematic",
place_on = {"mcl_core:snowblock", "mcl_core:snow", "group:grass_block_snow"},
terrain_feature = true,
sidelen = 80,
noise_params = {
offset = 0.00040,
@ -48,7 +50,7 @@ mcl_mapgen_core.register_decoration({
biomes = {"IcePlainsSpikes"},
y_min = 4,
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",
flags = "place_center_x, place_center_z",
})
@ -57,6 +59,7 @@ mcl_mapgen_core.register_decoration({
mcl_mapgen_core.register_decoration({
deco_type = "schematic",
place_on = {"mcl_core:snowblock", "mcl_core:snow", "group:grass_block_snow"},
terrain_feature = true,
sidelen = 80,
noise_params = {
offset = 0.005,
@ -69,7 +72,18 @@ mcl_mapgen_core.register_decoration({
biomes = {"IcePlainsSpikes"},
y_min = 4,
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",
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
vl_biomes.register_biome({
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},
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
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
vl_biomes.register_biome({
name = "MegaSpruceTaiga",
@ -26,47 +24,6 @@ vl_biomes.register_biome({
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
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_min = 1,
y_offset = -3,
prepare = { tolerance=3, padding = 0, clear=false },
prepare = { tolerance = 3, padding = 0, clear = false },
biomes = { "Desert" },
filenames = {
modpath.."/schematics/obelisk_sand_1.mts",
@ -23,7 +23,7 @@ vl_structures.register_structure("obelisk_light",{
y_max = mcl_vars.mg_overworld_max,
y_min = 1,
y_offset = -2,
prepare = { tolerance=2, padding = 0, clear=false },
prepare = { tolerance = 2, padding = 0, clear = false },
biomes = { "Desert" },
filenames = {
modpath.."/schematics/obelisk_fire.mts",
@ -42,7 +42,7 @@ vl_structures.register_structure("obelisk_cobble",{
y_max = mcl_vars.mg_overworld_max,
y_min = 1,
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", },
filenames = {
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 c = minetest.find_node_near(p1,15,{"mcl_cauldrons:cauldron"})
if c then
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 not peaceful then
witch = minetest.add_entity(vector.offset(nn[math.random(#nn)],0,1,0),"mobs_mc:witch"):get_luaentity()
witch._home = c
witch.can_despawn = false
end
local catobject = minetest.add_entity(vector.offset(nn[math.random(#nn)],0,1,0),"mobs_mc:cat")
if catobject and catobject:get_pos() then
local cat=catobject:get_luaentity()
cat.object:set_properties({textures = {"mobs_mc_cat_black.png"}})
cat.owner = "!witch!" --so it's not claimable by player
cat._home = c
cat.can_despawn = false
end
return
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"})
if #nn == 0 then return end
local witchobj = not peaceful and minetest.add_entity(vector.offset(nn[math.random(#nn)],0,1,0),"mobs_mc:witch")
if witchobj then
local witch = witchobj:get_luaentity()
witch._home = c
witch.can_despawn = false
end
local catobj = minetest.add_entity(vector.offset(nn[math.random(#nn)],0,1,0),"mobs_mc:cat")
if catobj then
local cat = catobj:get_luaentity()
cat.object:set_properties({textures = {"mobs_mc_cat_black.png"}})
cat.owner = "!witch!" --so it's not claimable by player
cat._home = c
cat.can_despawn = false
end
end
vl_structures.register_structure("witches_circle",{
place_on = {"group:grass_block", "group:dirt", "mclx_core:river_water_source"},
flags = "place_center_x, place_center_z, all_surfaces",
chunk_probability = 14,
prepare = { tolerance=4, clear_bottom=1, clear_top=-1, padding=0, corners=3, foundation=-2 },
chunk_probability = 20,
prepare = { tolerance = 3, clear_bottom = 1, clear_top = 0, padding = 0, corners = 1, foundation = -2 },
y_max = mcl_vars.mg_overworld_max,
y_min = 1,
y_offset = -1,

View File

@ -15,8 +15,8 @@ local function parse_prepare(prepare)
end
-- check "enabled" tolerances
local function tolerance_enabled(tolerance, mode)
return mode ~= "off" and tolerance and (tolerance == "max" or tolerance == "min" or tolerance >= 0) and true
local function tolerance_enabled(tolerance, surface, mode)
return tolerance ~= "off" and (tolerance or surface or mode) and true
end
--- 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
-- 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
pos, surface_mat = vl_terraforming.find_level(pos, size, prepare.tolerance, prepare.mode)
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.surface, prepare.mode)
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.")
return
end
pos.y = pos.y + 1 -- above surface
-- 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_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.
@ -9,6 +9,8 @@ This module provides the following key functionalities:
- build a baseplate for a building
- clear the area above a building
All methods have a `_vm` version to work with Lua Voxel Manipulators
## Rounded corners support
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$.
## 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.
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.
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.
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.
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,
where sy < 0, so that you can later place the structure at (px, py, pz).
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)`.
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.
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
- platform_mat below surface nodes
- stone_mat randomly used below platform_mat
- dust_mat on top of surface nodes (snow cover, optional)
- `surface_mat` for surface nodes
- `platform_mat` below surface nodes
- `stone_mat` randomly used below `platform_mat`
- `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
- [ ] make even more configurable
- [ ] add ceiling placement
- [ ] add an API that works on VM buffers
- [ ] add an API version working on the non-VM API
- [ ] benchmark if VM is actually faster than not using VM (5.9 has some optimizations not yet in VM)
- [ ] benchmark when VM is faster than not using VM (5.9 has some optimizations not yet in VM)
- [ ] improve tree removal

View File

@ -1,13 +1,17 @@
local AIR = {name = "air"}
local AIR = vl_terraforming._AIR
local abs = math.abs
local max = math.max
local floor = math.floor
local vector_new = vector.new
local is_solid_not_tree = vl_terraforming._is_solid_not_tree
local is_tree_not_leaves = vl_terraforming._is_tree_not_leaves
local get_node = core.get_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
--
-- 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
if xi >= px and xi < px+sx and zi >= pz and zi < pz+sz and dx2+dz2 <= 1 then
vec.y = py
if get_node(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
local n = get_node(vec)
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
for yi = py+1,min_clear do -- full height for inner area
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
elseif dx21+dz21 <= 1 then
-- widen the cave above by 1, to make easier to enter for mobs
-- todo: make configurable?
vec.y = py + 1
local name = get_node(vec).name
if name ~= "mcl_core:bedrock" then
if not immutable(get_node(vec)) then
local mat = AIR
if dust_mat then
vec.y = py
@ -73,7 +76,7 @@ function vl_terraforming.clearance(px, py, pz, sx, sy, sz, corners, surface_mat,
end
for yi = py+2,min_clear-1 do
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
local p = (yi-py) / (max_clear-py)
--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)
if dust_mat and yi == py then
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
else
if n and n.name ~= surface_mat.name and is_solid_not_tree(n) then
swap_node(vec, surface_mat)
if dust_mat then
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
break
@ -118,12 +121,12 @@ function vl_terraforming.clearance(px, py, pz, sx, sy, sz, corners, surface_mat,
if py+4 < sy then
for yi = py+2,py+4 do
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
for yi = py+1,py-1,-1 do
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)
else
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?
if dx22+dy2+dz22 <= 1 then
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
if name == "air" or name == "ignore" or name == "mcl_core:bedrock" or name == "mcl_villages:no_paths" then goto continue end
local meta = minetest.registered_items[name]
local groups = meta and meta.groups
local is_tree = groups.leaves or groups.tree or (groups.compostability or 0 > 50)
if is_air(nod) or immutable(nod) then goto continue end
local is_tree = is_tree_or_leaves(nod)
if keep_trees and is_tree then goto continue end
vec.y = yi-1
-- do not clear above solid
local name_below = get_node(vec).name
if name_below ~= "air" and name_below ~= "ignore" and name_below ~= "mcl_core:bedrock" then goto continue end
local nod_below = get_node(vec)
if not is_air(nod_below) and not immutable(nod_below) then goto continue end
-- try to completely remove trees overhead
-- stop randomly depending on fill, to narrow down the caves
if not keep_trees and not is_tree and (pr:next(0,1e9)/1e9)^0.5 > 1-(dx22+dy2+dz22-0.1) then goto continue end

View File

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

View File

@ -1,12 +1,14 @@
local abs = math.abs
local max = math.max
local vector_new = vector.new
local is_solid_not_tree = vl_terraforming._is_solid_not_tree
local make_solid = vl_terraforming._make_solid
local get_node = core.get_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
-- @param xi number: x 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?
if (pr:next(0,1e9)/1e9)^2 > c/9.1 then return false end
pos.x, pos.y, pos.z = xi, yi, zi
if get_node(pos).name == "mcl_core:bedrock" then return false end
if immutable(get_node(pos)) then return false end
swap_node(pos, platform_mat)
return true
end
@ -77,11 +79,11 @@ function vl_terraforming.foundation(px, py, pz, sx, sy, sz, corners, surface_mat
pos.z = zi
if xi >= px and xi < px+sx and zi >= pz and zi < pz+sz and dx2+dz2 <= 1 then
pos.y = py
if get_node(pos).name ~= "mcl_core:bedrock" then
if not immutable(get_node(pos)) then
swap_node(pos, surface_mat)
if dust_mat then
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
pos.y = py - 1
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)
if dust_mat then
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

View File

@ -2,7 +2,9 @@ local abs = math.abs
local max = math.max
local vector_new = vector.new
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_vm = vl_terraforming._make_solid_vm
--- 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 stone_mat Node: stone material node
local function grow_foundation_vm(vm,xi,yi,zi,pr,surface_mat,platform_mat,stone_mat)
local get_node_at = vm.get_node_at
local get_node = vm.get_node_at
local swap_node = vm.set_node_at
local pos, n, c = vector_new(xi,yi,zi), nil, 0
if is_solid_not_tree(get_node_at(vm, pos)) then return false end -- already solid, nothing to do
if is_solid_not_tree(get_node(vm, pos)) then return false end -- already solid, nothing to do
pos.y = pos.y + 1
local cur = get_node_at(vm, pos)
local cur = get_node(vm, pos)
if not is_solid_not_tree(cur) then return false end -- above is empty, do not fill below
if cur and cur.name and cur.name ~= surface_mat.name then platform_mat = cur end
if pr:next(1,4) == 1 then platform_mat = stone_mat end -- randomly switch to stone sometimes
@ -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 z = zi-1,zi+1 do
pos.x, pos.z = x, z
if is_solid_not_tree(get_node_at(vm, pos)) then c = c + 1 end
if is_solid_not_tree(get_node(vm, pos)) then c = c + 1 end
end
end
-- stop randomly depending on fill, to narrow down the foundation
-- TODO: allow controlling the random depth with an additional parameter?
if (pr:next(0,1e9)/1e9)^2 > c/9.1 then return false end
pos.x, pos.y, pos.z = xi, yi, zi
if get_node_at(vm, pos).name == "mcl_core:bedrock" then return false end
vm:set_node_at(pos, platform_mat)
if immutable(get_node(vm, pos)) then return false end
swap_node(vm, pos, platform_mat)
return true
end
--- 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
function vl_terraforming.foundation_vm(vm, px, py, pz, sx, sy, sz, corners, surface_mat, platform_mat, stone_mat, dust_mat, pr)
if sx <= 0 or sy >= 0 or sz <= 0 then return end
local get_node_at = vm.get_node_at
local set_node_at = vm.set_node_at
local get_node = vm.get_node_at
local swap_node = vm.set_node_at
corners = corners or 0
local wx2, wz2 = max(sx - corners, 1)^-2 * 2, max(sz - corners, 1)^-2 * 2
local cx, cz = px + sx * 0.5 - 0.5, pz + sz * 0.5 - 0.5
@ -80,11 +83,11 @@ function vl_terraforming.foundation_vm(vm, px, py, pz, sx, sy, sz, corners, surf
pos.z = zi
if xi >= px and xi < px+sx and zi >= pz and zi < pz+sz and dx2+dz2 <= 1 then
pos.y = py
if get_node_at(vm, pos).name ~= "mcl_core:bedrock" then
set_node_at(vm, pos, surface_mat)
if not immutable(get_node(vm, pos)) then
swap_node(vm, pos, surface_mat)
if dust_mat then
pos.y = py + 1
if get_node_at(vm, pos).name == "air" then set_node_at(vm, pos, dust_mat) end
if is_air(get_node(vm, pos)) then swap_node(vm, pos, dust_mat) end
end
pos.y = py - 1
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)
if dust_mat then
pos.y = py
if get_node_at(vm, pos).name == "air" then set_node_at(vm, pos, dust_mat) end
if is_air(get_node(vm, pos)) then swap_node(vm, pos, dust_mat) 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
-- @param cpos vector: center
-- @param size vector: area size
-- @param tolerance number or string: maximum height difference allowed, default 8.
-- @param mode string: "solid" (default), "liquid_surface", "under_air"
-- @param tolerance number or string: maximum height difference allowed, default 8,
-- @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)
function vl_terraforming.find_level(cpos, size, tolerance, mode)
function vl_terraforming.find_level(cpos, size, tolerance, surface, mode)
local _find_ground = find_ground
if mode == "liquid_surface" or mode == "liquid" then _find_ground = find_liquid_surface end
if mode == "under_water" or mode == "water" then _find_ground = find_under_water_surface end
if mode == "under_air" then _find_ground = find_under_air end
if surface == "liquid_surface" or surface == "liquid" then _find_ground = find_liquid_surface end
if surface == "under_water" or surface == "water" then _find_ground = find_under_water_surface end
if surface == "under_air" then _find_ground = find_under_air end
-- begin at center, then top-left and clockwise
local pos, surface_material = _find_ground(cpos)
if not pos then
-- minetest.log("action", "[vl_terraforming] no ground at starting position "..minetest.pos_to_string(cpos).." mode "..tostring(mode or "default"))
-- minetest.log("action", "[vl_terraforming] no ground at starting position "..minetest.pos_to_string(cpos).." surface "..tostring(surface or "default"))
return nil, nil
end
local ys = { pos.y }
@ -239,21 +240,19 @@ function vl_terraforming.find_level(cpos, size, tolerance, mode)
table.sort(ys)
tolerance = tolerance or 8
if tolerance == "min" then
cpos.y = ys[1] + 1
return cpos, surface_material
end
if tolerance == "max" then
cpos.y = ys[#ys] + 1
return cpos, surface_material
end
-- well supported base, not too uneven?
if #ys < 5 or min(ys[#ys-1]-ys[1], ys[#ys]-ys[2]) > tolerance then
-- minetest.log("action", "[vl_terraforming] ground too uneven: "..#ys.." positions: "..({dump(ys):gsub("[\n\t ]+", " ")})[1]
-- .." tolerance "..tostring(#ys > 2 and min(ys[#ys-1]-ys[1], ys[#ys]-ys[2])).." > "..tolerance)
return nil, nil
end
cpos.y = floor(0.5 * (ys[floor(1 + (#ys - 1) * 0.5)] + ys[ceil(1 + (#ys - 1) * 0.5)]) + 1) -- median except for largest, rounded, over surface
return cpos, surface_material
if mode == "min" then
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

View File

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

View File

@ -1,6 +1,17 @@
local get_node = core.get_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
-- @param node string or Node: node or node name
-- @return true for air and ignore nodes
@ -13,31 +24,48 @@ end
-- @param node LUA node or node name
-- @return truthy when solid but not tree/decoration/fungi
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 == "mcl_nether:soul_sand" then return true end -- not "solid". Other exceptions we need?
if name == "mcl_nether:nether_wart_block" 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
-- is deco_block if name == "mcl_crimson:shroomlight" then return false end -- crimson forest, treat as tree
-- is deco_block if name == "mcl_core:snow" then return false end
if name == "mcl_nether:soul_sand" then return true end -- not "walkable". Other exceptions we need?
if name == "mcl_crimson:crimson_hyphae" then return false end -- crimson forest, treat as tree
if name == "mcl_nether:nether_wart_block" then return false end -- crimson forest, treat as leaves
if name == "mcl_crimson:warped_hyphae" then return false end -- warped forest, treat as tree
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
local meta = minetest.registered_items[name]
local groups = meta and meta.groups
return meta and meta.walkable and not (groups and ((groups.deco_block or 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
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
-- @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)
local name = node.name or node
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:warped_wart_block" then return true end -- warped forest, treat as tree
-- if name == "mcl_crimson:shroomlight" then return true end -- crimson forest, treat as tree
if name == "mcl_crimson:crimson_hyphae" then return true end -- crimson forest, treat as tree
if name == "mcl_crimson:warped_hyphae" then return true end -- warped forest, treat as tree
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
--- check if a node is liquid