2022-06-10 02:40:33 +02:00
mcl_structures.registered_structures = { }
2022-10-29 00:18:48 +02:00
local peaceful = minetest.settings : get_bool ( " only_peaceful_mobs " , false )
2022-10-29 00:43:15 +02:00
local mob_cap_player = tonumber ( minetest.settings : get ( " mcl_mob_cap_player " ) ) or 75
2022-10-29 01:26:32 +02:00
local mob_cap_animal = tonumber ( minetest.settings : get ( " mcl_mob_cap_animal " ) ) or 10
2024-08-06 20:37:44 +02:00
local structure_boost = tonumber ( minetest.settings : get ( " mcl_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
2022-10-29 00:18:48 +02:00
2024-08-06 20:37:44 +02:00
local logging = minetest.settings : get_bool ( " mcl_logging_structures " , true )
2022-09-13 01:14:02 +02:00
2022-10-29 00:43:15 +02:00
local mg_name = minetest.get_mapgen_setting ( " mg_name " )
2024-08-06 20:37:44 +02:00
local disabled_structures = minetest.settings : get ( " mcl_disabled_structures " )
if disabled_structures then disabled_structures = disabled_structures : split ( " , " )
else disabled_structures = { } end
2022-08-09 19:28:30 +02:00
function mcl_structures . is_disabled ( structname )
2022-09-11 23:44:33 +02:00
return table.indexof ( disabled_structures , structname ) ~= - 1
2022-08-09 19:28:30 +02:00
end
2022-06-24 23:24:51 +02:00
2024-08-06 20:37:44 +02:00
local ROTATIONS = { " 0 " , " 90 " , " 180 " , " 270 " }
function mcl_structures . parse_rotation ( rotation , pr )
if rotation == " random " and pr then return ROTATIONS [ pr : next ( 1 , # ROTATIONS ) ] end
return rotation
2022-10-24 05:26:47 +02:00
end
2024-08-06 20:37:44 +02:00
--- Get the size after rotation.
-- @param size vector: Size information
-- @param rotation string or number: only 0, 90, 180, 270 are allowed
-- @return vector: new vector, for safety
function mcl_structures . size_rotated ( size , rotation )
if rotation == " 90 " or rotation == " 270 " or rotation == 90 or rotation == 270 then
return vector.new ( size.z , size.y , size.x )
2022-10-24 05:26:47 +02:00
end
2024-08-06 20:37:44 +02:00
return vector.copy ( size )
2022-10-24 05:26:47 +02:00
end
2024-08-06 20:37:44 +02:00
--- Get top left position after apply centering flags and padding.
-- @param pos vector: Placement position
-- @param[opt] size vector: Size information
-- @param[opt] flags string or table: as in minetest.place_schematic, place_center_x, place_center_y
-- @param[opt] padding number: optional margin (integer)
-- @return vector: new vector, for safety
function mcl_structures . top_left_from_flags ( pos , size , flags , padding )
local dx , dy , dz = 0 , 0 , 0
-- must match src/mapgen/mg_schematic.cpp to be consistent
if type ( flags ) == " table " then
if flags [ " place_center_x " ] ~= nil then dx = - math.floor ( ( size.x - 1 ) * 0.5 ) end
if flags [ " place_center_y " ] ~= nil then dy = - math.floor ( ( size.y - 1 ) * 0.5 ) end
if flags [ " place_center_z " ] ~= nil then dz = - math.floor ( ( size.z - 1 ) * 0.5 ) end
elseif type ( flags ) == " string " then
if string.find ( flags , " place_center_x " ) then dx = - math.floor ( ( size.x - 1 ) * 0.5 ) end
if string.find ( flags , " place_center_y " ) then dy = - math.floor ( ( size.y - 1 ) * 0.5 ) end
if string.find ( flags , " place_center_z " ) then dz = - math.floor ( ( size.z - 1 ) * 0.5 ) end
2022-10-24 05:26:47 +02:00
end
2024-08-06 20:37:44 +02:00
if padding then
dx = dx - padding
dz = dz - padding
2022-10-24 05:26:47 +02:00
end
2024-08-06 20:37:44 +02:00
return vector.offset ( pos , dx , dy , dz )
2022-10-24 05:26:47 +02:00
end
2024-08-06 20:37:44 +02:00
-- Expected contents of param:
-- pos vector: position (center.x, base.y, center.z) -- flags NOT supported
-- size vector: structure size after rotation (!)
-- yoffset number: relative to base.y, typically <= 0
-- y_min number: minimum y range permitted
-- y_max number: maximum y range permitted
-- schematic string or schematic: as in minetest.place_schematic
-- rotation string: as in minetest.place_schematic
-- replacement table: as in minetest.place_schematic
-- force_placement boolean: as in minetest.place_schematic
-- prepare table: instructions for preparation (usually from definition)
-- tolerance number: tolerable ground unevenness, -1 to disable, default 10
-- foundation boolean or number: level ground underneath structure (true is a minimum depth of -3)
-- clearance boolean or string or number: clear overhead area (offset, or "top" to begin over the structure only)
-- padding number: additional padding to increase the area, default 1
-- corners number: corner smoothing of foundation and clearance, default 1
-- pr PcgRandom: random generator
-- name string: for logging
local function emerge_schematic_vm ( vm , param )
local pos , size , prepare , surface_mat = param.pos , param.size , param.prepare , nil
-- adjust ground to a move level position
if pos and size and prepare and ( prepare.tolerance or 10 ) >= 0 then
pos , surface_mat = mcl_structures.find_level ( vm , pos , size , prepare.tolerance )
if not pos then
minetest.log ( " warning " , " [mcl_structures] Not spawning " .. tostring ( param.name or param.schematic . name ) .. " at " .. minetest.pos_to_string ( param.pos ) .. " because ground is too uneven. " )
return nil
2022-06-24 03:36:21 +02:00
end
2024-08-06 20:37:44 +02:00
if param.y_max and pos.y > param.y_max then pos.y = param.y_max end
if param.y_min and pos.y < param.y_min then pos.y = param.y_min end
2022-06-24 03:36:21 +02:00
end
2024-08-06 20:37:44 +02:00
-- Prepare the environment
if prepare and ( prepare.clearance or prepare.foundation ) then
-- Get materials from biome:
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 or ( surface_mat and surface_mat.name ) or " mcl_core:dirt_with_grass "
local node_filler = b and b.node_filler or " mcl_core:dirt "
local node_stone = b and b.node_stone or " mcl_core:stone "
-- FIXME: not yet used: local node_dust = b and b.node_dust
local node_top_param2 = node_top == " mcl_core:dirt_with_grass " and b._mcl_grass_palette_index or 0 -- grass color, also other materials?
local corners , padding , depth = prepare.corners or 1 , prepare.padding or 1 , ( type ( prepare.foundation ) == " number " and prepare.foundation ) or - 4
local gp = vector.offset ( pos , - math.floor ( ( size.x - 1 ) * 0.5 ) - padding , 0 , - math.floor ( ( size.z - 1 ) * 0.5 ) - padding )
local gs = vector.offset ( size , padding * 2 , depth , padding * 2 )
if prepare.clearance then
-- minetest.log("action", "[mcl_structures] clearing air "..minetest.pos_to_string(gp).." +"..minetest.pos_to_string(gs).." corners "..corners)
-- TODO: add more parameters?
local yoff , height = 0 , size.y + ( param.yoffset or 0 )
if prepare.clearance == " top " or prepare.clearance == " above " then
yoff , height = height , 0
elseif type ( prepare.clearance ) == " number " then
yoff , height = prepare.clearance , height - prepare.clearance
end
mcl_structures.clearance ( vm , gp.x , gp.y + yoff , gp.z , gs.x , height , gs.z , corners , { name = node_top , param2 = node_top_param2 } , param.pr )
end
if prepare.foundation then
-- minetest.log("action", "[mcl_structures] fill foundation "..minetest.pos_to_string(gp).." +"..minetest.pos_to_string(gs).." corners "..corners)
local depth = ( type ( prepare.foundation ) == " number " and prepare.foundation ) or - 3
mcl_structures.foundation ( vm , gp.x , gp.y - 1 , gp.z , gs.x , depth , gs.z , corners ,
{ name = node_top , param2 = node_top_param2 } , { name = node_filler } , { name = node_stone } , param.pr )
end
2022-09-11 22:41:36 +02:00
end
2024-08-06 20:37:44 +02:00
-- place the actual schematic
pos.y = pos.y + ( param.yoffset or 0 )
minetest.place_schematic_on_vmanip ( vm , pos , param.schematic , param.rotation , param.replacements , param.force_placement , " place_center_x,place_center_z " )
return pos
2022-09-11 22:41:36 +02:00
end
2024-08-06 20:37:44 +02:00
-- Additional parameters:
-- emin vector: emerge area minimum
-- emax vector: emerge area maximum
-- after_placement_callback function: callback after placement, (pmin, pmax, size, rotation, pr, param)
-- callback_param table: additional parameters to callback function
local function emerge_schematic ( blockpos , action , calls_remaining , param )
if calls_remaining >= 1 then return end
local vm = VoxelManip ( )
vm : read_from_map ( param.emin , param.emax )
local pos = emerge_schematic_vm ( vm , param )
vm : write_to_map ( true )
if not pos then return end
-- repair walls (TODO: port to vmanip? but no "vm.find_nodes_in_area" yet)
local pmin = vector.offset ( pos , - math.floor ( ( param.size . x - 1 ) * 0.5 ) , 0 , - math.floor ( ( param.size . z - 1 ) * 0.5 ) )
local pmax = vector.offset ( pmin , param.size . x - 1 , param.size . y - 1 , param.size . z - 1 )
if pmin and pmax and mcl_walls then
for _ , n in pairs ( minetest.find_nodes_in_area ( pmin , pmax , { " group:wall " } ) ) do
mcl_walls.update_wall ( n )
end
2022-06-24 14:02:29 +02:00
end
2024-08-06 20:37:44 +02:00
if pmin and pmax and param.after_placement_callback then
param.after_placement_callback ( pmin , pmax , param.size , param.rotation , param.pr , param.callback_param )
2022-06-24 14:02:29 +02:00
end
end
2024-08-06 20:37:44 +02:00
local DEFAULT_PREPARE = { tolerance = 8 , foundation = - 3 , clearance = false , padding = 1 , corners = 1 }
local DEFAULT_FLAGS = " place_center_x,place_center_z "
function mcl_structures . place_schematic ( pos , yoffset , y_min , y_max , schematic , rotation , replacements , force_placement , flags , prepare , pr , after_placement_callback , callback_param )
if schematic and not schematic.size then -- e.g., igloo still passes filenames
schematic = loadstring ( minetest.serialize_schematic ( schematic , " lua " , { lua_use_comments = false , lua_num_indent_spaces = 0 } ) .. " return schematic " ) ( )
end
rotation = mcl_structures.parse_rotation ( rotation , pr )
local size = mcl_structures.size_rotated ( schematic.size , rotation )
-- area to emerge; note that alignment flags could be non-center, although we almost always use place_center_x,place_center_z
local pmin = mcl_structures.top_left_from_flags ( pos , flags or DEFAULT_FLAGS )
local ppos = vector.offset ( pmin , math.floor ( ( size.x - 1 ) * 0.5 ) , 0 , math.floor ( ( size.z - 1 ) * 0.5 ) ) -- center
local pmax = vector.offset ( pmin , size.x - 1 , size.y - 1 , size.z - 1 )
if prepare == nil or prepare == true then prepare = DEFAULT_PREPARE end
if prepare == false then prepare = { } end
-- area to emerge. Add some margin to allow for finding better suitable ground etc.
local emin , emax = vector.offset ( pmin , - 1 , - 5 , - 1 ) , vector.offset ( pmax , 1 , 5 , 1 )
if prepare then emin.y = emin.y - ( prepare.tolerance or 10 ) end
-- if we need to generate a foundation, we need to emerge a larger area:
if prepare.foundation or prepare.clearance then
-- these functions need some extra margins
local padding , depth , height = ( prepare.padding or 0 ) + 3 , ( prepare.depth or - 4 ) - 15 , size.y * 2 + 6
emin = vector.offset ( pmin , - padding , depth + math.min ( yoffset or 0 , 0 ) , - padding )
emax = vector.offset ( pmax , padding , height + math.max ( yoffset or 0 , 0 ) , padding )
2022-08-10 15:14:51 +02:00
end
2024-08-06 20:37:44 +02:00
minetest.emerge_area ( emin , emax , emerge_schematic , {
emin = emin , emax = emax , name = schematic.name or ( type ( schematic ) == " string " and schematic ) ,
pos = ppos , size = size , yoffset = yoffset , y_min = y_min , y_max = y_max ,
schematic = schematic , rotation = rotation , replacements = replacements , force_placement = force_placement ,
prepare = prepare , pr = pr ,
after_placement_callback = after_placement_callback , callback_param = callback_param
} )
2022-08-10 15:14:51 +02:00
end
2024-08-06 20:37:44 +02:00
-- Call all on_construct handlers
-- also called from mcl_villages for job sites
function mcl_structures . init_node_construct ( pos )
local node = minetest.get_node ( pos )
local def = node and minetest.registered_nodes [ node.name ]
if def and def.on_construct then def.on_construct ( pos ) end
2022-08-01 01:43:23 +02:00
end
2024-08-06 20:37:44 +02:00
-- Find nodes to call on_construct handlers for
function mcl_structures . construct_nodes ( p1 , p2 , nodes )
local nn = minetest.find_nodes_in_area ( p1 , p2 , nodes )
for _ , p in pairs ( nn ) do mcl_structures.init_node_construct ( p ) end
end
2022-08-01 01:43:23 +02:00
2024-08-06 20:37:44 +02:00
function mcl_structures . fill_chests ( p1 , p2 , loot , pr )
for it , lt in pairs ( loot ) do
local nodes = minetest.find_nodes_in_area ( p1 , p2 , it )
for _ , p in pairs ( nodes ) do
local lootitems = mcl_loot.get_multi_loot ( lt , pr )
mcl_structures.init_node_construct ( p )
local meta = minetest.get_meta ( p )
local inv = meta : get_inventory ( )
mcl_loot.fill_inventory ( inv , " main " , lootitems , pr )
2022-08-01 01:43:23 +02:00
end
end
end
2022-11-05 00:55:17 +01:00
function mcl_structures . spawn_mobs ( mob , spawnon , p1 , p2 , pr , n , water )
2022-10-29 00:18:48 +02:00
n = n or 1
2022-11-05 00:55:17 +01:00
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
2022-10-29 00:18:48 +02:00
table.shuffle ( sp )
2024-09-15 23:15:30 +02:00
local count = 0
local mob_def = minetest.registered_entities [ mob ]
local enabled = ( not peaceful ) or ( mob_def and mob_def.spawn_class ~= " hostile " )
for _ , node in pairs ( sp ) do
if enabled and count < n and minetest.add_entity ( vector.offset ( node , 0 , 1 , 0 ) , mob ) then
count = count + 1
2022-10-29 00:18:48 +02:00
end
2024-09-15 23:15:30 +02:00
minetest.get_meta ( node ) : set_string ( " spawnblock " , " yes " ) -- note: also in peaceful mode!
2022-10-29 00:18:48 +02:00
end
end
2022-10-24 07:07:56 +02:00
2022-10-29 00:43:15 +02:00
function mcl_structures . place_structure ( pos , def , pr , blockseed , rot )
2024-08-06 20:37:44 +02:00
if not def then return end
2022-09-13 01:14:02 +02:00
local log_enabled = logging and not def.terrain_feature
2024-08-06 20:37:44 +02:00
-- currently only used by fallen_tree, to check for sufficient empty space to fall
2022-07-02 03:44:13 +02:00
if def.on_place and not def.on_place ( pos , def , pr , blockseed ) then
2022-09-13 01:14:02 +02:00
if log_enabled then
2024-08-06 20:37:44 +02:00
minetest.log ( " warning " , " [mcl_structures] " .. def.name .. " at " .. minetest.pos_to_string ( pos ) .. " not placed. on_place conditions not satisfied. " )
2022-06-22 16:37:44 +02:00
end
2022-06-19 00:17:43 +02:00
return false
end
2024-08-06 20:37:44 +02:00
-- Apply vertical offset for schematic
local yoffset = ( type ( def.y_offset ) == " function " and def.y_offset ( pr ) ) or def.y_offset or 0
if def.schematics and # def.schematics > 0 then
local schematic = def.schematics [ pr : next ( 1 , # def.schematics ) ]
rot = mcl_structures.parse_rotation ( rot or " random " , pr )
if not def.daughters then
mcl_structures.place_schematic ( pos , yoffset , def.y_min , def.y_max , schematic , rot , def.replacements , def.force_placement , " place_center_x,place_center_z " , def.prepare , pr ,
function ( p1 , p2 , size , rotation )
if def.loot then mcl_structures.fill_chests ( p1 , p2 , def.loot , pr ) end
if def.construct_nodes then mcl_structures.construct_nodes ( p1 , p2 , def.construct_nodes ) end
if def.after_place then def.after_place ( pos , def , pr , p1 , p2 , size , rotation ) end
if log_enabled then
minetest.log ( " action " , " [mcl_structures] " .. def.name .. " spawned at " .. minetest.pos_to_string ( pos ) )
end
end )
else -- currently only nether bulwarks + nether outpost with bridges?
-- FIXME: this really needs to be run in a single emerge!
mcl_structures.place_schematic ( pos , yoffset , def.y_min , def.y_max , schematic , rot , def.replacements , def.force_placement , " place_center_x,place_center_z " , def.prepare , pr ,
function ( p1 , p2 , size , rotation )
for i , d in pairs ( def.daughters ) do
local ds = d.files [ pr : next ( 1 , # d.files ) ]
-- Daughter schematics are not loaded yet.
if ds and not ds.size then
ds = loadstring ( minetest.serialize_schematic ( ds , " lua " , { lua_use_comments = false , lua_num_indent_spaces = 0 } ) .. " return schematic " ) ( )
end
-- FIXME: apply centering, apply parent rotation.
2022-11-01 03:27:34 +01:00
local rot = d.rot or 0
2024-08-06 20:37:44 +02:00
local dsize = mcl_structures.size_rotated ( ds.size , rot )
local p = vector.new ( math.floor ( ( p1.x + p2.x ) * 0.5 ) + d.pos . x - math.floor ( ( dsize.x - 1 ) * 0.5 ) , p1.y + ( yoffset or 0 ) + d.pos . y , math.floor ( ( p1.z + p2.z ) * 0.5 ) + d.pos . z - math.floor ( ( dsize.z - 1 ) * 0.5 ) )
local callback = nil
if i == # def.daughters then
callback = function ( )
-- Note: deliberately pos, p1 and p2 from the parent, as these are calls to the parent.
if def.loot then mcl_structures.fill_chests ( p1 , p2 , def.loot , pr ) end
if def.construct_nodes then mcl_structures.construct_nodes ( p1 , p2 , def.construct_nodes ) end
if def.after_place then def.after_place ( pos , def , pr , p1 , p2 , size , rotation ) end
if log_enabled then
minetest.log ( " action " , " [mcl_structures] " .. def.name .. " spawned at " .. minetest.pos_to_string ( pos ) )
end
2022-10-24 07:07:56 +02:00
end
2024-08-06 20:37:44 +02:00
end
mcl_structures.place_schematic ( p , yoffset , d.y_min or def.y_min , d.y_max or def.y_max , ds , rot , nil , true , " place_center_x,place_center_y " , d.prepare , pr , callback )
2022-10-24 07:07:56 +02:00
end
2024-08-06 20:37:44 +02:00
end )
end
if log_enabled then
minetest.log ( " verbose " , " [mcl_structures] " .. def.name .. " to be placed at " .. minetest.pos_to_string ( pos ) )
end
return true
end
if not def.place_func then
minetest.log ( " warning " , " [mcl_structures] no schematics and no place_func for schematic " .. def.name )
return false
end
if def.solid_ground and def.sidelen and not def.prepare then
-- TODO: this assumes place_center, make padding configurable, use actual size?
local ground_p1 = vector.offset ( pos , - math.floor ( def.sidelen / 2 ) , - 1 , - math.floor ( def.sidelen / 2 ) )
local ground_p2 = vector.offset ( ground_p1 , def.sidelen - 1 , 0 , def.sidelen - 1 )
local solid = minetest.find_nodes_in_area ( ground_p1 , ground_p2 , { " group:solid " } )
if # solid < def.sidelen * def.sidelen then
2022-09-13 01:14:02 +02:00
if log_enabled then
2024-08-06 20:37:44 +02:00
minetest.log ( " warning " , " [mcl_structures] " .. def.name .. " at " .. minetest.pos_to_string ( pos ) .. " not placed. No solid ground. " )
2022-06-24 02:11:24 +02:00
end
2024-08-06 20:37:44 +02:00
return false
2022-06-22 16:37:44 +02:00
end
2024-08-06 20:37:44 +02:00
end
local pp = yoffset ~= 0 and vector.offset ( pos , 0 , yoffset , 0 ) or pos
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 ( pp , def , pr , blockseed ) ) then
if def.prepare then
minetest.log ( " warning " , " [mcl_structures] needed prepare for " .. def.name .. " placed at " .. minetest.pos_to_string ( pp ) .. " but did not have size information " )
end
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 mcl_structures.fill_chests ( p1 , p2 , def.loot , pr ) end
if def.construct_nodes then mcl_structures.construct_nodes ( p1 , p2 , def.construct_nodes ) end
end
2022-09-13 01:14:02 +02:00
if log_enabled then
2022-06-25 06:16:18 +02:00
minetest.log ( " action " , " [mcl_structures] " .. def.name .. " placed at " .. minetest.pos_to_string ( pp ) )
2022-06-22 16:37:44 +02:00
end
2022-06-10 04:34:58 +02:00
return true
2024-08-06 20:37:44 +02:00
else
minetest.log ( " warning " , " [mcl_structures] after_place failed for schematic " .. def.name )
return false
2022-06-10 04:34:58 +02:00
end
2024-08-06 20:37:44 +02:00
elseif log_enabled then
minetest.log ( " warning " , " [mcl_structures] place_func failed for schematic " .. def.name )
2022-06-22 16:37:44 +02:00
end
2022-06-10 01:06:24 +02:00
end
2024-08-31 21:01:44 +02:00
local EMPTY_SCHEMATIC = { size = { x = 0 , y = 0 , z = 0 } , data = { } }
function mcl_structures . register_structure ( name , def , nospawn ) --nospawn means it will not be placed by mapgen decoration mechanism
2022-08-09 19:28:30 +02:00
if mcl_structures.is_disabled ( name ) then return end
2022-06-06 03:34:31 +02:00
def.name = name
2024-08-06 20:37:44 +02:00
def.prepare = def.prepare or ( type ( def.make_foundation ) == table and def.make_foundation )
def.flags = def.flags or " place_center_x, place_center_z, force_placement "
if def.filenames then
if # def.filenames == 0 then
minetest.log ( " warning " , " [mcl_structures] schematic " .. name .. " has an empty list of filenames. " )
end
def.schematics = def.schematics or { }
for _ , filename in ipairs ( def.filenames ) do
if not mcl_util.file_exists ( filename ) then
minetest.log ( " warning " , " [mcl_structures] schematic " .. name .. " is missing file " .. tostring ( filename ) )
else
-- load, and ensure we have size information
local s = nil --minetest.read_schematic(filename)
if not s or not s.size then
s = loadstring ( minetest.serialize_schematic ( filename , " lua " , { lua_use_comments = false , lua_num_indent_spaces = 0 } ) .. " return schematic " ) ( )
end
if not s then
minetest.log ( " warning " , " [mcl_structures] failed to load schematic " .. tostring ( filename ) )
elseif not s.size then
minetest.log ( " warning " , " [mcl_structures] no size information for schematic " .. tostring ( filename ) )
else
if logging then
minetest.log ( " verbose " , " [mcl_structures] loaded schematic " .. tostring ( filename ) .. " size " .. minetest.pos_to_string ( s.size ) )
end
if not s.name then s.name = name or filename end
table.insert ( def.schematics , s )
end
end
end
end
if not def.noise_params and def.chunk_probability and not def.fill_ratio then
def.fill_ratio = 1.1 / 80 / 80 -- 1 per chunk, controlled by chunk probability only
end
mcl_structures.registered_structures [ name ] = def
if nospawn then return end -- ice column, boulder
if def.place_on then
2024-08-31 21:01:44 +02:00
minetest.register_on_mods_loaded ( function ( ) --make sure all previous decorations and biomes have been registered
2024-08-03 20:28:23 +02:00
mcl_mapgen_core.register_decoration ( {
name = " mcl_structures: " .. name ,
rank = def.rank or ( def.terrain_feature and 900 ) or 100 , -- run before regular decorations
2024-08-31 21:01:44 +02:00
deco_type = " schematic " ,
schematic = EMPTY_SCHEMATIC ,
place_on = def.place_on ,
spawn_by = def.spawn_by ,
num_spawn_by = def.num_spawn_by ,
2024-08-06 20:37:44 +02:00
sidelen = 80 , -- no def.sidelen subdivisions for now
2024-08-31 21:01:44 +02:00
fill_ratio = def.fill_ratio ,
noise_params = def.noise_params ,
2024-08-06 20:37:44 +02:00
flags = def.flags ,
2024-08-31 21:01:44 +02:00
biomes = def.biomes ,
y_max = def.y_max ,
y_min = def.y_min
2024-08-03 20:28:23 +02:00
} ,
function ( )
def.deco_id = minetest.get_decoration_id ( " mcl_structures: " .. name )
minetest.set_gen_notify ( { decoration = true } , { def.deco_id } )
--catching of gennotify happens in mcl_mapgen_core
end
)
2024-08-31 21:01:44 +02:00
end )
2022-06-06 03:34:31 +02:00
end
end
2022-10-29 00:18:48 +02:00
local structure_spawns = { }
function mcl_structures . register_structure_spawn ( def )
2022-10-29 00:43:15 +02:00
--name,y_min,y_max,spawnon,biomes,chance,interval,limit
2022-10-29 00:18:48 +02:00
minetest.register_abm ( {
label = " Spawn " .. def.name ,
nodenames = def.spawnon ,
min_y = def.y_min or - 31000 ,
max_y = def.y_max or 31000 ,
2022-10-29 00:43:15 +02:00
interval = def.interval or 60 ,
chance = def.chance or 5 ,
2022-10-29 00:18:48 +02:00
action = function ( pos , node , active_object_count , active_object_count_wider )
2022-10-29 00:43:15 +02:00
local limit = def.limit or 7
2022-10-29 01:26:32 +02:00
if active_object_count_wider > limit + mob_cap_animal then return end
2022-10-29 00:43:15 +02:00
if active_object_count_wider > mob_cap_player then return end
2022-10-29 00:18:48 +02:00
local p = vector.offset ( pos , 0 , 1 , 0 )
2024-09-15 23:15:30 +02:00
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
2022-10-29 00:18:48 +02:00
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
2024-09-10 10:18:35 +02:00
-- To avoid a cyclic dependency, run this when modules have finished loading
minetest.register_on_mods_loaded ( function ( )
mcl_mapgen_core.register_generator ( " structures " , nil , function ( minp , maxp , blockseed )
local gennotify = minetest.get_mapgen_object ( " gennotify " )
for _ , struct in pairs ( mcl_structures.registered_structures ) do
if struct.deco_id then
for _ , pos in pairs ( gennotify [ " decoration# " .. struct.deco_id ] or { } ) do
local pr = PcgRandom ( minetest.hash_node_position ( pos ) + blockseed + RANDOM_SEED_OFFSET )
if struct.chunk_probability == nil or pr : next ( 1 , struct.chunk_probability ) == 1 then
mcl_structures.place_structure ( vector.offset ( pos , 0 , 1 , 0 ) , struct , pr , blockseed )
if struct.chunk_probability then break end -- one (attempt) per chunk only
end
end
elseif struct.static_pos then
local pr = PcgRandom ( blockseed + RANDOM_SEED_OFFSET )
for _ , pos in pairs ( struct.static_pos ) do
if vector.in_area ( pos , minp , maxp ) then
mcl_structures.place_structure ( pos , struct , pr , blockseed )
end
end
end
end
return false , false , false
end , 100 , true )
end )
2024-08-06 20:37:44 +02:00