VoxeLibre/mods/MAPGEN/mcl_villages/foundation.lua
2024-10-29 19:28:42 +01:00

200 lines
7.5 KiB
Lua

local foundation_materials = {}
foundation_materials["mcl_core:sand"] = "mcl_core:sandstone"
foundation_materials["mcl_core:redsand"] = "mcl_core:redsandstone"
local function is_air(node)
return not node or node.name == "air" or node.name == "ignore" or node.name == "mcl_villages:no_paths"
end
local function is_solid(node)
if not node or node.name == "air" or node.name == "ignore" or node.name == "mcl_villages:no_paths" then return false end
--if string.find(node.name,"leaf") then return false end
--if string.find(node.name,"tree") then return false end
local ndef = minetest.registered_nodes[node.name]
return ndef and ndef.walkable
end
local function make_solid(lvm, cp, with, except)
local cur = lvm:get_node_at(cp)
if not is_solid(cur) or (except and cur.name == except.name) then
lvm:set_node_at(cp, with)
end
end
local function excavate(lvm,xi,yi,zi,pr)
local pos, n, c = vector.new(xi,yi,zi), nil, 0
local node = lvm:get_node_at(pos)
if is_air(node) then return false end -- already empty, nothing to do
pos.y = pos.y-1
if not is_air(lvm:get_node_at(pos)) then return false end -- below is solid, do not clear above anymore
-- count empty nodes below otherwise
for x = xi-1,xi+1 do
for z = zi-1,zi+1 do
pos.x, pos.z = x, z
if is_air(lvm:get_node_at(pos)) then c = c + 1 end
end
end
-- try to completely remove trees overhead
if not string.find(node.name, "leaf") and not string.find(node.name, "tree") then
-- stop randomly depending on fill, to narrow down the caves
if (pr:next(0,1e9)/1e9)^2 > c/9.1 then return false end
end
lvm:set_node_at(vector.new(xi, yi, zi),{name="air"})
return true -- modified
end
local function grow_foundation(lvm,xi,yi,zi,pr,surface_mat,platform_mat)
local pos, n, c = vector.new(xi,yi,zi), nil, 0
if is_solid(lvm:get_node_at(pos)) then return false end -- already solid, nothing to do
pos.y = pos.y+1
local cur = lvm:get_node_at(pos)
if not is_solid(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,5) == 5 then -- randomly switch to stone sometimes
platform_mat = { name = "mcl_core:stone" }
end
-- count solid nodes above otherwise
for x = xi-1,xi+1 do
for z = zi-1,zi+1 do
pos.x, pos.z = x, z
if is_solid(lvm:get_node_at(pos)) then c = c + 1 end
end
end
-- stop randomly depending on fill, to narrow down the foundation
if (pr:next(0,1e9)/1e9)^2 > c/9.1 then return false end
lvm:set_node_at(vector.new(xi, yi, zi), platform_mat)
return true -- modified
end
-------------------------------------------------------------------------------
-- function clear space above baseplate
-------------------------------------------------------------------------------
function mcl_villages.terraform(lvm, settlement, pr)
-- TODO: further optimize by using raw data arrays instead of set_node_at. But OK for a first draft.
--local lvm, emin, emax = minetest.get_mapgen_object("voxelmanip")
--local lvm = VoxelManip()
-- we make the foundations 1 node wider than requested, to have one node for path laying
for i, building in ipairs(settlement) do
--lvm:read_from_map(vector.new(pos.x-2, pos.y-20, pos.z-2), vector.new(pos.x+sx+2, pos.y+sy+20, pos.z+sz+2))
--lvm:get_data()
if not building.no_clearance then
local pos, size = building.pos, building.size
pos = vector.offset(pos, -math.floor(size.x/2)-1, 0, -math.floor(size.z/2)-1)
mcl_villages.clearance(lvm, pos.x, pos.y, pos.z, size.x+2, size.y, size.z+2, pr)
end
--lvm:write_to_map(false)
end
for i, building in ipairs(settlement) do
if not building.no_ground_turnip then
local pos, size = building.pos, building.size
local surface_mat = building.surface_mat
local platform_mat = building.platform_mat or { name = foundation_materials[surface_mat.name] or "mcl_core:dirt" }
building.platform_mat = platform_mat -- remember for use in schematic placement
pos = vector.offset(pos, -math.floor(size.x/2)-1, 0, -math.floor(size.z/2)-1)
mcl_villages.foundation(lvm, pos.x, pos.y, pos.z, size.x+2, -4, size.z+2, surface_mat, platform_mat, pr)
end
end
end
local AIR = {name = "air"}
function mcl_villages.clearance(lvm, px, py, pz, sx, sy, sz, pr)
-- excavate the needed volume, some headroom, and add a baseplate
for xi = px,px+sx-1 do
for zi = pz,pz+sz-1 do
lvm:set_node_at(vector.new(xi, py+1, zi),AIR)
-- py+2 to py+5 are filled larger below!
for yi = py+6,py+sy do
lvm:set_node_at(vector.new(xi, yi, zi),AIR)
end
end
end
-- slightly widen the cave, to make easier to enter for mobs
for xi = px-1,px+sx do
for zi = pz-1,pz+sz do
for yi = py+2,py+5 do
lvm:set_node_at(vector.new(xi, yi, zi),AIR)
end
end
end
-- some extra gaps
for xi = px-2,px+sx+1 do
for zi = pz-2,pz+sz+1 do
if pr:next(1,4) == 1 then
for yi = py+3,py+5 do
lvm:set_node_at(vector.new(xi, yi, zi),AIR)
end
end
end
end
-- cave some additional area overhead, try to make it interesting though
for yi = py+3,py+sy*3 do
local active = false
for xi = px-2,px+sx+1 do
for zi = pz-2,pz+sz+1 do
if excavate(lvm,xi,yi,zi,pr) then active = true end
end
end
if not active and yi > py+sy+5 then break end
end
end
function mcl_villages.foundation(lvm, px, py, pz, sx, sy, sz, surface_mat, platform_mat, pr)
-- generate a baseplate
for xi = px,px+sx-1 do
for zi = pz,pz+sz-1 do
lvm:set_node_at(vector.new(xi, py, zi), surface_mat)
make_solid(lvm, vector.new(xi, py - 1, zi), platform_mat)
end
end
-- slightly widen the baseplate, to make easier to enter for mobs
for xi = px,px+sx-1 do
make_solid(lvm, vector.new(xi, py-1, pz-1), surface_mat, platform_mat)
make_solid(lvm, vector.new(xi, py-1, pz), platform_mat)
make_solid(lvm, vector.new(xi, py-1, pz+sz-1), platform_mat)
make_solid(lvm, vector.new(xi, py-1, pz+sz), surface_mat, platform_mat)
end
for zi = pz,pz+sz-1 do
make_solid(lvm, vector.new(px-1, py-1, zi), surface_mat, platform_mat)
make_solid(lvm, vector.new(px, py-1, zi), platform_mat)
make_solid(lvm, vector.new(px+sx-1, py-1, zi), platform_mat)
make_solid(lvm, vector.new(px+sx, py-1, zi), surface_mat, platform_mat)
end
-- make some additional steps, along both x sides
for xi = px,px+sx-1 do
local cp = vector.new(xi, py-3, pz-1)
if is_solid(lvm:get_node_at(cp)) then
cp = vector.new(xi, py-2, pz-1)
make_solid(lvm, cp, surface_mat, platform_mat)
cp.z = pz-2
make_solid(lvm, cp, surface_mat, platform_mat)
end
local cp = vector.new(xi, py-3, pz+sz)
if is_solid(lvm:get_node_at(cp)) then
cp = vector.new(xi, py-2, pz+sz)
make_solid(lvm, cp, surface_mat, platform_mat)
cp.z = pz + sz + 1
make_solid(lvm, cp, surface_mat, platform_mat)
end
end
-- make some additional steps, along both z sides
for zi = pz,pz+sz-1 do
local cp = vector.new(px-1, py-3, zi)
if is_solid(lvm:get_node_at(cp)) then
cp = vector.new(px-1, py-2, zi)
make_solid(lvm, cp, surface_mat, platform_mat)
cp.x = px-2
make_solid(lvm, cp, surface_mat, platform_mat)
end
local cp = vector.new(px+sx, py-3, zi)
if is_solid(lvm:get_node_at(cp)) then
cp = vector.new(px+sx, py-2, zi)
make_solid(lvm, cp, surface_mat, platform_mat)
cp.x = px+sx+1
make_solid(lvm, cp, surface_mat, platform_mat)
end
end
-- construct additional baseplate below, also try to make it interesting
for yi = py-2,py-20,-1 do
local active = false
for xi = px-1,px+sx do
for zi = pz-1,pz+sz do
if grow_foundation(lvm,xi,yi,zi,pr,surface_mat,platform_mat) then active = true end
end
end
if not active and yi < py + sy then break end
end
end