cleanups and fixes

This commit is contained in:
kno10 2024-09-05 01:03:33 +02:00
parent 27b6963d20
commit d7ac288ba1
18 changed files with 300 additions and 229 deletions

View File

@ -4,7 +4,7 @@ local modpath = minetest.get_modpath(modname)
vl_structures.register_structure("fossil",{
place_on = {"group:material_stone","group:sand"},
flags = "place_center_x, place_center_z",
prepare = { },
prepare = false,
chunk_probability = 15, -- was 25, FIXME: needs rebalancing
y_offset = function(pr) return pr:next(-32,-16) end,
y_max = 15,

View File

@ -1,5 +1,6 @@
local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)
local water_level = minetest.get_mapgen_setting("water_level")
local cold_oceans = {
"RoofedForest_ocean",
@ -71,14 +72,15 @@ local warm_oceans = {
local cold = {
place_on = {"group:sand","mcl_core:gravel","mcl_core:dirt","mcl_core:clay","group:material_stone"},
spawn_by = {"mcl_core:water_source"},
spawn_by = {"group:water"},
num_spawn_by = 2,
flags = "place_center_x, place_center_z, force_placement",
y_offset = -1,
y_min = mcl_vars.mg_overworld_min,
y_max = -2,
chunk_probability = 10, -- todo: 15?
biomes = cold_oceans,
chunk_probability = 10,
y_min = mcl_vars.mg_overworld_min,
y_max = water_level - 6,
y_offset = -1,
flags = "place_center_x, place_center_z, force_placement",
prepare = { foundation = -2, clear = false, mode="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_place = true,
prepare = { tolerance = -1, clear = false, foundation = false },
prepare = { tolerance = 8, clear = false, foundation = 3, mode="water" },
biomes = ocean_biomes,
y_max = water_level-4,
y_min = mcl_vars.mg_overworld_min,
@ -90,7 +90,7 @@ vl_structures.register_structure("ocean_temple",{
modpath .. "/schematics/mcl_structures_ocean_temple.mts",
modpath .. "/schematics/mcl_structures_ocean_temple_2.mts",
},
y_offset = function(pr) return pr:next(-2,0) end,
y_offset = -1, --function(pr) return pr:next(-2,-1) end, -- fewer mobs if buried in sand
after_place = function(p, _, pr, p1, p2)
vl_structures.spawn_mobs("mobs_mc:guardian",spawnon,p1,p2,pr,5,true)
vl_structures.spawn_mobs("mobs_mc:guardian_elder",spawnon,p1,p2,pr,1,true)

View File

@ -6,7 +6,7 @@ local spawnon = {"mcl_core:stripped_oak","mcl_stairs:slab_birchwood_top"}
vl_structures.register_structure("pillager_outpost",{
place_on = {"group:grass_block","group:dirt","mcl_core:dirt_with_grass","group:sand"},
flags = "place_center_x, place_center_z",
prepare = { padding = 3, corners = 4, foundation = -6, clear = true },
prepare = { padding = 3, corners = 4, foundation = -8, clear = true },
y_offset = 0,
chunk_probability = 15,
y_max = mcl_vars.mg_overworld_max,

View File

@ -2,15 +2,6 @@ local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)
local water_level = minetest.get_mapgen_setting("water_level")
--schematics by chmodsayshello
local schems = {
modpath.."/schematics/mcl_structures_shipwreck_full_damaged.mts",
modpath.."/schematics/mcl_structures_shipwreck_full_normal.mts",
modpath.."/schematics/mcl_structures_shipwreck_full_back_damaged.mts",
modpath.."/schematics/mcl_structures_shipwreck_half_front.mts",
modpath.."/schematics/mcl_structures_shipwreck_half_back.mts",
}
local ocean_biomes = {
"RoofedForest_ocean",
"JungleEdgeM_ocean",
@ -74,44 +65,27 @@ local ocean_biomes = {
"JungleM_ocean"
}
local beach_biomes = {
"FlowerForest_beach",
"Forest_beach",
"StoneBeach",
"ColdTaiga_beach_water",
"Taiga_beach",
"Savanna_beach",
"Plains_beach",
"ExtremeHills_beach",
"ColdTaiga_beach",
"Swampland_shore",
"MushroomIslandShore",
"JungleM_shore",
"Jungle_shore"
}
-- FIXME: integrate treasure maps from MCLA
vl_structures.register_structure("shipwreck",{
place_on = {"group:sand","mcl_core:gravel"},
spawn_by = {"group:water"},
num_spawn_by = 4,
noise_params = {
offset = 0,
scale = 0.000022,
spread = {x = 250, y = 250, z = 250},
seed = 3,
octaves = 3,
persist = 0.001,
flags = "absvalue",
},
flags = "force_placement",
chunk_probability = 10, -- todo: 15?
biomes = ocean_biomes,
y_max = water_level-4,
y_min = mcl_vars.mg_overworld_min,
prepare = { tolerance = -1, clear = false, foundation = false },
filenames = schems,
y_max = water_level-4,
y_offset = function(pr) return pr:next(-4,-2) end,
flags = "place_center_x, place_center_z, force_placement",
prepare = { tolerance = -1, clear = false, foundation = -2, mode = "water" },
filenames = {
--schematics by chmodsayshello
modpath.."/schematics/mcl_structures_shipwreck_full_damaged.mts",
modpath.."/schematics/mcl_structures_shipwreck_full_normal.mts",
modpath.."/schematics/mcl_structures_shipwreck_full_back_damaged.mts",
modpath.."/schematics/mcl_structures_shipwreck_half_front.mts",
modpath.."/schematics/mcl_structures_shipwreck_half_back.mts",
},
loot = {
["mcl_chests:chest_small"] = {
{

View File

@ -251,7 +251,7 @@ function mcl_villages.post_process_village(blockseed)
local jobs, beds = {}, {}
local bell_pos = vector.copy(settlement_info[1].pos)
local bell = vector.offset(bell_pos, 0, 1, 0)
local bell = vector.offset(bell_pos, 0, 2, 0)
local biome_name = minetest.get_biome_name(minetest.get_biome_data(bell_pos).biome)
-- Spawn Golem

View File

@ -1,37 +1,107 @@
# vl_structures
Updated API for structure spawning for VoxeLibre and Mineclonia
Updated API for structure spawning.
## vl_structures.register_structure(name,structure definition,nospawn)
If nospawn is truthy the structure will not be placed by mapgen and the decoration parameters can be omitted. This is intended for secondary structures the placement of which gets triggered by the placement of other structures. It can also be used to register testing structures so they can be used with /spawnstruct.
This module was developed with VoxeLibre and Mineclonia in mind, but means to be portable or at least easy to adapt to other games.
### structure definition
## structure definition
Structures in this API are defined using the following table:
```
{
fill_ratio = OR noise = {},
biomes = {},
y_min =,
y_max =,
place_on = {},
spawn_by = {},
num_spawn_by =,
flags = (default: "place_center_x, place_center_z, force_placement")
(same as decoration def)
y_offset =, --can be a number or a function returning a number
filenames = {} OR place_func = function(pos,def,pr)
-- filenames can be a list of any schematics accepted by mcl_structures.place_schematic / minetest.place_schematic
on_place = function(pos,def,pr) end,
-- called before placement. denies placement when returning falsy.
after_place = function(pos,def,pr)
-- executed after successful placement
prepare = table, -- a foundation is automatically built for the structure
loot = ,
--a table of loot tables for mcl_loot indexed by node names
-- e.g. { ["mcl_chests:chest_small"] = {loot},... }
name =, -- structure identifier for logging
priority = 100, -- priority to make placement order more deterministic. Default 100 except for terrain features (900)
chunk_probability =, -- ratio that a block is chosen, 10 means 1-in-10 blocks
fill_ratio = nil, -- OR number of structure spawn attempts per map chunk, default is 1/(80 x 80) when chunk_probability is set
noise = nil, -- OR specify noise parameters, as per minetest.register_decoration
y_min =, -- minimum depth
y_max =, -- maximum depth
biomes = {}, -- biome restriction
place_on = {}, -- if nil, the structure will not be automatically spawned
spawn_by = {}, -- nodes required nearby, as in minetest.register_decoration
num_spawn_by =, -- number of nodes required nearby
prepare =, -- configure foundation and clearing, see vl_terraforming -- ignored for place_func
flags =, -- minetest.register_decoration placement flags, default: "place_center_x, place_center_z"
y_offset =, -- vertical placement offset, can be a number or a function(pr) returning a number
filenames = {}, -- table of schematic filenames
schematics = {}, -- OR table of preloaded schematics
place_func = function(pos,def,pr,blockseed)
-- OR a function to place a structure
after_place = function(pos,def,pr,pmin,pmax,size,rotation)
-- callback executed after successful placement
loot =, -- a table of loot tables for mcl_loot indexed by node names -- ignored for place_func, to be removed
-- e.g. { ["mcl_chests:chest_small"] = {loot},... }
terrain_feature =, -- affects placement priority and disables logging for uninteresting structures
daughters =, -- substructures to spawn, unstable API
}
```
## vl_structures.register_structure(name,def)
Register a new structure spawn.
For extension modules, if you choose a larger (later) placement priority, this
should be less likely to change spawning of original structures and keep the
resulting maps more consistent across seeds.
## vl_structures.load_schematic(filename, name)
Load a schematic from a given file name, the name is used for error logging; otherwise it will be derived from the file name.
## vl_structures.place_structure(pos, def, pr, blockseed, rot)
Places structure defined by def at position pos, using the pseudorandom pr.
blockseed is only used by the place_func call, and unused for many simple structures.
rot is optional, it will then be chosen randomly.
This is usually called from the mapgen decoration gennotify mechanism, but can be used for substructure spawns.
## vl_structures.registered_structures
Table of the registered structure defintions indexed by name.
## vl_structures.place_structure(pos, def, pr)
Places a structure using the mapgen placement function
## vl_structures.place_schematic(pos, yoffset, schematic, rotation, def, pr)
Spawn a structure as defined by "def" at the given position, yoffset, schematic, and rotation.
This is primarily meant for substructure placement where size (and hence schematic and offsets) need to be fixed before computing the position.
## vl_structures.parse_rotation(rotation, pr)
Parse a rotation value (stirngs "0", "90", "180", "270" or "random"), or choosing a random rotation.
## vl_structures.size_rotated(size, rotation)
Return the size after rotation, i.e., if rotation is 90 or 270, the x and z sizes are swapped.
## vl_structures.top_left_from_flags(pos, size, flags)
Compute the top left corner from the flags, i.e., parse place_center_x, place_center_z etc.
## vl_structures.get_extends(pos, size, yoffset, rotation, flags)
Parse rotation and flags, and return the center, minimum corner, maximum corner, and size.
## vl_structures.init_node_construct(pos)
Call on_construct callbacks for the node at the given position.
## vl_structures.construct_nodes(p1,p2,nodes)
Find all nodes of the listed types in the area and call their on_construct callbacks.
## vl_structures.fill_chests(p1,p2,loot,pr)
Fill all loot containers in the area, requires mcl_loot and likely should be moved to the loot API.
## vl_structures.spawn_mobs(mob,spawnon,p1,p2,pr,n,water)
This function spawns the desired mobs in the given area. The function should move to the mobs API.
## vl_structures.register_structure_spawn(def)
This function creates a spawn ABM for the desired mobs. The function should move to the mobs API.
## vl_structures.place_schematic(pos, schematic, rotation, replacements, force_placement, flags, after_placement_callback, pr, callback_param)

View File

@ -1,21 +1,17 @@
vl_structures.registered_structures = {}
local structure_boost = tonumber(minetest.settings:get("vl_structures_boost")) or 1
local worldseed = minetest.get_mapgen_setting("seed")
local RANDOM_SEED_OFFSET = 959 -- random constant that should be unique across each library
local vector_offset = vector.offset
-- FIXME: switch to vl_structures_logging?
local logging = true or minetest.settings:get_bool("mcl_logging_structures", true)
-- FIXME: switch to vl_structures_disabled?
local disabled_structures = minetest.settings:get("mcl_disabled_structures")
local logging = minetest.settings:get_bool("vl_structures_logging", false)
local disabled_structures = minetest.settings:get("vl_structures_disabled")
disabled_structures = disabled_structures and disabled_structures:split(",") or {}
function vl_structures.is_disabled(structname)
return table.indexof(disabled_structures,structname) ~= -1
end
local worldseed = minetest.get_mapgen_setting("seed")
local RANDOM_SEED_OFFSET = 959 -- random constant that should be unique across each library
local vector_offset = vector.offset
--- Trim a full path name to its last two parts as short name for logging
local function basename(filename)
local fn = string.split(filename, "/")
@ -101,30 +97,28 @@ function vl_structures.place_structure(pos, def, pr, blockseed, rot)
end
-- structure has a custom place function
if not def.place_func then
minetest.log("warning","[vl_structures] no schematics and no place_func for schematic "..def.name)
minetest.log("warning", "[vl_structures] no schematics and no place_func for schematic "..def.name)
return false
end
local pp = yoffset ~= 0 and vector_offset(pos, 0, yoffset, 0) or pos
if def.place_func and def.prepare then
minetest.log("warning", "[vl_structures] needed prepare for "..def.name.." placed at "..minetest.pos_to_string(pp).." but do not have size information")
minetest.log("warning", "[vl_structures] needed prepare for "..def.name.." placed at "..minetest.pos_to_string(pp).." but do not have size information.")
end
if def.place_func and def.place_func(pp,def,pr,blockseed) then
if not def.after_place or (def.after_place and def.after_place(pos,def,pr,pmin,pmax,size,param.rotation)) then
if def.sidelen then
local p1, p2 = vector_offset(pos,-def.sidelen,-def.sidelen,-def.sidelen), vector.offset(pos,def.sidelen,def.sidelen,def.sidelen)
if def.loot then vl_structures.fill_chests(p1,p2,def.loot,pr) end
if def.construct_nodes then vl_structures.construct_nodes(p1,p2,def.construct_nodes) end
end
if log_enabled then
minetest.log("action","[vl_structures] "..def.name.." placed at "..minetest.pos_to_string(pp))
end
return true
else
minetest.log("warning","[vl_structures] after_place failed for schematic "..def.name)
if def.after_place and not def.after_place(pos,def,pr,pmin,pmax,size,param.rotation) then
minetest.log("warning", "[vl_structures] after_place failed for structure "..def.name)
return false
end
if log_enabled then
minetest.log("action","[vl_structures] "..def.name.." placed at "..minetest.pos_to_string(pp))
end
return true
elseif log_enabled then
minetest.log("warning","[vl_structures] place_func failed for schematic "..def.name)
if def.place_func then
minetest.log("warning","[vl_structures] place_func failed for structure "..def.name)
else
minetest.log("warning","[vl_structures] do not know how to place structure "..def.name)
end
end
end
@ -139,13 +133,13 @@ function vl_structures.register_structure(name,def)
def.name = name
vl_structures.registered_structures[name] = def
if def.prepare and def.prepare.clear == nil and (def.prepare.clear_bottom or def.prepare.clear_top) then def.prepare.clear = true end
if not def.noise_params and def.chunk_probability and not def.fill_ratio then
if not def.fill_ratio and def.chunk_probability and not def.noise_params then
def.fill_ratio = 1.1/80/80 -- 1 per chunk, controlled by chunk probability only
end
def.flags = def.flags or vl_structures.DEFAULT_FLAGS
if def.filenames then
for _, filename in ipairs(def.filenames) do
if not mcl_util.file_exists(filename) then
if mcl_util and not mcl_util.file_exists(filename) then
minetest.log("warning","[vl_structures] schematic "..(name or "unknown").." is missing file "..basename(filename))
return nil
end
@ -153,21 +147,22 @@ function vl_structures.register_structure(name,def)
end
if def.place_on then
minetest.register_on_mods_loaded(function()
def.deco = mcl_mapgen_core.register_decoration({
local register_decoration = mcl_mapgen_core.register_decoration or minetest.register_decoration -- optional dependency
register_decoration({
name = "vl_structures:"..name,
priority = def.priority or (def.terrain_feature and 900) or 100, -- run before regular decorations
deco_type = "schematic",
schematic = EMPTY_SCHEMATIC, -- use gennotify only
fill_ratio = def.fill_ratio,
noise_params = def.noise_params,
y_max = def.y_max,
y_min = def.y_min,
biomes = def.biomes,
place_on = def.place_on,
spawn_by = def.spawn_by,
num_spawn_by = def.num_spawn_by,
sidelen = 80, -- no def.sidelen subdivisions for now, this field was used differently before
fill_ratio = def.fill_ratio,
noise_params = def.noise_params,
flags = def.flags,
biomes = def.biomes,
y_max = def.y_max,
y_min = def.y_min,
deco_type = "schematic",
schematic = EMPTY_SCHEMATIC, -- use gennotify only
gen_callback = function(t,minp,maxp,blockseed)
for _, pos in ipairs(t) do
local pr = PcgRandom(minetest.hash_node_position(pos) + worldseed + RANDOM_SEED_OFFSET)
@ -183,6 +178,7 @@ function vl_structures.register_structure(name,def)
end
-- To avoid a cyclic dependency, run this when modules have finished loading
-- Maybe we can eventually remove this - the end portal should likely go into the mapgen itself.
minetest.register_on_mods_loaded(function()
mcl_mapgen_core.register_generator("static structures", nil, function(minp, maxp, blockseed)
for _,struct in pairs(vl_structures.registered_structures) do
@ -200,37 +196,3 @@ mcl_mapgen_core.register_generator("static structures", nil, function(minp, maxp
end, 100, true)
end)
local structure_spawns = {}
function vl_structures.register_structure_spawn(def)
--name,y_min,y_max,spawnon,biomes,chance,interval,limit
minetest.register_abm({
label = "Spawn "..def.name,
nodenames = def.spawnon,
min_y = def.y_min or -31000,
max_y = def.y_max or 31000,
interval = def.interval or 60,
chance = def.chance or 5,
action = function(pos, node, active_object_count, active_object_count_wider)
local limit = def.limit or 7
if active_object_count_wider > limit + mob_cap_animal then return end
if active_object_count_wider > mob_cap_player then return end
local p = vector_offset(pos, 0, 1, 0)
local pname = minetest.get_node(p).name
if def.type_of_spawning == "water" then
if pname ~= "mcl_core:water_source" and pname ~= "mclx_core:river_water_source" then return end
else
if pname ~= "air" then return end
end
if minetest.get_meta(pos):get_string("spawnblock") == "" then return end
if mg_name ~= "v6" and mg_name ~= "singlenode" and def.biomes then
if table.indexof(def.biomes, minetest.get_biome_name(minetest.get_biome_data(p).biome)) == -1 then
return
end
end
local mobdef = minetest.registered_entities[def.name]
if mobdef.can_spawn and not mobdef.can_spawn(p) then return end
minetest.add_entity(p, def.name)
end,
})
end

View File

@ -1,22 +1,14 @@
local DEFAULT_FLAGS = vl_structures.DEFAULT_FLAGS
local DEFAULT_PREPARE = vl_structures.DEFAULT_PREPARE
local vector_offset = vector.offset
local floor = math.floor
-- FIXME: switch to vl_structures_logging?
local logging = true or minetest.settings:get_bool("mcl_logging_structures", true)
local logging = minetest.settings:get_bool("vl_structures_logging", false)
local mg_name = minetest.get_mapgen_setting("mg_name")
-- parse the prepare parameter
local function parse_prepare(prepare)
if prepare == nil or prepare == true then return DEFAULT_PREPARE end
if prepare == nil or prepare == true then return vl_structures.DEFAULT_PREPARE end
if prepare == false then return {} end
if prepare.foundation == true then
prepare = table.copy(prepare)
prepare.foundation = DEFAULT_PREPARE.foundation
end
if prepare.foundation == true then prepare.foundation = vl_structures.DEFAULT_PREPARE.foundation end
return prepare
end
@ -68,17 +60,17 @@ local function emerge_schematics(blockpos, action, calls_remaining, param)
local prepare_start = os.clock()
-- Get materials from biome (TODO: make this a function + table?):
local b = mg_name ~= "v6" and minetest.registered_biomes[minetest.get_biome_name(minetest.get_biome_data(pos).biome)]
local node_top = b and b.node_top and { name = b.node_top } or surface_mat or { name = "mcl_core:dirt_with_grass" }
local node_filler = { name = b and b.node_filler or "mcl_core:dirt" }
local node_stone = { name = b and b.node_stone or "mcl_core:stone" }
local node_dust = b and b.node_dust and { name = b.node_dust } or nil
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
local node_dust = b and b.node_dust and { name = b.node_dust } or vl_structures.DEFAULT_DUST
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
local corners, padding, depth = prepare.corners or 1, prepare.padding or 1, (type(prepare.foundation) == "number" and prepare.foundation) or -4
local corners, padding = prepare.corners or 1, prepare.padding or 1
local gp = vector_offset(pmin, -padding, -yoffset, -padding) -- base level
if prepare.clear then
local yoff, ymax = prepare.clear_bottom or 0, size.y + yoffset + (prepare.clear_top or DEFAULT_PREPARE.clear_top)
local yoff, ymax = prepare.clear_bottom or 0, size.y + yoffset + (prepare.clear_top or vl_structures.DEFAULT_PREPARE.clear_top)
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_vm(vm, gp.x, gp.y + yoff, gp.z,
@ -91,7 +83,7 @@ local function emerge_schematics(blockpos, action, calls_remaining, param)
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
local yoff, ymax = ddp.clear_bottom or 0, dsize.y + yoffset + (ddp.clear_top or DEFAULT_PREPARE.clear_top)
local yoff, ymax = ddp.clear_bottom or 0, dsize.y + yoffset + (ddp.clear_top or vl_structures.DEFAULT_PREPARE.clear_top)
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,
@ -110,7 +102,7 @@ local function emerge_schematics(blockpos, action, calls_remaining, param)
-- Step 2b: baseplate underneath
if prepare.foundation then
-- minetest.log("action", "[vl_structures] fill foundation "..minetest.pos_to_string(gp).." with "..tostring(node_top.name).." "..tostring(node_filler.name))
local depth = (type(prepare.foundation) == "number" and prepare.foundation) or DEFAULT_PREPARE.foundation
local depth = (type(prepare.foundation) == "number" and prepare.foundation) or vl_structures.DEFAULT_PREPARE.foundation
vl_terraforming.foundation_vm(vm, gp.x, gp.y - 1, gp.z,
size.x + padding * 2, depth, size.z + padding * 2,
corners, node_top, node_filler, node_stone, node_dust, pr)
@ -121,7 +113,7 @@ local function emerge_schematics(blockpos, action, calls_remaining, param)
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
local depth = (type(ddp.foundation) == "number" and ddp.foundation) or DEFAULT_PREPARE.foundation
local depth = (type(ddp.foundation) == "number" and ddp.foundation) or vl_structures.DEFAULT_PREPARE.foundation
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)
@ -158,16 +150,16 @@ vl_structures.place_schematic = function(pos, yoffset, schematic, rotation, def,
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)
local ppos, pmin, pmax, size = vl_structures.get_extends(pos, schematic.size, yoffset, rotation, def.flags or DEFAULT_FLAGS)
local ppos, pmin, pmax, size = vl_structures.get_extends(pos, schematic.size, yoffset, rotation, def.flags or vl_structures.DEFAULT_FLAGS)
-- area to emerge. Add some margin to allow for finding better suitable ground etc.
local tolerance = prepare.tolerance or DEFAULT_PREPARE.tolerance -- may be negative to disable foundations
local tolerance = prepare.tolerance or vl_structures.DEFAULT_PREPARE.tolerance -- may be negative to disable foundations
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:
if prepare.foundation or prepare.clear then -- these functions need some extra margins. Must match mcl_foundations!
if prepare.foundation or prepare.clear then -- these functions need some extra margins. Must match vl_terraforming!
local padding = (prepare.padding or 0) + 3
local depth = prepare.foundation and ((type(prepare.foundation) == "number" and prepare.foundation or DEFAULT_PREPARE.foundation) - 3) or 0 -- minimum depth
local height = prepare.clear and ((prepare.clear_top or DEFAULT_PREPARE.clear_top)*1.5+0.5*(size.y+yoffset)+2) or 0 -- headroom
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
emin = vector_offset(emin, -padding, depth, -padding)
emax = vector_offset(emax, padding, height, padding)
end

View File

@ -1,13 +1,18 @@
local modname = minetest.get_current_modname()
local S = minetest.get_translator(modname)
local modpath = minetest.get_modpath(modname)
vl_structures = {}
---- Customization parameters for different games
-- see vl_terraforming for documentation
vl_structures.DEFAULT_PREPARE = { tolerance = 10, foundation = -3, clear = false, clear_bottom = 0, clear_top = 4, padding = 1, corners = 1 }
vl_structures.DEFAULT_FLAGS = "place_center_x,place_center_z"
-- fallback types
vl_structures.DEFAULT_SURFACE = { name = "mcl_core:dirt_with_grass" }
vl_structures.DEFAULT_FILLER = { name = "mcl_core:dirt" }
vl_structures.DEFAULT_STONE = { name = "mcl_core:stone" }
vl_structures.DEFAULT_DUST = nil
local modpath = minetest.get_modpath(modname)
dofile(modpath.."/util.lua")
dofile(modpath.."/emerge.lua")
dofile(modpath.."/api.lua")

View File

@ -1,4 +1,5 @@
name = vl_structures
author = kno10
description = Structures API for VoxeLibre and Mineclonia
depends = mcl_init, mcl_util, mcl_loot, vl_terraforming
description = Structure spawning API for VoxeLibre
depends = vl_terraforming
optional_depends = mcl_util, mcl_loot

View File

@ -1,10 +1,54 @@
-- todo: move this mostly to the mcl_mobs module?
-- TODO: move this to the mcl_mobs module?
local mob_cap_player = tonumber(minetest.settings:get("mcl_mob_cap_player")) or 75
local mob_cap_animal = tonumber(minetest.settings:get("mcl_mob_cap_animal")) or 10
local peaceful = minetest.settings:get_bool("only_peaceful_mobs", false)
local mg_name = minetest.get_mapgen_setting("mg_name")
local vector_offset = vector.offset
-- check if a node is an air node
local function is_air(node)
return node == "air"
-- todo: or: not walkable and not liquid?
end
-- check if a node is a water node
local function is_water(node)
return minetest.get_item_group(node.name, "water") ~= 0
-- return node.name == "mcl_core:water_source" or node.name ~= "mclx_core:river_water_source"
end
--- Spawn mobs for a structure
-- @param mob string: mob to spawn
-- @param spawnon string or table: nodes to spawn on
-- @param p1 vector: Lowest coordinates of range
-- @param p2 vector: Highest coordinates of range
-- @param pr PseudoRandom: random generator
-- @param n number: Number of mobs to spawn
-- @param water boolean: Spawn water mobs
function vl_structures.spawn_mobs(mob,spawnon,p1,p2,pr,n,water)
n = n or 1
local sp = {}
if water then
local nn = minetest.find_nodes_in_area(p1,p2,spawnon)
for k,v in pairs(nn) do
if is_water(minetest.get_node(vector_offset(v,0,1,0))) then
table.insert(sp,v)
end
end
else
sp = minetest.find_nodes_in_area_under_air(p1,p2,spawnon)
end
table.shuffle(sp)
local count = 0
local mob_def = minetest.registered_entities[mob]
local enabled = (not peaceful) or (mob_def and mob_spawn_class ~= "hostile")
for _, node in pairs(sp) do
if enabled and count < n and minetest.add_entity(vector_offset(node, 0, 0.5, 0), mob) then
count = count + 1
end
minetest.get_meta(node):set_string("spawnblock", "yes") -- note: also in peaceful mode!
end
end
local structure_spawns = {}
--- Structure spawns via ABM
-- @param def table: containing
@ -30,12 +74,8 @@ function vl_structures.register_structure_spawn(def)
if active_object_count_wider > limit + mob_cap_animal then return end
if active_object_count_wider > mob_cap_player then return end
local p = vector_offset(pos, 0, 1, 0)
local pname = minetest.get_node(p).name
if def.type_of_spawning == "water" then
if pname ~= "mcl_core:water_source" and pname ~= "mclx_core:river_water_source" then return end
else
if pname ~= "air" then return end -- FIXME: allow everything non-walkable, non-water, non-lava?
end
local pnode = minetest.get_node(p)
if not (def.type_of_spawning == "water" and is_water or is_air)(pnode) then return end
if minetest.get_meta(pos):get_string("spawnblock") == "" then return end
if mg_name ~= "v6" and mg_name ~= "singlenode" and def.biomes then
if table.indexof(def.biomes, minetest.get_biome_name(minetest.get_biome_data(p).biome)) == -1 then
@ -49,36 +89,3 @@ function vl_structures.register_structure_spawn(def)
})
end
--- Spawn mobs for a structure
-- @param mob string: mob to spawn
-- @param spawnon string or table: nodes to spawn on
-- @param p1 vector: Lowest coordinates of range
-- @param p2 vector: Highest coordinates of range
-- @param pr PseudoRandom: random generator
-- @param n number: Number of mobs to spawn
-- @param water boolean: Spawn water mobs
function vl_structures.spawn_mobs(mob,spawnon,p1,p2,pr,n,water)
n = n or 1
local sp = {}
if water then
local nn = minetest.find_nodes_in_area(p1,p2,spawnon)
for k,v in pairs(nn) do
if minetest.get_item_group(minetest.get_node(vector_offset(v,0,1,0)).name,"water") > 0 then
table.insert(sp,v)
end
end
else
sp = minetest.find_nodes_in_area_under_air(p1,p2,spawnon)
end
table.shuffle(sp)
local count = 0
local mob_def = minetest.registered_entities[mob]
local enabled = (not peaceful) or (mob_def and mob_spawn_class ~= "hostile")
for _, node in pairs(sp) do
if enabled and count < n and minetest.add_entity(vector_offset(node, 0, 0.5, 0), mob) then
count = count + 1
end
minetest.get_meta(node):set_string("spawnblock", "yes") -- note: also in peaceful mode!
end
end

View File

@ -83,6 +83,7 @@ end
-- @param loot table: Loot table
-- @param pr PseudoRandom: random generator
function vl_structures.fill_chests(p1,p2,loot,pr)
if not mcl_loot then return end -- optional dependency
for it,lt in pairs(loot) do
local nodes = minetest.find_nodes_in_area(p1, p2, it)
for _,p in pairs(nodes) do

View File

@ -1,5 +1,6 @@
# vl_terraforming
Terraforming module for VoxeLibre and MineClonia
Terraforming module built with VoxeLibre and MineClonia in mind, but also useful for other games.
This module provides the following key functionalities:
@ -8,7 +9,6 @@ This module provides the following key functionalities:
- build a baseplate for a building
- clear the area above a building
## Rounded corners support
To get nicer looking baseplates, the code supports rounded corners.
@ -55,7 +55,7 @@ One of these values may be "extreme", and tolerance specifies the maximum height
The (rounded) median of these values is used, unless tolerance is set to "min" or "max".
The "mode" can be set to "solid" (default), "liquid" (liquid surfaces only), "under_air" (both liquid and solid surfaces).
The "mode" can be set to "solid" (default), "liquid" (liquid surfaces only), "under_air" (both liquid and solid surfaces), "under_water" (solid below water).
## vl_terraforming.foundation_vm(vm, px, py, pz, sx, sy, sz, corners, surface_mat, platform_mat, stone_mat, dust_mat, pr)
@ -94,6 +94,6 @@ pr is a PcgRandom random generator
- [ ] 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 in VM)
- [ ] benchmark if VM is actually faster than not using VM (5.9 has some optimizations not yet in VM)
- [ ] improve tree removal

View File

@ -102,7 +102,7 @@ function vl_terraforming.foundation_vm(vm, px, py, pz, sx, sy, sz, corners, surf
end
-- construct additional baseplate below, also try to make it interesting
for yi = py-2,py-20,-1 do
local dy2 = max(0,py-2-yi)^2*0.05
local dy2 = max(0,py-2-yi)^2*0.10
local active = false
for xi = px-1,px+sx do
local dx22 = max(abs(cx-xi)-1.49,0)^2*wx2

View File

@ -15,7 +15,7 @@ function vl_terraforming.find_ground_vm(vm, pos)
local cur = vm:get_node_at(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)
minetest.log("warning", "find_ground with invalid position (outside of emerged area?) at "..minetest.pos_to_string(pos)
..": "..tostring(cur and cur.name).." area: "..minetest.pos_to_string(e1).." "..minetest.pos_to_string(e2))
return nil
end
@ -64,7 +64,7 @@ function vl_terraforming.find_under_air_vm(vm, pos)
local cur = vm:get_node_at(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)
minetest.log("warning", "find_under_air with invalid position (outside of emerged area?) at "..minetest.pos_to_string(pos)
..": "..tostring(cur and cur.name).." area: "..minetest.pos_to_string(e1).." "..minetest.pos_to_string(e2))
return nil
end
@ -112,7 +112,7 @@ function vl_terraforming.find_liquid_surface_vm(vm, pos)
local cur = vm:get_node_at(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)
minetest.log("warning", "find_liquid_surface with invalid position (outside of emerged area?) at "..minetest.pos_to_string(pos)
..": "..tostring(cur and cur.name).." area: "..minetest.pos_to_string(e1).." "..minetest.pos_to_string(e2))
return nil
end
@ -154,6 +154,59 @@ function vl_terraforming.find_liquid_surface_vm(vm, pos)
end
local find_liquid_surface_vm = vl_terraforming.find_liquid_surface_vm
--- Find under water surface for a given position
-- @param vm VoxelManip: buffer
-- @param pos vector: Start position
-- @return position and material of surface
function vl_terraforming.find_under_water_surface_vm(vm, pos)
if not pos then return nil, nil end
pos = vector_copy(pos)
local cur = vm:get_node_at(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)
..": "..tostring(cur and cur.name).." area: "..minetest.pos_to_string(e1).." "..minetest.pos_to_string(e2))
return nil
end
if is_solid_not_tree(cur) then -- find up
local prev = cur
while true do
pos.y = pos.y + 1
local cur = vm:get_node_at(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
end
if is_liquid(cur) then
pos.y = pos.y - 1
-- minetest.log("action", "Found surface: "..minetest.pos_to_string(pos).." "..tostring(prev and prev.name).." under "..tostring(cur and cur.name))
return pos, prev
end
prev = cur
end
else -- find down
while true do
pos.y = pos.y - 1
local prev = cur
local cur = vm:get_node_at(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
end
if is_solid_not_tree(cur) then
if is_liquid(prev) then
-- minetest.log("action", "Found surface: "..minetest.pos_to_string(pos).." "..(cur and cur.name).." over "..(prev and prev.name))
return pos, cur
else
-- minetest.log("action", "No ground, "..tostring(cur and cur.name).." below "..tostring(prev and prev.name).." at "..minetest.pos_to_string(pos))
return nil, nil
end
end
end
end
end
local find_under_water_surface_vm = vl_terraforming.find_under_water_surface_vm
--- find suitable height for a structure of this size
-- @param vm VoxelManip: to read data
-- @param cpos vector: center
@ -164,10 +217,14 @@ local find_liquid_surface_vm = vl_terraforming.find_liquid_surface_vm
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
-- begin at center, then top-left and clockwise
local pos, surface_material = find_ground(vm, cpos)
if not pos then return nil, nil end
if not pos then
-- minetest.log("action", "[vl_terraforming] no ground at starting position "..minetest.pos_to_string(cpos).." mode "..tostring(mode or "default"))
return nil, nil
end
local ys = { pos.y }
pos.y = pos.y + 1 -- position above surface
if size.x == 1 and size.z == 1 then return pos end
@ -200,8 +257,8 @@ function vl_terraforming.find_level_vm(vm, cpos, size, tolerance, mode)
end
-- well supported base, not too uneven?
if #ys < 4 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)
-- 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

View File

@ -1,3 +1,3 @@
name = vl_terraforming
author = kno10
description = Terraforming API for VoxeLibre and Mineclonia
description = Terraforming API

View File

@ -40,7 +40,7 @@ mcl_doTileDrops (Blocks have drops) bool true
mcl_explosions_griefing (Explosions destroy blocks) bool true
# Comma separated list of disabled structure names
mcl_disabled_structures (Disabled structures) string
vl_structures_disabled (Disabled structures) string
# Comma separated list of disabled event names
mcl_disabled_events (Disabled events) string
@ -344,7 +344,7 @@ mcl_logging_mobs_spawn (Log Mob Spawning) bool false
mcl_logging_mapgen (Chunk generation logging) bool false
# If enabled generated structures will be logged
mcl_logging_structures (Structure generation logging) bool false
vl_structures_logging (Structure generation logging) bool false
#Complete debug logging for mcl_signs events. Use this if you have issues with signs.
mcl_logging_mcl_signs (Complete debug logging for mcl_signs) bool false