forked from VoxeLibre/VoxeLibre
Huge update of new terraforming, structures, and village code
This commit is contained in:
parent
b3efd6f681
commit
9411e60b8c
|
@ -128,7 +128,7 @@ mcl_mobs.register_mob("mobs_mc:enderdragon", {
|
|||
on_die = function(self, pos, cmi_cause)
|
||||
if self._portal_pos then
|
||||
mcl_portals.spawn_gateway_portal()
|
||||
mcl_structures.place_structure(self._portal_pos,mcl_structures.registered_structures["end_exit_portal_open"],PseudoRandom(minetest.get_mapgen_setting("seed")))
|
||||
vl_structures.place_structure(self._portal_pos,vl_structures.registered_structures["end_exit_portal_open"],PseudoRandom(minetest.get_mapgen_setting("seed")))
|
||||
if self._initial then
|
||||
mcl_experience.throw_xp(pos, 11500) -- 500 + 11500 = 12000
|
||||
minetest.set_node(vector.add(self._portal_pos, vector.new(0, 5, 0)), {name = "mcl_end:dragon_egg"})
|
||||
|
|
|
@ -80,7 +80,7 @@ local function spawn_crystal(pos)
|
|||
crystal_explode(crystal)
|
||||
end
|
||||
local portal_pos = vector.add(portal_center, vector.new(0, -1, 0))
|
||||
mcl_structures.place_structure(portal_pos,mcl_structures.registered_structures["end_exit_portal"],PseudoRandom(minetest.get_mapgen_setting("seed")),-1)
|
||||
vl_structures.place_structure(portal_pos,vl_structures.registered_structures["end_exit_portal"],PseudoRandom(minetest.get_mapgen_setting("seed")),-1)
|
||||
end
|
||||
|
||||
minetest.register_entity("mcl_end:crystal", {
|
||||
|
|
|
@ -89,7 +89,7 @@ minetest.register_craftitem("mcl_end:ender_eye", {
|
|||
local player_name = user:get_player_name()
|
||||
local origin = user:get_pos()
|
||||
origin.y = origin.y + 1.5
|
||||
local strongholds = mcl_structures.registered_structures["end_shrine"].static_pos
|
||||
local strongholds = vl_structures.registered_structures["end_shrine"].static_pos
|
||||
local dim = mcl_worlds.pos_to_dimension(origin)
|
||||
local is_creative = minetest.is_creative_enabled(player_name)
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ local gateway_positions = {
|
|||
local path_gateway_portal = minetest.get_modpath("mcl_structures").."/schematics/mcl_structures_end_gateway_portal.mts"
|
||||
|
||||
local function spawn_gateway_portal(pos, dest_str)
|
||||
return mcl_structures.place_schematic(vector.add(pos, vector.new(-1, -2, -1)), 0, nil, nil, path_gateway_portal, "0", nil, true, nil, nil, nil,
|
||||
return vl_structures.place_schematic(vector.offset(pos, -1, -2, -1), 0, nil, nil, path_gateway_portal, "0", nil, true, nil, nil, nil,
|
||||
dest_str and function()
|
||||
minetest.get_meta(pos):set_string("mcl_portals:gateway_destination", dest_str)
|
||||
end)
|
||||
|
|
|
@ -183,12 +183,12 @@ local dimension_to_teleport = { nether = "overworld", overworld = "nether" }
|
|||
|
||||
local limits = {
|
||||
nether = {
|
||||
pmin = {x=LIM_MIN, y = N_Y_MIN, z = LIM_MIN},
|
||||
pmax = {x=LIM_MAX, y = N_Y_MAX, z = LIM_MAX},
|
||||
pmin = vector.new(LIM_MIN, N_Y_MIN, LIM_MIN),
|
||||
pmax = vector.new(LIM_MAX, N_Y_MAX, LIM_MAX),
|
||||
},
|
||||
overworld = {
|
||||
pmin = {x=LIM_MIN, y = O_Y_MIN, z = LIM_MIN},
|
||||
pmax = {x=LIM_MAX, y = O_Y_MAX, z = LIM_MAX},
|
||||
pmin = vector.new(LIM_MIN, O_Y_MIN, LIM_MIN),
|
||||
pmax = vector.new(LIM_MAX, O_Y_MAX, LIM_MAX),
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -206,12 +206,10 @@ end
|
|||
-- for old portals, so that players don't get surprises. New portals, or portals that lost
|
||||
-- node storage due to destruction should use the lookup table.
|
||||
local function get_portal_pos(pos)
|
||||
local p1 = vector.offset(pos,-5,-1,-5)
|
||||
local p2 = vector.offset(pos,5,5,5)
|
||||
local nn = find_nodes_in_area(p1,p2,{"mcl_portals:portal"})
|
||||
local nn = find_nodes_in_area(vector.offset(pos,-5,-1,-5), vector.offset(pos,5,5,5), {"mcl_portals:portal"})
|
||||
for _,p in pairs(nn) do
|
||||
local m = minetest.get_meta(p):get_string("target_portal")
|
||||
if m and m ~= "" and mcl_vars.get_node(p).name == "mcl_portals:portal" then
|
||||
if m and m ~= "" and minetest.get_node(p).name == "mcl_portals:portal" then
|
||||
return minetest.get_position_from_hash(m)
|
||||
end
|
||||
end
|
||||
|
@ -229,15 +227,13 @@ end
|
|||
local function add_exit(p)
|
||||
local retval = {key=false, new=false}
|
||||
|
||||
if not p or not p.y or not p.z or not p.x then
|
||||
return retval
|
||||
end
|
||||
if not p or not p.y or not p.z or not p.x then return retval end
|
||||
local x, y, z = floor(p.x), floor(p.y), floor(p.z)
|
||||
local p = {x = x, y = y, z = z}
|
||||
local p = vector.new(x, y, z)
|
||||
|
||||
if get_node({x=x,y=y-1,z=z}).name ~= OBSIDIAN
|
||||
if get_node(vector.new(x, y-1, z)).name ~= OBSIDIAN
|
||||
or get_node(p).name ~= PORTAL
|
||||
or get_node({x=x,y=y+1,z=z}).name ~= PORTAL
|
||||
or get_node(vector.new(x, y+1, z)).name ~= PORTAL
|
||||
then
|
||||
return retval
|
||||
end
|
||||
|
@ -301,7 +297,7 @@ local function remove_exit(p)
|
|||
end
|
||||
|
||||
local x, y, z = floor(p.x), floor(p.y), floor(p.z)
|
||||
local p = {x = x, y = y, z = z}
|
||||
local p = vector.new(x, y, z)
|
||||
|
||||
local k = get_exit_key(p)
|
||||
if not exits[k] then
|
||||
|
@ -529,14 +525,14 @@ local function destroy_nether_portal(pos, node)
|
|||
while i <= #nodes do
|
||||
pos = nodes[i]
|
||||
if orientation == 0 then
|
||||
check_remove({x = pos.x - 1, y = pos.y, z = pos.z})
|
||||
check_remove({x = pos.x + 1, y = pos.y, z = pos.z})
|
||||
check_remove(vector.offset(pos, -1, 0, 0))
|
||||
check_remove(vector.offset(pos, 1, 0, 0))
|
||||
else
|
||||
check_remove({x = pos.x, y = pos.y, z = pos.z - 1})
|
||||
check_remove({x = pos.x, y = pos.y, z = pos.z + 1})
|
||||
check_remove(vector.offset(pos, 0, 0, -1))
|
||||
check_remove(vector.offset(pos, 0, 0, 1))
|
||||
end
|
||||
check_remove({x = pos.x, y = pos.y - 1, z = pos.z})
|
||||
check_remove({x = pos.x, y = pos.y + 1, z = pos.z})
|
||||
check_remove(vector.offset(pos, 0, -1, 0))
|
||||
check_remove(vector.offset(pos, 0, 1, 0))
|
||||
remove_exits({pos})
|
||||
i = i + 1
|
||||
end
|
||||
|
@ -624,7 +620,7 @@ local function build_and_light_frame(x1, y1, z1, x2, y2, z2, name)
|
|||
else
|
||||
set_node(pos, {name = PORTAL, param2 = orientation})
|
||||
add_exits({
|
||||
{x=pos.x, y=pos.y-1, z=pos.z}
|
||||
vector.new(pos.x, pos.y-1, pos.z)
|
||||
})
|
||||
end
|
||||
end
|
||||
|
@ -701,8 +697,8 @@ function build_nether_portal(cube_pos1, width, height, orientation, name, clear_
|
|||
-- Build obsidian platform:
|
||||
for x = pos.x - orientation, pos.x + orientation + (width_inner - 1) * (1 - orientation), 1 + orientation do
|
||||
for z = pos.z - 1 + orientation, pos.z + 1 - orientation + (width_inner - 1) * orientation, 2 - orientation do
|
||||
local pp = {x = x, y = pos.y - 1, z = z}
|
||||
local pp_1 = {x = x, y = pos.y - 2, z = z}
|
||||
local pp = vector.new(x, pos.y - 1, z)
|
||||
local pp_1 = vector.new(x, pos.y - 2, z)
|
||||
local nn = get_node(pp).name
|
||||
local nn_1 = get_node(pp_1).name
|
||||
if ((nn=="air" and nn_1 == "air") or not registered_nodes[nn].is_ground_content) and not is_protected(pp, name) then
|
||||
|
@ -823,7 +819,7 @@ local function finalize_teleport(obj, exit)
|
|||
local _, dim = mcl_worlds.y_to_layer(exit.y)
|
||||
|
||||
-- If player stands, player is at ca. something+0.5 which might cause precision problems, so we used ceil for objpos.y
|
||||
objpos = {x = floor(objpos.x+0.5), y = ceil(objpos.y), z = floor(objpos.z+0.5)}
|
||||
objpos = vector.new(floor(objpos.x+0.5), ceil(objpos.y), floor(objpos.z+0.5))
|
||||
if get_node(objpos).name ~= PORTAL then
|
||||
log("action", "Entity no longer standing in portal")
|
||||
return
|
||||
|
@ -1085,19 +1081,19 @@ local function search_for_build_location(blockpos, action, calls_remaining, para
|
|||
if nc2 >= (W_MIN*(H_MIN-1)*W_MIN) - ACCEPTABLE_PORTAL_REPLACES then
|
||||
-- We have sorted the candidates by distance, this is the best location.
|
||||
distance = distance0
|
||||
pos0 = {x=node.x, y=node.y, z=node.z}
|
||||
pos0 = vector.new(node.x, node.y, node.z)
|
||||
log("verbose", "Found acceptable location at "..pos_to_string(pos0)..", distance "..distance0..", air nodes "..nc2)
|
||||
break
|
||||
elseif not most_airy_pos or nc2>most_airy_count then
|
||||
-- Remember the cube with the most amount of air as a fallback.
|
||||
most_airy_count = nc2
|
||||
most_airy_distance = distance0
|
||||
most_airy_pos = {x=node.x, y=node.y, z=node.z}
|
||||
most_airy_pos = vector.new(node.x, node.y, node.z)
|
||||
log("verbose", "Found fallback location at "..pos_to_string(most_airy_pos)..", distance "..distance0..", air nodes "..nc2)
|
||||
elseif most_airy_pos and nc2==most_airy_count and distance0<most_airy_distance then
|
||||
-- Use distance as a tiebreaker.
|
||||
most_airy_distance = distance0
|
||||
most_airy_pos = {x=node.x, y=node.y, z=node.z}
|
||||
most_airy_pos = vector.new(node.x, node.y, node.z)
|
||||
log("verbose", "Found fallback location at "..pos_to_string(most_airy_pos)..", distance "..distance0..", air nodes "..nc2)
|
||||
end
|
||||
|
||||
|
@ -1128,7 +1124,7 @@ local function search_for_build_location(blockpos, action, calls_remaining, para
|
|||
log("verbose", "No space found, emerging one chunk below")
|
||||
end
|
||||
|
||||
local new_target = {x=target.x, y=target.y + direction * mcl_vars.chunk_size_in_nodes, z=target.z}
|
||||
local new_target = vector.new(target.x, target.y + direction * mcl_vars.chunk_size_in_nodes, target.z)
|
||||
pos1, pos2 = find_build_limits(new_target, param.target_dim)
|
||||
local diff = add(pos2, mul(pos1, -1))
|
||||
|
||||
|
@ -1202,7 +1198,7 @@ local function create_portal(origin, target, target_dim, name, obj)
|
|||
origin = origin,
|
||||
target = target,
|
||||
target_dim = target_dim,
|
||||
ideal_target = vector.new(target.x, target.y, target.z), -- copy
|
||||
ideal_target = vector.copy(target),
|
||||
pos1 = pos1,
|
||||
pos2 = pos2,
|
||||
name=name,
|
||||
|
@ -1223,13 +1219,12 @@ local function available_for_nether_portal(p)
|
|||
end
|
||||
|
||||
local function check_and_light_shape(pos, orientation)
|
||||
local stack = {{x = pos.x, y = pos.y, z = pos.z}}
|
||||
local stack = {vector.copy(pos)}
|
||||
local node_list = {}
|
||||
local index_list = {}
|
||||
local node_counter = 0
|
||||
-- Search most low node from the left (pos1) and most right node from the top (pos2)
|
||||
local pos1 = {x = pos.x, y = pos.y, z = pos.z}
|
||||
local pos2 = {x = pos.x, y = pos.y, z = pos.z}
|
||||
local pos1, pos2 = vector.copy(pos), vector.copy(pos)
|
||||
|
||||
local kx, ky, kz = pos.x - 1999, pos.y - 1999, pos.z - 1999
|
||||
while #stack > 0 do
|
||||
|
@ -1247,22 +1242,22 @@ local function check_and_light_shape(pos, orientation)
|
|||
return false
|
||||
end
|
||||
node_counter = node_counter + 1
|
||||
node_list[node_counter] = {x = x, y = y, z = z}
|
||||
node_list[node_counter] = vector.new(x, y, z)
|
||||
index_list[k] = true
|
||||
stack[i].y = y - 1
|
||||
stack[i + 1] = {x = x, y = y + 1, z = z}
|
||||
stack[i + 1] = vector.new(x, y + 1, z)
|
||||
if orientation == 0 then
|
||||
stack[i + 2] = {x = x - 1, y = y, z = z}
|
||||
stack[i + 3] = {x = x + 1, y = y, z = z}
|
||||
stack[i + 2] = vector.new(x - 1, y, z)
|
||||
stack[i + 3] = vector.new(x + 1, y, z)
|
||||
else
|
||||
stack[i + 2] = {x = x, y = y, z = z - 1}
|
||||
stack[i + 3] = {x = x, y = y, z = z + 1}
|
||||
stack[i + 2] = vector.new(x, y, z - 1)
|
||||
stack[i + 3] = vector.new(x, y, z + 1)
|
||||
end
|
||||
if (y < pos1.y) or (y == pos1.y and (x < pos1.x or z < pos1.z)) then
|
||||
pos1 = {x = x, y = y, z = z}
|
||||
pos1 = vector.new(x, y, z)
|
||||
end
|
||||
if (x > pos2.x or z > pos2.z) or (x == pos2.x and z == pos2.z and y > pos2.y) then
|
||||
pos2 = {x = x, y = y, z = z}
|
||||
pos2 = vector.new(x, y, z)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1344,7 +1339,7 @@ local function check_portal_then_teleport(obj, origin, exit)
|
|||
remove_exits({exit})
|
||||
-- Also remove from structure storage, otherwise ABM will try the same bad exit again.
|
||||
local objpos = obj:get_pos()
|
||||
delete_portal_pos({x = floor(objpos.x+0.5), y = ceil(objpos.y), z = floor(objpos.z+0.5)})
|
||||
delete_portal_pos(vector.new(floor(objpos.x+0.5), ceil(objpos.y), floor(objpos.z+0.5)))
|
||||
|
||||
origin_flush(origin, nil)
|
||||
return
|
||||
|
@ -1367,7 +1362,7 @@ local function teleport_no_delay(obj, portal_pos)
|
|||
local target_dim = dimension_to_teleport[current_dim]
|
||||
|
||||
-- If player stands, player is at ca. something+0.5 which might cause precision problems, so we used ceil for objpos.y
|
||||
origin = {x = floor(objpos.x+0.5), y = ceil(objpos.y), z = floor(objpos.z+0.5)}
|
||||
origin = vector.new(floor(objpos.x+0.5), ceil(objpos.y), floor(objpos.z+0.5))
|
||||
if get_node(origin).name ~= PORTAL then return end
|
||||
|
||||
local target = get_target(origin)
|
||||
|
@ -1450,8 +1445,8 @@ local function animation(player, playername)
|
|||
end
|
||||
minetest.add_particlespawner({
|
||||
amount = 1,
|
||||
minpos = {x = pos.x - 0.1, y = pos.y + 1.4, z = pos.z - 0.1},
|
||||
maxpos = {x = pos.x + 0.1, y = pos.y + 1.6, z = pos.z + 0.1},
|
||||
minpos = vector.offset(pos, -0.1, 1.4, -0.1),
|
||||
maxpos = vector.offset(pos, 0.1, 1.6, 0.1),
|
||||
minvel = 0,
|
||||
maxvel = 0,
|
||||
minacc = 0,
|
||||
|
@ -1496,11 +1491,11 @@ minetest.register_abm({
|
|||
local time = random() * 1.9 + 0.5
|
||||
local velocity, acceleration
|
||||
if o == 1 then
|
||||
velocity = {x = random() * 0.7 + 0.3, y = random() - 0.5, z = random() - 0.5}
|
||||
acceleration = {x = random() * 1.1 + 0.3, y = random() - 0.5, z = random() - 0.5}
|
||||
velocity = vector.new(random() * 0.7 + 0.3, random() - 0.5, random() - 0.5)
|
||||
acceleration = vector.new(random() * 1.1 + 0.3, random() - 0.5, random() - 0.5)
|
||||
else
|
||||
velocity = {x = random() - 0.5, y = random() - 0.5, z = random() * 0.7 + 0.3}
|
||||
acceleration = {x = random() - 0.5, y = random() - 0.5, z = random() * 1.1 + 0.3}
|
||||
velocity = vector.new(random() - 0.5, random() - 0.5, random() * 0.7 + 0.3)
|
||||
acceleration = vector.new(random() - 0.5, random() - 0.5, random() * 1.1 + 0.3)
|
||||
end
|
||||
local distance = add(mul(velocity, time), mul(acceleration, time * time / 2))
|
||||
if d == 1 then
|
||||
|
@ -1566,12 +1561,12 @@ minetest.override_item(OBSIDIAN, {
|
|||
end
|
||||
|
||||
-- check each of 6 sides of it and destroy every portal
|
||||
check_remove({x = pos.x - 1, y = pos.y, z = pos.z})
|
||||
check_remove({x = pos.x + 1, y = pos.y, z = pos.z})
|
||||
check_remove({x = pos.x, y = pos.y, z = pos.z - 1})
|
||||
check_remove({x = pos.x, y = pos.y, z = pos.z + 1})
|
||||
check_remove({x = pos.x, y = pos.y - 1, z = pos.z})
|
||||
check_remove({x = pos.x, y = pos.y + 1, z = pos.z})
|
||||
check_remove(vector.offset(pos, -1, 0, 0))
|
||||
check_remove(vector.offset(pos, 1, 0, 0))
|
||||
check_remove(vector.offset(pos, 0, 0, -1))
|
||||
check_remove(vector.offset(pos, 0, 0, 1))
|
||||
check_remove(vector.offset(pos, 0, -1, 0))
|
||||
check_remove(vector.offset(pos, 0, 1, 0))
|
||||
end,
|
||||
|
||||
_on_ignite = function(user, pointed_thing)
|
||||
|
@ -1579,16 +1574,16 @@ minetest.override_item(OBSIDIAN, {
|
|||
-- Check empty spaces around obsidian and light all frames found.
|
||||
-- Permit igniting of portals that are partly protected to maintain integrity.
|
||||
local portals_placed =
|
||||
mcl_portals.light_nether_portal({x = x - 1, y = y, z = z}) or mcl_portals.light_nether_portal({x = x + 1, y = y, z = z}) or
|
||||
mcl_portals.light_nether_portal({x = x, y = y - 1, z = z}) or mcl_portals.light_nether_portal({x = x, y = y + 1, z = z}) or
|
||||
mcl_portals.light_nether_portal({x = x, y = y, z = z - 1}) or mcl_portals.light_nether_portal({x = x, y = y, z = z + 1})
|
||||
mcl_portals.light_nether_portal(vector.new(x - 1, y, z)) or mcl_portals.light_nether_portal(vector.new(x + 1, y, z)) or
|
||||
mcl_portals.light_nether_portal(vector.new(x, y - 1, z)) or mcl_portals.light_nether_portal(vector.new(x, y + 1, z)) or
|
||||
mcl_portals.light_nether_portal(vector.new(x, y, z - 1)) or mcl_portals.light_nether_portal(vector.new(x, y, z + 1))
|
||||
if portals_placed then
|
||||
log("verbose", "Nether portal activated at "..pos_to_string({x=x,y=y,z=z})..".")
|
||||
log("verbose", "Nether portal activated at "..pos_to_string(vector.new(x, y, z))..".")
|
||||
if minetest.get_modpath("doc") then
|
||||
doc.mark_entry_as_revealed(user:get_player_name(), "nodes", PORTAL)
|
||||
|
||||
-- Achievement for finishing a Nether portal TO the Nether
|
||||
local dim = mcl_worlds.pos_to_dimension({x=x, y=y, z=z})
|
||||
local dim = mcl_worlds.pos_to_dimension(vector.new(x, y, z))
|
||||
if minetest.get_modpath("awards") and dim ~= "nether" and user:is_player() then
|
||||
awards.unlock(user:get_player_name(), "mcl:buildNetherPortal")
|
||||
end
|
||||
|
@ -1600,13 +1595,13 @@ minetest.override_item(OBSIDIAN, {
|
|||
end,
|
||||
})
|
||||
|
||||
mcl_structures.register_structure("nether_portal",{
|
||||
vl_structures.register_structure("nether_portal",{
|
||||
nospawn = true,
|
||||
filenames = {
|
||||
modpath.."/schematics/mcl_portals_nether_portal.mts"
|
||||
}
|
||||
})
|
||||
mcl_structures.register_structure("nether_portal_open",{
|
||||
vl_structures.register_structure("nether_portal_open",{
|
||||
nospawn = true,
|
||||
filenames = {
|
||||
modpath.."/schematics/mcl_portals_nether_portal_open.mts"
|
||||
|
|
|
@ -26,6 +26,9 @@ local mod_mcl_crimson = minetest.get_modpath("mcl_crimson")
|
|||
local mod_mcl_blackstone = minetest.get_modpath("mcl_blackstone")
|
||||
local mod_mcl_mangrove = minetest.get_modpath("mcl_mangrove")
|
||||
|
||||
-- these are registered asynchronously
|
||||
local deco_ids_fungus = {}
|
||||
local deco_ids_trees = {}
|
||||
local deco_id_chorus_plant
|
||||
|
||||
--
|
||||
|
@ -3988,7 +3991,11 @@ local function register_decorations()
|
|||
schematic = mod_mcl_mangrove .. "/schematics/mcl_mangrove_tree_1.mts",
|
||||
flags = "place_center_x, place_center_z, force_placement",
|
||||
rotation = "random",
|
||||
})
|
||||
}, function()
|
||||
local f = minetest.get_decoration_id("mcl_biomes:mangrove_tree_1")
|
||||
table.insert(deco_ids_trees, f)
|
||||
minetest.set_gen_notify({decoration = true}, {f})
|
||||
end)
|
||||
mcl_mapgen_core.register_decoration({
|
||||
name = "mcl_biomes:mangrove_tree_2",
|
||||
deco_type = "schematic",
|
||||
|
@ -4001,7 +4008,11 @@ local function register_decorations()
|
|||
schematic = mod_mcl_mangrove .. "/schematics/mcl_mangrove_tree_2.mts",
|
||||
flags = "place_center_x, place_center_z, force_placement",
|
||||
rotation = "random",
|
||||
})
|
||||
}, function()
|
||||
local f = minetest.get_decoration_id("mcl_biomes:mangrove_tree_2")
|
||||
table.insert(deco_ids_trees, f)
|
||||
minetest.set_gen_notify({decoration = true}, {f})
|
||||
end)
|
||||
mcl_mapgen_core.register_decoration({
|
||||
name = "mcl_biomes:mangrove_tree_3",
|
||||
deco_type = "schematic",
|
||||
|
@ -4014,7 +4025,11 @@ local function register_decorations()
|
|||
schematic = mod_mcl_mangrove .. "/schematics/mcl_mangrove_tree_3.mts",
|
||||
flags = "place_center_x, place_center_z, force_placement",
|
||||
rotation = "random",
|
||||
})
|
||||
}, function()
|
||||
local f = minetest.get_decoration_id("mcl_biomes:mangrove_tree_3")
|
||||
table.insert(deco_ids_trees, f)
|
||||
minetest.set_gen_notify({decoration = true}, {f})
|
||||
end)
|
||||
mcl_mapgen_core.register_decoration({
|
||||
name = "mcl_biomes:mangrove_tree_4",
|
||||
deco_type = "schematic",
|
||||
|
@ -4027,7 +4042,11 @@ local function register_decorations()
|
|||
schematic = mod_mcl_mangrove .. "/schematics/mcl_mangrove_tree_4.mts",
|
||||
flags = "place_center_x, place_center_z, force_placement",
|
||||
rotation = "random",
|
||||
})
|
||||
}, function()
|
||||
local f = minetest.get_decoration_id("mcl_biomes:mangrove_tree_4")
|
||||
table.insert(deco_ids_trees, f)
|
||||
minetest.set_gen_notify({decoration = true}, {f})
|
||||
end)
|
||||
mcl_mapgen_core.register_decoration({
|
||||
name = "mcl_biomes:mangrove_tree_5",
|
||||
deco_type = "schematic",
|
||||
|
@ -4040,7 +4059,11 @@ local function register_decorations()
|
|||
schematic = mod_mcl_mangrove .. "/schematics/mcl_mangrove_tree_5.mts",
|
||||
flags = "place_center_x, place_center_z, force_placement",
|
||||
rotation = "random",
|
||||
})
|
||||
}, function()
|
||||
local f = minetest.get_decoration_id("mcl_biomes:mangrove_tree_5")
|
||||
table.insert(deco_ids_trees, f)
|
||||
minetest.set_gen_notify({decoration = true}, {f})
|
||||
end)
|
||||
mcl_mapgen_core.register_decoration({
|
||||
name = "mcl_biomes:mangrove_bee_nest",
|
||||
deco_type = "schematic",
|
||||
|
@ -4063,7 +4086,11 @@ local function register_decorations()
|
|||
rotation = "random",
|
||||
spawn_by = "group:flower",
|
||||
priority = 1550,
|
||||
})
|
||||
}, function()
|
||||
local f = minetest.get_decoration_id("mcl_biomes:mangrove_bee_nest")
|
||||
table.insert(deco_ids_trees, f)
|
||||
minetest.set_gen_notify({decoration = true}, {f})
|
||||
end)
|
||||
mcl_mapgen_core.register_decoration({
|
||||
deco_type = "simple",
|
||||
place_on = {"mcl_mud:mud"},
|
||||
|
@ -5577,7 +5604,11 @@ local function register_dimension_decorations()
|
|||
schematic = mod_mcl_crimson .. "/schematics/warped_fungus_1.mts",
|
||||
size = vector.new(5, 11, 5),
|
||||
rotation = "random",
|
||||
})
|
||||
}, function()
|
||||
local f = minetest.get_decoration_id("mcl_biomes:warped_tree1")
|
||||
table.insert(deco_ids_fungus, f)
|
||||
minetest.set_gen_notify({decoration = true}, {f})
|
||||
end)
|
||||
mcl_mapgen_core.register_decoration({
|
||||
deco_type = "schematic",
|
||||
name = "mcl_biomes:warped_tree2",
|
||||
|
@ -5591,7 +5622,11 @@ local function register_dimension_decorations()
|
|||
schematic = mod_mcl_crimson .. "/schematics/warped_fungus_2.mts",
|
||||
size = vector.new(5, 6, 5),
|
||||
rotation = "random",
|
||||
})
|
||||
}, function()
|
||||
local f = minetest.get_decoration_id("mcl_biomes:warped_tree2")
|
||||
table.insert(deco_ids_fungus, f)
|
||||
minetest.set_gen_notify({decoration = true}, {f})
|
||||
end)
|
||||
mcl_mapgen_core.register_decoration({
|
||||
deco_type = "schematic",
|
||||
name = "mcl_biomes:warped_tree3",
|
||||
|
@ -5605,7 +5640,11 @@ local function register_dimension_decorations()
|
|||
schematic = mod_mcl_crimson .. "/schematics/warped_fungus_3.mts",
|
||||
size = vector.new(5, 12, 5),
|
||||
rotation = "random",
|
||||
})
|
||||
}, function()
|
||||
local f = minetest.get_decoration_id("mcl_biomes:warped_tree3")
|
||||
table.insert(deco_ids_fungus, f)
|
||||
minetest.set_gen_notify({decoration = true}, {f})
|
||||
end)
|
||||
mcl_mapgen_core.register_decoration({
|
||||
deco_type = "simple",
|
||||
place_on = {"mcl_crimson:warped_nylium", "mcl_crimson:twisting_vines"},
|
||||
|
@ -5653,7 +5692,7 @@ local function register_dimension_decorations()
|
|||
})
|
||||
mcl_mapgen_core.register_decoration({
|
||||
deco_type = "schematic",
|
||||
name = "mcl_biomes:crimson_tree",
|
||||
name = "mcl_biomes:crimson_tree1",
|
||||
place_on = {"mcl_crimson:crimson_nylium"},
|
||||
sidelen = 16,
|
||||
fill_ratio = 0.008,
|
||||
|
@ -5664,7 +5703,12 @@ local function register_dimension_decorations()
|
|||
schematic = mod_mcl_crimson .. "/schematics/crimson_fungus_1.mts",
|
||||
size = vector.new(5, 8, 5),
|
||||
rotation = "random",
|
||||
})
|
||||
}, function()
|
||||
local f = minetest.get_decoration_id("mcl_biomes:crimson_tree1")
|
||||
table.insert(deco_ids_fungus, f)
|
||||
minetest.set_gen_notify({decoration = true}, {f})
|
||||
end)
|
||||
minetest.register_alias("mcl_biomes:crimson_tree", "mcl_biomes:crimson_tree1") -- legacy inconsistency, fixed 08/2024
|
||||
mcl_mapgen_core.register_decoration({
|
||||
deco_type = "schematic",
|
||||
name = "mcl_biomes:crimson_tree2",
|
||||
|
@ -5678,7 +5722,11 @@ local function register_dimension_decorations()
|
|||
schematic = mod_mcl_crimson .. "/schematics/crimson_fungus_2.mts",
|
||||
size = vector.new(5, 12, 5),
|
||||
rotation = "random",
|
||||
})
|
||||
}, function()
|
||||
local f = minetest.get_decoration_id("mcl_biomes:crimson_tree2")
|
||||
table.insert(deco_ids_fungus, f)
|
||||
minetest.set_gen_notify({decoration = true}, {f})
|
||||
end)
|
||||
mcl_mapgen_core.register_decoration({
|
||||
deco_type = "schematic",
|
||||
name = "mcl_biomes:crimson_tree3",
|
||||
|
@ -5692,7 +5740,11 @@ local function register_dimension_decorations()
|
|||
schematic = mod_mcl_crimson .. "/schematics/crimson_fungus_3.mts",
|
||||
size = vector.new(7, 13, 7),
|
||||
rotation = "random",
|
||||
})
|
||||
}, function()
|
||||
local f = minetest.get_decoration_id("mcl_biomes:crimson_tree3")
|
||||
table.insert(deco_ids_fungus, f)
|
||||
minetest.set_gen_notify({decoration = true}, {f})
|
||||
end)
|
||||
mcl_mapgen_core.register_decoration({
|
||||
deco_type = "simple",
|
||||
place_on = {"mcl_crimson:warped_nylium", "mcl_crimson:weeping_vines", "mcl_nether:netherrack"},
|
||||
|
@ -5903,13 +5955,11 @@ local function register_dimension_decorations()
|
|||
decoration = "mcl_end:chorus_flower",
|
||||
height = 1,
|
||||
biomes = {"End", "EndMidlands", "EndHighlands", "EndBarrens", "EndSmallIslands"},
|
||||
})
|
||||
|
||||
},function()
|
||||
deco_id_chorus_plant = minetest.get_decoration_id("mcl_biomes:chorus_plant")
|
||||
minetest.set_gen_notify({decoration = true}, {deco_id_chorus_plant})
|
||||
|
||||
end)
|
||||
-- TODO: End cities
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
@ -5943,30 +5993,6 @@ if mg_name ~= "singlenode" then
|
|||
register_dimension_decorations()
|
||||
|
||||
-- Overworld decorations for v6 are handled in mcl_mapgen_core
|
||||
|
||||
local deco_ids_fungus = {
|
||||
minetest.get_decoration_id("mcl_biomes:crimson_tree1"),
|
||||
minetest.get_decoration_id("mcl_biomes:crimson_tree2"),
|
||||
minetest.get_decoration_id("mcl_biomes:crimson_tree3"),
|
||||
minetest.get_decoration_id("mcl_biomes:warped_tree1"),
|
||||
minetest.get_decoration_id("mcl_biomes:warped_tree2"),
|
||||
minetest.get_decoration_id("mcl_biomes:warped_tree3")
|
||||
}
|
||||
local deco_ids_trees = {
|
||||
minetest.get_decoration_id("mcl_biomes:mangrove_tree_1"),
|
||||
minetest.get_decoration_id("mcl_biomes:mangrove_tree_2"),
|
||||
minetest.get_decoration_id("mcl_biomes:mangrove_tree_3"),
|
||||
minetest.get_decoration_id("mcl_biomes:mangrove_tree_4"),
|
||||
minetest.get_decoration_id("mcl_biomes:mangrove_tree_5"),
|
||||
minetest.get_decoration_id("mcl_biomes:mangrove_bee_nest"),
|
||||
}
|
||||
for _, f in pairs(deco_ids_fungus) do
|
||||
minetest.set_gen_notify({decoration = true}, {f})
|
||||
end
|
||||
for _, f in pairs(deco_ids_trees) do
|
||||
minetest.set_gen_notify({decoration = true}, {f})
|
||||
end
|
||||
|
||||
local function mangrove_roots_gen(gennotify, pr)
|
||||
for _, f in pairs(deco_ids_trees) do
|
||||
for _, pos in ipairs(gennotify["decoration#" .. f] or {}) do
|
||||
|
@ -6020,9 +6046,7 @@ if mg_name ~= "singlenode" then
|
|||
local biomemap = minetest.get_mapgen_object("biomemap")
|
||||
local swamp_biome_id = minetest.get_biome_id("MangroveSwamp")
|
||||
local swamp_shore_id = minetest.get_biome_id("MangroveSwamp_shore")
|
||||
local is_swamp = table.indexof(biomemap, swamp_biome_id) ~= -1
|
||||
local is_swamp_shore = table.indexof(biomemap, swamp_shore_id) ~= -1
|
||||
if is_swamp or is_swamp_shore then
|
||||
if biomemap and (table.indexof(biomemap, swamp_biome_id) ~= -1 or table.indexof(biomemap, swamp_shore_id) ~= -1) then
|
||||
mangrove_roots_gen(gennotify, pr)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
mcl_dungeons = {}
|
||||
|
||||
local logging = minetest.settings:get_bool("mcl_logging_dungeons", false)
|
||||
local mg_name = minetest.get_mapgen_setting("mg_name")
|
||||
-- Are dungeons disabled?
|
||||
if mcl_vars.mg_dungeons == false or mg_name == "singlenode" then return end
|
||||
|
@ -234,7 +235,9 @@ local function ecb_spawn_dungeon(blockpos, action, calls_remaining, param)
|
|||
-- Check conditions. If okay, start generating
|
||||
if check and (openings_counter < 1 or openings_counter > 5) then return end
|
||||
|
||||
if logging then
|
||||
minetest.log("action","[mcl_dungeons] Placing new dungeon at "..minetest.pos_to_string(vector_new(x, y, z)))
|
||||
end
|
||||
-- Okay! Spawning starts!
|
||||
|
||||
-- Remember spawner chest positions to set metadata later
|
||||
|
@ -369,7 +372,9 @@ local function ecb_spawn_dungeon(blockpos, action, calls_remaining, param)
|
|||
|
||||
set_node(pos, {name="mcl_chests:chest", param2=facedir})
|
||||
local meta = get_meta(pos)
|
||||
if logging then
|
||||
minetest.log("action", "[mcl_dungeons] Filling chest " .. tostring(c) .. " at " .. minetest.pos_to_string(pos))
|
||||
end
|
||||
mcl_loot.fill_inventory(meta:get_inventory(), "main", mcl_loot.get_multi_loot(loottable, pr), pr)
|
||||
end
|
||||
|
||||
|
@ -404,7 +409,9 @@ local function dungeons_nodes(minp, maxp, blockseed)
|
|||
local z = pr:next(minp.z, maxp.z-dim.z-1)
|
||||
local p1 = vector_new(x, y, z)
|
||||
local p2 = vector_new(x+dim.x+1, y+dim.y+1, z+dim.z+1)
|
||||
if logging then
|
||||
minetest.log("verbose","[mcl_dungeons] size=" ..minetest.pos_to_string(dim) .. ", emerge from "..minetest.pos_to_string(p1) .. " to " .. minetest.pos_to_string(p2))
|
||||
end
|
||||
emerge_area(p1, p2, ecb_spawn_dungeon, {p1=p1, p2=p2, dim=dim, pr=pr})
|
||||
end
|
||||
end
|
||||
|
@ -414,7 +421,9 @@ function mcl_dungeons.spawn_dungeon(p1, _, pr)
|
|||
if not p1 or not pr or not p1.x or not p1.y or not p1.z then return end
|
||||
local dim = dungeonsizes[pr:next(1, #dungeonsizes)]
|
||||
local p2 = vector_new(p1.x+dim.x+1, p1.y+dim.y+1, p1.z+dim.z+1)
|
||||
if logging then
|
||||
minetest.log("verbose","[mcl_dungeons] size=" ..minetest.pos_to_string(dim) .. ", emerge from "..minetest.pos_to_string(p1) .. " to " .. minetest.pos_to_string(p2))
|
||||
end
|
||||
emerge_area(p1, p2, ecb_spawn_dungeon, {p1=p1, p2=p2, dim=dim, pr=pr, dontcheck=true})
|
||||
end
|
||||
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
local registered_generators = {}
|
||||
|
||||
local lvm, nodes, param2 = 0, 0, 0
|
||||
local lvm_buffer, lvm_buffer2 = {}, {}
|
||||
|
||||
local logging = minetest.settings:get_bool("mcl_logging_mapgen", false)
|
||||
local log_timing = minetest.settings:get_bool("mcl_logging_mapgen_timing", false) -- detailed, for performance debugging
|
||||
|
||||
local registered_generators = {}
|
||||
local lvm, nodes, param2 = 0, 0, 0
|
||||
|
||||
local function run_generators(minp, maxp, blockseed)
|
||||
if nodes == 0 then return end
|
||||
for _, rec in ipairs(registered_generators) do
|
||||
|
@ -25,8 +23,8 @@ minetest.register_on_generated(function(minp, maxp, blockseed)
|
|||
if lvm > 0 then
|
||||
local vm, emin, emax = minetest.get_mapgen_object("voxelmanip")
|
||||
local area = VoxelArea(emin, emax)
|
||||
local data = vm:get_data(lvm_buffer)
|
||||
local data2 = param2 > 0 and vm:get_param2_data(lvm_buffer2)
|
||||
local data = vm:get_data()
|
||||
local data2 = param2 > 0 and vm:get_param2_data()
|
||||
if log_timing then
|
||||
minetest.log("action", string.format("[mcl_mapgen_core] %-20s %s ... %s %8.2fms", "get_data", minetest.pos_to_string(minp), minetest.pos_to_string(maxp), (os.clock() - t1)*1000))
|
||||
end
|
||||
|
@ -148,12 +146,11 @@ end
|
|||
-- adding for example a new structure can still change everything that comes
|
||||
-- later, because currently decoration blockseeds are incremented sequentially
|
||||
-- c.f., https://github.com/minetest/minetest/issues/14919
|
||||
local minetest_register_decoration = minetest.register_decoration
|
||||
local pending_decorations = {}
|
||||
function mcl_mapgen_core.register_decoration(def, callback)
|
||||
if pending_decorations == nil then
|
||||
minetest.log("warning", "Decoration registered after mapgen: "..tostring(def.name))
|
||||
minetest_register_decoration(def)
|
||||
minetest.register_decoration(def)
|
||||
if callback ~= nil then callback() end
|
||||
return
|
||||
end
|
||||
|
@ -186,8 +183,7 @@ local function sort_decorations()
|
|||
end
|
||||
table.sort(keys)
|
||||
for _, key in ipairs(keys) do
|
||||
-- minetest.log("action", "Deco: "..key)
|
||||
minetest_register_decoration(map[key])
|
||||
minetest.register_decoration(map[key])
|
||||
if map[key].callback then map[key].callback() end
|
||||
end
|
||||
pending_decorations = nil -- as we will not run again
|
||||
|
|
|
@ -509,7 +509,7 @@ local function generate_mgv6_structures()
|
|||
local surface = minetest.find_nodes_in_area({x=p.x,y=p.y-1,z=p.z}, floor, "mcl_core:snowblock")
|
||||
local surface2 = minetest.find_nodes_in_area({x=p.x,y=p.y-1,z=p.z}, floor, "mcl_core:dirt_with_grass_snow")
|
||||
if #surface + #surface2 >= 63 then
|
||||
mcl_structures.call_struct(p, "igloo", nil, pr)
|
||||
vl_structures.call_struct(p, "igloo", nil, pr)
|
||||
chunk_has_igloo = true
|
||||
end
|
||||
end
|
||||
|
@ -528,7 +528,7 @@ local function generate_mgv6_structures()
|
|||
local nodes = minetest.find_nodes_in_area(p1, p2, {"mcl_core:sandstone", "mcl_core:stone", "mcl_core:diorite", "mcl_core:andesite", "mcl_core:granite", "mcl_core:stone_with_coal", "mcl_core:dirt", "mcl_core:gravel"})
|
||||
|
||||
if #nodes >= 100 then -- >= 80%
|
||||
mcl_structures.call_struct(p1, "fossil", nil, pr)
|
||||
vl_structures.call_struct(p1, "fossil", nil, pr)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -565,10 +565,7 @@ local function generate_mgv6_structures()
|
|||
if #free_nodes >= ((size.x+1)*(size.y+1)*(size.z+1)) then
|
||||
local place = {x=p.x, y=WITCH_HUT_HEIGHT-1, z=p.z}
|
||||
|
||||
-- FIXME: For some mysterious reason (black magic?) this
|
||||
-- function does sometimes NOT spawn the witch hut. One can only see the
|
||||
-- oak wood nodes in the water, but no hut. :-/
|
||||
mcl_structures.place_structure(place,mcl_structures.registered_structures["witch_hut"],pr)
|
||||
vl_structures.place_structure(place,vl_structures.registered_structures["witch_hut"],pr)
|
||||
|
||||
local function place_tree_if_free(pos, prev_result)
|
||||
local nn = minetest.get_node(pos).name
|
||||
|
@ -637,7 +634,7 @@ local function generate_mgv6_structures()
|
|||
local spruce_collisions = minetest.find_nodes_in_area({x=p.x+1,y=p.y+2,z=p.z+1}, {x=p.x+4, y=p.y+6, z=p.z+4}, {"mcl_core:sprucetree", "mcl_core:spruceleaves"})
|
||||
|
||||
if #surface >= 9 and #spruce_collisions == 0 then
|
||||
mcl_structures.place_structure(p,mcl_structures.registered_structures["ice_spike_large"],pr)
|
||||
vl_structures.place_structure(p,vl_structures.registered_structures["ice_spike_large"],pr)
|
||||
end
|
||||
elseif spike < 100 then
|
||||
-- Check surface
|
||||
|
@ -648,7 +645,7 @@ local function generate_mgv6_structures()
|
|||
local spruce_collisions = minetest.find_nodes_in_area({x=p.x+1,y=p.y+1,z=p.z+1}, {x=p.x+6, y=p.y+6, z=p.z+6}, {"mcl_core:sprucetree", "mcl_core:spruceleaves"})
|
||||
|
||||
if #surface >= 25 and #spruce_collisions == 0 then
|
||||
mcl_structures.place_structure(p,mcl_structures.registered_structures["ice_spike_small"],pr)
|
||||
vl_structures.place_structure(p,vl_structures.registered_structures["ice_spike_small"],pr)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,20 +5,18 @@ local peaceful = minetest.settings:get_bool("only_peaceful_mobs", false)
|
|||
|
||||
local BLAZE_SPAWNER_MAX_LIGHT = 11
|
||||
|
||||
mcl_structures.register_structure("nether_outpost",{
|
||||
vl_structures.register_structure("nether_outpost",{
|
||||
place_on = {"mcl_nether:netherrack","mcl_crimson:crimson_nylium","mcl_crimson:warped_nylium","mcl_blackstone:basalt","mcl_blackstone:soul_soil","mcl_blackstone:blackstone","mcl_nether:soul_sand"},
|
||||
chunk_probability = 23,
|
||||
flags = "all_floors",
|
||||
flags = "place_center_x, place_center_y, all_floors",
|
||||
biomes = {"Nether","SoulsandValley","WarpedForest","CrimsonForest","BasaltDelta"},
|
||||
sidelen = 24,
|
||||
solid_ground = true,
|
||||
prepare = { tolerance=20, padding=2, corners=5, foundation=true, clearance=true },
|
||||
prepare = { tolerance = 20, padding = 4, corners = 5, foundation = true, clear = true, clear_top = 4 },
|
||||
y_min = mcl_vars.mg_lava_nether_max - 1,
|
||||
y_max = mcl_vars.mg_nether_max - 30,
|
||||
filenames = { modpath.."/schematics/mcl_nether_fortresses_nether_outpost.mts" },
|
||||
y_offset = 0,
|
||||
after_place = function(pos)
|
||||
local sp = minetest.find_nodes_in_area(pos,vector.offset(pos,0,20,0),{"mcl_mobspawners:spawner"})
|
||||
after_place = function(pos,def,pr,p1,p2)
|
||||
local sp = minetest.find_nodes_in_area(p1,p2,{"mcl_mobspawners:spawner"})
|
||||
if not sp[1] then return end
|
||||
mcl_mobspawners.setup_spawner(sp[1], "mobs_mc:blaze", 0, BLAZE_SPAWNER_MAX_LIGHT, 10, 8, 0)
|
||||
end
|
||||
|
@ -29,85 +27,101 @@ local nbridges = {
|
|||
modpath.."/schematics/mcl_nether_fortresses_nether_bridge_3.mts",
|
||||
modpath.."/schematics/mcl_nether_fortresses_nether_bridge_4.mts",
|
||||
}
|
||||
mcl_structures.register_structure("nether_bridge",{
|
||||
vl_structures.register_structure("nether_bridge",{
|
||||
place_on = {"mcl_nether:nether_lava_source","mcl_nether:netherrack","mcl_crimson:crimson_nylium","mcl_crimson:warped_nylium","mcl_blackstone:basalt","mcl_blackstone:soul_soil","mcl_blackstone:blackstone","mcl_nether:soul_sand"},
|
||||
chunk_probability = 5, -- because of the small height allowed, these are quite rare otherwise
|
||||
flags = "all_floors, liquid_surface",
|
||||
prepare = { tolerance=-1, clearance = 6 },
|
||||
chunk_probability = 8, -- because of the y restriction these are quite rare
|
||||
flags = "place_center_x, place_center_y, all_floors",
|
||||
prepare = { tolerance = 50, padding = -1, corners = 0, clear_bottom = 8, clear_top = 6 }, -- asymmetric padding would be nice to have
|
||||
force_placement = true,
|
||||
sidelen = 38,
|
||||
solid_ground = false,
|
||||
y_min = mcl_vars.mg_lava_nether_max - 5,
|
||||
y_max = mcl_vars.mg_lava_nether_max + 15,
|
||||
y_min = mcl_vars.mg_lava_nether_max,
|
||||
y_max = mcl_vars.mg_lava_nether_max + 25, -- otherwise, we may see some very long legs
|
||||
filenames = nbridges,
|
||||
y_offset = function(pr) return pr:next(-12, -5) end,
|
||||
after_place = function(pos,def,pr)
|
||||
local p1 = vector.offset(pos,-14,0,-14)
|
||||
local p2 = vector.offset(pos,14,24,14)
|
||||
mcl_structures.spawn_mobs("mobs_mc:witherskeleton",{"mcl_blackstone:blackstone_chiseled_polished"},p1,p2,pr,5)
|
||||
y_offset = function(pr) return pr:next(-8, -5) end,
|
||||
after_place = function(pos,def,pr,p1,p2)
|
||||
vl_structures.spawn_mobs("mobs_mc:witherskeleton",{"mcl_blackstone:blackstone_chiseled_polished"},p1,p2,pr,5)
|
||||
-- p1.y is not a typo, we want to lowest level only
|
||||
local legs = minetest.find_nodes_in_area(vector.new(p1.x,p1.y,p1.z),vector.new(p2.x,p1.y,p2.z), "mcl_nether:nether_brick")
|
||||
local bricks = {}
|
||||
-- TODO: port leg generation to voxel manipulators?
|
||||
for _,leg in pairs(legs) do
|
||||
while true do
|
||||
leg = vector.offset(leg,0,-1,0)
|
||||
local nodename = minetest.get_node(leg).name
|
||||
if nodename == "ignore" then break end
|
||||
if nodename ~= "air" and nodename ~= "mcl_core:lava_source" and minetest.get_item_group(nodename, "solid") ~= 0 then break end
|
||||
table.insert(bricks,leg)
|
||||
end
|
||||
end
|
||||
minetest.bulk_set_node(bricks, {name = "mcl_nether:nether_brick", param2 = 2})
|
||||
end
|
||||
})
|
||||
|
||||
mcl_structures.register_structure("nether_outpost_with_bridges",{
|
||||
vl_structures.register_structure("nether_outpost_with_bridges",{
|
||||
place_on = {"mcl_nether:netherrack","mcl_crimson:crimson_nylium","mcl_crimson:warped_nylium","mcl_blackstone:basalt","mcl_blackstone:soul_soil","mcl_blackstone:blackstone","mcl_nether:soul_sand","mcl_nether:nether_lava_source"},
|
||||
chunk_probability = 33,
|
||||
flags = "all_floors",
|
||||
chunk_probability = 10, -- because of the y restriction, it will still be rare
|
||||
flags = "place_center_x, place_center_y, all_floors",
|
||||
biomes = {"Nether","SoulsandValley","WarpedForest","CrimsonForest","BasaltDelta"},
|
||||
sidelen = 24,
|
||||
solid_ground = true,
|
||||
prepare = { tolerance=30, padding=4, corners=5, foundation=true, clearance=true },
|
||||
prepare = { tolerance = 20, padding = 4, corners = 5, foundation = true, clear_top = 3 },
|
||||
y_min = mcl_vars.mg_lava_nether_max - 1,
|
||||
y_max = mcl_vars.mg_nether_max - 30,
|
||||
y_max = mcl_vars.mg_lava_nether_max + 40,
|
||||
-- todo: spawn_by a lot of air?
|
||||
filenames = { modpath.."/schematics/mcl_nether_fortresses_nether_outpost.mts" },
|
||||
daughters = {{
|
||||
files = { nbridges[1] },
|
||||
pos = vector.new(0,-3,-25),
|
||||
rot = 180,
|
||||
prepare = { tolerance = -1, foundation = false, clearance = 14, padding = -2, corners=2 },
|
||||
},
|
||||
emerge_padding = { vector.new(-38,-8,-38), vector.new(38,0,38) },
|
||||
daughters = {
|
||||
{
|
||||
files = { nbridges[1] },
|
||||
filenames = { nbridges[1], nbridges[2] },
|
||||
pos = vector.new(0,-3,24),
|
||||
rot = 0,
|
||||
rotation= 0,
|
||||
no_level = true,
|
||||
prepare = { tolerance = -1, foundation = false, clearance = 14, padding = -2, corners=2 },
|
||||
prepare = { tolerance = -1, foundation = false, clear = true, clear_bottom = 16, clear_top = 2, padding = 1, corners = 4 },
|
||||
},
|
||||
{
|
||||
files = { nbridges[1] },
|
||||
pos = vector.new(-25,-3,0),
|
||||
rot = 270,
|
||||
no_level = true,
|
||||
prepare = { tolerance = -1, foundation = false, clearance = 14, padding = -2, corners=2 },
|
||||
},
|
||||
{
|
||||
files = { nbridges[1] },
|
||||
filenames = { nbridges[1], nbridges[2] },
|
||||
pos = vector.new(24,-3,0),
|
||||
rot = 90,
|
||||
rotation = 90,
|
||||
no_level = true,
|
||||
prepare = { tolerance = -1, foundation = false, clearance = 14, padding = -2, corners=2 },
|
||||
prepare = { tolerance = -1, foundation = false, clear = true, clear_bottom = 16, clear_top = 2, padding = 1, corners = 4 },
|
||||
},
|
||||
{
|
||||
filenames = { nbridges[1], nbridges[2] },
|
||||
pos = vector.new(0,-3,-25),
|
||||
rotation = 180,
|
||||
no_level = true,
|
||||
prepare = { tolerance = -1, foundation = false, clear = true, clear_bottom = 16, clear_top = 2, padding = 1, corners = 4 },
|
||||
},
|
||||
{
|
||||
filenames = { nbridges[1], nbridges[2] },
|
||||
pos = vector.new(-25,-3,0),
|
||||
rotation = 270,
|
||||
no_level = true,
|
||||
prepare = { tolerance = -1, foundation = false, clear = true, clear_bottom = 16, clear_top = 2, padding = 1, corners = 4 },
|
||||
},
|
||||
},
|
||||
after_place = function(pos,def,pr)
|
||||
local sp = minetest.find_nodes_in_area(pos,vector.offset(pos,0,20,0),{"mcl_mobspawners:spawner"})
|
||||
after_place = function(pos,def,pr,p1,p2)
|
||||
local sp = minetest.find_nodes_in_area(p1,p2,{"mcl_mobspawners:spawner"})
|
||||
if not sp[1] then return end
|
||||
mcl_mobspawners.setup_spawner(sp[1], "mobs_mc:blaze", 0, BLAZE_SPAWNER_MAX_LIGHT, 10, 8, 0)
|
||||
|
||||
local legs = minetest.find_nodes_in_area(vector.offset(pos,-45,-2,-45),vector.offset(pos,45,0,45), "mcl_nether:nether_brick")
|
||||
-- the -3 offset needs to be carefully aligned with the bridges above
|
||||
local legs = minetest.find_nodes_in_area(vector.offset(pos,-45,-3,-45),vector.offset(pos,45,-3,45), "mcl_nether:nether_brick")
|
||||
local bricks = {}
|
||||
-- TODO: port leg generation to voxel manipulators?
|
||||
for _,leg in pairs(legs) do
|
||||
while minetest.get_item_group(mcl_vars.get_node(vector.offset(leg,0,-1,0), true, 333333).name, "solid") == 0 do
|
||||
while true do
|
||||
leg = vector.offset(leg,0,-1,0)
|
||||
local nodename = minetest.get_node(leg).name
|
||||
if nodename == "ignore" then break end
|
||||
if nodename ~= "air" and nodename ~= "mcl_core:lava_source" and minetest.get_item_group(nodename, "solid") ~= 0 then break end
|
||||
table.insert(bricks,leg)
|
||||
end
|
||||
end
|
||||
minetest.bulk_set_node(bricks, {name = "mcl_nether:nether_brick", param2 = 2})
|
||||
|
||||
local p1, p2 = vector.offset(pos,-45,12,-45), vector.offset(pos,45,22,45)
|
||||
mcl_structures.spawn_mobs("mobs_mc:witherskeleton",{"mcl_blackstone:blackstone_chiseled_polished"},p1,p2,pr,5)
|
||||
vl_structures.spawn_mobs("mobs_mc:witherskeleton",{"mcl_blackstone:blackstone_chiseled_polished"},p1,p2,pr,5)
|
||||
end
|
||||
})
|
||||
|
||||
mcl_structures.register_structure_spawn({
|
||||
vl_structures.register_structure_spawn({
|
||||
name = "mobs_mc:witherskeleton",
|
||||
y_min = mcl_vars.mg_lava_nether_max,
|
||||
y_max = mcl_vars.mg_nether_max,
|
||||
|
@ -117,14 +131,12 @@ mcl_structures.register_structure_spawn({
|
|||
spawnon = { "mcl_blackstone:blackstone_chiseled_polished" },
|
||||
})
|
||||
|
||||
mcl_structures.register_structure("nether_bulwark",{
|
||||
vl_structures.register_structure("nether_bulwark",{
|
||||
place_on = {"mcl_nether:netherrack","mcl_crimson:crimson_nylium","mcl_crimson:warped_nylium","mcl_blackstone:basalt","mcl_blackstone:soul_soil","mcl_blackstone:blackstone","mcl_nether:soul_sand"},
|
||||
chunk_probability = 29,
|
||||
flags = "all_floors",
|
||||
flags = "place_center_x, place_center_y, all_floors",
|
||||
biomes = {"Nether","SoulsandValley","WarpedForest","CrimsonForest"},
|
||||
sidelen = 36,
|
||||
solid_ground = true,
|
||||
prepare = { tolerance=15, padding=4, corners=4, foundation=true, clearance=true },
|
||||
prepare = { tolerance=10, padding=4, corners=5, foundation=-5, clear_top=0 },
|
||||
y_min = mcl_vars.mg_lava_nether_max - 1,
|
||||
y_max = mcl_vars.mg_nether_max - 30,
|
||||
filenames = {
|
||||
|
@ -134,24 +146,24 @@ mcl_structures.register_structure("nether_bulwark",{
|
|||
modpath.."/schematics/mcl_nether_fortresses_nether_bulwark_4.mts",
|
||||
},
|
||||
daughters = {{
|
||||
files = {
|
||||
filenames = {
|
||||
modpath.."/schematics/mcl_nether_fortresses_nether_bulwark_interior_1.mts",
|
||||
modpath.."/schematics/mcl_nether_fortresses_nether_bulwark_interior_2.mts",
|
||||
modpath.."/schematics/mcl_nether_fortresses_nether_bulwark_interior_3.mts",
|
||||
modpath.."/schematics/mcl_nether_fortresses_nether_bulwark_interior_4.mts",
|
||||
},
|
||||
pos = vector.new(0,1,0),
|
||||
force_place = true,
|
||||
prepare = { tolerance = -1, foundation = false, clearance = false },
|
||||
rotation = "random",
|
||||
force_placement = true,
|
||||
prepare = { tolerance = -1, foundation = false, clear = false },
|
||||
},
|
||||
},
|
||||
y_offset = 0,
|
||||
construct_nodes = {"group:wall"},
|
||||
after_place = function(pos,def,pr)
|
||||
local p1, p2 = vector.offset(pos,-14,0,-14), vector.offset(pos,14,24,14)
|
||||
mcl_structures.spawn_mobs("mobs_mc:piglin",{"mcl_blackstone:blackstone_brick_polished","mcl_stairs:slab_blackstone_polished"},p1,p2,pr,5)
|
||||
mcl_structures.spawn_mobs("mobs_mc:piglin_brute",{"mcl_blackstone:blackstone_brick_polished","mcl_stairs:slab_blackstone_polished"},p1,p2,pr)
|
||||
mcl_structures.spawn_mobs("mobs_mc:hoglin",{"mcl_blackstone:nether_gold"},p1,p2,pr,4)
|
||||
after_place = function(pos,def,pr,p1,p2)
|
||||
vl_structures.spawn_mobs("mobs_mc:piglin",{"mcl_blackstone:blackstone_brick_polished","mcl_stairs:slab_blackstone_polished"},p1,p2,pr,5)
|
||||
vl_structures.spawn_mobs("mobs_mc:piglin_brute",{"mcl_blackstone:blackstone_brick_polished","mcl_stairs:slab_blackstone_polished"},p1,p2,pr)
|
||||
vl_structures.spawn_mobs("mobs_mc:hoglin",{"mcl_blackstone:nether_gold"},p1,p2,pr,4)
|
||||
end,
|
||||
loot = {
|
||||
["mcl_chests:chest_small" ] ={
|
||||
|
@ -198,22 +210,22 @@ mcl_structures.register_structure("nether_bulwark",{
|
|||
},
|
||||
})
|
||||
|
||||
mcl_structures.register_structure_spawn({
|
||||
vl_structures.register_structure_spawn({
|
||||
name = "mobs_mc:piglin",
|
||||
y_min = mcl_vars.mg_nether_min,
|
||||
y_max = mcl_vars.mg_nether_max,
|
||||
chance = 10,
|
||||
interval = 60,
|
||||
limit = 9,
|
||||
spawnon = {"mcl_blackstone:blackstone_brick_polished","mcl_stairs:slab_blackstone_polished"},
|
||||
spawnon = {"mcl_blackstone:blackstone_brick_polished", "mcl_stairs:slab_blackstone_polished"},
|
||||
})
|
||||
|
||||
mcl_structures.register_structure_spawn({
|
||||
vl_structures.register_structure_spawn({
|
||||
name = "mobs_mc:piglin_brute",
|
||||
y_min = mcl_vars.mg_nether_min,
|
||||
y_max = mcl_vars.mg_nether_max,
|
||||
chance = 20,
|
||||
interval = 60,
|
||||
limit = 4,
|
||||
spawnon = {"mcl_blackstone:blackstone_brick_polished","mcl_stairs:slab_blackstone_polished"},
|
||||
spawnon = {"mcl_blackstone:blackstone_brick_polished", "mcl_stairs:slab_blackstone_polished"},
|
||||
})
|
||||
|
|
|
@ -83,19 +83,20 @@ local function generate_strongholds(minp, maxp, blockseed)
|
|||
pos.z = maxp.z - 7
|
||||
end
|
||||
|
||||
--mcl_structures.call_struct(pos, "end_portal_shrine", nil, pr)
|
||||
--vl_structures.call_struct(pos, "end_portal_shrine", nil, pr)
|
||||
strongholds[s].generated = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
mcl_structures.register_structure("end_shrine",{
|
||||
vl_structures.register_structure("end_shrine",{
|
||||
static_pos = init_strongholds(),
|
||||
prepare = { tolerance = -1, foundation = false, clear = false },
|
||||
filenames = {
|
||||
minetest.get_modpath("mcl_structures").."/schematics/mcl_structures_end_portal_room_simple.mts"
|
||||
},
|
||||
after_place = function(pos,def,pr,blockseed,p1,p2,size,rotation)
|
||||
after_place = function(pos,def,pr,p1,p2,size,rotation)
|
||||
local p1 = vector.subtract(pos,size)
|
||||
local p2 = vector.add(pos,size)
|
||||
local spawners = minetest.find_nodes_in_area(p1, p2, "mcl_mobspawners:spawner")
|
||||
|
|
|
@ -1,38 +1,7 @@
|
|||
# mcl_structures
|
||||
Structure placement API for MCL2.
|
||||
VoxeLibre structures
|
||||
|
||||
## mcl_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 contains standard VoxeLibre structures such as nether portals.
|
||||
|
||||
### structure definition
|
||||
{
|
||||
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
|
||||
sidelen = int, --length of one side of the structure. used for foundations.
|
||||
solid_ground = bool, -- structure requires solid ground
|
||||
make_foundation = bool, -- a foundation is automatically built for the structure. needs the sidelen param
|
||||
loot = ,
|
||||
--a table of loot tables for mcl_loot indexed by node names
|
||||
-- e.g. { ["mcl_chests:chest_small"] = {loot},... }
|
||||
}
|
||||
## mcl_structures.registered_structures
|
||||
Table of the registered structure defintions indexed by name.
|
||||
The API has been redesigned and moved to the vl_structures module.
|
||||
|
||||
## mcl_structures.place_structure(pos, def, pr)
|
||||
Places a structure using the mapgen placement function
|
||||
|
||||
## mcl_structures.place_schematic(pos, schematic, rotation, replacements, force_placement, flags, after_placement_callback, pr, callback_param)
|
||||
|
|
|
@ -1,464 +0,0 @@
|
|||
mcl_structures.registered_structures = {}
|
||||
|
||||
local peaceful = minetest.settings:get_bool("only_peaceful_mobs", false)
|
||||
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 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
|
||||
|
||||
local logging = minetest.settings:get_bool("mcl_logging_structures", true)
|
||||
|
||||
local mg_name = minetest.get_mapgen_setting("mg_name")
|
||||
|
||||
local disabled_structures = minetest.settings:get("mcl_disabled_structures")
|
||||
if disabled_structures then disabled_structures = disabled_structures:split(",")
|
||||
else disabled_structures = {} end
|
||||
function mcl_structures.is_disabled(structname)
|
||||
return table.indexof(disabled_structures,structname) ~= -1
|
||||
end
|
||||
|
||||
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
|
||||
end
|
||||
|
||||
--- 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)
|
||||
end
|
||||
return vector.copy(size)
|
||||
end
|
||||
|
||||
--- 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
|
||||
end
|
||||
if padding then
|
||||
dx = dx - padding
|
||||
dz = dz - padding
|
||||
end
|
||||
return vector.offset(pos, dx, dy, dz)
|
||||
end
|
||||
|
||||
-- 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
|
||||
end
|
||||
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
|
||||
end
|
||||
-- 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
|
||||
end
|
||||
-- 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
|
||||
end
|
||||
|
||||
-- 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
|
||||
end
|
||||
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)
|
||||
end
|
||||
end
|
||||
|
||||
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)
|
||||
end
|
||||
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
|
||||
})
|
||||
end
|
||||
|
||||
-- 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
|
||||
end
|
||||
|
||||
-- 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
|
||||
|
||||
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)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function mcl_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_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
|
||||
end
|
||||
minetest.get_meta(node):set_string("spawnblock", "yes") -- note: also in peaceful mode!
|
||||
end
|
||||
end
|
||||
|
||||
function mcl_structures.place_structure(pos, def, pr, blockseed, rot)
|
||||
if not def then return end
|
||||
local log_enabled = logging and not def.terrain_feature
|
||||
-- currently only used by fallen_tree, to check for sufficient empty space to fall
|
||||
if def.on_place and not def.on_place(pos,def,pr,blockseed) then
|
||||
if log_enabled then
|
||||
minetest.log("warning","[mcl_structures] "..def.name.." at "..minetest.pos_to_string(pos).." not placed. on_place conditions not satisfied.")
|
||||
end
|
||||
return false
|
||||
end
|
||||
-- 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.
|
||||
local rot = d.rot or 0
|
||||
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
|
||||
end
|
||||
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)
|
||||
end
|
||||
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
|
||||
if log_enabled then
|
||||
minetest.log("warning", "[mcl_structures] "..def.name.." at "..minetest.pos_to_string(pos).." not placed. No solid ground.")
|
||||
end
|
||||
return false
|
||||
end
|
||||
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
|
||||
if log_enabled then
|
||||
minetest.log("action","[mcl_structures] "..def.name.." placed at "..minetest.pos_to_string(pp))
|
||||
end
|
||||
return true
|
||||
else
|
||||
minetest.log("warning","[mcl_structures] after_place failed for schematic "..def.name)
|
||||
return false
|
||||
end
|
||||
elseif log_enabled then
|
||||
minetest.log("warning","[mcl_structures] place_func failed for schematic "..def.name)
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
if mcl_structures.is_disabled(name) then return end
|
||||
def.name = name
|
||||
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
|
||||
minetest.register_on_mods_loaded(function() --make sure all previous decorations and biomes have been registered
|
||||
def.deco = mcl_mapgen_core.register_decoration({
|
||||
name = "mcl_structures:deco_"..name,
|
||||
priority = def.priority or (def.terrain_feature and 900) or 100, -- run before regular decorations
|
||||
deco_type = "schematic",
|
||||
schematic = EMPTY_SCHEMATIC,
|
||||
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
|
||||
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
|
||||
}, function()
|
||||
def.deco_id = minetest.get_decoration_id("mcl_structures:deco_"..name)
|
||||
minetest.set_gen_notify({decoration=true}, { def.deco_id })
|
||||
end)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
local structure_spawns = {}
|
||||
function mcl_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
|
||||
|
||||
-- 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)
|
||||
|
|
@ -2,15 +2,12 @@ local modname = minetest.get_current_modname()
|
|||
local S = minetest.get_translator(modname)
|
||||
local modpath = minetest.get_modpath(modname)
|
||||
|
||||
local function temple_placement_callback(pos,def, pr)
|
||||
local hl = def.sidelen / 2
|
||||
local p1 = vector.offset(pos,-hl,-hl,-hl)
|
||||
local p2 = vector.offset(pos,hl,hl,hl)
|
||||
local function temple_placement_callback(pos,def,pr,p1,p2)
|
||||
-- Delete cacti leftovers:
|
||||
local cactus_nodes = minetest.find_nodes_in_area_under_air(p1, p2, "mcl_core:cactus")
|
||||
if cactus_nodes and #cactus_nodes > 0 then
|
||||
for _, pos in pairs(cactus_nodes) do
|
||||
local node_below = minetest.get_node({x=pos.x, y=pos.y-1, z=pos.z})
|
||||
local node_below = minetest.get_node(vector.offset(pos,0,-1,0))
|
||||
if node_below and node_below.name == "mcl_core:sandstone" then
|
||||
minetest.swap_node(pos, {name="air"})
|
||||
end
|
||||
|
@ -32,12 +29,11 @@ local function temple_placement_callback(pos,def, pr)
|
|||
end
|
||||
end
|
||||
|
||||
mcl_structures.register_structure("desert_temple",{
|
||||
vl_structures.register_structure("desert_temple",{
|
||||
place_on = {"group:sand"},
|
||||
flags = "place_center_x, place_center_z",
|
||||
solid_ground = true,
|
||||
sidelen = 18,
|
||||
y_offset = -12,
|
||||
prepare = { padding = 3, corners = 3, foundation = true, clear = false },
|
||||
chunk_probability = 8,
|
||||
y_max = mcl_vars.mg_overworld_max,
|
||||
y_min = 1,
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
local modname = minetest.get_current_modname()
|
||||
--local S = minetest.get_translator(modname)
|
||||
local modpath = minetest.get_modpath(modname)
|
||||
|
||||
vl_structures.register_structure("desert_well",{
|
||||
place_on = {"group:sand"},
|
||||
flags = "place_center_x, place_center_z",
|
||||
chunk_probability = 15,
|
||||
y_max = mcl_vars.mg_overworld_max,
|
||||
y_min = 1,
|
||||
y_offset = -2,
|
||||
biomes = { "Desert" },
|
||||
filenames = { modpath.."/schematics/mcl_structures_desert_well.mts" },
|
||||
})
|
||||
|
|
@ -4,38 +4,34 @@ local modpath = minetest.get_modpath(modname)
|
|||
|
||||
local spawnon = {"mcl_end:purpur_block"}
|
||||
|
||||
local function spawn_shulkers(pos,def,pr)
|
||||
local p1 = vector.offset(pos,-def.sidelen/2,-1,-def.sidelen/2)
|
||||
local p2 = vector.offset(pos,def.sidelen/2,def.sidelen,def.sidelen/2)
|
||||
mcl_structures.spawn_mobs("mobs_mc:shulker",spawnon,p1,p2,pr,1)
|
||||
|
||||
local guard = minetest.find_node_near(pos,def.sidelen,{"mcl_itemframes:item_frame"})
|
||||
if guard then
|
||||
minetest.add_entity(vector.offset(guard,0,-1.5,0),"mobs_mc:shulker")
|
||||
local function spawn_shulkers(pos,def,pr,p1,p2)
|
||||
vl_structures.spawn_mobs("mobs_mc:shulker",spawnon,p1,p2,pr,1)
|
||||
local guard = minetest.find_nodes_in_area(p1,p2,{"mcl_itemframes:item_frame"})
|
||||
if #guard > 0 then
|
||||
minetest.add_entity(vector.offset(guard[1],0,-1.5,0),"mobs_mc:shulker")
|
||||
end
|
||||
end
|
||||
|
||||
mcl_structures.register_structure("end_shipwreck",{
|
||||
vl_structures.register_structure("end_shipwreck",{
|
||||
place_on = {"mcl_end:end_stone"},
|
||||
flags = "place_center_x, place_center_z, all_floors",
|
||||
y_offset = function(pr) return pr:next(-50,-20) end,
|
||||
y_offset = function(pr) return pr:next(20,50) end,
|
||||
force_placement = false,
|
||||
prepare = { foundation = false, clear = false },
|
||||
chunk_probability = 25,
|
||||
--y_max = mcl_vars.mg_end_max,
|
||||
--y_min = mcl_vars.mg_end_min -100,
|
||||
biomes = { "End", "EndHighlands", "EndMidlands", "EndBarrens", "EndSmallIslands" },
|
||||
sidelen = 32,
|
||||
filenames = {
|
||||
modpath.."/schematics/mcl_structures_end_shipwreck_1.mts",
|
||||
},
|
||||
construct_nodes = {"mcl_chests:ender_chest_small","mcl_chests:ender_chest","mcl_brewing:stand_000","mcl_chests:violet_shulker_box_small"},
|
||||
after_place = function(pos,def,pr)
|
||||
local fr = minetest.find_node_near(pos,def.sidelen,{"mcl_itemframes:item_frame"})
|
||||
if fr then
|
||||
if mcl_itemframes then
|
||||
mcl_itemframes.update_item_entity(fr,minetest.get_node(fr))
|
||||
after_place = function(pos,def,pr,p1,p2)
|
||||
local fr = minetest.find_nodes_in_area(p1,p2,{"mcl_itemframes:item_frame"})
|
||||
if #fr > 0 and mcl_itemframes then
|
||||
mcl_itemframes.update_item_entity(fr[1],minetest.get_node(fr))
|
||||
end
|
||||
end
|
||||
return spawn_shulkers(pos,def,pr)
|
||||
return spawn_shulkers(pos,def,pr,p1,p2)
|
||||
end,
|
||||
loot = {
|
||||
[ "mcl_itemframes:item_frame" ] ={{
|
||||
|
@ -52,7 +48,7 @@ mcl_structures.register_structure("end_shipwreck",{
|
|||
{ itemstring = "mcl_mobitems:bone", weight = 20, amount_min = 4, amount_max=6 },
|
||||
{ itemstring = "mcl_farming:beetroot_seeds", weight = 16, amount_min = 1, amount_max=10 },
|
||||
{ itemstring = "mcl_core:gold_ingot", weight = 15, amount_min = 2, amount_max = 7 },
|
||||
--{ itemstring = "mcl_bamboo:bamboo", weight = 15, amount_min = 1, amount_max=3 }, --FIXME BAMBOO
|
||||
{ itemstring = "mcl_bamboo:bamboo", weight = 15, amount_min = 1, amount_max=3 },
|
||||
{ itemstring = "mcl_core:iron_ingot", weight = 15, amount_min = 4, amount_max = 8 },
|
||||
{ itemstring = "mcl_core:diamond", weight = 3, amount_min = 2, amount_max = 7 },
|
||||
{ itemstring = "mcl_mobitems:saddle", weight = 3, },
|
||||
|
@ -85,16 +81,16 @@ mcl_structures.register_structure("end_shipwreck",{
|
|||
}
|
||||
})
|
||||
|
||||
mcl_structures.register_structure("end_boat",{
|
||||
vl_structures.register_structure("end_boat",{
|
||||
place_on = {"mcl_end:end_stone"},
|
||||
fill_ratio = 0.01,
|
||||
flags = "place_center_x, place_center_z, all_floors",
|
||||
y_offset = function(pr) return pr:next(15,30) end,
|
||||
chunk_probability = 900,
|
||||
force_placement = false,
|
||||
prepare = { foundation = false, clear = false },
|
||||
chunk_probability = 10,
|
||||
--y_max = mcl_vars.mg_end_max,
|
||||
--y_min = mcl_vars.mg_end_min -100,
|
||||
biomes = { "End", "EndHighlands", "EndMidlands", "EndBarrens", "EndSmallIslands" },
|
||||
sidelen = 20,
|
||||
filenames = {
|
||||
modpath.."/schematics/mcl_structures_end_boat.mts",
|
||||
},
|
||||
|
@ -133,7 +129,7 @@ mcl_structures.register_structure("end_boat",{
|
|||
}
|
||||
})
|
||||
|
||||
mcl_structures.register_structure_spawn({
|
||||
vl_structures.register_structure_spawn({
|
||||
name = "mobs_mc:shulker",
|
||||
y_min = mcl_vars.mg_end_min,
|
||||
y_max = mcl_vars.mg_end_max,
|
||||
|
|
|
@ -2,8 +2,7 @@ local modname = minetest.get_current_modname()
|
|||
local S = minetest.get_translator(modname)
|
||||
local modpath = minetest.get_modpath(modname)
|
||||
|
||||
|
||||
mcl_structures.register_structure("end_spawn_obsidian_platform",{
|
||||
vl_structures.register_structure("end_spawn_obsidian_platform",{
|
||||
static_pos ={mcl_vars.mg_end_platform_pos},
|
||||
place_func = function(pos,def,pr)
|
||||
local obby = minetest.find_nodes_in_area(vector.offset(pos,-2,0,-2),vector.offset(pos,2,0,2),{"air","mcl_end:end_stone"})
|
||||
|
@ -14,17 +13,13 @@ mcl_structures.register_structure("end_spawn_obsidian_platform",{
|
|||
end,
|
||||
})
|
||||
|
||||
mcl_structures.register_structure("end_exit_portal",{
|
||||
vl_structures.register_structure("end_exit_portal",{
|
||||
static_pos = { mcl_vars.mg_end_exit_portal_pos },
|
||||
filenames = {
|
||||
modpath.."/schematics/mcl_structures_end_exit_portal.mts"
|
||||
},
|
||||
after_place = function(pos,def,pr,blockseed)
|
||||
if minetest.settings:get_bool("only_peaceful_mobs", false) then
|
||||
return
|
||||
end
|
||||
local p1 = vector.offset(pos,-16,-16,-16)
|
||||
local p2 = vector.offset(pos,16,21,16)
|
||||
after_place = function(pos,def,pr,p1,p2)
|
||||
if minetest.settings:get_bool("only_peaceful_mobs", false) then return end
|
||||
minetest.emerge_area(p1,p2,function(blockpos, action, calls_remaining, param)
|
||||
if calls_remaining > 0 then return end
|
||||
minetest.bulk_set_node(minetest.find_nodes_in_area(p1,p2,{"mcl_portals:portal_end"}),{name="air"})
|
||||
|
@ -32,9 +27,9 @@ mcl_structures.register_structure("end_exit_portal",{
|
|||
if obj then
|
||||
local dragon_entity = obj:get_luaentity()
|
||||
dragon_entity._portal_pos = pos
|
||||
if blockseed ~= -1 then
|
||||
-- FIXME: if blockseed ~= -1 then
|
||||
dragon_entity._initial = true
|
||||
end
|
||||
-- end
|
||||
else
|
||||
minetest.log("error", "[mcl_mapgen_core] ERROR! Ender dragon doesn't want to spawn")
|
||||
end
|
||||
|
@ -42,17 +37,15 @@ mcl_structures.register_structure("end_exit_portal",{
|
|||
end)
|
||||
end
|
||||
})
|
||||
mcl_structures.register_structure("end_exit_portal_open",{
|
||||
vl_structures.register_structure("end_exit_portal_open",{
|
||||
filenames = {
|
||||
modpath.."/schematics/mcl_structures_end_exit_portal.mts"
|
||||
},
|
||||
after_place = function(pos,def,pr)
|
||||
local p1 = vector.offset(pos,-16,-16,-16)
|
||||
local p2 = vector.offset(pos,16,16,16)
|
||||
after_place = function(pos,def,pr,p1,p2)
|
||||
minetest.fix_light(p1,p2)
|
||||
end
|
||||
})
|
||||
mcl_structures.register_structure("end_gateway_portal",{
|
||||
vl_structures.register_structure("end_gateway_portal",{
|
||||
filenames = {
|
||||
modpath.."/schematics/mcl_structures_end_gateway_portal.mts"
|
||||
},
|
||||
|
@ -79,7 +72,6 @@ end
|
|||
|
||||
function make_cage(pos,width)
|
||||
local nodes = {}
|
||||
local nodes2 = {}
|
||||
local r = math.max(1,math.floor(width/2) - 2)
|
||||
for x=-r,r do for y = 0,width do for z = -r,r do
|
||||
if x == r or x == -r or z==r or z == -r then
|
||||
|
@ -97,13 +89,13 @@ end
|
|||
local function get_points_on_circle(pos,r,n)
|
||||
local rt = {}
|
||||
for i=1, n do
|
||||
table.insert(rt,vector.offset(pos,r * math.cos(((i-1)/n) * (2*math.pi)),0, r* math.sin(((i-1)/n) * (2*math.pi)) ))
|
||||
table.insert(rt,vector.offset(pos,r * math.cos(((i-1)/n) * (2*math.pi)),0, r * math.sin(((i-1)/n) * (2*math.pi)) ))
|
||||
end
|
||||
return rt
|
||||
end
|
||||
|
||||
mcl_structures.register_structure("end_spike",{
|
||||
static_pos =get_points_on_circle(vector.offset(mcl_vars.mg_end_exit_portal_pos,0,-20,0),43,10),
|
||||
vl_structures.register_structure("end_spike",{
|
||||
static_pos = get_points_on_circle(vector.offset(mcl_vars.mg_end_exit_portal_pos, 0, -20, 0), 43, 10),
|
||||
place_func = function(pos,def,pr)
|
||||
local d = pr:next(6,12)
|
||||
local h = d * pr:next(4,6)
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
local modname = minetest.get_current_modname()
|
||||
--local S = minetest.get_translator(modname)
|
||||
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 = { },
|
||||
chunk_probability = 15, -- was 25
|
||||
y_offset = function(pr) return pr:next(-16,-32) end,
|
||||
y_max = 15,
|
||||
y_min = mcl_vars.mg_overworld_min + 35,
|
||||
biomes = { "Desert" },
|
||||
filenames = {
|
||||
modpath.."/schematics/mcl_structures_fossil_skull_1.mts", -- 4×5×5
|
||||
modpath.."/schematics/mcl_structures_fossil_skull_2.mts", -- 5×5×5
|
||||
modpath.."/schematics/mcl_structures_fossil_skull_3.mts", -- 5×5×7
|
||||
modpath.."/schematics/mcl_structures_fossil_skull_4.mts", -- 7×5×5
|
||||
modpath.."/schematics/mcl_structures_fossil_spine_1.mts", -- 3×3×13
|
||||
modpath.."/schematics/mcl_structures_fossil_spine_2.mts", -- 5×4×13
|
||||
modpath.."/schematics/mcl_structures_fossil_spine_3.mts", -- 7×4×13
|
||||
modpath.."/schematics/mcl_structures_fossil_spine_4.mts", -- 8×5×13
|
||||
},
|
||||
})
|
||||
|
|
@ -1,334 +0,0 @@
|
|||
local AIR = {name = "air"}
|
||||
local abs = math.abs
|
||||
local max = math.max
|
||||
|
||||
-- fairly strict: air, ignore, or no_paths marker
|
||||
local function is_air(node)
|
||||
return not node or node.name == "air" or node.name == "ignore" or node.name == "mcl_villages:no_paths"
|
||||
end
|
||||
-- check if a node is walkable (solid), but not tree/leaves
|
||||
local function is_solid_not_tree(node)
|
||||
if not node or node.name == "air" or node.name == "ignore" or node.name == "mcl_villages:no_paths" then return false end
|
||||
local meta = minetest.registered_items[node.name]
|
||||
local groups = meta and meta.groups
|
||||
return meta and meta.walkable and not (groups and (groups["deco_block"] or groups["tree"] or groups["leaves"] or groups["plant"]))
|
||||
end
|
||||
-- check if a node is walkable (solid), but not tree/leaves or buildungs
|
||||
local function is_solid_not_tree_or_building(node)
|
||||
if not node or node.name == "air" or node.name == "ignore" or node.name == "mcl_villages:no_paths" then return false end
|
||||
local meta = minetest.registered_items[node.name]
|
||||
local groups = meta and meta.groups
|
||||
return meta and meta.walkable and not (groups and (groups["deco_block"] or groups["tree"] or groups["leaves"] or groups["plant"] or groups["building_block"]))
|
||||
end
|
||||
-- check if a node is tree
|
||||
local function is_tree(node)
|
||||
if not node or node.name == "air" or node.name == "ignore" or node.name == "mcl_villages:no_paths" then return false end
|
||||
local meta = minetest.registered_items[node.name]
|
||||
local groups = meta and meta.groups
|
||||
return groups and (groups["tree"] or groups["leaves"])
|
||||
end
|
||||
-- replace a non-solid node, optionally also "additional"
|
||||
local function make_solid(lvm, cp, with, additional)
|
||||
local cur = lvm:get_node_at(cp)
|
||||
if not is_solid_not_tree(cur) or (additional and cur.name == additional.name) then
|
||||
lvm:set_node_at(cp, with)
|
||||
end
|
||||
end
|
||||
local function excavate(lvm,xi,yi,zi,pr,keep_trees)
|
||||
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
|
||||
if keep_trees and is_tree(node) then return false end
|
||||
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
|
||||
-- stop randomly depending on fill, to narrow down the caves
|
||||
if not keep_trees and not is_tree(node) and (pr:next(0,1e9)/1e9)^2 > c/9.1 then return false end
|
||||
lvm:set_node_at(vector.new(xi, yi, zi), AIR)
|
||||
return true -- modified
|
||||
end
|
||||
function mcl_structures.clearance(lvm, px, py, pz, sx, sy, sz, corners, surface_mat, pr)
|
||||
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
|
||||
-- excavate the needed volume and some headroom
|
||||
for xi = px,px+sx-1 do
|
||||
local dx2 = (cx-xi)^2*wx2
|
||||
for zi = pz,pz+sz-1 do
|
||||
local dz2 = (cz-zi)^2*wz2
|
||||
if dx2+dz2 <= 1 then
|
||||
lvm:set_node_at(vector.new(xi, py, zi), AIR)
|
||||
local n = lvm:get_node_at(vector.new(xi, py-1, zi))
|
||||
if n and n.name ~= surface_mat.name and is_solid_not_tree_or_building(n) then
|
||||
lvm:set_node_at(vector.new(xi, py-1, zi), surface_mat)
|
||||
end
|
||||
-- py+1 to py+4 are filled wider below, this is the top of the building only
|
||||
for yi = py+5,py+sy do
|
||||
lvm:set_node_at(vector.new(xi, yi, zi), AIR)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
-- slightly widen the cave above, to make easier to enter for mobs
|
||||
for xi = px-1,px+sx do
|
||||
local dx2 = max(abs(cx-xi)-1,0)^2*wx2
|
||||
for zi = pz-1,pz+sz do
|
||||
local dz2 = max(abs(cz-zi)-1,0)^2*wz2
|
||||
if dx2+dz2 <= 1 then
|
||||
for yi = py+1,py+4 do
|
||||
lvm:set_node_at(vector.new(xi, yi, zi), AIR)
|
||||
end
|
||||
local n = lvm:get_node_at(vector.new(xi, py, zi))
|
||||
for yi = py,py-1,-1 do
|
||||
local n = lvm:get_node_at(vector.new(xi, yi, zi))
|
||||
if is_tree(n) then
|
||||
lvm:set_node_at(vector.new(xi, yi, zi), AIR)
|
||||
else
|
||||
if n and n.name ~= surface_mat.name and is_solid_not_tree_or_building(n) then
|
||||
lvm:set_node_at(vector.new(xi, yi, zi), surface_mat)
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
-- some extra gaps for entry
|
||||
for xi = px-2,px+sx+1 do
|
||||
local dx2 = max(abs(cx-xi)-2,0)^2*wx2
|
||||
for zi = pz-2,pz+sz+1 do
|
||||
local dz2 = max(abs(cz-zi)-2,0)^2*wz2
|
||||
if dx2+dz2 <= 1 and pr:next(1,4) == 1 then
|
||||
for yi = py+2,py+4 do
|
||||
lvm:set_node_at(vector.new(xi, yi, zi), AIR)
|
||||
end
|
||||
local n = lvm:get_node_at(vector.new(xi, py+1, zi))
|
||||
for yi = py+1,py-1,-1 do
|
||||
local n = lvm:get_node_at(vector.new(xi, yi, zi))
|
||||
if is_tree(n) then
|
||||
lvm:set_node_at(vector.new(xi, yi, zi), AIR)
|
||||
else
|
||||
if n and n.name ~= surface_mat.name and is_solid_not_tree_or_building(n) then
|
||||
lvm:set_node_at(vector.new(xi, py+1, zi), surface_mat)
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
-- cave some additional area overhead, try to make it interesting though
|
||||
local min_clear, max_clear = sy+5, sy*2+5 -- FIXME: make parameters
|
||||
for yi = py+2,py+max_clear do
|
||||
local dy2 = (py-yi)^2*0.025
|
||||
local active = false
|
||||
for xi = px-2,px+sx+1 do
|
||||
local dx2 = max(abs(cx-xi)-2,0)^2*wx2
|
||||
for zi = pz-2,pz+sz+1 do
|
||||
local dz2 = max(abs(cz-zi)-2,0)^2*wz2
|
||||
local keep_trees = (xi<px or xi>=px+sx) or (zi<pz or zi>=pz+sz) -- TODO make configurable?
|
||||
if dx2+dz2+dy2 <= 1 and excavate(lvm,xi,yi,zi,pr,keep_trees) then active = true end
|
||||
end
|
||||
end
|
||||
if not active and yi > py+min_clear then break end
|
||||
end
|
||||
end
|
||||
-- TODO: allow controlling the random depth?
|
||||
-- TODO: add support for dust_mat (snow)
|
||||
local function grow_foundation(lvm,xi,yi,zi,pr,surface_mat,platform_mat,stone_mat)
|
||||
local pos, n, c = vector.new(xi,yi,zi), nil, 0
|
||||
if is_solid_not_tree(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_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,5) == 5 then -- randomly switch to stone sometimes
|
||||
platform_mat = stone_mat
|
||||
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_not_tree(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
|
||||
-- generate a foundations around px,py,pz with size sx,sy,sz (sy < 0)
|
||||
-- TODO: add support for dust_mat (snow)
|
||||
-- Rounding: we model an ellipse. At zero rounding, we want the line go through the corner, at sx/2, sz/2.
|
||||
-- For this, we need to make ellipse sized 2a=sqrt(2)*sx, 2b=sqrt(2)*sz,
|
||||
-- Which yields a = sx/sqrt(2), b=sz/sqrt(2) and a^2=sx^2*0.5, b^2=sz^2*0.5
|
||||
-- To get corners, we decrease a and b by approx. corners each
|
||||
-- The ellipse condition dx^2/a^2+dz^2/b^2 <= 1 then yields dx^2/(sx^2*0.5) + dz^2/(sz^2*0.5) <= 1
|
||||
-- We use wx2=(sx^2)^-2*2, wz2=(sz^2)^-2*2 and then dx^2*wx2+dz^2*wz2 <= 1
|
||||
function mcl_structures.foundation(lvm, px, py, pz, sx, depth, sz, corners, surface_mat, platform_mat, stone_mat, pr)
|
||||
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
|
||||
-- generate a baseplate
|
||||
for xi = px,px+sx-1 do
|
||||
local dx2 = (cx-xi)^2*wx2
|
||||
for zi = pz,pz+sz-1 do
|
||||
local dz2 = (cz-zi)^2*wz2
|
||||
if dx2+dz2 <= 1 then
|
||||
lvm:set_node_at(vector.new(xi, py, zi), surface_mat)
|
||||
make_solid(lvm, vector.new(xi, py-1, zi), platform_mat)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- slightly widen the baseplate below, to make easier to enter for mobs
|
||||
if corners and corners > 0 then
|
||||
for xi = px-1,px+sx do
|
||||
local dx2 = max(abs(cx-xi)-1,0)^2*wx2
|
||||
-- TODO: compute the z value ranges directly?
|
||||
for zi = pz-1,pz+sz do
|
||||
local dz2 = max(abs(cz-zi)-1,0)^2*wz2
|
||||
if dx2+dz2 <= 1 then
|
||||
make_solid(lvm, vector.new(xi, py-1, zi), surface_mat)
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
for xi = px+1,px+sx-1-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+1,pz+sz-1-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+1,px+sx-2 do
|
||||
local cp = vector.new(xi, py-3, pz-1)
|
||||
if is_solid_not_tree(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_not_tree(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+1,pz+sz-2 do
|
||||
local cp = vector.new(px-1, py-3, zi)
|
||||
if is_solid_not_tree(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_not_tree(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
|
||||
end
|
||||
-- construct additional baseplate below, also try to make it interesting
|
||||
for yi = py-2,py-20,-1 do
|
||||
local dy2 = (py-yi)^2*0.025
|
||||
local active = false
|
||||
for xi = px-1,px+sx do
|
||||
local dx2 = max(abs(cx-xi)-1,0)^2*wx2
|
||||
for zi = pz-1,pz+sz do
|
||||
local dz2 = max(abs(cz-zi)-1,0)^2*wz2
|
||||
if dx2+dy2+dz2 <= 1 then
|
||||
if grow_foundation(lvm,xi,yi,zi,pr,surface_mat,platform_mat,stone_mat) then active = true end
|
||||
end
|
||||
end
|
||||
end
|
||||
if not active and yi < py + depth then break end
|
||||
end
|
||||
end
|
||||
-- return position and material of surface
|
||||
function mcl_structures.find_ground(lvm, pos)
|
||||
if not pos then return nil, nil end
|
||||
pos = vector.copy(pos)
|
||||
local cur = lvm:get_node_at(pos)
|
||||
if not cur or cur.name == "ignore" then
|
||||
local e1, e2 = lvm:get_emerged_area()
|
||||
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
|
||||
if is_solid_not_tree(cur) then -- find up
|
||||
local prev = cur
|
||||
while true do
|
||||
pos.y = pos.y + 1
|
||||
local cur = lvm: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 not is_solid_not_tree(cur) then
|
||||
pos.y = pos.y - 1
|
||||
return pos, prev
|
||||
end
|
||||
prev = cur
|
||||
end
|
||||
else -- find down
|
||||
while true do
|
||||
pos.y = pos.y - 1
|
||||
local prev = cur
|
||||
local cur = lvm: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
|
||||
return pos, cur
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
-- find suitable height for a structure of this size
|
||||
-- @param lvm VoxelManip: to read data
|
||||
-- @param cpos vector: center
|
||||
-- @param size vector: area size
|
||||
-- @param tolerance number: maximum height difference allowed, default 8
|
||||
-- @return position, surface material
|
||||
function mcl_structures.find_level(lvm, cpos, size, tolerance)
|
||||
local cpos, surface_material = mcl_structures.find_ground(lvm, cpos)
|
||||
if not cpos then return nil, nil end
|
||||
local ys = {cpos.y}
|
||||
local pos = vector.offset(cpos, -math.floor((size.x-1)/2), 0, -math.floor((size.z-1)/2)) -- top left
|
||||
local pos_c = mcl_structures.find_ground(lvm, pos)
|
||||
if pos_c then table.insert(ys, pos_c.y) end
|
||||
local pos_c = mcl_structures.find_ground(lvm, vector.offset(pos, size.x-1, 0, 0))
|
||||
if pos_c then table.insert(ys, pos_c.y) end
|
||||
local pos_c = mcl_structures.find_ground(lvm, vector.offset(pos, 0, 0, size.z-1))
|
||||
if pos_c then table.insert(ys, pos_c.y) end
|
||||
local pos_c = mcl_structures.find_ground(lvm, vector.offset(pos, size.x-1, 0, size.z-1))
|
||||
if pos_c then table.insert(ys, pos_c.y) end
|
||||
table.sort(ys)
|
||||
-- well supported base, not too uneven?
|
||||
if #ys <= 4 or math.max(ys[#ys-1]-ys[1], ys[#ys]-ys[2]) > (tolerance or 8) then
|
||||
minetest.log("action", "[mcl_structures] ground too uneven: "..#ys.." positions, trimmed difference "..(#ys < 2 and "" or math.max(ys[#ys-1]-ys[1], ys[#ys]-ys[2])))
|
||||
return nil, nil
|
||||
end
|
||||
cpos.y = math.round(0.5*(ys[math.floor(#ys * 0.5)] + ys[math.ceil(#ys * 0.5)])) + 1 -- median, rounded, over surface
|
||||
return cpos, surface_material
|
||||
end
|
||||
|
|
@ -75,7 +75,7 @@ local function makegeode(pos,def,pr)
|
|||
return true
|
||||
end
|
||||
|
||||
mcl_structures.register_structure("geode",{
|
||||
vl_structures.register_structure("geode",{
|
||||
place_on = {"group:material_stone"},
|
||||
noise_params = {
|
||||
offset = 0,
|
||||
|
|
|
@ -19,33 +19,15 @@ local function spawn_mobs(p1,p2,vi,zv)
|
|||
end
|
||||
end
|
||||
|
||||
local function generate_igloo_basement(pos, orientation, loot, pr)
|
||||
end
|
||||
|
||||
local function generate_igloo(pos, def, pr)
|
||||
local path = modpath.."/schematics/mcl_structures_igloo_top.mts"
|
||||
local rotation = tostring(pr:next(0,3)*90)
|
||||
-- TODO: ymin, ymax
|
||||
mcl_structures.place_schematic(pos, -2, nil, nil, path, rotation, nil, true, nil, {padding=0, corners=2}, pr, function(p1, p2)
|
||||
mcl_structures.construct_nodes(p1, p2, {"mcl_furnaces:furnace","mcl_books:bookshelf"})
|
||||
local function igloo_callback(cpos,def,pr,p1,p2,size,rotation)
|
||||
vl_structures.construct_nodes(p1, p2, {"mcl_furnaces:furnace","mcl_books:bookshelf"})
|
||||
-- Place igloo basement with 50% chance
|
||||
local r = 1--pr:next(1,2)
|
||||
if r == 1 then
|
||||
if pr:next(1,2) == 1 then return end
|
||||
local pos = p1 -- we use top left as reference
|
||||
-- Select basement depth
|
||||
local dim = mcl_worlds.pos_to_dimension(pos)
|
||||
local buffer
|
||||
if dim == "nether" then
|
||||
buffer = pos.y - (mcl_vars.mg_lava_nether_max + 10)
|
||||
elseif dim == "end" then
|
||||
buffer = pos.y - (mcl_vars.mg_end_min + 1)
|
||||
elseif dim == "overworld" then
|
||||
buffer = pos.y - (mcl_vars.mg_lava_overworld_max + 10)
|
||||
else
|
||||
return true
|
||||
end
|
||||
if buffer <= 9 then return true end
|
||||
local depth = pr:next(9, buffer)
|
||||
local bpos = vector.new(pos.x, pos.y-depth, pos.z)
|
||||
local maxdepth = pos.y - (mcl_vars.mg_lava_overworld_max + 10)
|
||||
if maxdepth <= 9 then return true end
|
||||
local depth = pr:next(9, maxdepth)
|
||||
-- trapdoor position and orientation
|
||||
local tpos, dir, tdir
|
||||
if rotation == "0" then
|
||||
|
@ -79,52 +61,48 @@ local function generate_igloo(pos, def, pr)
|
|||
end
|
||||
minetest.set_node(pos, {name=brick})
|
||||
end
|
||||
local real_depth = 0
|
||||
local real_depth = 2
|
||||
-- Check how deep we can actually dig
|
||||
for y=1, depth-5 do
|
||||
for y=pos.y-real_depth, pos.y-depth, -1 do
|
||||
real_depth = real_depth + 1
|
||||
local node = minetest.get_node(vector.new(tpos.x, tpos.y-y, tpos.z))
|
||||
local node = minetest.get_node(vector.new(tpos.x, y, tpos.z))
|
||||
local def = node and minetest.registered_nodes[node.name]
|
||||
if not (def and def.walkable and def.liquidtype == "none" and def.is_ground_content) then
|
||||
bpos.y = tpos.y-y+1
|
||||
break
|
||||
end
|
||||
if not (def and def.walkable and def.liquidtype == "none" and def.is_ground_content) then break end
|
||||
end
|
||||
local bpos = vector.new(cpos.x, pos.y-real_depth+1, cpos.z)
|
||||
if real_depth <= 6 then
|
||||
minetest.log("not deep enough")
|
||||
minetest.log("action", "Ground not deep enough for igloo basement: "..real_depth)
|
||||
return false
|
||||
end
|
||||
local path = modpath.."/schematics/mcl_structures_igloo_basement.mts"
|
||||
mcl_structures.place_schematic(bpos, 0, nil, nil, path, rotation, nil, true, nil, nil, pr, function(p1, p2)
|
||||
local prepare = { tolerance = -1, foundation = false, clear = false }
|
||||
vl_structures.place_schematic(bpos, -1, nil, nil, path, rotation, nil, true, nil, prepare, pr, function(p1, p2)
|
||||
-- Generate ladder to basement
|
||||
local ladder = {name="mcl_core:ladder", param2=minetest.dir_to_wallmounted(tdir)}
|
||||
minetest.set_node(tpos, {name="mcl_doors:trapdoor", param2=20+minetest.dir_to_facedir(dir)}) -- TODO: more reliable param2
|
||||
for y=1, real_depth do
|
||||
set_brick(vector.new(tpos.x-1, tpos.y-y, tpos.z ))
|
||||
set_brick(vector.new(tpos.x+1, tpos.y-y, tpos.z ))
|
||||
set_brick(vector.new(tpos.x , tpos.y-y, tpos.z-1))
|
||||
set_brick(vector.new(tpos.x , tpos.y-y, tpos.z+1))
|
||||
minetest.set_node(vector.new(tpos.x, tpos.y-y, tpos.z), ladder)
|
||||
for y = tpos.y-1, bpos.y+4, -1 do
|
||||
set_brick(vector.new(tpos.x-1, y, tpos.z ))
|
||||
set_brick(vector.new(tpos.x+1, y, tpos.z ))
|
||||
set_brick(vector.new(tpos.x , y, tpos.z-1))
|
||||
set_brick(vector.new(tpos.x , y, tpos.z+1))
|
||||
minetest.set_node(vector.new(tpos.x, y, tpos.z), ladder)
|
||||
end
|
||||
mcl_structures.fill_chests(p1,p2,def.loot,pr)
|
||||
mcl_structures.construct_nodes(p1,p2,{"mcl_brewing:stand_000","mcl_books:bookshelf"})
|
||||
vl_structures.fill_chests(p1,p2,def.loot,pr)
|
||||
vl_structures.construct_nodes(p1,p2,{"mcl_brewing:stand_000","mcl_books:bookshelf"})
|
||||
spawn_mobs(p1,p2)
|
||||
end)
|
||||
end
|
||||
end)
|
||||
return true
|
||||
end
|
||||
|
||||
mcl_structures.register_structure("igloo",{
|
||||
vl_structures.register_structure("igloo",{
|
||||
filenames = { modpath.."/schematics/mcl_structures_igloo_top.mts" },
|
||||
place_on = {"mcl_core:snowblock","mcl_core:snow","group:grass_block_snow"},
|
||||
sidelen = 16,
|
||||
chunk_probability = 7,
|
||||
solid_ground = true,
|
||||
prepare = { padding = 1, corners = 1, foundation = -6, clear_top=-1 },
|
||||
y_max = mcl_vars.mg_overworld_max,
|
||||
y_min = 0,
|
||||
y_offset = -2,
|
||||
biomes = { "ColdTaiga", "IcePlainsSpikes", "IcePlains" },
|
||||
place_func = generate_igloo,
|
||||
after_place = igloo_callback,
|
||||
loot = {
|
||||
["mcl_chests:chest_small"] = {{
|
||||
stacks_min = 1,
|
||||
|
|
|
@ -1,62 +1,34 @@
|
|||
local modname = minetest.get_current_modname()
|
||||
local S = minetest.get_translator(modname)
|
||||
local modpath = minetest.get_modpath(modname)
|
||||
|
||||
mcl_structures = {}
|
||||
|
||||
dofile(modpath.."/api.lua")
|
||||
dofile(modpath.."/foundation.lua")
|
||||
dofile(modpath.."/shipwrecks.lua")
|
||||
-- some legacy API adapters
|
||||
mcl_structures.is_disabled = vl_structures.is_disabled
|
||||
mcl_structures.init_node_construct = vl_structures.init_node_construct
|
||||
mcl_structures.construct_nodes = vl_structures.construct_nodes
|
||||
mcl_structures.fill_chests = vl_structures.fill_chests
|
||||
mcl_structures.spawn_mobs = vl_structures.spawn_mobs
|
||||
|
||||
-- TODO: provide more legacy adapters that translate parameters?
|
||||
|
||||
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")
|
||||
dofile(modpath.."/witch_hut.lua")
|
||||
dofile(modpath.."/igloo.lua")
|
||||
dofile(modpath.."/woodland_mansion.lua")
|
||||
dofile(modpath.."/ruined_portal.lua")
|
||||
dofile(modpath.."/geode.lua")
|
||||
dofile(modpath.."/ocean_temple.lua")
|
||||
dofile(modpath.."/pillager_outpost.lua")
|
||||
dofile(modpath.."/end_spawn.lua")
|
||||
dofile(modpath.."/end_city.lua")
|
||||
dofile(modpath.."/ruined_portal.lua")
|
||||
dofile(modpath.."/shipwrecks.lua")
|
||||
dofile(modpath.."/witch_hut.lua")
|
||||
dofile(modpath.."/woodland_mansion.lua")
|
||||
|
||||
|
||||
mcl_structures.register_structure("desert_well",{
|
||||
place_on = {"group:sand"},
|
||||
flags = "place_center_x, place_center_z",
|
||||
not_near = { "desert_temple_new" },
|
||||
solid_ground = true,
|
||||
sidelen = 4,
|
||||
chunk_probability = 15,
|
||||
y_max = mcl_vars.mg_overworld_max,
|
||||
y_min = 1,
|
||||
y_offset = -2,
|
||||
biomes = { "Desert" },
|
||||
filenames = { modpath.."/schematics/mcl_structures_desert_well.mts" },
|
||||
})
|
||||
|
||||
mcl_structures.register_structure("fossil",{
|
||||
place_on = {"group:material_stone","group:sand"},
|
||||
flags = "place_center_x, place_center_z",
|
||||
solid_ground = true,
|
||||
sidelen = 13,
|
||||
chunk_probability = 25,
|
||||
y_offset = function(pr) return ( pr:next(1,16) * -1 ) -16 end,
|
||||
y_max = 15,
|
||||
y_min = mcl_vars.mg_overworld_min + 35,
|
||||
biomes = { "Desert" },
|
||||
filenames = {
|
||||
modpath.."/schematics/mcl_structures_fossil_skull_1.mts", -- 4×5×5
|
||||
modpath.."/schematics/mcl_structures_fossil_skull_2.mts", -- 5×5×5
|
||||
modpath.."/schematics/mcl_structures_fossil_skull_3.mts", -- 5×5×7
|
||||
modpath.."/schematics/mcl_structures_fossil_skull_4.mts", -- 7×5×5
|
||||
modpath.."/schematics/mcl_structures_fossil_spine_1.mts", -- 3×3×13
|
||||
modpath.."/schematics/mcl_structures_fossil_spine_2.mts", -- 5×4×13
|
||||
modpath.."/schematics/mcl_structures_fossil_spine_3.mts", -- 7×4×13
|
||||
modpath.."/schematics/mcl_structures_fossil_spine_4.mts", -- 8×5×13
|
||||
},
|
||||
})
|
||||
|
||||
mcl_structures.register_structure("boulder",{
|
||||
vl_structures.register_structure("boulder",{
|
||||
filenames = {
|
||||
modpath.."/schematics/mcl_structures_boulder_small.mts",
|
||||
modpath.."/schematics/mcl_structures_boulder_small.mts",
|
||||
|
@ -66,70 +38,11 @@ mcl_structures.register_structure("boulder",{
|
|||
},
|
||||
},true) --is spawned as a normal decoration. this is just for /spawnstruct
|
||||
|
||||
mcl_structures.register_structure("ice_spike_small",{
|
||||
vl_structures.register_structure("ice_spike_small",{
|
||||
filenames = { modpath.."/schematics/mcl_structures_ice_spike_small.mts" },
|
||||
},true) --is spawned as a normal decoration. this is just for /spawnstruct
|
||||
mcl_structures.register_structure("ice_spike_large",{
|
||||
sidelen = 6,
|
||||
|
||||
vl_structures.register_structure("ice_spike_large",{
|
||||
filenames = { modpath.."/schematics/mcl_structures_ice_spike_large.mts" },
|
||||
},true) --is spawned as a normal decoration. this is just for /spawnstruct
|
||||
|
||||
-- Debug command
|
||||
local function dir_to_rotation(dir)
|
||||
local ax, az = math.abs(dir.x), math.abs(dir.z)
|
||||
if ax > az then
|
||||
if dir.x < 0 then
|
||||
return "270"
|
||||
end
|
||||
return "90"
|
||||
end
|
||||
if dir.z < 0 then
|
||||
return "180"
|
||||
end
|
||||
return "0"
|
||||
end
|
||||
|
||||
minetest.register_chatcommand("spawnstruct", {
|
||||
params = "dungeon",
|
||||
description = S("Generate a pre-defined structure near your position."),
|
||||
privs = {debug = true},
|
||||
func = function(name, param)
|
||||
local player = minetest.get_player_by_name(name)
|
||||
if not player then return end
|
||||
local pos = player:get_pos()
|
||||
if not pos then return end
|
||||
pos = vector.round(pos)
|
||||
local dir = minetest.yaw_to_dir(player:get_look_horizontal())
|
||||
local rot = dir_to_rotation(dir)
|
||||
local seed = minetest.hash_node_position(pos)
|
||||
local pr = PcgRandom(seed)
|
||||
local errord = false
|
||||
local message = S("Structure placed.")
|
||||
if param == "dungeon" and mcl_dungeons and mcl_dungeons.spawn_dungeon then
|
||||
mcl_dungeons.spawn_dungeon(pos, rot, pr)
|
||||
elseif param == "" then
|
||||
message = S("Error: No structure type given. Please use “/spawnstruct <type>”.")
|
||||
errord = true
|
||||
else
|
||||
for n,d in pairs(mcl_structures.registered_structures) do
|
||||
if n == param then
|
||||
mcl_structures.place_structure(pos,d,pr,seed,rot)
|
||||
return true,message
|
||||
end
|
||||
end
|
||||
message = S("Error: Unknown structure type. Please use “/spawnstruct <type>”.")
|
||||
errord = true
|
||||
end
|
||||
minetest.chat_send_player(name, message)
|
||||
if errord then
|
||||
minetest.chat_send_player(name, S("Use /help spawnstruct to see a list of avaiable types."))
|
||||
end
|
||||
end
|
||||
})
|
||||
minetest.register_on_mods_loaded(function()
|
||||
local p = ""
|
||||
for n,_ in pairs(mcl_structures.registered_structures) do
|
||||
p = p .. " | "..n
|
||||
end
|
||||
minetest.registered_chatcommands["spawnstruct"].params = minetest.registered_chatcommands["spawnstruct"].params .. p
|
||||
end)
|
||||
|
|
|
@ -2,16 +2,14 @@ local modname = minetest.get_current_modname()
|
|||
local S = minetest.get_translator(modname)
|
||||
local modpath = minetest.get_modpath(modname)
|
||||
|
||||
mcl_structures.register_structure("jungle_temple",{
|
||||
vl_structures.register_structure("jungle_temple",{
|
||||
place_on = {"group:grass_block","group:dirt","mcl_core:dirt_with_grass"},
|
||||
flags = "place_center_x, place_center_z",
|
||||
solid_ground = true,
|
||||
y_offset = function(pr) return pr:next(-3,0) -5 end,
|
||||
chunk_probability = 5,
|
||||
y_max = mcl_vars.mg_overworld_max,
|
||||
y_min = 1,
|
||||
biomes = { "Jungle" },
|
||||
sidelen = 18,
|
||||
filenames = {
|
||||
modpath.."/schematics/mcl_structures_jungle_temple.mts",
|
||||
modpath.."/schematics/mcl_structures_jungle_temple_nice.mts",
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
name = mcl_structures
|
||||
author = Wuzzy, cora, kno10
|
||||
description = Structure placement for MCL2
|
||||
depends = mcl_init, mcl_util, mcl_loot
|
||||
depends = mcl_init, mcl_util, mcl_loot, vl_terraforming, vl_structures
|
||||
|
|
|
@ -75,13 +75,11 @@ local cold = {
|
|||
spawn_by = {"mcl_core:water_source"},
|
||||
num_spawn_by = 2,
|
||||
flags = "place_center_x, place_center_z, force_placement",
|
||||
solid_ground = true,
|
||||
y_offset = -1,
|
||||
y_min = mcl_vars.mg_overworld_min,
|
||||
y_max = -2,
|
||||
biomes = cold_oceans,
|
||||
chunk_probability = 10,
|
||||
sidelen = 20,
|
||||
filenames = {
|
||||
modpath.."/schematics/mcl_structures_ocean_ruins_cold_1.mts",
|
||||
modpath.."/schematics/mcl_structures_ocean_ruins_cold_2.mts",
|
||||
|
@ -126,5 +124,5 @@ warm.filenames = {
|
|||
modpath.."/schematics/mcl_structures_ocean_ruins_warm_4.mts",
|
||||
}
|
||||
|
||||
mcl_structures.register_structure("cold_ocean_ruins",cold)
|
||||
mcl_structures.register_structure("warm_ocean_ruins",warm)
|
||||
vl_structures.register_structure("cold_ocean_ruins",cold)
|
||||
vl_structures.register_structure("warm_ocean_ruins",warm)
|
||||
|
|
|
@ -0,0 +1,157 @@
|
|||
local modname = minetest.get_current_modname()
|
||||
local modpath = minetest.get_modpath(modname)
|
||||
--local S = minetest.get_translator(modname)
|
||||
local water_level = minetest.get_mapgen_setting("water_level")
|
||||
|
||||
local spawnon = { "mcl_stairs:slab_prismarine_dark" }
|
||||
|
||||
local ocean_biomes = {
|
||||
"RoofedForest_ocean",
|
||||
"JungleEdgeM_ocean",
|
||||
"BirchForestM_ocean",
|
||||
"BirchForest_ocean",
|
||||
"IcePlains_deep_ocean",
|
||||
"Jungle_deep_ocean",
|
||||
"Savanna_ocean",
|
||||
"MesaPlateauF_ocean",
|
||||
"ExtremeHillsM_deep_ocean",
|
||||
"Savanna_deep_ocean",
|
||||
"SunflowerPlains_ocean",
|
||||
"Swampland_deep_ocean",
|
||||
"Swampland_ocean",
|
||||
"MegaSpruceTaiga_deep_ocean",
|
||||
"ExtremeHillsM_ocean",
|
||||
"JungleEdgeM_deep_ocean",
|
||||
"SunflowerPlains_deep_ocean",
|
||||
"BirchForest_deep_ocean",
|
||||
"IcePlainsSpikes_ocean",
|
||||
"Mesa_ocean",
|
||||
"StoneBeach_ocean",
|
||||
"Plains_deep_ocean",
|
||||
"JungleEdge_deep_ocean",
|
||||
"SavannaM_deep_ocean",
|
||||
"Desert_deep_ocean",
|
||||
"Mesa_deep_ocean",
|
||||
"ColdTaiga_deep_ocean",
|
||||
"Plains_ocean",
|
||||
"MesaPlateauFM_ocean",
|
||||
"Forest_deep_ocean",
|
||||
"JungleM_deep_ocean",
|
||||
"FlowerForest_deep_ocean",
|
||||
"MushroomIsland_ocean",
|
||||
"MegaTaiga_ocean",
|
||||
"StoneBeach_deep_ocean",
|
||||
"IcePlainsSpikes_deep_ocean",
|
||||
"ColdTaiga_ocean",
|
||||
"SavannaM_ocean",
|
||||
"MesaPlateauF_deep_ocean",
|
||||
"MesaBryce_deep_ocean",
|
||||
"ExtremeHills+_deep_ocean",
|
||||
"ExtremeHills_ocean",
|
||||
"MushroomIsland_deep_ocean",
|
||||
"Forest_ocean",
|
||||
"MegaTaiga_deep_ocean",
|
||||
"JungleEdge_ocean",
|
||||
"MesaBryce_ocean",
|
||||
"MegaSpruceTaiga_ocean",
|
||||
"ExtremeHills+_ocean",
|
||||
"Jungle_ocean",
|
||||
"RoofedForest_deep_ocean",
|
||||
"IcePlains_ocean",
|
||||
"FlowerForest_ocean",
|
||||
"ExtremeHills_deep_ocean",
|
||||
"MesaPlateauFM_deep_ocean",
|
||||
"Desert_ocean",
|
||||
"Taiga_ocean",
|
||||
"BirchForestM_deep_ocean",
|
||||
"Taiga_deep_ocean",
|
||||
"JungleM_ocean"
|
||||
}
|
||||
|
||||
vl_structures.register_structure("ocean_temple",{
|
||||
place_on = {"group:sand","mcl_core:gravel"},
|
||||
spawn_by = {"group:water"},
|
||||
num_spawn_by = 4,
|
||||
noise_params = {
|
||||
offset = 0,
|
||||
scale = 0.0000122,
|
||||
spread = {x = 250, y = 250, z = 250},
|
||||
seed = 32345,
|
||||
octaves = 3,
|
||||
persist = 0.001,
|
||||
flags = "absvalue",
|
||||
},
|
||||
force_place = true,
|
||||
biomes = ocean_biomes,
|
||||
y_max = water_level-4,
|
||||
y_min = mcl_vars.mg_overworld_min,
|
||||
filenames = {
|
||||
modpath .. "/schematics/mcl_structures_ocean_temple.mts",
|
||||
modpath .. "/schematics/mcl_structures_ocean_temple_2.mts",
|
||||
},
|
||||
prepare = { tolerance = -1, clear = false, foundation = false },
|
||||
y_offset = function(pr) return pr:next(-2,0) end,
|
||||
after_place = function(p,def,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)
|
||||
vl_structures.construct_nodes(p1,p2,{"group:wall"})
|
||||
end,
|
||||
loot = {
|
||||
["mcl_chests:chest_small"] = {
|
||||
{
|
||||
stacks_min = 3,
|
||||
stacks_max = 10,
|
||||
items = {
|
||||
{ itemstring = "mcl_sus_stew:stew", weight = 10, amount_min = 1, amount_max = 1 },
|
||||
{ itemstring = "mcl_core:paper", weight = 8, amount_min = 1, amount_max = 12 },
|
||||
{ itemstring = "mcl_fishing:fish_raw", weight = 5, amount_min = 8, amount_max = 21 },
|
||||
{ itemstring = "mcl_fishing:salmon_raw", weight = 7, amount_min = 4, amount_max = 8 },
|
||||
{ itemstring = "mcl_tnt:tnt", weight = 1, amount_min = 1, amount_max = 2 },
|
||||
}
|
||||
},
|
||||
{
|
||||
stacks_min = 2,
|
||||
stacks_max = 6,
|
||||
items = {
|
||||
{ itemstring = "mcl_core:iron_ingot", weight = 10, amount_min = 1, amount_max = 5 },
|
||||
{ itemstring = "mcl_core:goldblock", weight = 1, amount_min = 1, amount_max = 2 },
|
||||
{ itemstring = "mcl_experience:bottle", weight = 5, amount_min = 1, amount_max = 1 },
|
||||
{ itemstring = "mcl_core:diamond", weight = 5, amount_min = 1, amount_max = 1 },
|
||||
{ itemstring = "mcl_fishing:fishing_rod", weight = 1, amount_min = 1, amount_max = 1 },
|
||||
}
|
||||
},
|
||||
{
|
||||
stacks_min = 4,
|
||||
stacks_max = 4,
|
||||
items = {
|
||||
--{ itemstring = "FIXME TREASURE MAP", weight = 8, amount_min = 1, amount_max = 5 },
|
||||
{ itemstring = "mcl_books:book", weight = 1, amount_min = 1, amount_max = 5 },
|
||||
{ itemstring = "mcl_clock:clock", weight = 1, amount_min = 1, amount_max = 1 },
|
||||
{ itemstring = "mcl_compass:compass", weight = 1, amount_min = 1, amount_max = 1 },
|
||||
{ itemstring = "mcl_maps:empty_map", weight = 1, amount_min = 1, amount_max = 1 },
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
vl_structures.register_structure_spawn({
|
||||
name = "mobs_mc:guardian",
|
||||
y_min = mcl_vars.mg_overworld_min,
|
||||
y_max = mcl_vars.mg_overworld_max,
|
||||
chance = 10,
|
||||
interval = 60,
|
||||
limit = 9,
|
||||
spawnon = spawnon,
|
||||
})
|
||||
|
||||
vl_structures.register_structure_spawn({
|
||||
name = "mobs_mc:guardian_elder",
|
||||
y_min = mcl_vars.mg_overworld_min,
|
||||
y_max = mcl_vars.mg_overworld_max,
|
||||
chance = 100,
|
||||
interval = 60,
|
||||
limit = 4,
|
||||
spawnon = spawnon,
|
||||
})
|
||||
|
|
@ -3,12 +3,10 @@ local modpath = minetest.get_modpath(modname)
|
|||
|
||||
local spawnon = {"mcl_core:stripped_oak","mcl_stairs:slab_birchwood_top"}
|
||||
|
||||
mcl_structures.register_structure("pillager_outpost",{
|
||||
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",
|
||||
solid_ground = true,
|
||||
prepare = { padding = 2, corners = 4, foundation = 6, clearance = true },
|
||||
sidelen = 20,
|
||||
prepare = { padding = 3, corners = 4, foundation = -6, clear = true },
|
||||
y_offset = 0,
|
||||
chunk_probability = 15,
|
||||
y_max = mcl_vars.mg_overworld_max,
|
||||
|
@ -61,13 +59,13 @@ mcl_structures.register_structure("pillager_outpost",{
|
|||
},
|
||||
after_place = function(p,def,pr)
|
||||
local p1, p2 = vector.offset(p,-9,0,-9), vector.offset(p,9,32,9)
|
||||
mcl_structures.spawn_mobs("mobs_mc:pillager",spawnon,p1,p2,pr,5)
|
||||
mcl_structures.spawn_mobs("mobs_mc:parrot",{"mesecons_pressureplates:pressure_plate_stone_off"},p1,p2,pr,3)
|
||||
mcl_structures.spawn_mobs("mobs_mc:iron_golem",{"mesecons_button:button_stone_off"},p1,p2,pr,1)
|
||||
vl_structures.spawn_mobs("mobs_mc:pillager",spawnon,p1,p2,pr,5)
|
||||
vl_structures.spawn_mobs("mobs_mc:parrot",{"mesecons_pressureplates:pressure_plate_stone_off"},p1,p2,pr,3)
|
||||
vl_structures.spawn_mobs("mobs_mc:iron_golem",{"mesecons_button:button_stone_off"},p1,p2,pr,1)
|
||||
end
|
||||
})
|
||||
|
||||
mcl_structures.register_structure_spawn({
|
||||
vl_structures.register_structure_spawn({
|
||||
name = "mobs_mc:pillager",
|
||||
y_min = mcl_vars.mg_overworld_min,
|
||||
y_max = mcl_vars.mg_overworld_max,
|
||||
|
|
|
@ -13,12 +13,10 @@ end
|
|||
local def = {
|
||||
place_on = {"group:grass_block","group:dirt","mcl_core:dirt_with_grass","group:grass_block","group:sand","group:grass_block_snow","mcl_core:snow"},
|
||||
flags = "place_center_x, place_center_z, all_floors",
|
||||
solid_ground = true,
|
||||
prepare = { padding = 0, corners = 3, tolerance = 10, foundation = true, clearance = true },
|
||||
prepare = { padding = 0, corners = 3, tolerance = 15, foundation = true, clear = true, clear_top = 0 },
|
||||
chunk_probability = 20,
|
||||
y_max = mcl_vars.mg_overworld_max,
|
||||
y_min = 1,
|
||||
sidelen = 12,
|
||||
y_offset = -5,
|
||||
filenames = {
|
||||
modpath.."/schematics/mcl_structures_ruined_portal_1.mts",
|
||||
|
@ -26,12 +24,10 @@ local def = {
|
|||
modpath.."/schematics/mcl_structures_ruined_portal_3.mts",
|
||||
modpath.."/schematics/mcl_structures_ruined_portal_4.mts",
|
||||
modpath.."/schematics/mcl_structures_ruined_portal_5.mts",
|
||||
modpath.."/schematics/mcl_structures_ruined_portal_6.mts",
|
||||
modpath.."/schematics/mcl_structures_ruined_portal_99.mts",
|
||||
},
|
||||
after_place = function(pos,def,pr)
|
||||
local hl = def.sidelen / 2
|
||||
local p1 = vector.offset(pos,-hl,-hl,-hl)
|
||||
local p2 = vector.offset(pos,hl,hl,hl)
|
||||
after_place = function(pos,def,pr,p1,p2)
|
||||
local gold = minetest.find_nodes_in_area(p1,p2,{"mcl_core:goldblock"})
|
||||
local lava = minetest.find_nodes_in_area(p1,p2,{"mcl_core:lava_source"})
|
||||
local rack = minetest.find_nodes_in_area(p1,p2,{"mcl_nether:netherrack"})
|
||||
|
@ -101,9 +97,10 @@ local def = {
|
|||
}}
|
||||
}
|
||||
}
|
||||
mcl_structures.register_structure("ruined_portal_overworld",def)
|
||||
vl_structures.register_structure("ruined_portal_overworld",def)
|
||||
|
||||
local ndef = table.copy(def)
|
||||
ndef.y_min=mcl_vars.mg_lava_nether_max +10
|
||||
ndef.y_max=mcl_vars.mg_nether_max - 15
|
||||
ndef.place_on = {"mcl_nether:netherrack","group:soul_block","mcl_blackstone:basalt,mcl_blackstone:blackstone","mcl_crimson:crimson_nylium","mcl_crimson:warped_nylium"},
|
||||
mcl_structures.register_structure("ruined_portal_nether",ndef)
|
||||
vl_structures.register_structure("ruined_portal_nether",ndef)
|
||||
|
|
Binary file not shown.
|
@ -1,10 +1,7 @@
|
|||
local modname = minetest.get_current_modname()
|
||||
local modpath = minetest.get_modpath(modname)
|
||||
--local S = minetest.get_translator(modname)
|
||||
|
||||
local seed = minetest.get_mapgen_setting("seed")
|
||||
local water_level = minetest.get_mapgen_setting("water_level")
|
||||
local pr = PseudoRandom(seed)
|
||||
|
||||
--schematics by chmodsayshello
|
||||
local schems = {
|
||||
|
@ -94,7 +91,7 @@ local beach_biomes = {
|
|||
"Jungle_shore"
|
||||
}
|
||||
|
||||
mcl_structures.register_structure("shipwreck",{
|
||||
vl_structures.register_structure("shipwreck",{
|
||||
place_on = {"group:sand","mcl_core:gravel"},
|
||||
spawn_by = {"group:water"},
|
||||
num_spawn_by = 4,
|
||||
|
@ -107,11 +104,11 @@ mcl_structures.register_structure("shipwreck",{
|
|||
persist = 0.001,
|
||||
flags = "absvalue",
|
||||
},
|
||||
sidelen = 16,
|
||||
flags = "force_placement",
|
||||
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_offset = function(pr) return pr:next(-4,-2) end,
|
||||
loot = {
|
||||
|
@ -174,93 +171,3 @@ mcl_structures.register_structure("shipwreck",{
|
|||
}
|
||||
})
|
||||
|
||||
local spawnon = { "mcl_stairs:slab_prismarine_dark"}
|
||||
|
||||
mcl_structures.register_structure("ocean_temple",{
|
||||
place_on = {"group:sand","mcl_core:gravel"},
|
||||
spawn_by = {"group:water"},
|
||||
num_spawn_by = 4,
|
||||
noise_params = {
|
||||
offset = 0,
|
||||
scale = 0.0000122,
|
||||
spread = {x = 250, y = 250, z = 250},
|
||||
seed = 32345,
|
||||
octaves = 3,
|
||||
persist = 0.001,
|
||||
flags = "absvalue",
|
||||
},
|
||||
sidelen = 32,
|
||||
flags = "force_placement",
|
||||
biomes = ocean_biomes,
|
||||
y_max = water_level-4,
|
||||
y_min = mcl_vars.mg_overworld_min,
|
||||
filenames = {
|
||||
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,
|
||||
after_place = function(p,def,pr)
|
||||
local p1 = vector.offset(p,-9,0,-9)
|
||||
local p2 = vector.offset(p,9,32,9)
|
||||
mcl_structures.spawn_mobs("mobs_mc:guardian",spawnon,p1,p2,pr,5,true)
|
||||
mcl_structures.spawn_mobs("mobs_mc:guardian_elder",spawnon,p1,p2,pr,1,true)
|
||||
mcl_structures.construct_nodes(p1,p2,{"group:wall"})
|
||||
end,
|
||||
loot = {
|
||||
["mcl_chests:chest_small"] = {
|
||||
{
|
||||
stacks_min = 3,
|
||||
stacks_max = 10,
|
||||
items = {
|
||||
{ itemstring = "mcl_sus_stew:stew", weight = 10, amount_min = 1, amount_max = 1 },
|
||||
{ itemstring = "mcl_core:paper", weight = 8, amount_min = 1, amount_max = 12 },
|
||||
{ itemstring = "mcl_fishing:fish_raw", weight = 5, amount_min = 8, amount_max = 21 },
|
||||
{ itemstring = "mcl_fishing:salmon_raw", weight = 7, amount_min = 4, amount_max = 8 },
|
||||
{ itemstring = "mcl_tnt:tnt", weight = 1, amount_min = 1, amount_max = 2 },
|
||||
}
|
||||
},
|
||||
{
|
||||
stacks_min = 2,
|
||||
stacks_max = 6,
|
||||
items = {
|
||||
{ itemstring = "mcl_core:iron_ingot", weight = 10, amount_min = 1, amount_max = 5 },
|
||||
{ itemstring = "mcl_core:goldblock", weight = 1, amount_min = 1, amount_max = 2 },
|
||||
{ itemstring = "mcl_experience:bottle", weight = 5, amount_min = 1, amount_max = 1 },
|
||||
{ itemstring = "mcl_core:diamond", weight = 5, amount_min = 1, amount_max = 1 },
|
||||
{ itemstring = "mcl_fishing:fishing_rod", weight = 1, amount_min = 1, amount_max = 1 },
|
||||
}
|
||||
},
|
||||
{
|
||||
stacks_min = 4,
|
||||
stacks_max = 4,
|
||||
items = {
|
||||
--{ itemstring = "FIXME TREASURE MAP", weight = 8, amount_min = 1, amount_max = 5 },
|
||||
{ itemstring = "mcl_books:book", weight = 1, amount_min = 1, amount_max = 5 },
|
||||
{ itemstring = "mcl_clock:clock", weight = 1, amount_min = 1, amount_max = 1 },
|
||||
{ itemstring = "mcl_compass:compass", weight = 1, amount_min = 1, amount_max = 1 },
|
||||
{ itemstring = "mcl_maps:empty_map", weight = 1, amount_min = 1, amount_max = 1 },
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
mcl_structures.register_structure_spawn({
|
||||
name = "mobs_mc:guardian",
|
||||
y_min = mcl_vars.mg_overworld_min,
|
||||
y_max = mcl_vars.mg_overworld_max,
|
||||
chance = 10,
|
||||
interval = 60,
|
||||
limit = 9,
|
||||
spawnon = spawnon,
|
||||
})
|
||||
|
||||
mcl_structures.register_structure_spawn({
|
||||
name = "mobs_mc:guardian_elder",
|
||||
y_min = mcl_vars.mg_overworld_min,
|
||||
y_max = mcl_vars.mg_overworld_max,
|
||||
chance = 100,
|
||||
interval = 60,
|
||||
limit = 4,
|
||||
spawnon = spawnon,
|
||||
})
|
||||
|
|
|
@ -23,14 +23,16 @@ local function spawn_witch(p1,p2)
|
|||
end
|
||||
end
|
||||
|
||||
local function hut_placement_callback(pos,def,pr)
|
||||
local hl = def.sidelen / 2
|
||||
local p1 = vector.offset(pos,-hl,-hl,-hl)
|
||||
local p2 = vector.offset(pos,hl,hl,hl)
|
||||
local legs = minetest.find_nodes_in_area(vector.offset(pos,-hl,0,-hl),vector.offset(pos,hl,0,hl), "mcl_core:tree")
|
||||
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 minetest.get_item_group(mcl_vars.get_node(vector.offset(leg,0,-1,0), true, 333333).name, "water") ~= 0 do
|
||||
while true do
|
||||
local name = minetest.get_node(vector.offset(leg,0,-1,0)).name
|
||||
if name == "ignore" then break end
|
||||
if name ~= "air" and minetest.get_item_group(name, "water") == 0 then break end
|
||||
leg = vector.offset(leg,0,-1,0)
|
||||
table.insert(tree,leg)
|
||||
end
|
||||
|
@ -39,14 +41,16 @@ local function hut_placement_callback(pos,def,pr)
|
|||
spawn_witch(p1,p2)
|
||||
end
|
||||
|
||||
mcl_structures.register_structure("witch_hut",{
|
||||
place_on = {"mcl_core:water_source","mclx_core:river_water_source"},
|
||||
flags = "place_center_x, place_center_z, liquid_surface, force_placement",
|
||||
sidelen = 8,
|
||||
vl_structures.register_structure("witch_hut",{
|
||||
place_on = {"mcl_core:water_source","group:sand","group:grass_block","group:dirt","mclx_core:river_water_source"},
|
||||
spawn_by = {"mcl_core:water_source","mclx_core:river_water_source"},
|
||||
check_offset = -1,
|
||||
num_spawn_by = 3,
|
||||
flags = "place_center_x, place_center_z, all_surfaces",
|
||||
chunk_probability = 8,
|
||||
prepare = { tolerance=-1, clearance="top", foundation=false },
|
||||
prepare = { mode="under_air", tolerance=4, clear_bottom=3, padding=0, corners=1, foundation=false },
|
||||
y_max = mcl_vars.mg_overworld_max,
|
||||
y_min = -4,
|
||||
y_min = -5,
|
||||
y_offset = 0,
|
||||
biomes = { "Swampland", "Swampland_ocean", "Swampland_shore" },
|
||||
filenames = { modpath.."/schematics/mcl_structures_witch_hut.mts" },
|
||||
|
|
|
@ -5,28 +5,24 @@ local peaceful = minetest.settings:get_bool("only_peaceful_mobs", false)
|
|||
|
||||
local spawnon = {"mcl_deepslate:deepslate","mcl_core:birchwood","mcl_wool:red_carpet","mcl_wool:brown_carpet"}
|
||||
|
||||
mcl_structures.register_structure("woodland_cabin",{
|
||||
vl_structures.register_structure("woodland_cabin",{
|
||||
place_on = {"group:grass_block","group:dirt","mcl_core:dirt_with_grass"},
|
||||
flags = "place_center_x, place_center_z",
|
||||
solid_ground = true,
|
||||
prepare = { padding = 2, corners = 5, foundation = true, clearance = true },
|
||||
prepare = { padding = 2, corners = 5, foundation = true, clear = true, clear_top = 2 },
|
||||
force_placement = false,
|
||||
chunk_probability = 20,
|
||||
y_max = mcl_vars.mg_overworld_max,
|
||||
y_min = 1,
|
||||
biomes = { "RoofedForest" },
|
||||
sidelen = 32,
|
||||
filenames = {
|
||||
modpath.."/schematics/mcl_structures_woodland_cabin.mts",
|
||||
modpath.."/schematics/mcl_structures_woodland_outpost.mts",
|
||||
},
|
||||
construct_nodes = {"mcl_barrels:barrel_closed","mcl_books:bookshelf"},
|
||||
after_place = function(p,def,pr)
|
||||
local p1=vector.offset(p,-def.sidelen,-1,-def.sidelen)
|
||||
local p2=vector.offset(p,def.sidelen,def.sidelen,def.sidelen)
|
||||
mcl_structures.spawn_mobs("mobs_mc:vindicator",spawnon,p1,p2,pr,5)
|
||||
mcl_structures.spawn_mobs("mobs_mc:evoker",spawnon,p1,p2,pr,1)
|
||||
mcl_structures.spawn_mobs("mobs_mc:parrot",{"mcl_heads:wither_skeleton"},p1,p2,pr,1)
|
||||
after_place = function(p,def,pr,p1,p2)
|
||||
vl_structures.spawn_mobs("mobs_mc:vindicator",spawnon,p1,p2,pr,5)
|
||||
vl_structures.spawn_mobs("mobs_mc:evoker",spawnon,p1,p2,pr,1)
|
||||
vl_structures.spawn_mobs("mobs_mc:parrot",{"mcl_heads:wither_skeleton"},p1,p2,pr,1)
|
||||
end,
|
||||
loot = {
|
||||
["mcl_chests:chest_small" ] ={{
|
||||
|
@ -69,7 +65,7 @@ mcl_structures.register_structure("woodland_cabin",{
|
|||
}
|
||||
})
|
||||
|
||||
mcl_structures.register_structure_spawn({
|
||||
vl_structures.register_structure_spawn({
|
||||
name = "mobs_mc:vindicator",
|
||||
y_min = mcl_vars.mg_overworld_min,
|
||||
y_max = mcl_vars.mg_overworld_max,
|
||||
|
@ -79,7 +75,7 @@ mcl_structures.register_structure_spawn({
|
|||
spawnon = spawnon,
|
||||
})
|
||||
|
||||
mcl_structures.register_structure_spawn({
|
||||
vl_structures.register_structure_spawn({
|
||||
name = "mobs_mc:evoker",
|
||||
y_min = mcl_vars.mg_overworld_min,
|
||||
y_max = mcl_vars.mg_overworld_max,
|
||||
|
|
|
@ -83,57 +83,76 @@ end
|
|||
|
||||
local mushrooms = {"mcl_mushrooms:mushroom_brown","mcl_mushrooms:mushroom_red"}
|
||||
|
||||
local function get_fallen_tree_schematic(pos,pr)
|
||||
local function place_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 maxlen = 8
|
||||
local minlen = 2
|
||||
local vprob = 120
|
||||
local mprob = 160
|
||||
local minlen, maxlen = 3, 9
|
||||
local vrate, mrate = 120, 160
|
||||
local len = pr:next(minlen,maxlen)
|
||||
local schem = {
|
||||
size = {x = len + 2, y = 2, z = 3},
|
||||
data = {
|
||||
{name = "air", prob=0},
|
||||
{name = "air", prob=0},
|
||||
}
|
||||
}
|
||||
for i = 1,len do
|
||||
table.insert(schem.data,{name = "mcl_core:vine",param2=4, prob=vprob})
|
||||
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
|
||||
table.insert(schem.data,{name = "air", prob=0})
|
||||
table.insert(schem.data,{name = "air", prob=0})
|
||||
-- TODO: port this to voxel manipulators
|
||||
-- ensure we have room for the tree
|
||||
local minsupport, maxsupport = 99, 1
|
||||
for i = 1,len do
|
||||
table.insert(schem.data,{name = "air", prob=0})
|
||||
-- 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
|
||||
|
||||
table.insert(schem.data,{name = tree, param2 = 0})
|
||||
table.insert(schem.data,{name = "air", prob=0})
|
||||
for i = 1,len do
|
||||
table.insert(schem.data,{name = tree, param2 = 12})
|
||||
-- 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
|
||||
table.insert(schem.data,{name = "air", prob=0})
|
||||
table.insert(schem.data,{name = "air", prob=0})
|
||||
for i = 1,len do
|
||||
table.insert(schem.data,{name = mushrooms[pr:next(1,#mushrooms)], param2 = 12, prob=mprob})
|
||||
end
|
||||
|
||||
table.insert(schem.data,{name = "air", prob=0})
|
||||
table.insert(schem.data,{name = "air", prob=0})
|
||||
for i = 1,len do
|
||||
table.insert(schem.data,{name = "mcl_core:vine",param2=5, prob=vprob})
|
||||
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})
|
||||
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
|
||||
table.insert(schem.data,{name = "air", prob=0})
|
||||
table.insert(schem.data,{name = "air", prob=0})
|
||||
for i = 1,len do
|
||||
table.insert(schem.data,{name = "air", prob=0})
|
||||
end
|
||||
|
||||
return schem
|
||||
end
|
||||
|
||||
mcl_structures.register_structure("fallen_tree",{
|
||||
vl_structures.register_structure("fallen_tree",{
|
||||
priority = 1100, -- after regular trees
|
||||
place_on = {"group:grass_block"},
|
||||
terrain_feature = true,
|
||||
|
@ -146,24 +165,14 @@ mcl_structures.register_structure("fallen_tree",{
|
|||
persist = 0.66
|
||||
},
|
||||
flags = "place_center_x, place_center_z",
|
||||
sidelen = 18,
|
||||
sidelen = 10,
|
||||
solid_ground = true,
|
||||
y_max = mcl_vars.mg_overworld_max,
|
||||
y_min = minetest.get_mapgen_setting("water_level"),
|
||||
on_place = function(pos,def,pr)
|
||||
local air_p1 = vector.offset(pos,-def.sidelen/2,1,-def.sidelen/2)
|
||||
local air_p2 = vector.offset(air_p1,def.sidelen-1,0,def.sidelen-1)
|
||||
local air = minetest.find_nodes_in_area(air_p1,air_p2,{"air"})
|
||||
return #air >= (def.sidelen * def.sidelen) / 2
|
||||
end,
|
||||
place_func = function(pos,def,pr)
|
||||
local schem=get_fallen_tree_schematic(pos,pr)
|
||||
if not schem then return end
|
||||
return minetest.place_schematic(pos,schem,"random")
|
||||
end
|
||||
place_func = place_tree
|
||||
})
|
||||
|
||||
mcl_structures.register_structure("lavapool",{
|
||||
vl_structures.register_structure("lavapool",{
|
||||
place_on = {"group:sand", "group:dirt", "group:stone"},
|
||||
terrain_feature = true,
|
||||
noise_params = {
|
||||
|
@ -183,7 +192,7 @@ mcl_structures.register_structure("lavapool",{
|
|||
end
|
||||
})
|
||||
|
||||
mcl_structures.register_structure("water_lake",{
|
||||
vl_structures.register_structure("water_lake",{
|
||||
place_on = {"group:dirt","group:stone"},
|
||||
terrain_feature = true,
|
||||
noise_params = {
|
||||
|
@ -203,7 +212,7 @@ mcl_structures.register_structure("water_lake",{
|
|||
end
|
||||
})
|
||||
|
||||
mcl_structures.register_structure("water_lake_mangrove_swamp",{
|
||||
vl_structures.register_structure("water_lake_mangrove_swamp",{
|
||||
place_on = {"mcl_mud:mud"},
|
||||
biomes = { "MangroveSwamp" },
|
||||
terrain_feature = true,
|
||||
|
@ -224,7 +233,7 @@ mcl_structures.register_structure("water_lake_mangrove_swamp",{
|
|||
end
|
||||
})
|
||||
|
||||
mcl_structures.register_structure("basalt_column",{
|
||||
vl_structures.register_structure("basalt_column",{
|
||||
place_on = {"mcl_blackstone:blackstone","mcl_blackstone:basalt"},
|
||||
terrain_feature = true,
|
||||
spawn_by = {"air"},
|
||||
|
@ -268,7 +277,7 @@ mcl_structures.register_structure("basalt_column",{
|
|||
return true
|
||||
end
|
||||
})
|
||||
mcl_structures.register_structure("basalt_pillar",{
|
||||
vl_structures.register_structure("basalt_pillar",{
|
||||
place_on = {"mcl_blackstone:blackstone","mcl_blackstone:basalt"},
|
||||
terrain_feature = true,
|
||||
noise_params = {
|
||||
|
@ -310,7 +319,7 @@ mcl_structures.register_structure("basalt_pillar",{
|
|||
end
|
||||
})
|
||||
|
||||
mcl_structures.register_structure("lavadelta",{
|
||||
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,
|
||||
|
|
|
@ -15,45 +15,22 @@ function mcl_villages.initialize_settlement_info(pr)
|
|||
num_jobs = 0,
|
||||
num_beds = 0,
|
||||
}
|
||||
|
||||
for k, v in pairs(mcl_villages.schematic_houses) do
|
||||
count_buildings[v["name"]] = 0
|
||||
end
|
||||
for k, v in pairs(mcl_villages.schematic_jobs) do
|
||||
count_buildings[v["name"]] = 0
|
||||
end
|
||||
|
||||
return count_buildings
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- evaluate settlement_info and place schematics
|
||||
-------------------------------------------------------------------------------
|
||||
-- Initialize node
|
||||
local function construct_node(p1, p2, name)
|
||||
local r = minetest.registered_nodes[name]
|
||||
if not r or not r.on_construct then
|
||||
minetest.log("warning", "[mcl_villages] No on_construct defined for node name " .. name)
|
||||
end
|
||||
local nodes = minetest.find_nodes_in_area(p1, p2, name)
|
||||
for p=1, #nodes do
|
||||
r.on_construct(nodes[p])
|
||||
end
|
||||
return nodes
|
||||
end
|
||||
|
||||
local function spawn_cats(pos)
|
||||
local sp=minetest.find_nodes_in_area_under_air(vector.offset(pos,-20,-20,-20),vector.offset(pos,20,20,20),{"group:opaque"})
|
||||
for i=1,math.random(3) do
|
||||
local v = minetest.add_entity(vector.offset(sp[math.random(#sp)],0,1,0),"mobs_mc:cat"):get_luaentity()
|
||||
if v then
|
||||
v._home = pos
|
||||
end
|
||||
for _ = 1, math.random(3) do
|
||||
local v = minetest.add_entity(vector.offset(sp[math.random(#sp)],0,1,0),"mobs_mc:cat")
|
||||
if v and v:get_luaentity() then v:get_luaentity()._home = pos end
|
||||
end
|
||||
end
|
||||
|
||||
local function init_nodes(p1, p2, pr)
|
||||
mcl_structures.construct_nodes(p1, p2, {
|
||||
vl_structures.construct_nodes(p1, p2, {
|
||||
"mcl_itemframes:item_frame",
|
||||
"mcl_itemframes:glow_item_frame",
|
||||
"mcl_furnaces:furnace",
|
||||
|
@ -69,55 +46,26 @@ local function init_nodes(p1, p2, pr)
|
|||
-- Support mods with custom job sites
|
||||
local job_sites = minetest.find_nodes_in_area(p1, p2, mobs_mc.jobsites)
|
||||
for _, v in pairs(job_sites) do
|
||||
mcl_structures.init_node_construct(v)
|
||||
vl_structures.init_node_construct(v)
|
||||
end
|
||||
|
||||
-- Do new chest nodes first
|
||||
local nodes = construct_node(p1, p2, "mcl_chests:chest_small")
|
||||
if nodes and #nodes > 0 then
|
||||
local nodes = vl_structures.construct_nodes(p1, p2, {"mcl_chests:chest_small", "mcl_chests:chest" }) or {}
|
||||
for p=1, #nodes do
|
||||
mcl_villages.fill_chest(nodes[p], pr)
|
||||
end
|
||||
end
|
||||
|
||||
-- Do old chest nodes after
|
||||
local nodes = construct_node(p1, p2, "mcl_chests:chest")
|
||||
if nodes and #nodes > 0 then
|
||||
for p=1, #nodes do
|
||||
mcl_villages.fill_chest(nodes[p], pr)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- check ground for a single building, adjust position
|
||||
local function check_ground(lvm, cpos, size)
|
||||
local cpos, surface_material = mcl_villages.find_surface(lvm, cpos)
|
||||
if not cpos then return nil, nil end
|
||||
local pos = vector.offset(cpos, -math.floor((size.x-1)/2), 0, -math.floor((size.z-1)/2))
|
||||
local ys = {pos.y}
|
||||
local pos_c = mcl_villages.find_surface_down(lvm, vector.offset(pos, 0, size.y, 0))
|
||||
if pos_c then table.insert(ys, pos_c.y) end
|
||||
local pos_c = mcl_villages.find_surface_down(lvm, vector.offset(pos, size.x-1, size.y, 0))
|
||||
if pos_c then table.insert(ys, pos_c.y) end
|
||||
local pos_c = mcl_villages.find_surface_down(lvm, vector.offset(pos, 0, size.y, size.z-1))
|
||||
if pos_c then table.insert(ys, pos_c.y) end
|
||||
local pos_c = mcl_villages.find_surface_down(lvm, vector.offset(pos, size.x-1, size.y, size.z-1))
|
||||
if pos_c then table.insert(ys, pos_c.y) end
|
||||
table.sort(ys)
|
||||
-- well supported base, not too uneven?
|
||||
if #ys < 5 or ys[#ys]-ys[1] > 8 then return nil, nil end
|
||||
cpos.y = math.floor(0.5 * (ys[math.floor(#ys/2)] + ys[math.ceil(#ys/2)]) + 0.5) -- median, rounded
|
||||
return cpos, surface_material
|
||||
end
|
||||
|
||||
local function add_building(settlement, building, count_buildings)
|
||||
table.insert(settlement, building)
|
||||
count_buildings[building["name"]] = count_buildings[building["name"]] + 1
|
||||
count_buildings.num_jobs = count_buildings.num_jobs + (building["num_jobs"] or 0)
|
||||
count_buildings.num_beds = count_buildings.num_beds + (building["num_beds"] or 0)
|
||||
count_buildings[building.name] = (count_buildings[building.name] or 0) + 1
|
||||
count_buildings.num_jobs = count_buildings.num_jobs + (building.num_jobs or 0)
|
||||
count_buildings.num_beds = count_buildings.num_beds + (building.num_beds or 0)
|
||||
if building.group then
|
||||
count_buildings[building.group] = (count_buildings[building.group] or 0) + 1
|
||||
end
|
||||
end
|
||||
|
||||
local function layout_town(lvm, minp, maxp, pr, input_settlement)
|
||||
local function layout_town(vm, minp, maxp, pr, input_settlement)
|
||||
local center = vector.new(pr:next(minp.x + 24, maxp.x - 24), maxp.y, pr:next(minp.z + 24, maxp.z - 24))
|
||||
minetest.log("action", "[mcl_villages] sudo make me a village at: " .. minetest.pos_to_string(minp).." - "..minetest.pos_to_string(maxp))
|
||||
local possible_rotations = {"0", "90", "180", "270"}
|
||||
|
@ -126,8 +74,8 @@ local function layout_town(lvm, minp, maxp, pr, input_settlement)
|
|||
local settlement = {}
|
||||
-- now some buildings around in a circle, radius = size of town center
|
||||
local x, y, z, r, lastr = center.x, center.y, center.z, 0, 99
|
||||
local mindist = 4
|
||||
if #input_settlement >= 12 then mindist = 3 end
|
||||
local mindist = 3
|
||||
if #input_settlement >= 12 then mindist = 2 end
|
||||
-- draw j circles around center and increase radius by math.random(2,4)
|
||||
for j = 1,20 do
|
||||
local steps = math.min(math.floor(math.pi * 2 * r / 2), 30) -- try up to 30 angles
|
||||
|
@ -146,9 +94,9 @@ local function layout_town(lvm, minp, maxp, pr, input_settlement)
|
|||
-- ensure we have 3 space for terraforming, and avoid problems with VoxelManip
|
||||
if tlpos.x - 3 >= minp.x and tlpos.x + size.x + 3 <= maxp.x
|
||||
and tlpos.z + 3 >= minp.z and tlpos.z + size.y + 3 <= maxp.z then
|
||||
local pos, surface_material = check_ground(lvm, cpos, size)
|
||||
local pos, surface_material = vl_terraforming.find_level_vm(vm, cpos, size)
|
||||
-- check distance to other buildings. Note that we still want to add baseplates etc.
|
||||
if pos and mcl_villages.check_distance(settlement, cpos, size.x, size.z, mindist) then
|
||||
if pos and mcl_villages.surface_mat[surface_material.name] and mcl_villages.check_distance(settlement, cpos, size.x, size.z, mindist) then
|
||||
-- use town bell as new reference point for placement height
|
||||
if #settlement == 0 then
|
||||
center_surface, y = cpos, math.min(maxp.y, pos.y + mcl_villages.max_height_difference * 0.5 + 1)
|
||||
|
@ -186,7 +134,7 @@ local function layout_town(lvm, minp, maxp, pr, input_settlement)
|
|||
return settlement
|
||||
end
|
||||
|
||||
function mcl_villages.create_site_plan(lvm, minp, maxp, pr)
|
||||
function mcl_villages.create_site_plan(vm, minp, maxp, pr)
|
||||
local settlement = {}
|
||||
|
||||
-- initialize all settlement_info table
|
||||
|
@ -208,12 +156,12 @@ function mcl_villages.create_site_plan(lvm, minp, maxp, pr)
|
|||
local building_info = mcl_villages.schematic_jobs[rindex]
|
||||
|
||||
if
|
||||
(building_info["min_jobs"] == nil or count_buildings.number_of_jobs >= building_info["min_jobs"])
|
||||
and (building_info["max_jobs"] == nil or count_buildings.number_of_jobs <= building_info["max_jobs"])
|
||||
(building_info.min_jobs == nil or count_buildings.number_of_jobs >= building_info.min_jobs)
|
||||
and (building_info.max_jobs == nil or count_buildings.number_of_jobs <= building_info.max_jobs)
|
||||
and (
|
||||
building_info["num_others"] == nil
|
||||
or count_buildings[building_info["name"]] == 0
|
||||
or building_info["num_others"] * count_buildings[building_info["name"]] < count_buildings.num_jobs
|
||||
building_info.num_others == nil
|
||||
or (count_buildings[building_info.group or building_info.name] or 0) == 0
|
||||
or building_info.num_others * (count_buildings[building_info.group or building_info.name] or 0) < count_buildings.num_jobs
|
||||
)
|
||||
then
|
||||
add_building(settlement, building_info, count_buildings)
|
||||
|
@ -232,12 +180,12 @@ function mcl_villages.create_site_plan(lvm, minp, maxp, pr)
|
|||
local building_info = mcl_villages.schematic_houses[rindex]
|
||||
|
||||
if
|
||||
(building_info["min_jobs"] == nil or count_buildings.number_of_jobs >= building_info["min_jobs"])
|
||||
and (building_info["max_jobs"] == nil or count_buildings.number_of_jobs <= building_info["max_jobs"])
|
||||
(building_info.min_jobs == nil or count_buildings.number_of_jobs >= building_info.min_jobs)
|
||||
and (building_info.max_jobs == nil or count_buildings.number_of_jobs <= building_info.max_jobs)
|
||||
and (
|
||||
building_info["num_others"] == nil
|
||||
or count_buildings[building_info["name"]] == 0
|
||||
or building_info["num_others"] * count_buildings[building_info["name"]] < count_buildings.num_jobs
|
||||
building_info.num_others == nil
|
||||
or (count_buildings[building_info.group or building_info.name] or 0) == 0
|
||||
or building_info.num_others * (count_buildings[building_info.group or building_info.name] or 0) < count_buildings.num_jobs
|
||||
)
|
||||
then
|
||||
add_building(settlement, building_info, count_buildings)
|
||||
|
@ -246,7 +194,7 @@ function mcl_villages.create_site_plan(lvm, minp, maxp, pr)
|
|||
|
||||
-- Based on number of villagers
|
||||
local num_wells = pr:next(1, math.ceil(count_buildings.num_beds / 10))
|
||||
for i = 1, num_wells do
|
||||
for _ = 1, num_wells do
|
||||
local windex = pr:next(1, #mcl_villages.schematic_wells)
|
||||
local cur_schem = table.copy(mcl_villages.schematic_wells[windex])
|
||||
table.insert(settlement, pr:next(1, #settlement), cur_schem)
|
||||
|
@ -261,12 +209,12 @@ function mcl_villages.create_site_plan(lvm, minp, maxp, pr)
|
|||
end
|
||||
|
||||
table.insert(settlement, 1, bell_info)
|
||||
return layout_town(lvm, minp, maxp, pr, settlement)
|
||||
return layout_town(vm, minp, maxp, pr, settlement)
|
||||
end
|
||||
|
||||
|
||||
function mcl_villages.place_schematics(lvm, settlement, blockseed, pr)
|
||||
-- local lvm = VoxelManip()
|
||||
function mcl_villages.place_schematics(vm, settlement, blockseed, pr)
|
||||
-- local vm = VoxelManip()
|
||||
local bell_pos = vector.offset(settlement[1].minp, math.floor(settlement[1].size.x/2), 0, math.floor(settlement[1].size.z/2))
|
||||
local bell_center_pos
|
||||
local bell_center_node_type
|
||||
|
@ -284,12 +232,12 @@ function mcl_villages.place_schematics(lvm, settlement, blockseed, pr)
|
|||
local schematic = loadstring(schem_lua)()
|
||||
|
||||
-- the foundation and air space for the building was already built before
|
||||
-- lvm:read_from_map(vector.new(minp.x, minp.y, minp.z), vector.new(maxp.x, maxp.y, maxp.z))
|
||||
-- lvm:get_data()
|
||||
-- vm:read_from_map(vector.new(minp.x, minp.y, minp.z), vector.new(maxp.x, maxp.y, maxp.z))
|
||||
-- vm:get_data()
|
||||
-- now added in placement code already, pos has the primary height if (building.yadjust or 0) ~= 0 then minp = vector.offset(minp, 0, building.yadjust, 0) end
|
||||
-- minetest.log("debug", "placing schematics for "..building.name.." at "..minetest.pos_to_string(minp).." on "..surface_material)
|
||||
minetest.place_schematic_on_vmanip(
|
||||
lvm,
|
||||
vm,
|
||||
minp,
|
||||
schematic,
|
||||
rotation,
|
||||
|
@ -305,27 +253,27 @@ function mcl_villages.place_schematics(lvm, settlement, blockseed, pr)
|
|||
p.x = x
|
||||
for y = minp.y,maxp.y-1 do
|
||||
p.y = y
|
||||
local n = lvm:get_node_at(p)
|
||||
local n = vm:get_node_at(p)
|
||||
if n and n.name == "mcl_villages:no_paths" then
|
||||
p.y = y+1
|
||||
n = lvm:get_node_at(p)
|
||||
n = vm:get_node_at(p)
|
||||
if n and n.name == "air" then
|
||||
lvm:set_node_at(p, {name="mcl_villages:no_paths"})
|
||||
vm:set_node_at(p, {name="mcl_villages:no_paths"})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
mcl_villages.store_path_ends(lvm, minp, maxp, cpos, blockseed, bell_pos)
|
||||
mcl_villages.store_path_ends(vm, minp, maxp, cpos, blockseed, bell_pos)
|
||||
|
||||
if building.name == "belltower" then -- TODO: allow multiple types?
|
||||
bell_center_pos = cpos
|
||||
local center_node = lvm:get_node_at(cpos)
|
||||
local center_node = vm:get_node_at(cpos)
|
||||
bell_center_node_type = center_node.name
|
||||
end
|
||||
end
|
||||
|
||||
lvm:write_to_map(true) -- for path finder and light
|
||||
vm:write_to_map(true) -- for path finder and light
|
||||
|
||||
local biome_data = minetest.get_biome_data(bell_pos)
|
||||
local biome_name = minetest.get_biome_name(biome_data.biome)
|
||||
|
@ -344,8 +292,8 @@ function mcl_villages.place_schematics(lvm, settlement, blockseed, pr)
|
|||
end
|
||||
|
||||
-- read back any changes
|
||||
local emin, emax = lvm:get_emerged_area()
|
||||
lvm:read_from_map(emin, emax)
|
||||
local emin, emax = vm:get_emerged_area()
|
||||
vm:read_from_map(emin, emax)
|
||||
end
|
||||
|
||||
function mcl_villages.post_process_village(blockseed)
|
||||
|
@ -371,6 +319,7 @@ function mcl_villages.post_process_village(blockseed)
|
|||
else
|
||||
minetest.log("info", "Could not create a golem!")
|
||||
end
|
||||
|
||||
spawn_cats(bell)
|
||||
|
||||
for _, building in pairs(settlement_info) do
|
||||
|
@ -392,7 +341,7 @@ function mcl_villages.post_process_village(blockseed)
|
|||
|
||||
for _, bed_pos in pairs(bld_beds) do
|
||||
local bed_node = minetest.get_node(bed_pos)
|
||||
local bed_group = core.get_item_group(bed_node.name, "bed")
|
||||
local bed_group = minetest.get_item_group(bed_node.name, "bed")
|
||||
|
||||
-- We only spawn at bed bottoms
|
||||
-- 1 is bottom, 2 is top
|
||||
|
@ -413,7 +362,7 @@ function mcl_villages.post_process_village(blockseed)
|
|||
local m = minetest.get_meta(bed_pos)
|
||||
m:set_string("bell_pos", minetest.pos_to_string(bell_pos))
|
||||
if m:get_string("villager") == "" then
|
||||
local v = minetest.add_entity(bed_pos, "mobs_mc:villager")
|
||||
local v = minetest.add_entity(vector.offset(bed_pos, 0, 0.06, 0), "mobs_mc:villager")
|
||||
if v then
|
||||
local l = v:get_luaentity()
|
||||
l._bed = bed_pos
|
||||
|
@ -440,14 +389,14 @@ function mcl_villages.post_process_village(blockseed)
|
|||
end
|
||||
|
||||
-- Terraform for an entire village
|
||||
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.
|
||||
-- we make the foundations 1 node wider than requested, to have one node for path laying
|
||||
function mcl_villages.terraform(vm, settlement, pr)
|
||||
-- TODO: sort top-down, then bottom-up, or opposite?
|
||||
-- we make the foundations 2 node wider than necessary, to have one node for path laying
|
||||
for i, building in ipairs(settlement) do
|
||||
if not building.no_clearance then
|
||||
local pos, size = building.pos, building.size
|
||||
pos = vector.offset(pos, -math.floor(size.x/2), 0, -math.floor(size.z/2))
|
||||
mcl_structures.clearance(lvm, pos.x-1, pos.y, pos.z-1, size.x+2, size.y, size.z+2, 2, building.surface_mat, pr)
|
||||
vl_terraforming.clearance_vm(vm, pos.x-1, pos.y, pos.z-1, size.x+2, size.y, size.z+2, 2, building.surface_mat, building.dust_mat, pr)
|
||||
end
|
||||
end
|
||||
for i, building in ipairs(settlement) do
|
||||
|
@ -456,10 +405,11 @@ function mcl_villages.terraform(lvm, settlement, pr)
|
|||
local surface_mat = building.surface_mat
|
||||
local platform_mat = building.platform_mat or { name = foundation_materials[surface_mat.name] or "mcl_core:dirt" }
|
||||
local stone_mat = building.stone_mat or { name = "mcl_core:stone" }
|
||||
local dust_mat = building.dust_mat
|
||||
building.platform_mat = platform_mat -- remember for use in schematic placement
|
||||
building.stone_mat = stone_mat
|
||||
pos = vector.offset(pos, -math.floor((size.x-1)/2), 0, -math.floor((size.z-1)/2))
|
||||
mcl_structures.foundation(lvm, pos.x-2, pos.y, pos.z-2, size.x+4, -4, size.z+4, 2, surface_mat, platform_mat, stone_mat, pr)
|
||||
vl_terraforming.foundation_vm(vm, pos.x-2, pos.y, pos.z-2, size.x+4, -5, size.z+4, 2, surface_mat, platform_mat, stone_mat, dust_mat, pr)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
name = mcl_villages
|
||||
author = Rochambeau, kno10
|
||||
description = This mod adds settlements on world generation.
|
||||
depends = mcl_core, mcl_util, mcl_mapgen_core, mcl_structures, mcl_loot, mobs_mc
|
||||
depends = mcl_core, mcl_util, mcl_mapgen_core, mcl_structures, mcl_loot, mobs_mc, vl_terraforming
|
||||
optional_depends = mcl_farming
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,79 +1,3 @@
|
|||
local function is_above_surface(name)
|
||||
if name == "air" or name == "mcl_bamboo:bamboo" or name == "mcl_core:vine" or name == "mcl_core:snow" then
|
||||
return true
|
||||
end
|
||||
local meta = core.registered_items[name]
|
||||
local groups = meta and meta.groups
|
||||
return groups and (groups["deco_block"] or groups["tree"] or groups["leaves"] or groups["plant"])
|
||||
end
|
||||
function mcl_villages.find_surface_down(lvm, pos, surface_node)
|
||||
local p6 = vector.new(pos)
|
||||
surface_node = surface_node or lvm:get_node_at(p6)
|
||||
if not surface_node then return end
|
||||
local has_air = is_above_surface(surface_node.name)
|
||||
for y = p6.y - 1, math.max(0, p6.y - 80), -1 do
|
||||
p6.y = y
|
||||
local top_node = surface_node
|
||||
surface_node = lvm:get_node_at(p6)
|
||||
if not surface_node or surface_node.name == "ignore" then return nil end
|
||||
if is_above_surface(surface_node.name) then
|
||||
has_air = true
|
||||
else
|
||||
if has_air then
|
||||
if mcl_villages.surface_mat[surface_node.name] then
|
||||
--minetest.log("Found "..surface_node.name.." below "..top_node.name)
|
||||
return p6, surface_node
|
||||
else
|
||||
local ndef = minetest.registered_nodes[surface_node.name]
|
||||
if ndef and ndef.walkable then
|
||||
--minetest.log("Found non-suitable "..surface_node.name.." below "..top_node.name)
|
||||
return nil
|
||||
end
|
||||
end
|
||||
end
|
||||
has_air = false
|
||||
end
|
||||
end
|
||||
end
|
||||
function mcl_villages.find_surface_up(lvm, pos, surface_node)
|
||||
local p6 = vector.new(pos)
|
||||
surface_node = surface_node or lvm:get_node_at(p6) --, true, 1000000)
|
||||
if not surface_node then return end
|
||||
for y = p6.y + 1, p6.y + 80 do
|
||||
p6.y = y
|
||||
local top_node = lvm:get_node_at(p6)
|
||||
if not top_node or top_node.name == "ignore" then return nil end
|
||||
if is_above_surface(top_node.name) then
|
||||
if mcl_villages.surface_mat[surface_node.name] then
|
||||
-- minetest.log("Found "..surface_node.name.." below "..top_node.name)
|
||||
p6.y = p6.y - 1
|
||||
return p6, surface_node
|
||||
else
|
||||
local ndef = minetest.registered_nodes[surface_node.name]
|
||||
if ndef and ndef.walkable then
|
||||
-- minetest.log("Found non-suitable "..surface_node.name.." below "..top_node.name)
|
||||
return nil
|
||||
end
|
||||
end
|
||||
end
|
||||
surface_node = top_node
|
||||
end
|
||||
end
|
||||
-------------------------------------------------------------------------------
|
||||
-- function to find surface block y coordinate
|
||||
-- returns surface postion
|
||||
-------------------------------------------------------------------------------
|
||||
function mcl_villages.find_surface(lvm, pos)
|
||||
local p6 = vector.new(pos)
|
||||
if p6.y < 0 then p6.y = 0 end -- start at water level
|
||||
local surface_node = lvm:get_node_at(p6)
|
||||
-- downward, if starting position is empty
|
||||
if is_above_surface(surface_node.name) then
|
||||
return mcl_villages.find_surface_down(lvm, p6, surface_node)
|
||||
else
|
||||
return mcl_villages.find_surface_up(lvm, p6, surface_node)
|
||||
end
|
||||
end
|
||||
-- check the minimum distance of two squares, on axes
|
||||
function mcl_villages.check_distance(settlement, cpos, sizex, sizez, limit)
|
||||
for i, building in ipairs(settlement) do
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
name = MAPGEN
|
||||
description = Meta-modpack containing map generating mods for MineClone 2
|
||||
description = Meta-modpack containing map generating mods for VoxeLibre
|
||||
|
|
|
@ -1096,11 +1096,10 @@ local function create_corridor_system(main_cave_coords)
|
|||
return true
|
||||
end
|
||||
|
||||
mcl_structures.register_structure("mineshaft",{
|
||||
vl_structures.register_structure("mineshaft",{
|
||||
place_on = {"group:sand","group:grass_block","mcl_core:water_source","group:dirt","mcl_core:dirt_with_grass","mcl_core:gravel","group:material_stone","mcl_core:snow"},
|
||||
chunk_probability = 4,
|
||||
flags = "place_center_x, place_center_z, force_placement, all_floors",
|
||||
sidelen = 32,
|
||||
y_max = 40,
|
||||
y_min = mcl_vars.mg_overworld_min,
|
||||
place_func = function(pos,_,pr,blockseed)
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
# vl_structures
|
||||
|
||||
Updated API for structure spawning for VoxeLibre and Mineclonia
|
||||
|
||||
## 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.
|
||||
|
||||
### structure definition
|
||||
{
|
||||
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},... }
|
||||
}
|
||||
## 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, schematic, rotation, replacements, force_placement, flags, after_placement_callback, pr, callback_param)
|
|
@ -0,0 +1,518 @@
|
|||
vl_structures.registered_structures = {}
|
||||
|
||||
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 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 floor = math.floor
|
||||
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")
|
||||
disabled_structures = disabled_structures and disabled_structures:split(",") or {}
|
||||
function mcl_structures.is_disabled(structname)
|
||||
return table.indexof(disabled_structures,structname) ~= -1
|
||||
end
|
||||
|
||||
local mg_name = minetest.get_mapgen_setting("mg_name")
|
||||
|
||||
-- see vl_terraforming for documentation
|
||||
local DEFAULT_PREPARE = { tolerance = 10, foundation = -3, clear = false, clear_bottom = 0, clear_top = 4, padding = 1, corners = 1 }
|
||||
local DEFAULT_FLAGS = "place_center_x,place_center_z"
|
||||
|
||||
local function parse_prepare(prepare)
|
||||
if prepare == nil or prepare == true then return DEFAULT_PREPARE end
|
||||
if prepare == false then return {} end
|
||||
if prepare.foundation == true then
|
||||
prepare = table.copy(prepare)
|
||||
prepare.foundation = DEFAULT_PREPARE.foundation
|
||||
end
|
||||
return 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
|
||||
end
|
||||
|
||||
--- Trim a full path name to its last two parts as short name for logging
|
||||
local function basename(filename)
|
||||
local fn = string.split(filename, "/")
|
||||
return #fn > 1 and (fn[#fn-1].."/"..fn[#fn]) or fn[#fn]
|
||||
end
|
||||
|
||||
--- Load a schematic file
|
||||
-- @param filename string: file name
|
||||
-- @param name string: for logging, optional
|
||||
-- @return loaded schematic
|
||||
function vl_structures.load_schematic(filename, name)
|
||||
-- load, and ensure we have size information
|
||||
if filename == nil then error("Filename is nil for schematic "..tostring(name)) end
|
||||
if type(filename) == "string" then minetest.log("action", "Loading "..filename) end
|
||||
local s = loadstring(minetest.serialize_schematic(filename, "lua", {lua_use_comments = false, lua_num_indent_spaces = 0}) .. " return schematic")()
|
||||
if not s then
|
||||
minetest.log("warning", "[vl_structures] failed to load schematic "..basename(filename))
|
||||
return nil
|
||||
elseif not s.size then
|
||||
minetest.log("warning", "[vl_structures] no size information for schematic "..basename(filename))
|
||||
return nil
|
||||
end
|
||||
if logging then minetest.log("warning", "[vl_structures] loaded schematic "..basename(filename).." size "..minetest.pos_to_string(s.size)) end
|
||||
if not s.name then s.name = name or basename(filename) end
|
||||
return s
|
||||
end
|
||||
|
||||
-- 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)
|
||||
-- clear boolean: clear overhead area
|
||||
-- clear_min number or string: height from base to start clearing, "top" to start at top
|
||||
-- clear_max number: height from top to stop primary clearing
|
||||
-- padding number: additional padding to increase the area, default 1
|
||||
-- corners number: corner smoothing of foundation and clear, default 1
|
||||
-- pr PcgRandom: random generator
|
||||
-- name string: for logging
|
||||
local function emerge_schematic_vm(vm, param)
|
||||
local pos, size, yoffset, pr = param.pos, param.size, param.yoffset or 0, param.pr
|
||||
local prepare, surface_mat = parse_prepare(param.prepare), param.surface_mat
|
||||
-- Step 1: adjust ground to a more level position
|
||||
if pos and size and prepare and tolerance_enabled(prepare.tolerance, prepare.mode) then
|
||||
pos, surface_mat = vl_terraforming.find_level_vm(vm, pos, size, prepare.tolerance, prepare.mode)
|
||||
if not pos then
|
||||
minetest.log("warning", "[vl_structures] Not spawning "..tostring(param.schematic.name).." at "..minetest.pos_to_string(param.pos).." because ground is too uneven.")
|
||||
return
|
||||
end
|
||||
end
|
||||
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
|
||||
if prepare and (prepare.clear or prepare.foundation) then
|
||||
local prepare_start = os.clock()
|
||||
-- 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 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
|
||||
if node_top.name == "mcl_core:dirt_with_grass" and b then node_top.param2 = b._mcl_grass_palette_index end
|
||||
|
||||
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(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)
|
||||
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,
|
||||
size.x + padding * 2, ymax - yoff, size.z + padding * 2,
|
||||
corners, node_top, node_dust, pr)
|
||||
end
|
||||
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
|
||||
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)
|
||||
end
|
||||
end
|
||||
-- note: pos is always the center position
|
||||
minetest.place_schematic_on_vmanip(vm, vector_offset(pos, 0, (param.yoffset or 0), 0), param.schematic, param.rotation, param.replacements, param.force_placement, "place_center_x,place_center_z")
|
||||
return pos
|
||||
end
|
||||
|
||||
-- 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)
|
||||
if not pos then return end
|
||||
vm:write_to_map(true)
|
||||
-- repair walls (TODO: port to vmanip? but no "vm.find_nodes_in_area" yet)
|
||||
local pmin = vector_offset(pos, -floor((param.size.x-1)*0.5), 0, -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
|
||||
end
|
||||
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)
|
||||
end
|
||||
end
|
||||
|
||||
function vl_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 = vl_structures.load_schematic(schematic)
|
||||
end
|
||||
rotation = vl_structures.parse_rotation(rotation, pr)
|
||||
prepare = parse_prepare(prepare)
|
||||
local ppos, pmin, pmax, size = vl_structures.get_extends(pos, schematic.size, yoffset, rotation, flags or 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
|
||||
if not type(tolerance) == "number" then tolerance = 8 end -- for emerge only
|
||||
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
|
||||
local padding = (prepare.padding or 0) + 3
|
||||
local depth = prepare.foundation and ((prepare.depth or -4) - 15) or 0 -- minimum depth
|
||||
local height = prepare.clear and (size.y * 2 + 6) or 0 -- headroom
|
||||
emin = vector_offset(emin, -padding, depth, -padding)
|
||||
emax = vector_offset(emax, padding, height, padding)
|
||||
end
|
||||
minetest.emerge_area(emin, emax, emerge_schematic, {
|
||||
emin=emin, emax=emax, name=schematic.name,
|
||||
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
|
||||
})
|
||||
end
|
||||
|
||||
local function emerge_complex_schematics(blockpos, action, calls_remaining, param)
|
||||
if calls_remaining >= 1 then return end
|
||||
local start = os.clock()
|
||||
local vm = VoxelManip()
|
||||
vm:read_from_map(param.emin, param.emax)
|
||||
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
|
||||
|
||||
-- pick random daughter schematics + rotations
|
||||
local daughters = {}
|
||||
if def.daughters then
|
||||
for i,d in pairs(def.daughters) 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
|
||||
end
|
||||
|
||||
-- Step 1: adjust ground to a more level position
|
||||
if pos and size and prepare and tolerance_enabled(prepare.tolerance, prepare.mode) then
|
||||
pos, surface_mat = vl_terraforming.find_level_vm(vm, pos, size, prepare.tolerance, 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
|
||||
-- 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
|
||||
--if logging and not def.terrain_feature then minetest.log("action", "[vl_structures] "..def.name.." after find_level at "..minetest.pos_to_string(pos).." in "..string.format("%.2fms (main: %.2fms)", (os.clock()-start)*1000, (os.clock()-startmain)*1000)) end
|
||||
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)
|
||||
-- todo: also support checking ground of daughter schematics, but not used by current schematics
|
||||
-- Step 2: prepare ground foundations and clear
|
||||
-- todo: allow daugthers to use prepare when parent does not
|
||||
if prepare and (prepare.clear or prepare.foundation) then
|
||||
local prepare_start = os.clock()
|
||||
-- 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 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
|
||||
if node_top.name == "mcl_core:dirt_with_grass" and b then node_top.param2 = b._mcl_grass_palette_index end
|
||||
|
||||
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(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)
|
||||
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,
|
||||
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
|
||||
local yoff, ymax = ddp.clear_bottom or 0, dsize.y + yoffset + (ddp.clear_top or 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,
|
||||
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_vm(vm, gp.x, gp.y + yoff, gp.z,
|
||||
dsize.x + padding * 2, ymax - yoff, dsize.z + padding * 2,
|
||||
corners, node_top, node_dust, pr)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
-- if logging and not def.terrain_feature then minetest.log("action", "[vl_structures] "..def.name.." after clear at "..minetest.pos_to_string(pos).." in "..string.format("%.2fms (main: %.2fms)", (os.clock()-start)*1000, (os.clock()-prepare_start)*1000)) end
|
||||
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
|
||||
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)
|
||||
-- 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
|
||||
local depth = (type(ddp.foundation) == "number" and ddp.foundation) or 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)
|
||||
vl_terraforming.foundation_vm(vm, gp.x, gp.y - 1, gp.z,
|
||||
dsize.x + padding * 2, depth, dsize.z + padding * 2,
|
||||
corners, node_top, node_filler, node_stone, node_dust, pr)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- if logging and not def.terrain_feature then minetest.log("action", "[vl_structures] "..def.name.." prepared at "..minetest.pos_to_string(pos).." in "..string.format("%.2fms (main: %.2fms)", (os.clock()-start)*1000, (os.clock()-prepare_start)*1000)) end
|
||||
end
|
||||
|
||||
-- note: pos is always the center position
|
||||
minetest.place_schematic_on_vmanip(vm, vector_offset(pos, 0, (param.yoffset or 0), 0), param.schematic, param.rotation, param.replacements, param.force_placement, "place_center_x,place_center_z")
|
||||
|
||||
for _,tmp in ipairs(daughters) do
|
||||
local d, ds, rot = tmp[1], tmp[2], tmp[3]
|
||||
--local dsize = vl_structures.size_rotated(ds.size, rot)
|
||||
--local p = vector_offset(pos, d.pos.x - floor((ds.size.x-1)*0.5), d.pos.y + (yoffset or 0),
|
||||
-- d.pos.z - floor((ds.size.z-1)*0.5))
|
||||
local p = vector_offset(pos, d.pos.x, d.pos.y + (yoffset or 0), d.pos.z)
|
||||
minetest.place_schematic_on_vmanip(vm, p, ds, rot, d.replacements, d.force_placement, "place_center_x,place_center_z")
|
||||
end
|
||||
local endmain = os.clock()
|
||||
vm:write_to_map(true)
|
||||
-- Note: deliberately pos, p1 and p2 from the parent, as these are calls to the parent.
|
||||
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 logging and not def.terrain_feature then
|
||||
minetest.log("action", "[vl_structures] "..def.name.." spawned at "..minetest.pos_to_string(pos).." in "..string.format("%.2fms (main: %.2fms)", (os.clock()-start)*1000, (endmain-startmain)*1000))
|
||||
end
|
||||
end
|
||||
|
||||
--- Place a schematic with daughters (nether bulwark, nether outpost with bridges)
|
||||
local function place_complex_schematics(pos, yoffset, schematic, rotation, def, pr)
|
||||
if schematic and not schematic.size then -- e.g., igloo still passes filenames
|
||||
schematic = vl_structures.load_schematic(schematic)
|
||||
end
|
||||
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)
|
||||
-- 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
|
||||
if type(tolerance) ~= "number" then tolerance = 10 end -- 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!
|
||||
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
|
||||
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?
|
||||
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_complex_schematics, { name = def.name,
|
||||
emin=emin, emax=emax, def=def, schematic=schematic,
|
||||
pos=ppos, yoffset=yoffset, size=size, rotation=rotation,
|
||||
pr=pr
|
||||
})
|
||||
end
|
||||
|
||||
-- TODO: remove blockseed?
|
||||
function vl_structures.place_structure(pos, def, pr, blockseed, rot)
|
||||
if not def then return end
|
||||
local log_enabled = logging and not def.terrain_feature
|
||||
-- load schematics the first time
|
||||
if def.filenames and not def.schematics then
|
||||
if #def.filenames == 0 then minetest.log("warning","[vl_structures] schematic "..def.name.." has an empty list of filenames.") end
|
||||
def.schematics = {}
|
||||
for _, filename in ipairs(def.filenames) do
|
||||
local s = vl_structures.load_schematic(filename, def.name)
|
||||
if s then table.insert(def.schematics, s) end
|
||||
end
|
||||
if def.daughters then
|
||||
for _,d in pairs(def.daughters) do
|
||||
d.schematics = {}
|
||||
for _, filename in ipairs(d.filenames) do
|
||||
local s = vl_structures.load_schematic(filename, d.name)
|
||||
if s then table.insert(d.schematics, s) end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
-- 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 = vl_structures.parse_rotation(rot or "random", pr)
|
||||
place_complex_schematics(pos, yoffset, schematic, rot, def, pr)
|
||||
if log_enabled then
|
||||
minetest.log("verbose", "[vl_structures] "..def.name.." to be placed at "..minetest.pos_to_string(pos))
|
||||
end
|
||||
return true
|
||||
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)
|
||||
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")
|
||||
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)
|
||||
return false
|
||||
end
|
||||
elseif log_enabled then
|
||||
minetest.log("warning","[vl_structures] place_func failed for schematic "..def.name)
|
||||
end
|
||||
end
|
||||
|
||||
--nospawn means it will be placed by another (non-nospawn) structure that contains it's structblock i.e. it will not be placed by mapgen directly
|
||||
function vl_structures.register_structure(name,def,nospawn)
|
||||
if vl_structures.is_disabled(name) then return end
|
||||
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
|
||||
def.fill_ratio = 1.1/80/80 -- 1 per chunk, controlled by chunk probability only
|
||||
end
|
||||
if nospawn or def.nospawn then return end -- ice column, boulder
|
||||
if def.filenames then
|
||||
for _, filename in ipairs(def.filenames) do
|
||||
if not mcl_util.file_exists(filename) then
|
||||
minetest.log("warning","[vl_structures] schematic "..(name or "unknown").." is missing file "..basename(filename))
|
||||
return nil
|
||||
end
|
||||
end
|
||||
end
|
||||
if def.place_on then
|
||||
minetest.register_on_mods_loaded(function()
|
||||
def.deco = mcl_mapgen_core.register_decoration({
|
||||
name = "vl_structures:deco_"..name,
|
||||
priority = def.priority or (def.terrain_feature and 900) or 100, -- run before regular decorations
|
||||
deco_type = "schematic",
|
||||
schematic = { size = {x = 1, y = 1, z = 1}, data = { { name = "ignore" } } },
|
||||
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 or "place_center_x, place_center_z",
|
||||
biomes = def.biomes,
|
||||
y_max = def.y_max,
|
||||
y_min = def.y_min
|
||||
}, function()
|
||||
def.deco_id = minetest.get_decoration_id("vl_structures:deco_"..name)
|
||||
minetest.set_gen_notify({decoration=true}, { def.deco_id })
|
||||
end)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
-- 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(vl_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) + worldseed + RANDOM_SEED_OFFSET)
|
||||
local realpos = vector_offset(pos, 0, 1, 0)
|
||||
if struct.chunk_probability == nil or pr:next(0, 1e9)/1e9 * struct.chunk_probability <= structure_boost then
|
||||
vl_structures.place_structure(realpos, struct, pr, blockseed)
|
||||
if struct.chunk_probability then break end -- one (attempt) per chunk only
|
||||
end
|
||||
end
|
||||
elseif struct.static_pos then
|
||||
local pr
|
||||
for _, pos in pairs(struct.static_pos) do
|
||||
if vector.in_area(pos, minp, maxp) then
|
||||
pr = pr or PcgRandom(worldseed + RANDOM_SEED_OFFSET)
|
||||
vl_structures.place_structure(pos, struct, pr, blockseed)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return false, false, false
|
||||
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
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
local modname = minetest.get_current_modname()
|
||||
local S = minetest.get_translator(modname)
|
||||
local modpath = minetest.get_modpath(modname)
|
||||
|
||||
vl_structures = {}
|
||||
|
||||
dofile(modpath.."/util.lua")
|
||||
dofile(modpath.."/api.lua")
|
||||
|
||||
--- /spawnstruct chat command
|
||||
minetest.register_chatcommand("spawnstruct", {
|
||||
params = mcl_dungeons and "dungeon" or "",
|
||||
description = S("Generate a pre-defined structure near your position."),
|
||||
privs = {debug = true},
|
||||
func = function(name, param)
|
||||
local player = minetest.get_player_by_name(name)
|
||||
if not player then return end
|
||||
local pos = player:get_pos()
|
||||
if not pos then return end
|
||||
pos = vector.round(pos)
|
||||
local dir = minetest.yaw_to_dir(player:get_look_horizontal())
|
||||
local rot = math.abs(dir.x) > math.abs(dir.z) and (dir.x < 0 and "270" or "90") or (dir.z < 0 and "180" or "0")
|
||||
local seed = minetest.hash_node_position(pos)
|
||||
local pr = PcgRandom(seed)
|
||||
local errord = false
|
||||
if param == "dungeon" and mcl_dungeons and mcl_dungeons.spawn_dungeon then
|
||||
mcl_dungeons.spawn_dungeon(pos, rot, pr)
|
||||
return true, "Spawning "..param
|
||||
elseif param == "" then
|
||||
minetest.chat_send_player(name, S("Error: No structure type given. Please use “/spawnstruct <type>”."))
|
||||
else
|
||||
for n,d in pairs(vl_structures.registered_structures) do
|
||||
if n == param then
|
||||
vl_structures.place_structure(pos, d, pr, seed, rot)
|
||||
return true, "Spawning "..param
|
||||
end
|
||||
end
|
||||
minetest.chat_send_player(name, S("Error: Unknown structure type. Please use “/spawnstruct <type>”."))
|
||||
end
|
||||
end
|
||||
})
|
||||
minetest.register_on_mods_loaded(function()
|
||||
local p = ""
|
||||
for n,_ in pairs(vl_structures.registered_structures) do
|
||||
p = p .. " | ".. n
|
||||
end
|
||||
minetest.registered_chatcommands["spawnstruct"].params = minetest.registered_chatcommands["spawnstruct"].params .. p
|
||||
end)
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
name = vl_structures
|
||||
author = kno10
|
||||
description = Structures API for VoxeLibre and Mineclonia
|
||||
depends = mcl_init, mcl_util, mcl_loot, vl_terraforming
|
|
@ -0,0 +1,132 @@
|
|||
local peaceful = minetest.settings:get_bool("only_peaceful_mobs", false)
|
||||
|
||||
local floor = math.floor
|
||||
local vector_offset = vector.offset
|
||||
|
||||
local ROTATIONS = { "0", "90", "180", "270" }
|
||||
--- Parse a rotation value
|
||||
-- @param rotation string: when "random", a rotation is chosen at random
|
||||
-- @param[opt] pr PseudoRandom: random generator
|
||||
-- @return Rotation
|
||||
function vl_structures.parse_rotation(rotation, pr)
|
||||
if rotation == "random" and pr then return ROTATIONS[pr:next(1,#ROTATIONS)] end
|
||||
return rotation
|
||||
end
|
||||
|
||||
--- 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 vl_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) end
|
||||
return vector.copy(size)
|
||||
end
|
||||
|
||||
--- 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; default none
|
||||
-- @return vector: new vector, for safety
|
||||
function vl_structures.top_left_from_flags(pos, size, flags)
|
||||
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 = -floor((size.x-1)*0.5) end
|
||||
if flags["place_center_y"] ~= nil then dy = -floor((size.y-1)*0.5) end
|
||||
if flags["place_center_z"] ~= nil then dz = -floor((size.z-1)*0.5) end
|
||||
return vector_offset(pos, dx, dy, dz)
|
||||
elseif type(flags) == "string" then
|
||||
if string.find(flags, "place_center_x") then dx = -floor((size.x-1)*0.5) end
|
||||
if string.find(flags, "place_center_y") then dy = -floor((size.y-1)*0.5) end
|
||||
if string.find(flags, "place_center_z") then dz = -floor((size.z-1)*0.5) end
|
||||
return vector_offset(pos, dx, dy, dz)
|
||||
end
|
||||
return pos
|
||||
end
|
||||
|
||||
--- Get the extends of a schematic after rotation and flags
|
||||
-- @param pos vector: position of base
|
||||
-- @param size vector: size of structure
|
||||
-- @param[opt] yoffset number: vertical offset
|
||||
-- @param[opt] rotation string: rotation value
|
||||
-- @param[opt] flags string or table: as in minetest.place_schematic, place_center_x, place_center_y; default none
|
||||
-- @return center on base level, area minimum, area maximum, rotated size (=pmax-pmin+1)
|
||||
function vl_structures.get_extends(pos, size, yoffset, rotation, flags)
|
||||
local size = vl_structures.size_rotated(size, rotation)
|
||||
local pmin = vl_structures.top_left_from_flags(pos, size, flags or DEFAULT_FLAGS)
|
||||
local cent = vector_offset(pmin, floor((size.x-1)*0.5), 0, floor((size.z-1)*0.5)) -- center
|
||||
pmin.y = pmin.y + (yoffset or 0) -- to pmin and pmax only
|
||||
local pmax = vector_offset(pmin, size.x - 1, size.y - 1, size.z - 1)
|
||||
return cent, pmin, pmax, size
|
||||
end
|
||||
|
||||
--- Call all on_construct handlers. Also called from mcl_villages for job sites
|
||||
-- @param pos Node position
|
||||
function vl_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 return def.on_construct(pos) end
|
||||
end
|
||||
|
||||
--- Call on_construct handlers for all nodes of given types
|
||||
-- @param p1 vector: Lowest coordinates of range
|
||||
-- @param p2 vector: Highest coordinates of range
|
||||
-- @param nodes string or table: node name or list of node names
|
||||
-- @return nodes found
|
||||
function vl_structures.construct_nodes(p1,p2,nodes)
|
||||
local nn = minetest.find_nodes_in_area(p1,p2,nodes)
|
||||
for _,p in pairs(nn) do vl_structures.init_node_construct(p) end
|
||||
return nn or {}
|
||||
end
|
||||
|
||||
--- Fill loot chests
|
||||
-- @param p1 vector: Lowest coordinates of range
|
||||
-- @param p2 vector: Highest coordinates of range
|
||||
-- @param loot table: Loot table
|
||||
-- @param pr PseudoRandom: random generator
|
||||
function vl_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)
|
||||
vl_structures.init_node_construct(p)
|
||||
local meta = minetest.get_meta(p)
|
||||
local inv = meta:get_inventory()
|
||||
mcl_loot.fill_inventory(inv, "main", lootitems, pr)
|
||||
end
|
||||
end
|
||||
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, 1, 0), mob) then
|
||||
count = count + 1
|
||||
end
|
||||
minetest.get_meta(node):set_string("spawnblock", "yes") -- note: also in peaceful mode!
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
# vl_terraforming
|
||||
Terraforming module for VoxeLibre and MineClonia
|
||||
|
||||
This module provides the following key functionalities:
|
||||
|
||||
- given a position, find the ground surface
|
||||
- given a position and size, find a balanced height (trimmed median height)
|
||||
- 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.
|
||||
|
||||
These are obtained by intersecting the square with an ellipse.
|
||||
At zero rounding, we want the line go through the corner, at sx/2, sz/2.
|
||||
|
||||
For this, we need to make ellipse sized $2a=\sqrt{2} sx$, $2b=\sqrt{2} sz$,
|
||||
Which yields $a = sx/\sqrt{2}$, $b=sz/\sqrt{2}$ and $a^2=0.5 sx^2$, $b^2=0.5 sz^2$
|
||||
To get corners, we decrease $a$ and $b$ by the corners parameter each
|
||||
The ellipse condition $dx^2/a^2+dz^2/b^2 \leq 1$ then yields $dx^2/(0.5 sx^2) + dz^2/(0.5 sz^2) \leq 1$
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
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_level_vm(vm, cpos, size, tolerance, mode)
|
||||
|
||||
Find "level" ground for a building, 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 "mode" can be set to "solid" (default), "liquid" (liquid surfaces only), "under_air" (both liquid and solid surfaces).
|
||||
|
||||
|
||||
## vl_terraforming.foundation_vm(vm, 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 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.
|
||||
|
||||
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:
|
||||
|
||||
- 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
|
||||
|
||||
|
||||
## vl_terraforming.clearance_vm(vm, 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 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.
|
||||
|
||||
The surface_mat will be used to turn nodes into surface nodes when widening the area.
|
||||
|
||||
pr is a PcgRandom random generator
|
||||
|
||||
## TODO
|
||||
|
||||
- [ ] 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)
|
||||
- [ ] improve tree removal
|
||||
|
|
@ -0,0 +1,174 @@
|
|||
local AIR = {name = "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
|
||||
|
||||
--- 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.
|
||||
-- For this, we need to make ellipse sized 2a=sqrt(2)*sx, 2b=sqrt(2)*sz,
|
||||
-- Which yields a = sx/sqrt(2), b=sz/sqrt(2) and a^2=sx^2*0.5, b^2=sz^2*0.5
|
||||
-- To get corners, we decrease a and b by approx. corners each
|
||||
-- The ellipse condition dx^2/a^2+dz^2/b^2 <= 1 then yields dx^2/(sx^2*0.5) + dz^2/(sz^2*0.5) <= 1
|
||||
-- We use wx2=sx^-2*2, wz2=sz^-2*2 and then dx^2*wx2+dz^2*wz2 <= 1
|
||||
--
|
||||
-- @param vm VoxelManip: Lua voxel manipulator
|
||||
-- @param px number: lowest x
|
||||
-- @param py number: lowest y
|
||||
-- @param pz number: lowest z
|
||||
-- @param sx number: x width
|
||||
-- @param sy number: y height
|
||||
-- @param sz number: z depth
|
||||
-- @param corners number: corner rounding
|
||||
-- @param surface_mat Node: surface node material
|
||||
-- @param dust_mat Node: surface dust material
|
||||
-- @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
|
||||
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
|
||||
local min_clear, max_clear = py+sy, py+floor(sy*1.5+2) -- todo: make more parameterizable, but adds another parameter
|
||||
-- excavate the needed volume and some headroom
|
||||
local vec = vector_new(0, 0, 0) -- single vector, to avoid allocations -- performance!
|
||||
for xi = px-1,px+sx do
|
||||
local dx = abs(cx-xi)
|
||||
local dx2 = max(dx+0.51,0)^2*wx2
|
||||
local dx21 = max(dx-0.49,0)^2*wx2
|
||||
vec.x = xi
|
||||
for zi = pz-1,pz+sz do
|
||||
local dz = abs(cz-zi)
|
||||
local dz2 = max(dz+0.51,0)^2*wz2
|
||||
local dz21 = max(dz-0.49,0)^2*wz2
|
||||
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 vm:get_node_at(vec).name ~= "mcl_core:bedrock" then set_node_at(vm, vec, AIR) end
|
||||
vec.y = py - 1
|
||||
local n = get_node_at(vm, vec)
|
||||
if n and n.name ~= surface_mat.name and is_solid_not_tree(n) then
|
||||
set_node_at(vm, vec, surface_mat)
|
||||
end
|
||||
for yi = py+1,min_clear do -- full height for inner area
|
||||
vec.y = yi
|
||||
if vm:get_node_at(vec).name ~= "mcl_core:bedrock" then set_node_at(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 = vm:get_node_at(vec).name
|
||||
if name ~= "mcl_core:bedrock" then
|
||||
local mat = AIR
|
||||
if dust_mat then
|
||||
vec.y = py
|
||||
if vm:get_node_at(vec).name == surface_mat.name then mat = dust_mat end
|
||||
vec.y = py + 1
|
||||
end
|
||||
set_node_at(vm, vec, mat)
|
||||
end
|
||||
for yi = py+2,min_clear-1 do
|
||||
vec.y = yi
|
||||
if vm:get_node_at(vec).name ~= "mcl_core:bedrock" then set_node_at(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)
|
||||
if (pr:next(0,1e9)/1e9) < p then break end
|
||||
end
|
||||
end
|
||||
-- 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)
|
||||
if is_tree_not_leaves(n) then
|
||||
set_node_at(vm, vec, surface_mat)
|
||||
if dust_mat and yi == py then
|
||||
vec.y = yi + 1
|
||||
if vm:get_node_at(vec).name == "air" then set_node_at(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)
|
||||
if dust_mat then
|
||||
vec.y = yi + 1
|
||||
if vm:get_node_at(vec).name == "air" then set_node_at(vm, vec, dust_mat) end
|
||||
end
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
-- some extra gaps for entry
|
||||
-- todo: make optional instead of hard-coded 25%
|
||||
-- todo: only really useful if there is space at px-3,py+3 to px-3,py+5
|
||||
--[[
|
||||
for xi = px-2,px+sx+1 do
|
||||
local dx21 = max(abs(cx-xi)-0.49,0)^2*wx2
|
||||
local dx22 = max(abs(cx-xi)-1.49,0)^2*wx2
|
||||
for zi = pz-2,pz+sz+1 do
|
||||
local dz21 = max(abs(cz-zi)-0.49,0)^2*wz2
|
||||
local dz22 = max(abs(cz-zi)-1.49,0)^2*wz2
|
||||
if dx21+dz21 > 1 and dx22+dz22 <= 1 and pr:next(1,4) == 1 then
|
||||
if py+4 < sy then
|
||||
for yi = py+2,py+4 do
|
||||
vec = vector_new(xi, yi, zi)
|
||||
if vm:get_node_at(vec).name ~= "mcl_core:bedrock" then set_node_at(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)
|
||||
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)
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
]]--
|
||||
-- cave some additional area overhead, try to make it interesting though
|
||||
for yi = min_clear+1,max_clear do
|
||||
local dy2 = max(yi-min_clear-1,0)^2*0.05
|
||||
local active = false
|
||||
for xi = px-2,px+sx+1 do
|
||||
local dx22 = max(abs(cx-xi)-1.49,0)^2*wx2
|
||||
for zi = pz-2,pz+sz+1 do
|
||||
local dz22 = max(abs(cz-zi)-1.49,0)^2*wz2
|
||||
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
|
||||
-- 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 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
|
||||
-- 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)
|
||||
active = true
|
||||
::continue::
|
||||
end
|
||||
end
|
||||
end
|
||||
if not active then break end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,117 @@
|
|||
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_vm = vl_terraforming._make_solid_vm
|
||||
|
||||
--- Grow the foundation downwards
|
||||
-- @param vm VoxelManip: Lua Voxel Manipulator
|
||||
-- @param xi number: x coordinate
|
||||
-- @param yi number: y coordinate
|
||||
-- @param zi number: z coordinate
|
||||
-- @param pr PcgRandom: random generator
|
||||
-- @param surface_mat Node: surface material node
|
||||
-- @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 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
|
||||
pos.y = pos.y + 1
|
||||
local cur = get_node_at(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
|
||||
-- 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_not_tree(get_node_at(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)
|
||||
return true
|
||||
end
|
||||
--- Generate a foundation from px,py,pz with size sx,sy,sz (sy < 0) plus some margin
|
||||
-- TODO: add support for dust_mat (snow)
|
||||
--
|
||||
-- Rounding: we model an ellipse. At zero rounding, we want the line go through the corner, at sx/2, sz/2.
|
||||
-- For this, we need to make ellipse sized 2a=sqrt(2)*sx, 2b=sqrt(2)*sz,
|
||||
-- Which yields a = sx/sqrt(2), b=sz/sqrt(2) and a^2=sx^2*0.5, b^2=sz^2*0.5
|
||||
-- To get corners, we decrease a and b by approx. corners each
|
||||
-- The ellipse condition dx^2/a^2+dz^2/b^2 <= 1 then yields dx^2/(sx^2*0.5) + dz^2/(sz^2*0.5) <= 1
|
||||
-- We use wx2=sx^-2*2, wz2=sz^-2*2 and then dx^2*wx2+dz^2*wz2 <= 1
|
||||
--
|
||||
-- @param vm VoxelManip: Lua Voxel Manipulator
|
||||
-- @param px number: lowest x
|
||||
-- @param py number: lowest y
|
||||
-- @param pz number: lowest z
|
||||
-- @param sx number: x width
|
||||
-- @param sy number: y height
|
||||
-- @param sz number: z depth
|
||||
-- @param corners number: Corner rounding
|
||||
-- @param surface_mat Node: surface material node
|
||||
-- @param platform_mat Node: platform material node
|
||||
-- @param stone_mat Node: stone material node
|
||||
-- @param dust_mat Node: dust material, optional
|
||||
-- @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
|
||||
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
|
||||
-- generate a baseplate (2 layers, lower is wider
|
||||
local pos = vector_new(px, py, pz)
|
||||
for xi = px-1,px+sx do
|
||||
local dx2 = max(abs(cx-xi)+0.51,0)^2*wx2
|
||||
local dx21 = max(abs(cx-xi)-0.49,0)^2*wx2
|
||||
pos.x = xi
|
||||
for zi = pz-1,pz+sz do
|
||||
local dz2 = max(abs(cz-zi)+0.51,0)^2*wz2
|
||||
local dz21 = max(abs(cz-zi)-0.49,0)^2*wz2
|
||||
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 dust_mat then
|
||||
pos.y = py + 1
|
||||
if get_node_at(vm, pos).name == "air" then set_node_at(vm, pos, dust_mat) end
|
||||
end
|
||||
pos.y = py - 1
|
||||
make_solid_vm(vm, pos, platform_mat)
|
||||
end
|
||||
elseif dx21+dz21 <= 1 then -- and pr:next(1,4) < 4 then -- TODO: make randomness configurable.
|
||||
-- slightly widen the baseplate below, to make easier to enter for mobs
|
||||
pos.y = py - 1
|
||||
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
|
||||
end
|
||||
end
|
||||
end
|
||||
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 active = false
|
||||
for xi = px-1,px+sx do
|
||||
local dx22 = max(abs(cx-xi)-1.49,0)^2*wx2
|
||||
for zi = pz-1,pz+sz do
|
||||
local dz22 = max(abs(cz-zi)-1.49,0)^2*wz2
|
||||
if dx22+dy2+dz22 <= 1 and grow_foundation_vm(vm,xi,yi,zi,pr,surface_mat,platform_mat,stone_mat) then active = true end
|
||||
end
|
||||
end
|
||||
if not active and yi < py + sy then break end
|
||||
end
|
||||
-- TODO: add back additional steps for easier entering, optional, and less regular?
|
||||
end
|
|
@ -0,0 +1,8 @@
|
|||
local modname = minetest.get_current_modname()
|
||||
local modpath = minetest.get_modpath(modname)
|
||||
vl_terraforming = {}
|
||||
|
||||
dofile(modpath.."/util.lua")
|
||||
dofile(modpath.."/clearance.lua")
|
||||
dofile(modpath.."/foundation.lua")
|
||||
dofile(modpath.."/level.lua")
|
|
@ -0,0 +1,205 @@
|
|||
local min = math.min
|
||||
local floor = math.floor
|
||||
local ceil = math.ceil
|
||||
local vector_copy = vector.copy
|
||||
local is_liquid = vl_terraforming._is_liquid
|
||||
local is_solid_not_tree = vl_terraforming._is_solid_not_tree
|
||||
|
||||
--- Find ground below a given position
|
||||
-- @param vm VoxelManip: buffer
|
||||
-- @param pos vector: Start position
|
||||
-- @return position and material of surface
|
||||
function vl_terraforming.find_ground_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_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
|
||||
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 not is_solid_not_tree(cur) then
|
||||
pos.y = pos.y - 1
|
||||
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_liquid(cur) then
|
||||
return nil
|
||||
end
|
||||
if is_solid_not_tree(cur) then
|
||||
return pos, cur
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
local find_ground_vm = vl_terraforming.find_ground_vm
|
||||
|
||||
--- Find ground or liquid 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_air_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_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
|
||||
if is_solid_not_tree(cur) or is_liquid(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 not is_solid_not_tree(cur) and not 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) or is_liquid(cur) then
|
||||
-- minetest.log("action", "Found surface: "..minetest.pos_to_string(pos).." "..(cur and cur.name).." over "..(prev and prev.name))
|
||||
return pos, cur
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
local find_under_air_vm = vl_terraforming.find_under_air_vm
|
||||
|
||||
--- Find liquid surface for a given position
|
||||
-- @param vm VoxelManip: buffer
|
||||
-- @param pos vector: Start position
|
||||
-- @return position and material of surface
|
||||
function vl_terraforming.find_liquid_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_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
|
||||
if is_liquid(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 not 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
|
||||
-- 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_liquid(cur) then
|
||||
-- minetest.log("action", "Found surface: "..minetest.pos_to_string(pos).." "..(cur and cur.name).." over "..(prev and prev.name))
|
||||
return pos, cur
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
local find_liquid_surface_vm = vl_terraforming.find_liquid_surface_vm
|
||||
|
||||
--- find suitable height for a structure of this size
|
||||
-- @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"
|
||||
-- @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_air" then find_ground = find_under_air_vm end
|
||||
local pos, surface_material = find_ground(vm, cpos) -- center
|
||||
if not pos then return nil, nil end
|
||||
local ys = { pos.y }
|
||||
pos.y = pos.y + 1 -- above ground
|
||||
if size.x == 1 and size.z == 1 then return pos end
|
||||
pos.x, pos.z = pos.x - floor((size.x-1)/2), pos.z - floor((size.z-1)/2) -- top left
|
||||
local pos_c = find_ground(vm, pos)
|
||||
if pos_c then table.insert(ys, pos_c.y) end
|
||||
pos.x = pos.x + size.x - 1 -- top right
|
||||
local pos_c = find_ground(vm, pos)
|
||||
if pos_c then table.insert(ys, pos_c.y) end
|
||||
pos.z = pos.z + size.z - 1 -- bottom right
|
||||
local pos_c = find_ground(vm, pos)
|
||||
if pos_c then table.insert(ys, pos_c.y) end
|
||||
pos.x = pos.x - (size.x - 1) -- bottom left
|
||||
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 < 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)
|
||||
return nil, nil
|
||||
end
|
||||
cpos.y = floor(0.5 * (ys[floor(1 + (#ys - 1) * 0.5)] + ys[ceil(1 + (#ys - 1) * 0.5)]) + 0.55) -- median except for largest, rounded, over surface
|
||||
return cpos, surface_material
|
||||
end
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
name = vl_terraforming
|
||||
author = kno10
|
||||
description = Terraforming API for VoxeLibre and Mineclonia
|
|
@ -0,0 +1,63 @@
|
|||
--- fairly strict: air, ignore, or no_paths marker
|
||||
-- @param node string or Node: node or node name
|
||||
-- @return true for air and ignore nodes
|
||||
function vl_terraforming._is_air(node)
|
||||
local name = node.name or node
|
||||
return name == "air" or name == "ignore" or name == "mcl_villages:no_paths"
|
||||
end
|
||||
|
||||
--- check if a node is walkable (solid), but not tree/leaves/fungi/bamboo/vines/etc.
|
||||
-- @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
|
||||
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
|
||||
-- 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 groups.tree or groups.leaves or groups.plant))
|
||||
end
|
||||
local is_solid_not_tree = vl_terraforming._is_solid_not_tree
|
||||
|
||||
--- check if a node is tree
|
||||
-- @param node string or Node: node or node name
|
||||
-- @return true for tree, 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
|
||||
local meta = minetest.registered_items[name]
|
||||
return meta and meta.groups and meta.groups.tree
|
||||
end
|
||||
|
||||
--- check if a node is liquid
|
||||
-- @param node string or Node: node or node name
|
||||
-- @return true for water, lava
|
||||
function vl_terraforming._is_liquid(node)
|
||||
local name = node.name or node
|
||||
if name == "air" or name == "ignore" or name == "mcl_villages:no_paths" then return false end
|
||||
local meta = minetest.registered_items[name]
|
||||
local groups = meta and meta.groups
|
||||
return groups and groups.liquid
|
||||
end
|
||||
|
||||
--- replace a non-solid node, optionally also "additional"
|
||||
-- @param vm voxelmanip
|
||||
-- @param pos position
|
||||
-- @param with replacement Lua node (not just name)
|
||||
-- @param always additional node to awlays replace even when solid
|
||||
function vl_terraforming._make_solid_vm(vm, pos, with, always)
|
||||
local cur = vm:get_node_at(pos)
|
||||
if cur.name == "ignore" or cur.name == "mcl_core:bedrock" then return end
|
||||
if cur.name == always or not is_solid_not_tree(cur) then
|
||||
vm:set_node_at(pos, with)
|
||||
return true
|
||||
end
|
||||
end
|
|
@ -45,6 +45,9 @@ mcl_disabled_structures (Disabled structures) string
|
|||
# Comma separated list of disabled event names
|
||||
mcl_disabled_events (Disabled events) string
|
||||
|
||||
# Structure frequency multiplier, keep this less than 3 usually
|
||||
vl_structures_boost (Structure frequency) float 1.0 0.0 10.0
|
||||
|
||||
# Amount of village to generate
|
||||
mcl_villages_village_probability (Probability of villages) int 5 0 100
|
||||
|
||||
|
|
Loading…
Reference in New Issue