VoxeLibre/mods/MAPGEN/vl_structures/emerge.lua

189 lines
11 KiB
Lua
Raw Normal View History

2024-08-24 19:24:31 +02:00
local vector_offset = vector.offset
local floor = math.floor
local get_node = core.get_node
2024-08-24 19:24:31 +02:00
2024-09-05 01:03:33 +02:00
local logging = minetest.settings:get_bool("vl_structures_logging", false)
2024-08-24 19:24:31 +02:00
local mg_name = minetest.get_mapgen_setting("mg_name")
2024-08-24 19:24:31 +02:00
-- parse the prepare parameter
local function parse_prepare(prepare)
2024-09-05 01:03:33 +02:00
if prepare == nil or prepare == true then return vl_structures.DEFAULT_PREPARE end
2024-08-24 19:24:31 +02:00
if prepare == false then return {} end
2024-09-05 01:03:33 +02:00
if prepare.foundation == true then prepare.foundation = vl_structures.DEFAULT_PREPARE.foundation end
2024-08-24 19:24:31 +02:00
return prepare
end
-- check "enabled" tolerances
2024-10-31 20:14:29 +01:00
local function tolerance_enabled(tolerance, surface, mode)
return tolerance ~= "off" and (tolerance or surface or mode) and true
2024-08-24 19:24:31 +02:00
end
--- Main palcement step, when the area has been emerged
local function emerge_schematics(blockpos, action, calls_remaining, param)
if calls_remaining >= 1 then return end
local start = os.clock()
local startmain = os.clock()
local pos, size, yoffset, def, pr = param.pos, param.size, param.yoffset or 0, param.def, param.pr
local prepare, surface_mat = parse_prepare(param.prepare or def.prepare), param.surface_mat
2024-10-20 21:00:12 +02:00
local dust_mat = nil
2024-08-24 19:24:31 +02:00
-- Step 0: pick random daughter schematics + rotations
local daughters = {}
for i,d in pairs(def.daughters or {}) do
if not d.schematics or #d.schematics == 0 then
error("Daughter schematics not loaded for structure "..def.name)
end
local ds = d.schematics[#d.schematics > 1 and pr:next(1,#d.schematics) or 1]
local rotation = vl_structures.parse_rotation(d.rotation, pr)
table.insert(daughters, {d, ds, rotation})
end
2024-10-20 21:00:12 +02:00
-- hack to get dust nodes more often, in case the mapgen messed with biomes
local n = get_node(vector_offset(param.opos, 0, 1, 0))
2024-10-20 21:00:12 +02:00
if n.name == "mcl_core:snow" then dust_mat = n end
2024-08-24 19:24:31 +02:00
-- Step 1: adjust ground to a more level position
-- todo: also support checking ground of daughter schematics, but not used by current schematics
2024-10-31 20:14:29 +01:00
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)
2024-08-24 19:24:31 +02:00
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
2024-10-31 20:14:29 +01:00
pos.y = pos.y + 1 -- above surface
2024-08-24 19:24:31 +02:00
-- 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
end
-- Placement area from center position:
local pmin = vector_offset(pos, -floor((size.x-1)*0.5), yoffset, -floor((size.z-1)*0.5))
local pmax = vector_offset(pmin, size.x-1, size.y-1, size.z-1)
-- Step 2: prepare ground foundations and clear
-- todo: allow daugthers to use prepare when parent does not, currently not used
if prepare and (prepare.clear or prepare.foundation) then
local prepare_start = os.clock()
-- Get materials from biome (TODO: make this a function + table?):
2024-10-20 21:00:12 +02:00
-- use original position to not get a different biome than expected for the structure
local b = mg_name ~= "v6" and minetest.registered_biomes[minetest.get_biome_name(minetest.get_biome_data(vector_offset(param.pos,0,1,0)).biome)]
--minetest.log("action", tostring(def.name or param.schematic.name).." in biome: "..dump(b,""):gsub("\n",""))
2024-09-05 01:03:33 +02:00
local node_top = b and b.node_top and { name = b.node_top } or surface_mat or vl_structures.DEFAULT_SURFACE
local node_filler = b and b.node_filler and { name = b.node_filler } or vl_structures.DEFAULT_FILLER
local node_stone = b and b.node_stone and { name = b.node_stone } or vl_structures.DEFAULT_STONE
2024-10-20 21:00:12 +02:00
local node_dust = b and b.node_dust and { name = b.node_dust } or dust_mat or vl_structures.DEFAULT_DUST
2024-08-24 19:24:31 +02:00
if node_top.name == "mcl_core:dirt_with_grass" and b then node_top.param2 = b._mcl_grass_palette_index end
-- Step 2a: clear overhead area
2024-09-05 01:03:33 +02:00
local corners, padding = prepare.corners or 1, prepare.padding or 1
2024-08-24 19:24:31 +02:00
local gp = vector_offset(pmin, -padding, -yoffset, -padding) -- base level
if prepare.clear then
2024-09-05 01:03:33 +02:00
local yoff, ymax = prepare.clear_bottom or 0, size.y + yoffset + (prepare.clear_top or vl_structures.DEFAULT_PREPARE.clear_top)
2024-08-24 19:24:31 +02:00
if prepare.clear_bottom == "top" or prepare.clear_bottom == "above" then yoff = size.y + yoffset end
--minetest.log("action", "[vl_structures] clearing air "..minetest.pos_to_string(gp)..": ".. (size.x + padding * 2)..","..ymax..","..(size.z + padding * 2))
vl_terraforming.clearance(gp.x, gp.y + yoff, gp.z,
2024-08-24 19:24:31 +02:00
size.x + padding * 2, ymax - yoff, size.z + padding * 2,
corners, node_top, node_dust, pr)
-- clear for daughters
for _,tmp in ipairs(daughters) do
local dd, ds, dr = tmp[1], tmp[2], tmp[3]
local ddp = parse_prepare(dd.prepare)
if ddp and ddp.clear then
local dsize = vl_structures.size_rotated(ds.size, dr) -- FIXME: rotation of parent
local corners, padding, yoffset = ddp.corners or 1, ddp.padding or 1, ddp.yoffset or 0
2024-09-05 01:03:33 +02:00
local yoff, ymax = ddp.clear_bottom or 0, dsize.y + yoffset + (ddp.clear_top or vl_structures.DEFAULT_PREPARE.clear_top)
2024-08-24 19:24:31 +02:00
if ddp.clear_bottom == "top" or ddp.clear_bottom == "above" then yoff = dsize.y + yoffset end
local gp = vector_offset(pos, dd.pos.x - floor((dsize.x-1)*0.5) - padding,
dd.pos.y,
dd.pos.z - floor((dsize.z-1)*0.5) - padding)
local sy = ymax - yoff
--minetest.log("action", "[vl_structures] clearing air "..minetest.pos_to_string(gp)..": ".. (dsize.x + padding * 2)..","..sy..","..(dsize.z + padding * 2))
if sy > 0 then
vl_terraforming.clearance(gp.x, gp.y + yoff, gp.z,
2024-08-24 19:24:31 +02:00
dsize.x + padding * 2, ymax - yoff, dsize.z + padding * 2,
corners, node_top, node_dust, pr)
end
end
end
end
-- Step 2b: baseplate underneath
if prepare.foundation then
2024-10-20 21:00:12 +02:00
-- minetest.log("action", "[vl_structures] "..tostring(def.name or param.schematic.name).." fill foundation "..minetest.pos_to_string(gp).." with "..tostring(node_top.name).." "..tostring(node_filler.name).." "..tostring(node_dust and node_dust.name))
2024-09-05 01:03:33 +02:00
local depth = (type(prepare.foundation) == "number" and prepare.foundation) or vl_structures.DEFAULT_PREPARE.foundation
vl_terraforming.foundation(gp.x, gp.y - 1, gp.z,
2024-08-24 19:24:31 +02:00
size.x + padding * 2, depth, size.z + padding * 2,
corners, node_top, node_filler, node_stone, node_dust, pr)
-- foundation for daughters
for _, tmp in ipairs(daughters) do
local dd, ds, dr = tmp[1], tmp[2], tmp[3]
local ddp = parse_prepare(dd.prepare)
if ddp and ddp.foundation then
local dsize = vl_structures.size_rotated(ds.size, dr) -- FIXME: rotation of parent
local corners, padding, yoffset = ddp.corners or 1, ddp.padding or 1, ddp.yoffset or 0
2024-09-05 01:03:33 +02:00
local depth = (type(ddp.foundation) == "number" and ddp.foundation) or vl_structures.DEFAULT_PREPARE.foundation
2024-08-24 19:24:31 +02:00
local gp = vector_offset(pos, dd.pos.x - floor((dsize.x-1)*0.5) - padding,
dd.pos.y + (yoffset or 0),
dd.pos.z - floor((dsize.z-1)*0.5) - padding)
vl_terraforming.foundation(gp.x, gp.y - 1, gp.z,
2024-08-24 19:24:31 +02:00
dsize.x + padding * 2, depth, dsize.z + padding * 2,
corners, node_top, node_filler, node_stone, node_dust, pr)
end
end
end
end
-- Step 3: place schematic on center position
minetest.place_schematic(pmin, param.schematic, param.rotation, param.replacements, param.force_placement, "")
2024-08-24 19:24:31 +02:00
-- Step 3: place daughter schematics
for _,tmp in ipairs(daughters) do
local d, ds, rot = tmp[1], tmp[2], tmp[3]
local p = vector_offset(pos, d.pos.x, d.pos.y + (yoffset or 0), d.pos.z)
minetest.place_schematic(p, ds, rot, d.replacements, d.force_placement, "place_center_x,place_center_z")
2024-08-24 19:24:31 +02:00
-- todo: allow after_place callbacks for daughter schematics?
end
local endmain = os.clock()
2024-10-20 21:00:12 +02:00
-- TODO: step 4: sprinkle extra dust on top.
2024-08-24 19:24:31 +02:00
-- Note: deliberately pos, p1 and p2 from the parent, as these are calls to the parent script
if def.loot then vl_structures.fill_chests(pmin,pmax,def.loot,pr) end
if def.construct_nodes then vl_structures.construct_nodes(pmin,pmax,def.construct_nodes) end
if def.after_place then def.after_place(pos,def,pr,pmin,pmax,size,param.rotation) end
if def.name and not (def.terrain_feature or def.no_registry) then vl_structures.register_structures_spawn(def.name, pos) end
2024-08-24 19:24:31 +02:00
if logging and not def.terrain_feature then
minetest.log("action", "[vl_structures] "..(def.name or "unnamed").." spawned at "..minetest.pos_to_string(pos).." in "..string.format("%.2fms (main: %.2fms)", (os.clock()-start)*1000, (endmain-startmain)*1000))
end
end
--- Wrapper to emerge an appropriate area for a schematic (with daughters, such as nether bulwark, nether outpost with bridges)
vl_structures.place_schematic = function(pos, yoffset, schematic, rotation, def, pr)
if schematic and not schematic.size then schematic = vl_structures.load_schematic(schematic) end -- legacy
local rotation = vl_structures.parse_rotation(rotation, pr)
local prepare = parse_prepare(def.prepare)
2024-09-05 01:03:33 +02:00
local ppos, pmin, pmax, size = vl_structures.get_extends(pos, schematic.size, yoffset, rotation, def.flags or vl_structures.DEFAULT_FLAGS)
2024-08-24 19:24:31 +02:00
-- area to emerge. Add some margin to allow for finding better suitable ground etc.
2024-09-05 01:03:33 +02:00
local tolerance = prepare.tolerance or vl_structures.DEFAULT_PREPARE.tolerance -- may be negative to disable foundations
2024-08-24 19:24:31 +02:00
if type(tolerance) ~= "number" then tolerance = 10 end -- extra height for emerge only, min/max/liquid_surface
local emin, emax = vector_offset(pmin, 0, -math.max(tolerance, 0), 0), vector.offset(pmax, 0, math.max(tolerance, 0), 0)
-- if we need to generate a foundation, we need to emerge a larger area:
2024-09-05 01:03:33 +02:00
if prepare.foundation or prepare.clear then -- these functions need some extra margins. Must match vl_terraforming!
2024-08-24 19:24:31 +02:00
local padding = (prepare.padding or 0) + 3
2024-09-05 01:03:33 +02:00
local depth = prepare.foundation and ((type(prepare.foundation) == "number" and prepare.foundation or vl_structures.DEFAULT_PREPARE.foundation) - 3) or 0 -- minimum depth
local height = prepare.clear and ((prepare.clear_top or vl_structures.DEFAULT_PREPARE.clear_top)*1.5+0.5*(size.y+yoffset)+2) or 0 -- headroom
2024-08-24 19:24:31 +02:00
emin = vector_offset(emin, -padding, depth, -padding)
emax = vector_offset(emax, padding, height, padding)
end
-- finally, add the configured emerge margin for daugther schematics
-- TODO: compute this instead? But we do not know rotations and sizes of daughters yet
if def.emerge_padding then
if #def.emerge_padding ~= 2 then error("Schematic "..def.name.." has an incorrect 'emerge_padding'. Must be two vectors.") end
emin, emax = emin + def.emerge_padding[1], emax + def.emerge_padding[2]
end
-- if logging and not def.terrain_feature then minetest.log("action", "[vl_structures] "..def.name.." needs emerge "..minetest.pos_to_string(emin).."-"..minetest.pos_to_string(emax)) end
minetest.emerge_area(emin, emax, emerge_schematics, { name = def.name,
emin=emin, emax=emax, def=def, schematic=schematic,
2024-10-20 21:00:12 +02:00
pos=ppos, opos=pos, yoffset=yoffset, size=size, rotation=rotation,
2024-08-24 19:24:31 +02:00
pr=pr
})
end