Compare commits

...

41 Commits

Author SHA1 Message Date
kno10 c00bdaa9c2 Add cherry grove biome, based on MCLA.
Textures from pixel perfection, optimized with zopfipng. C.f., voxelibre#3463 voxelibre#4113
2024-10-26 19:09:56 +02:00
kno10 6412d164ef Register only one decoration per grass color for tallgrass. 2024-10-26 19:08:17 +02:00
kno10 c42b7e0fd8 Unnecessary use of mcl_util.get_node 2024-10-26 19:08:17 +02:00
kno10 a5df81545a Drop non-persistent tracking of generated chunks. 2024-10-26 19:08:17 +02:00
kno10 81fc19f0fb polishing 2024-10-26 19:08:17 +02:00
kno10 6577f54736 use mcl_util function 2024-10-24 17:10:23 +02:00
kno10 59f1c25296 template specialization for param2 2024-10-24 17:09:49 +02:00
kno10 ca5f2b51c6 Split biomes into separate files. 2024-10-24 12:42:44 +02:00
kno10 e477247485 igloo less slope, more snow, more rare 2024-10-20 21:58:38 +02:00
kno10 068469b1e9 height difference for villages 2024-10-20 21:58:37 +02:00
kno10 16cebc361e better paths, better village layouts 2024-10-20 21:00:12 +02:00
kno10 01962d2b49 add color tools for minetestmapper 2024-10-19 21:21:27 +02:00
kno10 f0562149b6 improve shipwrecks
reduce frequency, trim schematics, disable foundations again
2024-10-19 21:09:20 +02:00
kno10 edaf158686 more structures, more to discover. 2024-09-20 17:19:19 +02:00
kno10 162749decc hollow fallen trees 2024-09-20 17:19:19 +02:00
kno10 1435867673 add command /emerge 512 to emerge an area 2024-09-20 17:19:19 +02:00
kno10 4496f7adfc improvments to mineshaft cart variety + /locate 2024-09-20 17:19:19 +02:00
kno10 465b4162e7 use swap_node instead of set_node in mapgen, tweaks to end 2024-09-20 17:19:19 +02:00
kno10 e746ef46a9 eliminate the static_pos kludge except for strongholds for now 2024-09-20 17:19:19 +02:00
kno10 d7ac288ba1 cleanups and fixes 2024-09-20 17:19:19 +02:00
kno10 27b6963d20 clean up villages code, add biome farming support 2024-09-20 17:19:19 +02:00
kno10 0c4a120191 More fixes, make shulkers spawn on the floor. 2024-09-20 17:19:19 +02:00
kno10 1b5598f527 fix, and add small end city from MCLA 2024-09-20 17:19:19 +02:00
kno10 7cde4976de further fixes and improvements. 2024-09-20 17:19:19 +02:00
kno10 201ba616fe disable foliage 'fixes' that cause multi-colored trees 2024-09-20 17:19:19 +02:00
kno10 e8aeb34b93 code restructuring and cleanups 2024-09-20 17:19:19 +02:00
kno10 9411e60b8c Huge update of new terraforming, structures, and village code 2024-09-20 17:19:19 +02:00
kno10 b3efd6f681 Big rewrite of structure spawning using voxel manipulators 2024-09-20 17:19:19 +02:00
kno10 a22dadf412 fix bamboo biomes, add new schematic 2024-09-20 17:19:19 +02:00
kno10 73268386ec allow generating towns on mapblocks emerged with previous versions that did not become full settlements yet 2024-09-20 17:19:18 +02:00
kno10 eb2b1f8704 fixes and parameter tuning 2024-09-20 17:19:18 +02:00
kno10 5b520d8713 more voxelmanipulator, more MCLA 2024-09-20 17:19:18 +02:00
kno10 8e94eca3be finetuning 2024-09-20 17:19:18 +02:00
kno10 79d96e9ea0 add MCLA schematics 2024-09-20 17:19:18 +02:00
kno10 d89eec9d98 Big villages overhaul 2024-09-20 17:19:18 +02:00
kno10 238c5a650e move structures code from mcl_mapgen_core to mcl_structures 2024-09-20 17:19:18 +02:00
kno10 2564fdcf5b prioritize map decorations to make the generations more deterministic 2024-09-20 17:19:16 +02:00
kno10 f9a8033410 Make structure generation more deterministic
Pseudorandom now depends on position hash, not on blockseed + structure table order.
This should make structure generation more deterministic.
2024-09-20 17:18:22 +02:00
kno10 b42f883131 Avoid random jumps when standing due to gravity.
Closes: #4493 on VL side, further fixes in minetest pending
2024-09-20 17:18:22 +02:00
kno10 f73580bf3f Some optimizations for mapgen 2024-09-20 17:18:20 +02:00
kno10 e7fa2b8fb5 Bigger cleanup of mcl_core/functions 2024-09-20 17:11:19 +02:00
252 changed files with 14069 additions and 10948 deletions

View File

@ -195,48 +195,6 @@ minetest.craftitemdef_default.stack_max = 64
-- Set random seed for all other mods (Remember to make sure no other mod calls this function) -- Set random seed for all other mods (Remember to make sure no other mod calls this function)
math.randomseed(os.time()) math.randomseed(os.time())
local chunks = {} -- intervals of chunks generated
---@param pos Vector
function mcl_vars.add_chunk(pos)
local n = mcl_vars.get_chunk_number(pos) -- unsigned int
local prev
for i, d in pairs(chunks) do
if n <= d[2] then -- we've found it
if (n == d[2]) or (n >= d[1]) then return end -- already here
if n == d[1] - 1 then -- right before:
if prev and (prev[2] == n - 1) then
prev[2] = d[2]
table.remove(chunks, i)
return
end
d[1] = n
return
end
if prev and (prev[2] == n - 1) then --join to previous
prev[2] = n
return
end
table.insert(chunks, i, { n, n }) -- insert new interval before i
return
end
prev = d
end
chunks[#chunks + 1] = { n, n }
end
---@param pos Vector
---@return boolean
function mcl_vars.is_generated(pos)
local n = mcl_vars.get_chunk_number(pos) -- unsigned int
for i, d in pairs(chunks) do
if n <= d[2] then
return (n >= d[1])
end
end
return false
end
---"Trivial" (actually NOT) function to just read the node and some stuff to not just return "ignore", like mt 5.4 does. ---"Trivial" (actually NOT) function to just read the node and some stuff to not just return "ignore", like mt 5.4 does.
---@param pos Vector Position, if it's wrong, `{name="error"}` node will return. ---@param pos Vector Position, if it's wrong, `{name="error"}` node will return.
---@param force? boolean Optional (default: `false`), Do the maximum to still read the node within us_timeout. ---@param force? boolean Optional (default: `false`), Do the maximum to still read the node within us_timeout.
@ -253,32 +211,23 @@ function mcl_vars.get_node(pos, force, us_timeout)
return node return node
end end
-- copy vector to get sure it won't changed by other threads
local pos_copy = vector.copy(pos)
-- try LVM -- try LVM
minetest.get_voxel_manip():read_from_map(pos_copy, pos_copy) minetest.get_voxel_manip():read_from_map(pos, pos)
node = minetest.get_node(pos_copy) node = minetest.get_node(pos)
if node.name ~= "ignore" or not force then if node.name ~= "ignore" or not force then
return node return node
end end
-- all ways failed - need to emerge (or forceload if generated) -- try async emerge + BUSY wait (a really BAD idea, you should rather accept failure)
if mcl_vars.is_generated(pos_copy) then minetest.emerge_area(pos, pos) -- runs async!
minetest.chat_send_all("IMPOSSIBLE! Please report this to MCL2 issue tracker!")
minetest.forceload_block(pos_copy)
else
minetest.emerge_area(pos_copy, pos_copy)
end
local t = minetest.get_us_time() local t = minetest.get_us_time()
node = minetest.get_node(pos)
node = minetest.get_node(pos_copy)
while (not node or node.name == "ignore") and (minetest.get_us_time() - t < (us_timeout or 244)) do while (not node or node.name == "ignore") and (minetest.get_us_time() - t < (us_timeout or 244)) do
node = minetest.get_node(pos_copy) node = minetest.get_node(pos)
end end
return node return node
-- it still can return "ignore", LOL, even if force = true, but only after time out -- it still can return "ignore", LOL, even if force = true, but only after time out
end end

View File

@ -1131,3 +1131,16 @@ if not vector.in_area then
(pos.z >= min.z) and (pos.z <= max.z) (pos.z >= min.z) and (pos.z <= max.z)
end end
end end
if not minetest.bulk_swap_node then -- maybe in 5.10 https://github.com/minetest/minetest/pull/15043
minetest.bulk_swap_node = function(pos_list, node)
-- for dense and large sets, we could also try a VManip, but often this enough for now
local swap_node = minetest.swap_node
for _, pos in ipairs(pos_list) do
swap_node(pos, node)
end
end
-- async emerge environment, untested:
if minetest.set_node == minetest.swap_node then minetest.bulk_swap_node = minetest.bulk_set_node end
end

View File

@ -388,7 +388,7 @@ end
local function on_step_work (self, dtime) local function on_step_work(self, dtime, moveresult)
local pos = self.object:get_pos() local pos = self.object:get_pos()
if not pos then return end if not pos then return end
@ -402,7 +402,7 @@ local function on_step_work (self, dtime)
-- Do we abandon out of here now? -- Do we abandon out of here now?
end end
if self:falling(pos) then return end if self:falling(pos, moveresult) then return end
if self:step_damage (dtime, pos) then return end if self:step_damage (dtime, pos) then return end
if self.state == "die" then return end if self.state == "die" then return end
@ -502,11 +502,11 @@ end
-- main mob function -- main mob function
function mob_class:on_step(dtime) function mob_class:on_step(dtime, moveresult)
if not DEVELOPMENT then if not DEVELOPMENT then
-- Removed as bundled Lua (5.1 doesn't support xpcall) -- Removed as bundled Lua (5.1 doesn't support xpcall)
--local status, retVal = xpcall(on_step_work, on_step_error_handler, self, dtime) --local status, retVal = xpcall(on_step_work, on_step_error_handler, self, dtime)
local status, retVal = pcall(on_step_work, self, dtime) local status, retVal = pcall(on_step_work, self, dtime, moveresult)
if status then if status then
return retVal return retVal
else else
@ -521,7 +521,7 @@ function mob_class:on_step(dtime)
log_error (dump(retVal), dump(pos), dump(self)) log_error (dump(retVal), dump(pos), dump(self))
end end
else else
return on_step_work (self, dtime) return on_step_work (self, dtime, moveresult)
end end
end end

View File

@ -927,7 +927,8 @@ end
-- falling and fall damage -- falling and fall damage
-- returns true if mob died -- returns true if mob died
function mob_class:falling(pos) function mob_class:falling(pos, moveresult)
if moveresult and moveresult.touching_ground then return false end
if self.fly and self.state ~= "die" then if self.fly and self.state ~= "die" then
return return

View File

@ -128,7 +128,7 @@ mcl_mobs.register_mob("mobs_mc:enderdragon", {
on_die = function(self, pos, cmi_cause) on_die = function(self, pos, cmi_cause)
if self._portal_pos then if self._portal_pos then
mcl_portals.spawn_gateway_portal() 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")),-1) mcl_structures.spawn_end_exit_portal(self._portal_pos)
if self._initial then if self._initial then
mcl_experience.throw_xp(pos, 11500) -- 500 + 11500 = 12000 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"}) minetest.set_node(vector.add(self._portal_pos, vector.new(0, 5, 0)), {name = "mcl_end:dragon_egg"})

View File

@ -114,10 +114,9 @@ mcl_mobs.register_mob("mobs_mc:shulker", {
for n=1, math.min(64, #nodes) do for n=1, math.min(64, #nodes) do
local r = pr:next(1, #nodes) local r = pr:next(1, #nodes)
local nodepos = nodes[r] local nodepos = nodes[r]
local tg = vector.offset(nodepos,0,1,0) local tg = vector.offset(nodepos,0,0.5,0)
if check_spot(tg) then if check_spot(tg) then
telepos = tg telepos = tg
node_ok = true
end end
end end
if telepos then if telepos then

View File

@ -1244,7 +1244,7 @@ local function retrieve_my_jobsite (self)
mcl_log("find_jobsite. Invalid params. Should not happen") mcl_log("find_jobsite. Invalid params. Should not happen")
return return
end end
local n = mcl_vars.get_node(self._jobsite) local n = minetest.get_node(self._jobsite)
local m = minetest.get_meta(self._jobsite) local m = minetest.get_meta(self._jobsite)
if m:get_string("villager") == self._id then if m:get_string("villager") == self._id then
--mcl_log("find_jobsite. is my job.") --mcl_log("find_jobsite. is my job.")
@ -1411,7 +1411,7 @@ local function validate_bed(self)
if not self or not self._bed then if not self or not self._bed then
return false return false
end end
local n = mcl_vars.get_node(self._bed) local n = minetest.get_node(self._bed)
if not n then if not n then
self._bed = nil self._bed = nil
return false return false
@ -2328,6 +2328,16 @@ mcl_mobs.register_mob("mobs_mc:villager", {
end, end,
}) })
-- HACK: for compatibility with the new mcl_villages code, but will not allow easy modding yet
mobs_mc.jobsites = {}
for _,p in pairs(professions) do
if p.jobsite then
table.insert(mobs_mc.jobsites, p.jobsite)
end
end
function villager_employ(v, jobsite_pos)
if jobsite_pos then employ(v, jobsite_pos) end
end
--[[ --[[
Villager spawning in mcl_villages Villager spawning in mcl_villages

View File

@ -101,3 +101,42 @@ mesecon.register_button(
true, true,
nil, nil,
"mesecons_button_push_wood") "mesecons_button_push_wood")
minetest.register_node("mcl_cherry_blossom:pink_petals",{
description = S("Pink Petals"),
doc_items_longdesc = S("Pink Petals are ground decoration of cherry grove biomes"),
doc_items_hidden = false,
paramtype = "light",
paramtype2 = "facedir",
walkable = false,
sunlight_propagates = true,
floodable = true,
pointable = true,
drawtype = "nodebox",
node_box = {type = "fixed", fixed = {-1/2, -1/2, -1/2, 1/2, -7.9/16, 1/2}},
collision_box = {type = "fixed", fixed = {-1/2, -1/2, -1/2, 1/2, -7.9/16, 1/2}},
groups = {
shearsy=1,
handy=1,
flammable=3,
attached_node=1,
dig_by_piston=1,
compostability = 30,
deco_block=1
--not_in_creative_inventory=1,
},
use_texture_alpha = "clip",
sounds = mcl_sounds.node_sound_leaves_defaults(),
tiles = {
"mcl_cherry_blossom_pink_petals.png",
"mcl_cherry_blossom_pink_petals.png^[transformFY", -- mirror
"blank.png" -- empty
},
inventory_image = "mcl_cherry_blossom_pink_petals_inv.png",
_mcl_hardness = 0,
_mcl_blast_resistance = 0,
_on_bone_meal = function(_, _, _ , pos, n)
minetest.add_item(pos,n.name)
end
})

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,4 @@
-- Cactus and Sugar Cane -- Cactus and Sugar Cane
local S = minetest.get_translator(minetest.get_current_modname()) local S = minetest.get_translator(minetest.get_current_modname())
minetest.register_node("mcl_core:cactus", { minetest.register_node("mcl_core:cactus", {
@ -42,9 +41,8 @@ minetest.register_node("mcl_core:cactus", {
}, },
-- Only allow to place cactus on sand or cactus -- Only allow to place cactus on sand or cactus
on_place = mcl_util.generate_on_place_plant_function(function(pos, node) on_place = mcl_util.generate_on_place_plant_function(function(pos, node)
local node_below = minetest.get_node_or_nil({x=pos.x,y=pos.y-1,z=pos.z}) local node_below = minetest.get_node_or_nil(vector.offset(pos, 0, -1, 0))
if not node_below then return false end return node_below and (node_below.name == "mcl_core:cactus" or minetest.get_item_group(node_below.name, "sand") == 1)
return (node_below.name == "mcl_core:cactus" or minetest.get_item_group(node_below.name, "sand") == 1)
end), end),
_mcl_blast_resistance = 0.4, _mcl_blast_resistance = 0.4,
_mcl_hardness = 0.4, _mcl_hardness = 0.4,
@ -90,7 +88,7 @@ minetest.register_node("mcl_core:reeds", {
node_placement_prediction = "", node_placement_prediction = "",
drop = "mcl_core:reeds", -- to prevent color inheritation drop = "mcl_core:reeds", -- to prevent color inheritation
on_place = mcl_util.generate_on_place_plant_function(function(place_pos, place_node) on_place = mcl_util.generate_on_place_plant_function(function(place_pos, place_node)
local soil_pos = {x=place_pos.x, y=place_pos.y-1, z=place_pos.z} local soil_pos = vector.new(place_pos.x, place_pos.y-1, place_pos.z)
local soil_node = minetest.get_node_or_nil(soil_pos) local soil_node = minetest.get_node_or_nil(soil_pos)
if not soil_node then return false end if not soil_node then return false end
local snn = soil_node.name -- soil node name local snn = soil_node.name -- soil node name
@ -113,16 +111,12 @@ minetest.register_node("mcl_core:reeds", {
-- Legal water position rules are the same as for decoration spawn_by rules. -- Legal water position rules are the same as for decoration spawn_by rules.
-- This differs from MC, which does not allow diagonal neighbors -- This differs from MC, which does not allow diagonal neighbors
-- and neighbors 1 layer above. -- and neighbors 1 layer above.
local np1 = {x=soil_pos.x-1, y=soil_pos.y, z=soil_pos.z-1} if #minetest.find_nodes_in_area(vector.offset(soil_pos, -1, 0, -1), vector.offset(soil_pos, 1, 1, 1), {"group:water", "group:frosted_ice"}) > 0 then
local np2 = {x=soil_pos.x+1, y=soil_pos.y+1, z=soil_pos.z+1}
if #minetest.find_nodes_in_area(np1, np2, {"group:water", "group:frosted_ice"}) > 0 then
-- Water found! Sugar canes are happy! :-) -- Water found! Sugar canes are happy! :-)
return true return true
end end
-- No water found! Sugar canes are not amuzed and refuses to be placed. :-( -- No water found! Sugar canes are not amuzed and refuses to be placed. :-(
return false return false
end), end),
on_construct = function(pos) on_construct = function(pos)
local node = minetest.get_node(pos) local node = minetest.get_node(pos)
@ -135,4 +129,4 @@ minetest.register_node("mcl_core:reeds", {
end, end,
_mcl_blast_resistance = 0, _mcl_blast_resistance = 0,
_mcl_hardness = 0, _mcl_hardness = 0,
}) })

View File

@ -9,11 +9,11 @@ local wood_slab_groups = {handy = 1, axey = 1, material_wood = 1, wood_slab = 1}
local wood_stair_groups = {handy = 1, axey = 1, material_wood = 1, wood_stairs = 1} local wood_stair_groups = {handy = 1, axey = 1, material_wood = 1, wood_stairs = 1}
local function generate_warped_tree(pos) local function generate_warped_tree(pos)
minetest.place_schematic(pos,modpath.."/schematics/warped_fungus_1.mts","random",nil,false,"place_center_x,place_center_z") minetest.place_schematic(pos,modpath.."/schematics/warped_fungus_"..math.random(1,3)..".mts","random",nil,false,"place_center_x,place_center_z")
end end
function generate_crimson_tree(pos) function generate_crimson_tree(pos)
minetest.place_schematic(pos,modpath.."/schematics/crimson_fungus_1.mts","random",nil,false,"place_center_x,place_center_z") minetest.place_schematic(pos,modpath.."/schematics/crimson_fungus_"..math.random(1,3)..".mts","random",nil,false,"place_center_x,place_center_z")
end end
function grow_vines(pos, moreontop ,vine, dir) function grow_vines(pos, moreontop ,vine, dir)

View File

@ -80,7 +80,7 @@ local function spawn_crystal(pos)
crystal_explode(crystal) crystal_explode(crystal)
end end
local portal_pos = vector.add(portal_center, vector.new(0, -1, 0)) 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 end
minetest.register_entity("mcl_end:crystal", { minetest.register_entity("mcl_end:crystal", {

View File

@ -89,7 +89,7 @@ minetest.register_craftitem("mcl_end:ender_eye", {
local player_name = user:get_player_name() local player_name = user:get_player_name()
local origin = user:get_pos() local origin = user:get_pos()
origin.y = origin.y + 1.5 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 dim = mcl_worlds.pos_to_dimension(origin)
local is_creative = minetest.is_creative_enabled(player_name) local is_creative = minetest.is_creative_enabled(player_name)

View File

@ -11,20 +11,6 @@ mcl_flowers.registered_simple_flowers = {}
local smallflowerlongdesc = S("This is a small flower. Small flowers are mainly used for dye production and can also be potted.") local smallflowerlongdesc = S("This is a small flower. Small flowers are mainly used for dye production and can also be potted.")
local plant_usage_help = S("It can only be placed on a block on which it would also survive.") local plant_usage_help = S("It can only be placed on a block on which it would also survive.")
local get_palette_color_from_pos = function(pos)
local biome_data = minetest.get_biome_data(pos)
local index = 0
if biome_data then
local biome = biome_data.biome
local biome_name = minetest.get_biome_name(biome)
local reg_biome = minetest.registered_biomes[biome_name]
if reg_biome then
index = reg_biome._mcl_grass_palette_index
end
end
return index
end
-- on_place function for flowers -- on_place function for flowers
local on_place_flower = mcl_util.generate_on_place_plant_function(function(pos, node, itemstack) local on_place_flower = mcl_util.generate_on_place_plant_function(function(pos, node, itemstack)
local below = {x=pos.x, y=pos.y-1, z=pos.z} local below = {x=pos.x, y=pos.y-1, z=pos.z}
@ -32,13 +18,7 @@ local on_place_flower = mcl_util.generate_on_place_plant_function(function(pos,
if not soil_node then return false end if not soil_node then return false end
local has_palette = minetest.registered_nodes[itemstack:get_name()].palette ~= nil local has_palette = minetest.registered_nodes[itemstack:get_name()].palette ~= nil
local colorize local colorize = has_palette and mcl_util.get_palette_indexes_from_pos(pos).grass_palette_index or 0
if has_palette then
colorize = get_palette_color_from_pos(pos)
end
if not colorize then
colorize = 0
end
--[[ Placement requirements: --[[ Placement requirements:
* Dirt or grass block * Dirt or grass block
@ -47,10 +27,7 @@ local on_place_flower = mcl_util.generate_on_place_plant_function(function(pos,
]] ]]
local light_night = minetest.get_node_light(pos, 0.0) local light_night = minetest.get_node_light(pos, 0.0)
local light_day = minetest.get_node_light(pos, 0.5) local light_day = minetest.get_node_light(pos, 0.5)
local light_ok = false local light_ok = (light_night and light_night >= 8) or (light_day and light_day >= minetest.LIGHT_MAX)
if (light_night and light_night >= 8) or (light_day and light_day >= minetest.LIGHT_MAX) then
light_ok = true
end
if itemstack:get_name() == "mcl_flowers:wither_rose" and ( minetest.get_item_group(soil_node.name, "grass_block") > 0 or soil_node.name == "mcl_core:dirt" or soil_node.name == "mcl_core:coarse_dirt" or soil_node.name == "mcl_mud:mud" or soil_node.name == "mcl_moss:moss" or soil_node.name == "mcl_nether:netherrack" or minetest.get_item_group(soil_node.name, "soul_block") > 0 ) then if itemstack:get_name() == "mcl_flowers:wither_rose" and ( minetest.get_item_group(soil_node.name, "grass_block") > 0 or soil_node.name == "mcl_core:dirt" or soil_node.name == "mcl_core:coarse_dirt" or soil_node.name == "mcl_mud:mud" or soil_node.name == "mcl_moss:moss" or soil_node.name == "mcl_nether:netherrack" or minetest.get_item_group(soil_node.name, "soul_block") > 0 ) then
return true,colorize return true,colorize
end end
@ -359,10 +336,7 @@ local function add_large_plant(name, desc, longdesc, bottom_img, top_img, inv_im
-- * Only with light level >= 8 -- * Only with light level >= 8
-- * Only if two enough space -- * Only if two enough space
if (floor.name == "mcl_core:dirt" or minetest.get_item_group(floor.name, "grass_block") == 1 or (not is_flower and (floor.name == "mcl_core:coarse_dirt" or floor.name == "mcl_core:podzol" or floor.name == "mcl_core:podzol_snow"))) and bottom_buildable and top_buildable and light_ok then if (floor.name == "mcl_core:dirt" or minetest.get_item_group(floor.name, "grass_block") == 1 or (not is_flower and (floor.name == "mcl_core:coarse_dirt" or floor.name == "mcl_core:podzol" or floor.name == "mcl_core:podzol_snow"))) and bottom_buildable and top_buildable and light_ok then
local param2 local param2 = grass_color and mcl_util.get_palette_indexes_from_pos(bottom).grass_palette_index
if grass_color then
param2 = get_palette_color_from_pos(bottom)
end
-- Success! We can now place the flower -- Success! We can now place the flower
minetest.sound_play(minetest.registered_nodes[itemstring].sounds.place, {pos = bottom, gain=1}, true) minetest.sound_play(minetest.registered_nodes[itemstring].sounds.place, {pos = bottom, gain=1}, true)
minetest.set_node(bottom, {name=itemstring, param2=param2}) minetest.set_node(bottom, {name=itemstring, param2=param2})

View File

@ -4,34 +4,38 @@ local storage = mcl_portals.storage
local vector = vector local vector = vector
local gateway_positions = { local gateway_positions = {
{x = 96, y = -26925, z = 0}, vector.new(96, -26925, 0),
{x = 91, y = -26925, z = 29}, vector.new(91, -26925, 29),
{x = 77, y = -26925, z = 56}, vector.new(77, -26925, 56),
{x = 56, y = -26925, z = 77}, vector.new(56, -26925, 77),
{x = 29, y = -26925, z = 91}, vector.new(29, -26925, 91),
{x = 0, y = -26925, z = 96}, vector.new(0, -26925, 96),
{x = -29, y = -26925, z = 91}, vector.new(-29, -26925, 91),
{x = -56, y = -26925, z = 77}, vector.new(-56, -26925, 77),
{x = -77, y = -26925, z = 56}, vector.new(-77, -26925, 56),
{x = -91, y = -26925, z = 29}, vector.new(-91, -26925, 29),
{x = -96, y = -26925, z = 0}, vector.new(-96, -26925, 0),
{x = -91, y = -26925, z = -29}, vector.new(-91, -26925, -29),
{x = -77, y = -26925, z = -56}, vector.new(-77, -26925, -56),
{x = -56, y = -26925, z = -77}, vector.new(-56, -26925, -77),
{x = -29, y = -26925, z = -91}, vector.new(-29, -26925, -91),
{x = 0, y = -26925, z = -96}, vector.new(0, -26925, -96),
{x = 29, y = -26925, z = -91}, vector.new(29, -26925, -91),
{x = 56, y = -26925, z = -77}, vector.new(56, -26925, -77),
{x = 77, y = -26925, z = -56}, vector.new(77, -26925, -56),
{x = 91, y = -26925, z = -29}, vector.new(91, -26925, -29),
} }
local path_gateway_portal = minetest.get_modpath("mcl_structures").."/schematics/mcl_structures_end_gateway_portal.mts" local path_gateway_portal = minetest.get_modpath("mcl_structures").."/schematics/mcl_structures_end_gateway_portal.mts"
local function spawn_gateway_portal(pos, dest_str) local function spawn_gateway_portal(pos, dest_str)
return mcl_structures.place_schematic(vector.add(pos, vector.new(-1, -2, -1)), path_gateway_portal, "0", nil, true, nil, dest_str and function() return vl_structures.place_schematic(vector.offset(pos, -1, -2, -1), 0, path_gateway_portal, "0", {
minetest.get_meta(pos):set_string("mcl_portals:gateway_destination", dest_str) force_placement = true,
end) prepare = false,
after_place = dest_str and function()
minetest.get_meta(pos):set_string("mcl_portals:gateway_destination", dest_str)
end
}, nil)
end end
function mcl_portals.spawn_gateway_portal() function mcl_portals.spawn_gateway_portal()

View File

@ -183,12 +183,12 @@ local dimension_to_teleport = { nether = "overworld", overworld = "nether" }
local limits = { local limits = {
nether = { nether = {
pmin = {x=LIM_MIN, y = N_Y_MIN, z = LIM_MIN}, pmin = vector.new(LIM_MIN, N_Y_MIN, LIM_MIN),
pmax = {x=LIM_MAX, y = N_Y_MAX, z = LIM_MAX}, pmax = vector.new(LIM_MAX, N_Y_MAX, LIM_MAX),
}, },
overworld = { overworld = {
pmin = {x=LIM_MIN, y = O_Y_MIN, z = LIM_MIN}, pmin = vector.new(LIM_MIN, O_Y_MIN, LIM_MIN),
pmax = {x=LIM_MAX, y = O_Y_MAX, z = LIM_MAX}, 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 -- 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. -- node storage due to destruction should use the lookup table.
local function get_portal_pos(pos) local function get_portal_pos(pos)
local p1 = vector.offset(pos,-5,-1,-5) local nn = find_nodes_in_area(vector.offset(pos,-5,-1,-5), vector.offset(pos,5,5,5), {"mcl_portals:portal"})
local p2 = vector.offset(pos,5,5,5)
local nn = find_nodes_in_area(p1,p2,{"mcl_portals:portal"})
for _,p in pairs(nn) do for _,p in pairs(nn) do
local m = minetest.get_meta(p):get_string("target_portal") 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) return minetest.get_position_from_hash(m)
end end
end end
@ -229,15 +227,13 @@ end
local function add_exit(p) local function add_exit(p)
local retval = {key=false, new=false} local retval = {key=false, new=false}
if not p or not p.y or not p.z or not p.x then if not p or not p.y or not p.z or not p.x then return retval end
return retval
end
local x, y, z = floor(p.x), floor(p.y), floor(p.z) 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(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 then
return retval return retval
end end
@ -301,7 +297,7 @@ local function remove_exit(p)
end end
local x, y, z = floor(p.x), floor(p.y), floor(p.z) 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) local k = get_exit_key(p)
if not exits[k] then if not exits[k] then
@ -529,14 +525,14 @@ local function destroy_nether_portal(pos, node)
while i <= #nodes do while i <= #nodes do
pos = nodes[i] pos = nodes[i]
if orientation == 0 then if orientation == 0 then
check_remove({x = pos.x - 1, y = pos.y, z = pos.z}) check_remove(vector.offset(pos, -1, 0, 0))
check_remove({x = pos.x + 1, y = pos.y, z = pos.z}) check_remove(vector.offset(pos, 1, 0, 0))
else else
check_remove({x = pos.x, y = pos.y, z = pos.z - 1}) check_remove(vector.offset(pos, 0, 0, -1))
check_remove({x = pos.x, y = pos.y, z = pos.z + 1}) check_remove(vector.offset(pos, 0, 0, 1))
end end
check_remove({x = pos.x, y = pos.y - 1, z = pos.z}) check_remove(vector.offset(pos, 0, -1, 0))
check_remove({x = pos.x, y = pos.y + 1, z = pos.z}) check_remove(vector.offset(pos, 0, 1, 0))
remove_exits({pos}) remove_exits({pos})
i = i + 1 i = i + 1
end end
@ -624,7 +620,7 @@ local function build_and_light_frame(x1, y1, z1, x2, y2, z2, name)
else else
set_node(pos, {name = PORTAL, param2 = orientation}) set_node(pos, {name = PORTAL, param2 = orientation})
add_exits({ add_exits({
{x=pos.x, y=pos.y-1, z=pos.z} vector.new(pos.x, pos.y-1, pos.z)
}) })
end end
end end
@ -701,8 +697,8 @@ function build_nether_portal(cube_pos1, width, height, orientation, name, clear_
-- Build obsidian platform: -- Build obsidian platform:
for x = pos.x - orientation, pos.x + orientation + (width_inner - 1) * (1 - orientation), 1 + orientation do 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 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 = vector.new(x, pos.y - 1, z)
local pp_1 = {x = x, y = pos.y - 2, z = z} local pp_1 = vector.new(x, pos.y - 2, z)
local nn = get_node(pp).name local nn = get_node(pp).name
local nn_1 = get_node(pp_1).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 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) 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 -- 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 if get_node(objpos).name ~= PORTAL then
log("action", "Entity no longer standing in portal") log("action", "Entity no longer standing in portal")
return 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 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. -- We have sorted the candidates by distance, this is the best location.
distance = distance0 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) log("verbose", "Found acceptable location at "..pos_to_string(pos0)..", distance "..distance0..", air nodes "..nc2)
break break
elseif not most_airy_pos or nc2>most_airy_count then elseif not most_airy_pos or nc2>most_airy_count then
-- Remember the cube with the most amount of air as a fallback. -- Remember the cube with the most amount of air as a fallback.
most_airy_count = nc2 most_airy_count = nc2
most_airy_distance = distance0 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) 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 elseif most_airy_pos and nc2==most_airy_count and distance0<most_airy_distance then
-- Use distance as a tiebreaker. -- Use distance as a tiebreaker.
most_airy_distance = distance0 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) log("verbose", "Found fallback location at "..pos_to_string(most_airy_pos)..", distance "..distance0..", air nodes "..nc2)
end 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") log("verbose", "No space found, emerging one chunk below")
end 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) pos1, pos2 = find_build_limits(new_target, param.target_dim)
local diff = add(pos2, mul(pos1, -1)) local diff = add(pos2, mul(pos1, -1))
@ -1202,7 +1198,7 @@ local function create_portal(origin, target, target_dim, name, obj)
origin = origin, origin = origin,
target = target, target = target,
target_dim = target_dim, target_dim = target_dim,
ideal_target = vector.new(target.x, target.y, target.z), -- copy ideal_target = vector.copy(target),
pos1 = pos1, pos1 = pos1,
pos2 = pos2, pos2 = pos2,
name=name, name=name,
@ -1223,13 +1219,12 @@ local function available_for_nether_portal(p)
end end
local function check_and_light_shape(pos, orientation) 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 node_list = {}
local index_list = {} local index_list = {}
local node_counter = 0 local node_counter = 0
-- Search most low node from the left (pos1) and most right node from the top (pos2) -- 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 pos1, pos2 = vector.copy(pos), vector.copy(pos)
local pos2 = {x = pos.x, y = pos.y, z = pos.z}
local kx, ky, kz = pos.x - 1999, pos.y - 1999, pos.z - 1999 local kx, ky, kz = pos.x - 1999, pos.y - 1999, pos.z - 1999
while #stack > 0 do while #stack > 0 do
@ -1247,22 +1242,22 @@ local function check_and_light_shape(pos, orientation)
return false return false
end end
node_counter = node_counter + 1 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 index_list[k] = true
stack[i].y = y - 1 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 if orientation == 0 then
stack[i + 2] = {x = x - 1, y = y, z = z} stack[i + 2] = vector.new(x - 1, y, z)
stack[i + 3] = {x = x + 1, y = y, z = z} stack[i + 3] = vector.new(x + 1, y, z)
else else
stack[i + 2] = {x = x, y = y, z = z - 1} stack[i + 2] = vector.new(x, y, z - 1)
stack[i + 3] = {x = x, y = y, z = z + 1} stack[i + 3] = vector.new(x, y, z + 1)
end end
if (y < pos1.y) or (y == pos1.y and (x < pos1.x or z < pos1.z)) then 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 end
if (x > pos2.x or z > pos2.z) or (x == pos2.x and z == pos2.z and y > pos2.y) then 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 end
end end
@ -1344,7 +1339,7 @@ local function check_portal_then_teleport(obj, origin, exit)
remove_exits({exit}) remove_exits({exit})
-- Also remove from structure storage, otherwise ABM will try the same bad exit again. -- Also remove from structure storage, otherwise ABM will try the same bad exit again.
local objpos = obj:get_pos() 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) origin_flush(origin, nil)
return return
@ -1367,7 +1362,7 @@ local function teleport_no_delay(obj, portal_pos)
local target_dim = dimension_to_teleport[current_dim] 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 -- 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 if get_node(origin).name ~= PORTAL then return end
local target = get_target(origin) local target = get_target(origin)
@ -1450,8 +1445,8 @@ local function animation(player, playername)
end end
minetest.add_particlespawner({ minetest.add_particlespawner({
amount = 1, amount = 1,
minpos = {x = pos.x - 0.1, y = pos.y + 1.4, z = pos.z - 0.1}, minpos = vector.offset(pos, -0.1, 1.4, -0.1),
maxpos = {x = pos.x + 0.1, y = pos.y + 1.6, z = pos.z + 0.1}, maxpos = vector.offset(pos, 0.1, 1.6, 0.1),
minvel = 0, minvel = 0,
maxvel = 0, maxvel = 0,
minacc = 0, minacc = 0,
@ -1496,11 +1491,11 @@ minetest.register_abm({
local time = random() * 1.9 + 0.5 local time = random() * 1.9 + 0.5
local velocity, acceleration local velocity, acceleration
if o == 1 then if o == 1 then
velocity = {x = random() * 0.7 + 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 = {x = random() * 1.1 + 0.3, y = random() - 0.5, z = random() - 0.5} acceleration = vector.new(random() * 1.1 + 0.3, random() - 0.5, random() - 0.5)
else else
velocity = {x = random() - 0.5, y = random() - 0.5, z = random() * 0.7 + 0.3} velocity = vector.new(random() - 0.5, random() - 0.5, random() * 0.7 + 0.3)
acceleration = {x = random() - 0.5, y = random() - 0.5, z = random() * 1.1 + 0.3} acceleration = vector.new(random() - 0.5, random() - 0.5, random() * 1.1 + 0.3)
end end
local distance = add(mul(velocity, time), mul(acceleration, time * time / 2)) local distance = add(mul(velocity, time), mul(acceleration, time * time / 2))
if d == 1 then if d == 1 then
@ -1566,12 +1561,12 @@ minetest.override_item(OBSIDIAN, {
end end
-- check each of 6 sides of it and destroy every portal -- 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(vector.offset(pos, -1, 0, 0))
check_remove({x = pos.x + 1, y = pos.y, z = pos.z}) check_remove(vector.offset(pos, 1, 0, 0))
check_remove({x = pos.x, y = pos.y, z = pos.z - 1}) check_remove(vector.offset(pos, 0, 0, -1))
check_remove({x = pos.x, y = pos.y, z = pos.z + 1}) check_remove(vector.offset(pos, 0, 0, 1))
check_remove({x = pos.x, y = pos.y - 1, z = pos.z}) check_remove(vector.offset(pos, 0, -1, 0))
check_remove({x = pos.x, y = pos.y + 1, z = pos.z}) check_remove(vector.offset(pos, 0, 1, 0))
end, end,
_on_ignite = function(user, pointed_thing) _on_ignite = function(user, pointed_thing)
@ -1579,16 +1574,16 @@ minetest.override_item(OBSIDIAN, {
-- Check empty spaces around obsidian and light all frames found. -- Check empty spaces around obsidian and light all frames found.
-- Permit igniting of portals that are partly protected to maintain integrity. -- Permit igniting of portals that are partly protected to maintain integrity.
local portals_placed = 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(vector.new(x - 1, y, z)) or mcl_portals.light_nether_portal(vector.new(x + 1, y, 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(vector.new(x, y - 1, z)) or mcl_portals.light_nether_portal(vector.new(x, y + 1, 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, y, z - 1)) or mcl_portals.light_nether_portal(vector.new(x, y, z + 1))
if portals_placed then 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 if minetest.get_modpath("doc") then
doc.mark_entry_as_revealed(user:get_player_name(), "nodes", PORTAL) doc.mark_entry_as_revealed(user:get_player_name(), "nodes", PORTAL)
-- Achievement for finishing a Nether portal TO the Nether -- 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 if minetest.get_modpath("awards") and dim ~= "nether" and user:is_player() then
awards.unlock(user:get_player_name(), "mcl:buildNetherPortal") awards.unlock(user:get_player_name(), "mcl:buildNetherPortal")
end end
@ -1600,13 +1595,13 @@ minetest.override_item(OBSIDIAN, {
end, end,
}) })
mcl_structures.register_structure("nether_portal",{ vl_structures.register_structure("nether_portal",{
nospawn = true, nospawn = true,
filenames = { filenames = {
modpath.."/schematics/mcl_portals_nether_portal.mts" modpath.."/schematics/mcl_portals_nether_portal.mts"
} }
}) })
mcl_structures.register_structure("nether_portal_open",{ vl_structures.register_structure("nether_portal_open",{
nospawn = true, nospawn = true,
filenames = { filenames = {
modpath.."/schematics/mcl_portals_nether_portal_open.mts" modpath.."/schematics/mcl_portals_nether_portal_open.mts"

View File

@ -20,7 +20,7 @@ local function connectable(itemstring)
return (minetest.get_item_group(itemstring, "wall") == 1) or (minetest.get_item_group(itemstring, "solid") == 1) return (minetest.get_item_group(itemstring, "wall") == 1) or (minetest.get_item_group(itemstring, "solid") == 1)
end end
local function update_wall(pos) function mcl_walls.update_wall(pos)
local thisnode = minetest.get_node(pos) local thisnode = minetest.get_node(pos)
if minetest.get_item_group(thisnode.name, "wall") == 0 then if minetest.get_item_group(thisnode.name, "wall") == 0 then
@ -67,11 +67,12 @@ local function update_wall(pos)
minetest.add_node(pos, {name = basename..sum}) minetest.add_node(pos, {name = basename..sum})
end end
local update_wall = mcl_walls.update_wall
local function update_wall_global(pos) local function update_wall_global(pos)
for i = 1,5 do for i = 1,5 do
local dir = directions[i] local dir = directions[i]
update_wall({x = pos.x + dir.x, y = pos.y + dir.y, z = pos.z + dir.z}) update_wall(vector.offset(pos, dir.x, dir.y, dir.z))
end end
end end
@ -269,7 +270,7 @@ function mcl_walls.register_wall(nodename, description, source, tiles, inventory
fixed = {-4/16, -0.5, -4/16, 4/16, 1, 4/16} fixed = {-4/16, -0.5, -4/16, 4/16, 1, 4/16}
}, },
collisionbox = {-0.2, 0, -0.2, 0.2, 1.4, 0.2}, collisionbox = {-0.2, 0, -0.2, 0.2, 1.4, 0.2},
on_construct = update_wall, on_construct = mcl_walls.update_wall,
sounds = sounds, sounds = sounds,
_mcl_blast_resistance = blast_resistance, _mcl_blast_resistance = blast_resistance,
_mcl_hardness = hardness, _mcl_hardness = hardness,

View File

@ -1 +0,0 @@
Biomes mod. By Wuzzy and maikerumine.

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,7 @@
mcl_dungeons = {} mcl_dungeons = {}
local logging = minetest.settings:get_bool("mcl_logging_dungeons", false)
local mg_name = minetest.get_mapgen_setting("mg_name") local mg_name = minetest.get_mapgen_setting("mg_name")
-- Are dungeons disabled? -- Are dungeons disabled?
if mcl_vars.mg_dungeons == false or mg_name == "singlenode" then return end 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 -- Check conditions. If okay, start generating
if check and (openings_counter < 1 or openings_counter > 5) then return end if check and (openings_counter < 1 or openings_counter > 5) then return end
minetest.log("action","[mcl_dungeons] Placing new dungeon at "..minetest.pos_to_string(vector_new(x, y, z))) 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! -- Okay! Spawning starts!
-- Remember spawner chest positions to set metadata later -- Remember spawner chest positions to set metadata later
@ -253,7 +256,7 @@ local function ecb_spawn_dungeon(blockpos, action, calls_remaining, param)
-- If it failed again, tough luck! We stick with only 1 chest spawned. -- If it failed again, tough luck! We stick with only 1 chest spawned.
local lastRandom local lastRandom
local secondChance = true -- second chance is still available local secondChance = true -- second chance is still available
for i=1, totalChests do for _ = 1, totalChests do
local r = pr:next(1, totalChestSlots) local r = pr:next(1, totalChestSlots)
if r == lastRandom and secondChance then if r == lastRandom and secondChance then
-- Oops! Same slot selected. Try again. -- Oops! Same slot selected. Try again.
@ -368,8 +371,11 @@ local function ecb_spawn_dungeon(blockpos, action, calls_remaining, param)
end end
set_node(pos, {name="mcl_chests:chest", param2=facedir}) set_node(pos, {name="mcl_chests:chest", param2=facedir})
vl_structures.construct_nodes(pos, pos, {"mcl_chests:chest"})
local meta = get_meta(pos) local meta = get_meta(pos)
minetest.log("action", "[mcl_dungeons] Filling chest " .. tostring(c) .. " at " .. minetest.pos_to_string(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) mcl_loot.fill_inventory(meta:get_inventory(), "main", mcl_loot.get_multi_loot(loottable, pr), pr)
end end
@ -404,7 +410,9 @@ local function dungeons_nodes(minp, maxp, blockseed)
local z = pr:next(minp.z, maxp.z-dim.z-1) local z = pr:next(minp.z, maxp.z-dim.z-1)
local p1 = vector_new(x, y, z) local p1 = vector_new(x, y, z)
local p2 = vector_new(x+dim.x+1, y+dim.y+1, z+dim.z+1) local p2 = vector_new(x+dim.x+1, y+dim.y+1, z+dim.z+1)
minetest.log("verbose","[mcl_dungeons] size=" ..minetest.pos_to_string(dim) .. ", emerge from "..minetest.pos_to_string(p1) .. " to " .. minetest.pos_to_string(p2)) 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}) emerge_area(p1, p2, ecb_spawn_dungeon, {p1=p1, p2=p2, dim=dim, pr=pr})
end end
end end
@ -414,7 +422,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 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 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) local p2 = vector_new(p1.x+dim.x+1, p1.y+dim.y+1, p1.z+dim.z+1)
minetest.log("verbose","[mcl_dungeons] size=" ..minetest.pos_to_string(dim) .. ", emerge from "..minetest.pos_to_string(p1) .. " to " .. minetest.pos_to_string(p2)) 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}) emerge_area(p1, p2, ecb_spawn_dungeon, {p1=p1, p2=p2, dim=dim, pr=pr, dontcheck=true})
end end

View File

@ -1,77 +1,31 @@
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 registered_generators = {}
local lvm, nodes, param2 = 0, 0, 0 local lvm, nodes, param2 = 0, 0, 0
local lvm_buffer = {}
local logging = minetest.settings:get_bool("mcl_logging_mapgen",false) local seed = minetest.get_mapgen_setting("seed")
local function roundN(n, d)
if type(n) ~= "number" then return n end
local m = 10^d
return math.floor(n * m + 0.5) / m
end
local function run_generators (p1, p2, blockseed)
if nodes > 0 then
for _, rec in ipairs(registered_generators) do
if rec.nf then
rec.nf(p1, p2, blockseed)
end
end
end
end
local function update_data (vm, data, data2)
-- Write stuff
vm:set_data(data)
if param2 > 0 then
vm:set_param2_data(data2)
end
end
local function post_generator_processing(vm, minp, maxp, deco_used, deco_table, ore_used, ore_table)
if deco_table then
minetest.generate_decorations(vm,vector.new(minp.x,deco_table.min,minp.z),vector.new(maxp.x,deco_table.max,maxp.z))
elseif deco_used then
minetest.generate_decorations(vm)
end
if ore_table then
minetest.generate_ores(vm,vector.new(minp.x,ore_table.min,minp.z),vector.new(maxp.x,ore_table.max,maxp.z))
elseif ore_used then
minetest.generate_ores(vm)
end
end
local function post_generator_processing_2(vm, p1, p2, shadow)
vm:calc_lighting(p1, p2, shadow)
vm:write_to_map()
vm:update_liquids()
end
minetest.register_on_generated(function(minp, maxp, blockseed) minetest.register_on_generated(function(minp, maxp, blockseed)
local t1 = os.clock() local t1 = os.clock()
local p1, p2 = {x=minp.x, y=minp.y, z=minp.z}, {x=maxp.x, y=maxp.y, z=maxp.z}
if lvm > 0 then if lvm > 0 then
local lvm_used, shadow, deco_used, deco_table, ore_used, ore_table = false, false, false, false, false, false
local lb2 = {} -- param2
local vm, emin, emax = minetest.get_mapgen_object("voxelmanip") local vm, emin, emax = minetest.get_mapgen_object("voxelmanip")
local e1, e2 = {x=emin.x, y=emin.y, z=emin.z}, {x=emax.x, y=emax.y, z=emax.z} local area = VoxelArea(emin, emax)
local data2 local data = vm:get_data()
local data = vm:get_data(lvm_buffer) local data2 = param2 > 0 and vm:get_param2_data()
if param2 > 0 then if log_timing then
data2 = vm:get_param2_data(lb2) 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 end
local area = VoxelArea:new({MinEdge=e1, MaxEdge=e2})
local lvm_used, shadow, deco_used, deco_table, ore_used, ore_table = false, false, false, false, false, false
for _, rec in ipairs(registered_generators) do for _, rec in ipairs(registered_generators) do
if rec.vf then if rec.vf then
local gt1 = os.clock()
local p1, p2 = vector.copy(minp), vector.copy(maxp) -- defensive copies
local e1, e2 = vector.copy(emin), vector.copy(emax) -- defensive copies
local lvm_used0, shadow0, deco, ore = rec.vf(vm, data, data2, e1, e2, area, p1, p2, blockseed) local lvm_used0, shadow0, deco, ore = rec.vf(vm, data, data2, e1, e2, area, p1, p2, blockseed)
if lvm_used0 then lvm_used = lvm_used or lvm_used0
lvm_used = true shadow = shadow or shadow0
end
if shadow0 then
shadow = true
end
if deco and type(deco) == "table" then if deco and type(deco) == "table" then
deco_table = deco deco_table = deco
elseif deco then elseif deco then
@ -82,26 +36,65 @@ minetest.register_on_generated(function(minp, maxp, blockseed)
elseif deco then elseif deco then
ore_used = true ore_used = true
end end
if log_timing then
minetest.log("action", string.format("[mcl_mapgen_core] %-20s %s ... %s %8.2fms", rec.id, minetest.pos_to_string(minp), minetest.pos_to_string(maxp), (os.clock() - gt1)*1000))
end
end end
end end
if lvm_used then if lvm_used then
update_data (vm, data, data2) local gt1 = os.clock()
post_generator_processing(vm, minp, maxp, deco_used, deco_table, ore_used, ore_table) vm:set_data(data)
post_generator_processing_2(vm, p1, p2, shadow) if param2 > 0 then vm:set_param2_data(data2) end
if log_timing then
minetest.log("action", string.format("[mcl_mapgen_core] %-20s %s ... %s %8.2fms", "set_data", minetest.pos_to_string(minp), minetest.pos_to_string(maxp), (os.clock() - gt1)*1000))
end
local gt1 = os.clock()
if deco_table and #deco_table > 0 then
minetest.generate_decorations(vm,vector.new(minp.x,deco_table.min,minp.z),vector.new(maxp.x,deco_table.max,maxp.z))
elseif deco_used then
minetest.generate_decorations(vm)
end
if log_timing and (deco_table or deco_used) then
minetest.log("action", string.format("[mcl_mapgen_core] %-20s %s ... %s %8.2fms", "decorations", minetest.pos_to_string(minp), minetest.pos_to_string(maxp), (os.clock() - gt1)*1000))
end
local gt1 = os.clock()
if ore_table and #ore_table > 0 then
minetest.generate_ores(vm,vector.new(minp.x,ore_table.min,minp.z),vector.new(maxp.x,ore_table.max,maxp.z))
elseif ore_used then
minetest.generate_ores(vm)
end
if log_timing and (ore_table or ore_used) then
minetest.log("action", string.format("[mcl_mapgen_core] %-20s %s ... %s %8.2fms", "ores", minetest.pos_to_string(minp), minetest.pos_to_string(maxp), (os.clock() - gt1)*1000))
end
local gt1 = os.clock()
vm:calc_lighting(minp, maxp, shadow)
vm:write_to_map()
vm:update_liquids()
if log_timing then
minetest.log("action", string.format("[mcl_mapgen_core] %-20s %s ... %s %8.2fms", "light/write/liquids", minetest.pos_to_string(minp), minetest.pos_to_string(maxp), (os.clock() - gt1)*1000))
end
end end
end end
run_generators (p1, p2, blockseed) if nodes > 0 then
for _, rec in ipairs(registered_generators) do
if rec.nf then
local gt1 = os.clock()
local p1, p2 = vector.copy(minp), vector.copy(maxp) -- defensive copies
rec.nf(p1, p2, blockseed)
if log_timing then
minetest.log("action", string.format("[mcl_mapgen_core] %-20s %s ... %s %8.2fms", rec.id, minetest.pos_to_string(minp), minetest.pos_to_string(maxp), (os.clock() - gt1)*1000))
end
end
end
end
mcl_vars.add_chunk(minp)
if logging then if logging then
minetest.log("action", "[mcl_mapgen_core] Generating chunk " .. minetest.pos_to_string(minp) .. " ... " .. minetest.pos_to_string(maxp).."..."..tostring(roundN(((os.clock() - t1)*1000),2)).."ms") minetest.log("action", string.format("[mcl_mapgen_core] %-20s %s ... %s %8.2fms", "Generating chunk", minetest.pos_to_string(minp), minetest.pos_to_string(maxp), (os.clock() - t1)*1000))
end end
end) end)
function minetest.register_on_generated(node_function) function minetest.register_on_generated(node_function)
mcl_mapgen_core.register_generator("mod_"..minetest.get_current_modname().."_"..tostring(#registered_generators+1), nil, node_function) mcl_mapgen_core.register_generator("mod_"..minetest.get_current_modname().."_"..tostring(#registered_generators+1), nil, node_function)
end end
@ -145,3 +138,122 @@ function mcl_mapgen_core.unregister_generator(id)
if rec.needs_param2 then param2 = param2 - 1 end if rec.needs_param2 then param2 = param2 - 1 end
--if rec.needs_level0 then level0 = level0 - 1 end --if rec.needs_level0 then level0 = level0 - 1 end
end end
-- Try to make decorations more deterministic in order, by sorting by priority and name
-- At least for low-priority this should make map seeds more comparable, but
-- 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 pending_decorations = {}
local gennotify_map = {}
function mcl_mapgen_core.register_decoration(def, callback)
if def.gen_callback and not def.name then error("gen_callback requires a named decoration.") end
if callback then error("Callbacks have been redesigned.") end
-- customize foliage colors
if (def._mcl_foliage_palette_index or 0) > 0 then
if type(def.schematic) ~= "string" then error("Can currently only be used with file schematics.") end
-- Load the schema and apply foliage palette
local schem = minetest.read_schematic(def.schematic, {})
schem.name = def.schematic -- preserve the file name
local cache = {}
for _, node in ipairs(schem.data) do
local kind = cache[node.name]
if kind == nil then
local ndef = minetest.registered_nodes[node.name]
kind = ndef and (ndef.groups.foliage_palette and "color")
or (ndef.groups.foliage_palette_wallmounted and "colorwallmounted")
or false
cache[node.name] = kind
end
if kind == "color" then
node.param2 = def._mcl_foliage_palette_index
elseif kind == "colorwallmounted" then
node.param2 = def._mcl_foliage_palette_index * 8 + (node.param2 % 8)
end
end
def.schematic = schem
end
if pending_decorations == nil then
minetest.log("warning", "Decoration registered after mapgen core initialization: "..tostring(def.name))
minetest.register_decoration(def)
if def.gen_callback then
def.deco_id = minetest.get_decoration_id(def.name)
if not def.deco_id then
error("Failed to get the decoration id for "..tostring(def.name))
else
minetest.set_gen_notify({decoration = true}, {def.deco_id})
gennotify_map["decoration#" .. def.deco_id] = def
end
end
return
end
table.insert(pending_decorations, def)
end
local function sort_decorations()
local keys, map = {}, {}
for i, def in pairs(pending_decorations) do
local name = def.name
-- we try to generate fallback names to make order more deterministic
name = name or (def.decoration and string.format("%s:%04d", def.decoration, i))
if not name and type(def.schematic) == "string" then
local sc = string.split(def.schematic, "/")
name = string.format("%s:%04d", sc[#sc], i)
end
if not name and type(def.schematic) == "table" and def.schematic.name then
local sc = string.split(def.schematic.name, "/")
name = string.format("%s:%04d", sc[#sc], i)
end
if not name and type(def.schematic) == "table" and def.schematic.data then
name = ""
for _, v in ipairs(def.schematic.data) do
if v.name then name = name .. v.name .. ":" end
end
name = name .. string.format("%04d", i)
end
name = name or string.format("%04d", i)
local prio = (def.priority or 1000) + i/1000
local key = string.format("%08.3f:%s", prio, name)
table.insert(keys, key)
map[key] = def
end
table.sort(keys)
for _, key in ipairs(keys) do
local def = map[key]
if def.name and minetest.get_decoration_id(def.name) then
minetest.log("warning", "Decoration ID not unique: "..def.name)
end
local deco_id = minetest.register_decoration(def)
if not deco_id then
error("Failed to register decoration "..tostring(def.name).." - name not unique?")
end
if def.name then
deco_id = minetest.get_decoration_id(def.name)
if not deco_id then
error("Failed to register decoration "..tostring(def.name).." - name not unique?")
end
if def.gen_callback then
minetest.set_gen_notify({decoration = true}, {deco_id})
gennotify_map["decoration#" .. deco_id] = def
end
end
end
pending_decorations = nil -- as we will not run again
end
mcl_mapgen_core.register_generator("Gennotify callbacks", nil, function(minp, maxp, blockseed)
local pr = PcgRandom(blockseed + seed + 48214) -- constant seed offset
local gennotify = minetest.get_mapgen_object("gennotify")
for key, def in pairs(gennotify_map) do
local t = gennotify[key]
if t and #t > 0 then
-- Fisher-Yates shuffle, using pr
for i = 1, #t-1 do
local r = pr:next(i,#t)
t[i], t[r] = t[r], t[i]
end
def.gen_callback(t, minp, maxp, blockseed)
end
end
end)
minetest.register_on_mods_loaded(sort_decorations)

View File

@ -1,10 +1,4 @@
mcl_mapgen_core = {} mcl_mapgen_core = {}
local registered_generators = {}
local lvm, nodes, param2 = 0, 0, 0
local lvm_used = false
local lvm_buffer = {}
local modname = minetest.get_current_modname() local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname) local modpath = minetest.get_modpath(modname)
@ -122,8 +116,6 @@ elseif mg_name == "fractal" then
mg_flags.caverns = true mg_flags.caverns = true
end end
local mg_flags_str = "" local mg_flags_str = ""
for k,v in pairs(mg_flags) do for k,v in pairs(mg_flags) do
if v == false then if v == false then
@ -210,39 +202,42 @@ end
-- If function(pos_to_check, content_id_at_this_pos), will set node only if returns true. -- If function(pos_to_check, content_id_at_this_pos), will set node only if returns true.
-- min, max: Minimum and maximum Y levels of the layers to set -- min, max: Minimum and maximum Y levels of the layers to set
-- minp, maxp: minp, maxp of the on_generated -- minp, maxp: minp, maxp of the on_generated
-- lvm_used: Set to true if any node in this on_generated has been set before.
-- --
-- returns true if any node was set and lvm_used otherwise -- returns true if any node was set
local function set_layers(data, area, content_id, check, min, max, minp, maxp, lvm_used, pr) local function set_layers(data, area, content_id, check, min, max, minp, maxp, pr)
if (maxp.y >= min and minp.y <= max) then if maxp.y < min or minp.y > max then return false end
for y = math.max(min, minp.y), math.min(max, maxp.y) do local lvm_used = false
for x = minp.x, maxp.x do if not check then
for z = minp.z, maxp.z do for p_pos in area:iter(minp.x, math.max(min, minp.y), minp.z, maxp.x, math.min(max, maxp.y), maxp.z) do
local p_pos = area:index(x, y, z) data[p_pos] = content_id
if check then lvm_used = true
if type(check) == "function" and check({x=x,y=y,z=z}, data[p_pos], pr) then end
data[p_pos] = content_id elseif type(check) == "function" then
lvm_used = true -- slow path, needs vector coordinates (bedrock uses y only)
elseif check == data[p_pos] then for p_pos in area:iter(minp.x, math.max(min, minp.y), minp.z, maxp.x, math.min(max, maxp.y), maxp.z) do
data[p_pos] = content_id if check(area:position(p_pos), data[p_pos], pr) then
lvm_used = true data[p_pos] = content_id
end lvm_used = true
else end
data[p_pos] = content_id end
lvm_used = true else
end for p_pos in area:iter(minp.x, math.max(min, minp.y), minp.z, maxp.x, math.min(max, maxp.y), maxp.z) do
end if check == data[p_pos] then
data[p_pos] = content_id
lvm_used = true
end end
end end
end end
return lvm_used return lvm_used
end end
local function set_grass_palette(minp,maxp,data2,area,biomemap,nodes) local function set_grass_palette(minp,maxp,data2,area,nodes)
-- Flat area at y=0 to read biome 3 times faster than 5.3.0.get_biome_data(pos).biome: 43us vs 125us per iteration: -- Flat area at y=0 to read biome 3 times faster than 5.3.0.get_biome_data(pos).biome: 43us vs 125us per iteration:
local biomemap = minetest.get_mapgen_object("biomemap")
if not biomemap then return end if not biomemap then return end
local aream = VoxelArea:new({MinEdge={x=minp.x, y=0, z=minp.z}, MaxEdge={x=maxp.x, y=0, z=maxp.z}}) local aream = VoxelArea(vector.new(minp.x, 0, minp.z), vector.new(maxp.x, 0, maxp.z))
local nodes = minetest.find_nodes_in_area(minp, maxp, nodes) local nodes = minetest.find_nodes_in_area(minp, maxp, nodes)
local lvm_used = false
for n=1, #nodes do for n=1, #nodes do
local n = nodes[n] local n = nodes[n]
local p_pos = area:index(n.x, n.y, n.z) local p_pos = area:index(n.x, n.y, n.z)
@ -259,11 +254,13 @@ local function set_grass_palette(minp,maxp,data2,area,biomemap,nodes)
return lvm_used return lvm_used
end end
local function set_foliage_palette(minp,maxp,data2,area,biomemap,nodes) local function set_foliage_palette(minp,maxp,data2,area,nodes)
-- Flat area at y=0 to read biome 3 times faster than 5.3.0.get_biome_data(pos).biome: 43us vs 125us per iteration: -- Flat area at y=0 to read biome 3 times faster than 5.3.0.get_biome_data(pos).biome: 43us vs 125us per iteration:
local biomemap = minetest.get_mapgen_object("biomemap")
if not biomemap then return end if not biomemap then return end
local aream = VoxelArea:new({MinEdge={x=minp.x, y=0, z=minp.z}, MaxEdge={x=maxp.x, y=0, z=maxp.z}}) local aream = VoxelArea(vector.new(minp.x, 0, minp.z), vector.new(maxp.x, 0, maxp.z))
local nodes = minetest.find_nodes_in_area(minp, maxp, nodes) local nodes = minetest.find_nodes_in_area(minp, maxp, nodes)
local lvm_used = false
for n=1, #nodes do for n=1, #nodes do
local n = nodes[n] local n = nodes[n]
local p_pos = area:index(n.x, n.y, n.z) local p_pos = area:index(n.x, n.y, n.z)
@ -283,11 +280,14 @@ local function set_foliage_palette(minp,maxp,data2,area,biomemap,nodes)
return lvm_used return lvm_used
end end
local function set_water_palette(minp,maxp,data2,area,biomemap,nodes) local function set_water_palette(minp,maxp,data2,area,nodes)
-- Flat area at y=0 to read biome 3 times faster than 5.3.0.get_biome_data(pos).biome: 43us vs 125us per iteration: -- Flat area at y=0 to read biome 3 times faster than 5.3.0.get_biome_data(pos).biome: 43us vs 125us per iteration:
local biomemap = minetest.get_mapgen_object("biomemap")
if not biomemap then return end if not biomemap then return end
local aream = VoxelArea:new({MinEdge={x=minp.x, y=0, z=minp.z}, MaxEdge={x=maxp.x, y=0, z=maxp.z}}) local aream = VoxelArea(vector.new(minp.x, 0, minp.z), vector.new(maxp.x, 0, maxp.z))
-- FIXME: this relies on the voxelmanip already being written.
local nodes = minetest.find_nodes_in_area(minp, maxp, nodes) local nodes = minetest.find_nodes_in_area(minp, maxp, nodes)
local lvm_used = false
for n=1, #nodes do for n=1, #nodes do
local n = nodes[n] local n = nodes[n]
local p_pos = area:index(n.x, n.y, n.z) local p_pos = area:index(n.x, n.y, n.z)
@ -304,63 +304,62 @@ local function set_water_palette(minp,maxp,data2,area,biomemap,nodes)
return lvm_used return lvm_used
end end
--[[
local function set_seagrass_param2(minp,maxp,data2,area,nodes) local function set_seagrass_param2(minp,maxp,data2,area,nodes)
local aream = VoxelArea:new({MinEdge={x=minp.x, y=0, z=minp.z}, MaxEdge={x=maxp.x, y=0, z=maxp.z}})
local nodes = minetest.find_nodes_in_area(minp, maxp, nodes) local nodes = minetest.find_nodes_in_area(minp, maxp, nodes)
local lvm_used = false
for n=1, #nodes do for n=1, #nodes do
local n = nodes[n] local n = nodes[n]
local p_pos = area:index(n.x, n.y, n.z) data2[area:index(n.x, n.y, n.z)] = 3
data2[p_pos] = 3
lvm_used = true lvm_used = true
end end
return lvm_used return lvm_used
end end
]]
-- Below the bedrock, generate air/void -- Below the bedrock, generate air/void
local function world_structure(vm, data, data2, emin, emax, area, minp, maxp, blockseed) local function world_structure(vm, data, data2, emin, emax, area, minp, maxp, blockseed)
local biomemap --ymin, ymax
local lvm_used = false
local pr = PseudoRandom(blockseed) local pr = PseudoRandom(blockseed)
local lvm_used = false
-- The Void below the Nether: -- The Void below the Nether:
lvm_used = set_layers(data, area, c_void , nil, mcl_vars.mapgen_edge_min , mcl_vars.mg_nether_min -1, minp, maxp, lvm_used, pr) lvm_used = set_layers(data, area, c_void , nil, mcl_vars.mapgen_edge_min , mcl_vars.mg_nether_min -1, minp, maxp, pr) or lvm_used
-- [[ THE NETHER: mcl_vars.mg_nether_min mcl_vars.mg_nether_max ]] -- [[ THE NETHER: mcl_vars.mg_nether_min mcl_vars.mg_nether_max ]]
-- The Air on the Nether roof, https://git.minetest.land/VoxeLibre/VoxeLibre/issues/1186 -- The Air on the Nether roof, https://git.minetest.land/VoxeLibre/VoxeLibre/issues/1186
lvm_used = set_layers(data, area, c_air , nil, mcl_vars.mg_nether_max +1, mcl_vars.mg_nether_max + 128 , minp, maxp, lvm_used, pr) lvm_used = set_layers(data, area, c_air , nil, mcl_vars.mg_nether_max +1, mcl_vars.mg_nether_max + 128 , minp, maxp, pr) or lvm_used
-- The Void above the Nether below the End: -- The Void above the Nether below the End:
lvm_used = set_layers(data, area, c_void , nil, mcl_vars.mg_nether_max + 128 +1, mcl_vars.mg_end_min -1, minp, maxp, lvm_used, pr) lvm_used = set_layers(data, area, c_void , nil, mcl_vars.mg_nether_max + 128 +1, mcl_vars.mg_end_min -1, minp, maxp, pr) or lvm_used
-- [[ THE END: mcl_vars.mg_end_min mcl_vars.mg_end_max ]] -- [[ THE END: mcl_vars.mg_end_min mcl_vars.mg_end_max ]]
-- The Void above the End below the Realm barrier: -- The Void above the End below the Realm barrier:
lvm_used = set_layers(data, area, c_void , nil, mcl_vars.mg_end_max +1, mcl_vars.mg_realm_barrier_overworld_end_min-1, minp, maxp, lvm_used, pr) lvm_used = set_layers(data, area, c_void , nil, mcl_vars.mg_end_max +1, mcl_vars.mg_realm_barrier_overworld_end_min-1, minp, maxp, pr) or lvm_used
-- Realm barrier between the Overworld void and the End -- Realm barrier between the Overworld void and the End
lvm_used = set_layers(data, area, c_realm_barrier, nil, mcl_vars.mg_realm_barrier_overworld_end_min , mcl_vars.mg_realm_barrier_overworld_end_max , minp, maxp, lvm_used, pr) lvm_used = set_layers(data, area, c_realm_barrier, nil, mcl_vars.mg_realm_barrier_overworld_end_min , mcl_vars.mg_realm_barrier_overworld_end_max , minp, maxp, pr) or lvm_used
-- The Void above Realm barrier below the Overworld: -- The Void above Realm barrier below the Overworld:
lvm_used = set_layers(data, area, c_void , nil, mcl_vars.mg_realm_barrier_overworld_end_max+1, mcl_vars.mg_overworld_min -1, minp, maxp, lvm_used, pr) lvm_used = set_layers(data, area, c_void , nil, mcl_vars.mg_realm_barrier_overworld_end_max+1, mcl_vars.mg_overworld_min -1, minp, maxp, pr) or lvm_used
if mg_name ~= "singlenode" then if mg_name ~= "singlenode" then
-- Bedrock -- Bedrock
lvm_used = set_layers(data, area, c_bedrock, bedrock_check, mcl_vars.mg_bedrock_overworld_min, mcl_vars.mg_bedrock_overworld_max, minp, maxp, lvm_used, pr) lvm_used = set_layers(data, area, c_bedrock, bedrock_check, mcl_vars.mg_bedrock_overworld_min, mcl_vars.mg_bedrock_overworld_max, minp, maxp, pr) or lvm_used
lvm_used = set_layers(data, area, c_bedrock, bedrock_check, mcl_vars.mg_bedrock_nether_bottom_min, mcl_vars.mg_bedrock_nether_bottom_max, minp, maxp, lvm_used, pr) lvm_used = set_layers(data, area, c_bedrock, bedrock_check, mcl_vars.mg_bedrock_nether_bottom_min, mcl_vars.mg_bedrock_nether_bottom_max, minp, maxp, pr) or lvm_used
lvm_used = set_layers(data, area, c_bedrock, bedrock_check, mcl_vars.mg_bedrock_nether_top_min, mcl_vars.mg_bedrock_nether_top_max, minp, maxp, lvm_used, pr) lvm_used = set_layers(data, area, c_bedrock, bedrock_check, mcl_vars.mg_bedrock_nether_top_min, mcl_vars.mg_bedrock_nether_top_max, minp, maxp, pr) or lvm_used
-- Flat Nether -- Flat Nether
if mg_name == "flat" then if mg_name == "flat" then
lvm_used = set_layers(data, area, c_air, nil, mcl_vars.mg_flat_nether_floor, mcl_vars.mg_flat_nether_ceiling, minp, maxp, lvm_used, pr) lvm_used = set_layers(data, area, c_air, nil, mcl_vars.mg_flat_nether_floor, mcl_vars.mg_flat_nether_ceiling, minp, maxp, pr) or lvm_used
end end
-- Big lava seas by replacing air below a certain height -- Big lava seas by replacing air below a certain height
if mcl_vars.mg_lava then if mcl_vars.mg_lava then
lvm_used = set_layers(data, area, c_lava, c_air, mcl_vars.mg_overworld_min, mcl_vars.mg_lava_overworld_max, minp, maxp, lvm_used, pr) lvm_used = set_layers(data, area, c_lava, c_air, mcl_vars.mg_overworld_min, mcl_vars.mg_lava_overworld_max, minp, maxp, pr) or lvm_used
lvm_used = set_layers(data, area, c_nether_lava, c_air, mcl_vars.mg_nether_min, mcl_vars.mg_lava_nether_max, minp, maxp, lvm_used, pr) lvm_used = set_layers(data, area, c_nether_lava, c_air, mcl_vars.mg_nether_min, mcl_vars.mg_lava_nether_max, minp, maxp, pr) or lvm_used
end end
end end
local deco = false local deco, ores = false, false
local ores = false
if minp.y > mcl_vars.mg_nether_deco_max - 64 and maxp.y < mcl_vars.mg_nether_max + 128 then if minp.y > mcl_vars.mg_nether_deco_max - 64 and maxp.y < mcl_vars.mg_nether_max + 128 then
deco = {min=mcl_vars.mg_nether_deco_max,max=mcl_vars.mg_nether_max} deco = {min=mcl_vars.mg_nether_deco_max,max=mcl_vars.mg_nether_max}
end end
@ -371,58 +370,40 @@ local function world_structure(vm, data, data2, emin, emax, area, minp, maxp, bl
return lvm_used, lvm_used, deco, ores return lvm_used, lvm_used, deco, ores
end end
--[[ replaced with decoration hack to replace grass nodes
local function block_fixes_grass(vm, data, data2, emin, emax, area, minp, maxp, blockseed) local function block_fixes_grass(vm, data, data2, emin, emax, area, minp, maxp, blockseed)
local biomemap = minetest.get_mapgen_object("biomemap") -- Set param2 (=color) of nodes which use the grass colour palette.
local lvm_used = false return minp.y <= mcl_vars.mg_overworld_max and maxp.y >= mcl_vars.mg_overworld_min and
local pr = PseudoRandom(blockseed) set_grass_palette(minp,maxp,data2,area,{"group:grass_palette"})
if minp.y <= mcl_vars.mg_overworld_max and maxp.y >= mcl_vars.mg_overworld_min then end]]
-- Set param2 (=color) of nodes which use the grass colour palette.
lvm_used = set_grass_palette(minp,maxp,data2,area,biomemap,{"group:grass_palette"})
end
return lvm_used
end
--[[ replaced with schematic specialization per biome
local function block_fixes_foliage(vm, data, data2, emin, emax, area, minp, maxp, blockseed) local function block_fixes_foliage(vm, data, data2, emin, emax, area, minp, maxp, blockseed)
local biomemap = minetest.get_mapgen_object("biomemap") -- Set param2 (=color) of nodes which use the foliage colour palette.
local lvm_used = false return minp.y <= mcl_vars.mg_overworld_max and maxp.y >= mcl_vars.mg_overworld_min and
local pr = PseudoRandom(blockseed) set_foliage_palette(minp,maxp,data2,area,{"group:foliage_palette", "group:foliage_palette_wallmounted"})
if minp.y <= mcl_vars.mg_overworld_max and maxp.y >= mcl_vars.mg_overworld_min then end]]
-- Set param2 (=color) of nodes which use the foliage colour palette.
lvm_used = set_foliage_palette(minp,maxp,data2,area,biomemap,{"group:foliage_palette", "group:foliage_palette_wallmounted"})
end
return lvm_used
end
local function block_fixes_water(vm, data, data2, emin, emax, area, minp, maxp, blockseed) local function block_fixes_water(vm, data, data2, emin, emax, area, minp, maxp, blockseed)
local biomemap = minetest.get_mapgen_object("biomemap") -- Set param2 (=color) of nodes which use the water colour palette.
local lvm_used = false return minp.y <= mcl_vars.mg_overworld_max and maxp.y >= mcl_vars.mg_overworld_min and
local pr = PseudoRandom(blockseed) set_water_palette(minp,maxp,data2,area,{"group:water_palette"})
if minp.y <= mcl_vars.mg_overworld_max and maxp.y >= mcl_vars.mg_overworld_min then
-- Set param2 (=color) of nodes which use the water colour palette.
lvm_used = set_water_palette(minp,maxp,data2,area,biomemap,{"group:water_palette"})
end
return lvm_used
end end
--[[ no longer necessary, we generate them with param2=3
local function block_fixes_seagrass(vm, data, data2, emin, emax, area, minp, maxp, blockseed) local function block_fixes_seagrass(vm, data, data2, emin, emax, area, minp, maxp, blockseed)
local lvm_used = false -- Set param2 of seagrass to 3.
local pr = PseudoRandom(blockseed) return minp.y <= mcl_vars.mg_overworld_max and maxp.y >= mcl_vars.mg_overworld_min and
if minp.y <= mcl_vars.mg_overworld_max and maxp.y >= mcl_vars.mg_overworld_min then set_seagrass_param2(minp, maxp, data2, area, {"group:seagrass"})
-- Set param2 of seagrass to 3.
lvm_used = set_seagrass_param2(minp, maxp, data2, area, {"group:seagrass"})
end
return lvm_used
end end
]]
-- End block fixes: -- End block fixes:
local function end_basic(vm, data, data2, emin, emax, area, minp, maxp, blockseed) local function end_basic(vm, data, data2, emin, emax, area, minp, maxp, blockseed)
if maxp.y < mcl_vars.mg_end_min or minp.y > mcl_vars.mg_end_max then return end if maxp.y < mcl_vars.mg_end_min or minp.y > mcl_vars.mg_end_max then return end
local biomemap --ymin, ymax
local lvm_used = false local lvm_used = false
local pr = PseudoRandom(blockseed)
local nodes
if mg_name ~= "v6" then if mg_name ~= "v6" then
nodes = minetest.find_nodes_in_area(emin, emax, {"mcl_core:water_source"}) local nodes = minetest.find_nodes_in_area(emin, emax, {"mcl_core:water_source"})
if #nodes > 0 then if #nodes > 0 then
lvm_used = true lvm_used = true
for _,n in pairs(nodes) do for _,n in pairs(nodes) do
@ -430,51 +411,26 @@ local function end_basic(vm, data, data2, emin, emax, area, minp, maxp, blocksee
end end
end end
end end
return true, false lvm_used = true -- light is broken otherwise
return lvm_used, false
end end
mcl_mapgen_core.register_generator("world_structure", world_structure, nil, 1, true) mcl_mapgen_core.register_generator("world_structure", world_structure, nil, 1, true)
mcl_mapgen_core.register_generator("end_fixes", end_basic, function(minp,maxp) mcl_mapgen_core.register_generator("end_fixes", end_basic, nil, 9999, true)
if maxp.y < mcl_vars.mg_end_min or minp.y > mcl_vars.mg_end_max then return end
end, 9999, true)
if mg_name ~= "v6" and mg_name ~= "singlenode" then if mg_name ~= "v6" and mg_name ~= "singlenode" then
mcl_mapgen_core.register_generator("block_fixes_grass", block_fixes_grass, nil, 9999, true) -- replaced with decoration hack: mcl_mapgen_core.register_generator("block_fixes_grass", block_fixes_grass, nil, 9999, true)
mcl_mapgen_core.register_generator("block_fixes_foliage", block_fixes_foliage, nil, 9999, true) -- replaced with schema specialization: mcl_mapgen_core.register_generator("block_fixes_foliage", block_fixes_foliage, nil, 9999, true)
mcl_mapgen_core.register_generator("block_fixes_water", block_fixes_water, nil, 9999, true) mcl_mapgen_core.register_generator("block_fixes_water", block_fixes_water, nil, 9999, true)
mcl_mapgen_core.register_generator("block_fixes_seagrass", block_fixes_seagrass, nil, 9999, true) -- replaced with using param2=3 during generation mcl_mapgen_core.register_generator("block_fixes_seagrass", block_fixes_seagrass, nil, 9999, true)
end end
if mg_name == "v6" then if mg_name == "v6" then
dofile(modpath.."/v6.lua") dofile(modpath.."/v6.lua")
end end
-- This should be moved to mcl_structures eventually if the dependencies can be sorted out. -- still needed?
mcl_mapgen_core.register_generator("structures",nil, function(minp, maxp, blockseed) --[[
local gennotify = minetest.get_mapgen_object("gennotify")
local has = false
local poshash = minetest.hash_node_position(minp)
for _,struct in pairs(mcl_structures.registered_structures) do
local pr = PseudoRandom(blockseed + 42)
if struct.deco_id then
for _, pos in pairs(gennotify["decoration#"..struct.deco_id] or {}) do
if struct.chunk_probability == nil or (not has and pr:next(1,struct.chunk_probability) == 1 ) then
mcl_structures.place_structure(vector.offset(pos,0,1,0),struct,pr,blockseed)
has=true
end
end
elseif struct.static_pos then
for _,p in pairs(struct.static_pos) do
if vector.in_area(p,minp,maxp) then
mcl_structures.place_structure(p,struct,pr,blockseed)
end
end
end
end
return false, false, false
end, 100, true)
minetest.register_lbm({ minetest.register_lbm({
label = "Fix grass palette indexes", -- This LBM fixes any incorrect grass palette indexes. label = "Fix grass palette indexes", -- This LBM fixes any incorrect grass palette indexes.
name = "mcl_mapgen_core:fix_grass_palette_indexes", name = "mcl_mapgen_core:fix_grass_palette_indexes",
@ -484,11 +440,12 @@ minetest.register_lbm({
local grass_palette_index = mcl_util.get_palette_indexes_from_pos(pos).grass_palette_index local grass_palette_index = mcl_util.get_palette_indexes_from_pos(pos).grass_palette_index
if node.param2 ~= grass_palette_index then if node.param2 ~= grass_palette_index then
node.param2 = grass_palette_index node.param2 = grass_palette_index
minetest.set_node(pos, node) minetest.swap_node(pos, node)
end end
end end
}) })]]
--[[ FIXME: not yet replaced
minetest.register_lbm({ minetest.register_lbm({
label = "Fix foliage palette indexes", -- Set correct palette indexes of foliage in old mapblocks. label = "Fix foliage palette indexes", -- Set correct palette indexes of foliage in old mapblocks.
name = "mcl_mapgen_core:fix_foliage_palette_indexes", name = "mcl_mapgen_core:fix_foliage_palette_indexes",
@ -503,14 +460,14 @@ minetest.register_lbm({
minetest.place_node(vector.offset(pos, 0, 1, 0), node) -- Offset required, since otherwise the leaves sink one node for some reason. minetest.place_node(vector.offset(pos, 0, 1, 0), node) -- Offset required, since otherwise the leaves sink one node for some reason.
elseif node.param2 ~= foliage_palette_index and node.name ~= "mcl_core:vine" then elseif node.param2 ~= foliage_palette_index and node.name ~= "mcl_core:vine" then
node.param2 = foliage_palette_index node.param2 = foliage_palette_index
minetest.set_node(pos, node) minetest.swap_node(pos, node)
elseif node.name == "mcl_core:vine" then elseif node.name == "mcl_core:vine" then
local biome_param2 = foliage_palette_index local biome_param2 = foliage_palette_index
local rotation_param2 = mcl_util.get_colorwallmounted_rotation(pos) local rotation_param2 = mcl_util.get_colorwallmounted_rotation(pos)
local final_param2 = (biome_param2 * 8) + rotation_param2 local final_param2 = (biome_param2 * 8) + rotation_param2
if node.param2 ~= final_param2 then if node.param2 ~= final_param2 then
node.param2 = final_param2 node.param2 = final_param2
minetest.set_node(pos, node) minetest.swap_node(pos, node)
end end
end end
end end
@ -525,7 +482,7 @@ minetest.register_lbm({
local water_palette_index = mcl_util.get_palette_indexes_from_pos(pos).water_palette_index local water_palette_index = mcl_util.get_palette_indexes_from_pos(pos).water_palette_index
if node.param2 ~= water_palette_index then if node.param2 ~= water_palette_index then
node.param2 = water_palette_index node.param2 = water_palette_index
minetest.set_node(pos, node) minetest.swap_node(pos, node)
end end
end end
}) })
@ -538,15 +495,19 @@ minetest.register_lbm({
action = function(pos, node) action = function(pos, node)
if node.param2 ~= 3 then if node.param2 ~= 3 then
node.param2 = 3 node.param2 = 3
minetest.set_node(pos, node) minetest.swap_node(pos, node)
end end
end end
}) })
]]--
-- Can we get rid of this ugly hack?
--[[
-- We go outside x and y for where trees are placed next to a biome that has already been generated. -- We go outside x and y for where trees are placed next to a biome that has already been generated.
-- We go above maxp.y because trees can often get placed close to the top of a generated area and folliage may not -- We go above maxp.y because trees can often get placed close to the top of a generated area and folliage may not
-- be coloured correctly. -- be coloured correctly.
local function fix_folliage_missed (minp, maxp) local function fix_foliage_missed(minp, maxp, blockseed)
if maxp.y < 0 then return end
local pos1, pos2 = vector.offset(minp, -6, 0, -6), vector.offset(maxp, 6, 14, 6) local pos1, pos2 = vector.offset(minp, -6, 0, -6), vector.offset(maxp, 6, 14, 6)
local foliage = minetest.find_nodes_in_area(pos1, pos2, {"group:foliage_palette", "group:foliage_palette_wallmounted"}) local foliage = minetest.find_nodes_in_area(pos1, pos2, {"group:foliage_palette", "group:foliage_palette_wallmounted"})
for _, fpos in pairs(foliage) do for _, fpos in pairs(foliage) do
@ -554,22 +515,19 @@ local function fix_folliage_missed (minp, maxp)
local foliage_palette_index = mcl_util.get_palette_indexes_from_pos(fpos).foliage_palette_index local foliage_palette_index = mcl_util.get_palette_indexes_from_pos(fpos).foliage_palette_index
if fnode.param2 ~= foliage_palette_index and fnode.name ~= "mcl_core:vine" then if fnode.param2 ~= foliage_palette_index and fnode.name ~= "mcl_core:vine" then
fnode.param2 = foliage_palette_index fnode.param2 = foliage_palette_index
minetest.set_node(fpos, fnode) minetest.swap_node(fpos, fnode)
elseif fnode.name == "mcl_core:vine" then elseif fnode.name == "mcl_core:vine" then
local biome_param2 = foliage_palette_index local biome_param2 = foliage_palette_index
local rotation_param2 = mcl_util.get_colorwallmounted_rotation(fpos) local rotation_param2 = mcl_util.get_colorwallmounted_rotation(fpos)
local final_param2 = (biome_param2 * 8) + rotation_param2 local final_param2 = (biome_param2 * 8) + rotation_param2
if fnode.param2 ~= final_param2 then if fnode.param2 ~= final_param2 then
fnode.param2 = final_param2 fnode.param2 = final_param2
minetest.set_node(fpos, fnode) minetest.swap_node(fpos, fnode)
end end
end end
end end
end end
minetest.register_on_generated(function(minp, maxp, blockseed) -- Set correct palette indexes of missed foliage. minetest.register_on_generated(function(minp, maxp, blockseed) -- Set correct palette indexes of missed foliage.
if maxp.y < 0 then fix_foliage_missed(minp, maxp)
return end)]]
end
fix_folliage_missed (minp, maxp)
end)

View File

@ -136,7 +136,7 @@ minetest.register_ore({
clust_num_ores = 58, clust_num_ores = 58,
clust_size = 7, clust_size = 7,
y_min = deepslate_min, y_min = deepslate_min,
y_max = deepslate_max, y_max = deepslate_max,
noise_params = { noise_params = {
offset = 0, offset = 0,
scale = 1, scale = 1,
@ -450,14 +450,15 @@ if minetest.settings:get_bool("mcl_generate_ores", true) then
ore_type = "scatter", ore_type = "scatter",
ore = "mcl_nether:ancient_debris", ore = "mcl_nether:ancient_debris",
wherein = ancient_debris_wherein, wherein = ancient_debris_wherein,
clust_scarcity = 25000, -- 0.004% chance clust_scarcity = 15000,
-- in MC it's 0.004% chance (~= scarcity 25000) but reports and experiments show that ancient debris is unreasonably hard to find in survival with that value
clust_num_ores = 3, clust_num_ores = 3,
clust_size = 3, clust_size = 3,
y_min = mcl_vars.mg_nether_min + 8, y_min = mcl_vars.mg_nether_min + 8,
y_max = mcl_vars.mg_nether_min + 22, y_max = mcl_vars.mg_nether_min + 22,
}) })
-- Rare spawn (below) -- Rare spawn (below)
minetest.register_ore({ minetest.register_ore({
ore_type = "scatter", ore_type = "scatter",
ore = "mcl_nether:ancient_debris", ore = "mcl_nether:ancient_debris",

View File

@ -45,6 +45,7 @@ local function register_mgv6_decorations()
-- Doubletall grass -- Doubletall grass
minetest.register_decoration({ minetest.register_decoration({
priority = 1500,
deco_type = "schematic", deco_type = "schematic",
schematic = { schematic = {
size = { x=1, y=3, z=1 }, size = { x=1, y=3, z=1 },
@ -81,6 +82,7 @@ local function register_mgv6_decorations()
}, },
-- v6 hack: This makes sure large ferns only appear in jungles -- v6 hack: This makes sure large ferns only appear in jungles
spawn_by = { "mcl_core:jungletree", "mcl_flowers:fern" }, spawn_by = { "mcl_core:jungletree", "mcl_flowers:fern" },
priority = 1510, -- larger than fern
num_spawn_by = 1, num_spawn_by = 1,
place_on = {"group:grass_block_no_snow"}, place_on = {"group:grass_block_no_snow"},
@ -192,6 +194,7 @@ local function register_mgv6_decorations()
}, },
-- Small trick to make sure melon spawn in jungles -- Small trick to make sure melon spawn in jungles
spawn_by = { "mcl_core:jungletree", "mcl_flowers:fern" }, spawn_by = { "mcl_core:jungletree", "mcl_flowers:fern" },
priority = 1510, -- larger than fern
num_spawn_by = 1, num_spawn_by = 1,
y_min = 1, y_min = 1,
y_max = 40, y_max = 40,
@ -214,6 +217,7 @@ local function register_mgv6_decorations()
y_min = 1, y_min = 1,
y_max = mcl_vars.overworld_max, y_max = mcl_vars.overworld_max,
decoration = "mcl_flowers:tallgrass", decoration = "mcl_flowers:tallgrass",
priority = 1500,
}) })
minetest.register_decoration({ minetest.register_decoration({
deco_type = "simple", deco_type = "simple",
@ -230,6 +234,7 @@ local function register_mgv6_decorations()
y_min = 1, y_min = 1,
y_max = mcl_vars.overworld_max, y_max = mcl_vars.overworld_max,
decoration = "mcl_flowers:tallgrass", decoration = "mcl_flowers:tallgrass",
priority = 1500,
}) })
-- Seagrass and kelp -- Seagrass and kelp
@ -256,6 +261,7 @@ local function register_mgv6_decorations()
y_min = mcl_vars.overworld_min, y_min = mcl_vars.overworld_min,
y_max = 0, y_max = 0,
decoration = "mcl_ocean:seagrass_"..mat, decoration = "mcl_ocean:seagrass_"..mat,
priority = 1500,
}) })
minetest.register_decoration({ minetest.register_decoration({
deco_type = "simple", deco_type = "simple",
@ -276,6 +282,7 @@ local function register_mgv6_decorations()
y_min = mcl_vars.overworld_min, y_min = mcl_vars.overworld_min,
y_max = -5, y_max = -5,
decoration = "mcl_ocean:seagrass_"..mat, decoration = "mcl_ocean:seagrass_"..mat,
priority = 1500,
}) })
minetest.register_decoration({ minetest.register_decoration({
@ -356,6 +363,7 @@ local function register_mgv6_decorations()
y_min = 1, y_min = 1,
y_max = mcl_vars.overworld_max, y_max = mcl_vars.overworld_max,
decoration = "mcl_flowers:tallgrass", decoration = "mcl_flowers:tallgrass",
priority = 1500,
}) })
local mushrooms = {"mcl_mushrooms:mushroom_red", "mcl_mushrooms:mushroom_brown"} local mushrooms = {"mcl_mushrooms:mushroom_red", "mcl_mushrooms:mushroom_brown"}
@ -501,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 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") 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 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 chunk_has_igloo = true
end end
end end
@ -520,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"}) 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% if #nodes >= 100 then -- >= 80%
mcl_structures.call_struct(p1, "fossil", nil, pr) vl_structures.call_struct(p1, "fossil", nil, pr)
end end
end end
end end
@ -557,15 +565,12 @@ local function generate_mgv6_structures()
if #free_nodes >= ((size.x+1)*(size.y+1)*(size.z+1)) then 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} local place = {x=p.x, y=WITCH_HUT_HEIGHT-1, z=p.z}
-- FIXME: For some mysterious reason (black magic?) this vl_structures.place_structure(place,vl_structures.registered_structures["witch_hut"],pr)
-- 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)
local function place_tree_if_free(pos, prev_result) local function place_tree_if_free(pos, prev_result)
local nn = minetest.get_node(pos).name local nn = minetest.get_node(pos).name
if nn == "mcl_flowers:waterlily" or nn == "mcl_core:water_source" or nn == "mcl_core:water_flowing" or nn == "air" then if nn == "mcl_flowers:waterlily" or nn == "mcl_core:water_source" or nn == "mcl_core:water_flowing" or nn == "air" then
minetest.set_node(pos, {name="mcl_core:tree", param2=0}) minetest.swap_node(pos, {name="mcl_core:tree", param2=0})
return prev_result return prev_result
else else
return false return false
@ -629,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"}) 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 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 end
elseif spike < 100 then elseif spike < 100 then
-- Check surface -- Check surface
@ -640,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"}) 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 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 end
end end
@ -671,9 +676,9 @@ local function generate_underground_mushrooms(minp, maxp, seed)
local l = minetest.get_node_light(bpos, 0.5) local l = minetest.get_node_light(bpos, 0.5)
if bpos.y >= min and bpos.y <= max and l and l <= 12 and pr_shroom:next(1,1000) < 4 then if bpos.y >= min and bpos.y <= max and l and l <= 12 and pr_shroom:next(1,1000) < 4 then
if pr_shroom:next(1,2) == 1 then if pr_shroom:next(1,2) == 1 then
minetest.set_node(bpos, {name = "mcl_mushrooms:mushroom_brown"}) minetest.swap_node(bpos, {name = "mcl_mushrooms:mushroom_brown"})
else else
minetest.set_node(bpos, {name = "mcl_mushrooms:mushroom_red"}) minetest.swap_node(bpos, {name = "mcl_mushrooms:mushroom_red"})
end end
end end
end end
@ -710,14 +715,14 @@ local function generate_nether_decorations(minp, maxp, seed)
special_deco(rack, function(bpos) special_deco(rack, function(bpos)
-- Eternal fire on netherrack -- Eternal fire on netherrack
if pr_nether:next(1,100) <= 3 then if pr_nether:next(1,100) <= 3 then
minetest.set_node(bpos, {name = "mcl_fire:eternal_fire"}) minetest.swap_node(bpos, {name = "mcl_fire:eternal_fire"})
end end
end) end)
-- Eternal fire on magma cubes -- Eternal fire on magma cubes
special_deco(magma, function(bpos) special_deco(magma, function(bpos)
if pr_nether:next(1,150) == 1 then if pr_nether:next(1,150) == 1 then
minetest.set_node(bpos, {name = "mcl_fire:eternal_fire"}) minetest.swap_node(bpos, {name = "mcl_fire:eternal_fire"})
end end
end) end)
@ -728,9 +733,9 @@ local function generate_nether_decorations(minp, maxp, seed)
if bpos.y > mcl_vars.mg_lava_nether_max + 6 and l and l <= 12 and pr_nether:next(1,1000) <= 4 then if bpos.y > mcl_vars.mg_lava_nether_max + 6 and l and l <= 12 and pr_nether:next(1,1000) <= 4 then
-- TODO: Make mushrooms appear in groups, use Perlin noise -- TODO: Make mushrooms appear in groups, use Perlin noise
if pr_nether:next(1,2) == 1 then if pr_nether:next(1,2) == 1 then
minetest.set_node(bpos, {name = "mcl_mushrooms:mushroom_brown"}) minetest.swap_node(bpos, {name = "mcl_mushrooms:mushroom_brown"})
else else
minetest.set_node(bpos, {name = "mcl_mushrooms:mushroom_red"}) minetest.swap_node(bpos, {name = "mcl_mushrooms:mushroom_red"})
end end
end end
end) end)
@ -739,7 +744,7 @@ local function generate_nether_decorations(minp, maxp, seed)
-- TODO: Spawn in Nether fortresses -- TODO: Spawn in Nether fortresses
special_deco(ssand, function(bpos) special_deco(ssand, function(bpos)
if pr_nether:next(1, nether_wart_chance) == 1 then if pr_nether:next(1, nether_wart_chance) == 1 then
minetest.set_node(bpos, {name = "mcl_nether:nether_wart"}) minetest.swap_node(bpos, {name = "mcl_nether:nether_wart"})
end end
end) end)
end end

View File

@ -1,25 +1,20 @@
local modname = minetest.get_current_modname() local modname = minetest.get_current_modname()
local S = minetest.get_translator(modname)
local modpath = minetest.get_modpath(modname) local modpath = minetest.get_modpath(modname)
local peaceful = minetest.settings:get_bool("only_peaceful_mobs", false)
local BLAZE_SPAWNER_MAX_LIGHT = 11 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"}, 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"},
fill_ratio = 0.01, chunk_probability = 23,
chunk_probability = 900, flags = "place_center_x, place_center_y, all_floors",
flags = "all_floors",
biomes = {"Nether","SoulsandValley","WarpedForest","CrimsonForest","BasaltDelta"}, biomes = {"Nether","SoulsandValley","WarpedForest","CrimsonForest","BasaltDelta"},
sidelen = 24, prepare = { tolerance = 20, padding = 4, corners = 5, foundation = true, clear = true, clear_top = 4 },
solid_ground = true,
make_foundation = true,
y_min = mcl_vars.mg_lava_nether_max - 1, y_min = mcl_vars.mg_lava_nether_max - 1,
y_max = mcl_vars.mg_nether_max - 30, y_max = mcl_vars.mg_nether_max - 30,
filenames = { modpath.."/schematics/mcl_nether_fortresses_nether_outpost.mts" }, filenames = { modpath.."/schematics/mcl_nether_fortresses_nether_outpost.mts" },
y_offset = 0, y_offset = 0,
after_place = function(pos) after_place = function(pos,def,pr,p1,p2)
local sp = minetest.find_nodes_in_area(pos,vector.offset(pos,0,20,0),{"mcl_mobspawners:spawner"}) local sp = minetest.find_nodes_in_area(p1,p2,{"mcl_mobspawners:spawner"})
if not sp[1] then return end if not sp[1] then return end
mcl_mobspawners.setup_spawner(sp[1], "mobs_mc:blaze", 0, BLAZE_SPAWNER_MAX_LIGHT, 10, 8, 0) mcl_mobspawners.setup_spawner(sp[1], "mobs_mc:blaze", 0, BLAZE_SPAWNER_MAX_LIGHT, 10, 8, 0)
end end
@ -30,80 +25,101 @@ local nbridges = {
modpath.."/schematics/mcl_nether_fortresses_nether_bridge_3.mts", modpath.."/schematics/mcl_nether_fortresses_nether_bridge_3.mts",
modpath.."/schematics/mcl_nether_fortresses_nether_bridge_4.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","mcl_core:bedrock"}, 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"},
fill_ratio = 0.01, chunk_probability = 8, -- because of the y restriction these are quite rare
chunk_probability = 500, flags = "place_center_x, place_center_y, all_floors",
flags = "all_floors", prepare = { tolerance = 50, padding = -1, corners = 0, clear_bottom = 8, clear_top = 6 }, -- asymmetric padding would be nice to have
sidelen = 38, force_placement = true,
solid_ground = false, y_min = mcl_vars.mg_lava_nether_max,
make_foundation = false, y_max = mcl_vars.mg_lava_nether_max + 25, -- otherwise, we may see some very long legs
y_min = mcl_vars.mg_nether_min - 4,
y_max = mcl_vars.mg_lava_nether_max - 20,
filenames = nbridges, filenames = nbridges,
y_offset = function(pr) return pr:next(15,20) end, y_offset = function(pr) return pr:next(-8, -5) end,
after_place = function(pos,def,pr) after_place = function(pos,def,pr,p1,p2)
local p1 = vector.offset(pos,-14,0,-14) vl_structures.spawn_mobs("mobs_mc:witherskeleton",{"mcl_blackstone:blackstone_chiseled_polished"},p1,p2,pr,5)
local p2 = vector.offset(pos,14,24,14) -- p1.y is not a typo, we want to lowest level only
mcl_structures.spawn_mobs("mobs_mc:witherskeleton",{"mcl_blackstone:blackstone_chiseled_polished"},p1,p2,pr,5) 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")
end
})
mcl_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"},
fill_ratio = 0.01,
chunk_probability = 1300,
flags = "all_floors",
biomes = {"Nether","SoulsandValley","WarpedForest","CrimsonForest","BasaltDelta"},
sidelen = 24,
solid_ground = true,
make_foundation = true,
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" },
daughters = {{
files = { nbridges[1] },
pos = vector.new(0,-2,-24),
rot = 180,
},
{
files = { nbridges[1] },
pos = vector.new(0,-2,24),
rot = 0,
},
{
files = { nbridges[1] },
pos = vector.new(-24,-2,0),
rot = 270,
},
{
files = { nbridges[1] },
pos = vector.new(24,-2,0),
rot = 90,
},
},
after_place = function(pos,def,pr)
local sp = minetest.find_nodes_in_area(pos,vector.offset(pos,0,20,0),{"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")
local bricks = {} local bricks = {}
-- TODO: port leg generation to voxel manipulators?
for _,leg in pairs(legs) do 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) 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) table.insert(bricks,leg)
end end
end end
minetest.bulk_set_node(bricks, {name = "mcl_nether:nether_brick", param2 = 2}) minetest.bulk_swap_node(bricks, {name = "mcl_nether:nether_brick", param2 = 2})
local p1 = vector.offset(pos,-45,13,-45)
local p2 = vector.offset(pos,45,13,45)
mcl_structures.spawn_mobs("mobs_mc:witherskeleton",{"mcl_blackstone:blackstone_chiseled_polished"},p1,p2,pr,5)
end end
},true) })
mcl_structures.register_structure_spawn({ 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 = 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"},
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_lava_nether_max + 40,
-- todo: spawn_by a lot of air?
filenames = { modpath.."/schematics/mcl_nether_fortresses_nether_outpost.mts" },
emerge_padding = { vector.new(-38,-8,-38), vector.new(38,0,38) },
daughters = {
{
filenames = { nbridges[1], nbridges[2] },
pos = vector.new(0,-3,24),
rotation= 0,
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(24,-3,0),
rotation = 90,
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(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,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)
-- 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 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_swap_node(bricks, {name = "mcl_nether:nether_brick", param2 = 2})
local p1, p2 = vector.offset(pos,-45,12,-45), vector.offset(pos,45,22,45)
vl_structures.spawn_mobs("mobs_mc:witherskeleton",{"mcl_blackstone:blackstone_chiseled_polished"},p1,p2,pr,5)
end
})
vl_structures.register_structure_spawn({
name = "mobs_mc:witherskeleton", name = "mobs_mc:witherskeleton",
y_min = mcl_vars.mg_lava_nether_max, y_min = mcl_vars.mg_lava_nether_max,
y_max = mcl_vars.mg_nether_max, y_max = mcl_vars.mg_nether_max,
@ -113,15 +129,12 @@ mcl_structures.register_structure_spawn({
spawnon = { "mcl_blackstone:blackstone_chiseled_polished" }, 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"}, 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"},
fill_ratio = 0.01, chunk_probability = 29,
chunk_probability = 900, flags = "place_center_x, place_center_y, all_floors",
flags = "all_floors",
biomes = {"Nether","SoulsandValley","WarpedForest","CrimsonForest"}, biomes = {"Nether","SoulsandValley","WarpedForest","CrimsonForest"},
sidelen = 36, prepare = { tolerance=10, padding=4, corners=5, foundation=-5, clear_top=0 },
solid_ground = true,
make_foundation = true,
y_min = mcl_vars.mg_lava_nether_max - 1, y_min = mcl_vars.mg_lava_nether_max - 1,
y_max = mcl_vars.mg_nether_max - 30, y_max = mcl_vars.mg_nether_max - 30,
filenames = { filenames = {
@ -131,23 +144,24 @@ mcl_structures.register_structure("nether_bulwark",{
modpath.."/schematics/mcl_nether_fortresses_nether_bulwark_4.mts", modpath.."/schematics/mcl_nether_fortresses_nether_bulwark_4.mts",
}, },
daughters = {{ daughters = {{
files = { filenames = {
modpath.."/schematics/mcl_nether_fortresses_nether_bulwark_interior_1.mts", 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_2.mts",
modpath.."/schematics/mcl_nether_fortresses_nether_bulwark_interior_3.mts", modpath.."/schematics/mcl_nether_fortresses_nether_bulwark_interior_3.mts",
modpath.."/schematics/mcl_nether_fortresses_nether_bulwark_interior_4.mts", modpath.."/schematics/mcl_nether_fortresses_nether_bulwark_interior_4.mts",
}, },
pos = vector.new(0,0,0), pos = vector.new(0,1,0),
rotation = "random",
force_placement = true,
prepare = { tolerance = -1, foundation = false, clear = false },
}, },
}, },
y_offset = 0, y_offset = 0,
construct_nodes = {"group:wall"}, construct_nodes = {"group:wall"},
after_place = function(pos,def,pr) after_place = function(pos,def,pr,p1,p2)
local p1 = vector.offset(pos,-14,0,-14) vl_structures.spawn_mobs("mobs_mc:piglin",{"mcl_blackstone:blackstone_brick_polished","mcl_stairs:slab_blackstone_polished"},p1,p2,pr,5)
local p2 = vector.offset(pos,14,24,14) vl_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:piglin",{"mcl_blackstone:blackstone_brick_polished","mcl_stairs:slab_blackstone_polished"},p1,p2,pr,5) vl_structures.spawn_mobs("mobs_mc:hoglin",{"mcl_blackstone:nether_gold"},p1,p2,pr,4)
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)
end, end,
loot = { loot = {
["mcl_chests:chest_small" ] ={ ["mcl_chests:chest_small" ] ={
@ -194,22 +208,22 @@ mcl_structures.register_structure("nether_bulwark",{
}, },
}) })
mcl_structures.register_structure_spawn({ vl_structures.register_structure_spawn({
name = "mobs_mc:piglin", name = "mobs_mc:piglin",
y_min = mcl_vars.mg_nether_min, y_min = mcl_vars.mg_nether_min,
y_max = mcl_vars.mg_nether_max, y_max = mcl_vars.mg_nether_max,
chance = 10, chance = 10,
interval = 60, interval = 60,
limit = 9, 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", name = "mobs_mc:piglin_brute",
y_min = mcl_vars.mg_nether_min, y_min = mcl_vars.mg_nether_min,
y_max = mcl_vars.mg_nether_max, y_max = mcl_vars.mg_nether_max,
chance = 20, chance = 20,
interval = 60, interval = 60,
limit = 4, limit = 4,
spawnon = {"mcl_blackstone:blackstone_brick_polished","mcl_stairs:slab_blackstone_polished"}, spawnon = {"mcl_blackstone:blackstone_brick_polished", "mcl_stairs:slab_blackstone_polished"},
}) })

View File

@ -83,21 +83,20 @@ local function generate_strongholds(minp, maxp, blockseed)
pos.z = maxp.z - 7 pos.z = maxp.z - 7
end 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 strongholds[s].generated = true
end end
end end
end end
end end
mcl_structures.register_structure("end_shrine",{ vl_structures.register_structure("end_shrine",{
static_pos = init_strongholds(), static_pos = init_strongholds(),
prepare = { tolerance = -1, foundation = false, clear = false },
filenames = { filenames = {
minetest.get_modpath("mcl_structures").."/schematics/mcl_structures_end_portal_room_simple.mts" 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") local spawners = minetest.find_nodes_in_area(p1, p2, "mcl_mobspawners:spawner")
for s=1, #spawners do for s=1, #spawners do
--local meta = minetest.get_meta(spawners[s]) --local meta = minetest.get_meta(spawners[s])

View File

@ -1,38 +1,7 @@
# mcl_structures # mcl_structures
Structure placement API for MCL2. VoxeLibre structures
## mcl_structures.register_structure(name,structure definition,nospawn) This module contains standard VoxeLibre structures such as nether portals.
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 The API has been redesigned and moved to the vl_structures module.
{
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.
## 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)

View File

@ -1,393 +0,0 @@
mcl_structures.registered_structures = {}
local disabled_structures = minetest.settings:get("mcl_disabled_structures")
if disabled_structures then disabled_structures = disabled_structures:split(",")
else disabled_structures = {} end
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 logging = minetest.settings:get_bool("mcl_logging_structures",true)
local mg_name = minetest.get_mapgen_setting("mg_name")
local rotations = {
"0",
"90",
"180",
"270"
}
function mcl_structures.is_disabled(structname)
return table.indexof(disabled_structures,structname) ~= -1
end
local function ecb_place(blockpos, action, calls_remaining, param)
if calls_remaining >= 1 then return end
minetest.place_schematic(param.pos, param.schematic, param.rotation, param.replacements, param.force_placement, param.flags)
if param.after_placement_callback and param.p1 and param.p2 then
param.after_placement_callback(param.p1, param.p2, param.size, param.rotation, param.pr, param.callback_param)
end
end
function mcl_structures.place_schematic(pos, schematic, rotation, replacements, force_placement, flags, after_placement_callback, pr, callback_param)
if type(schematic) ~= "table" and not mcl_util.file_exists(schematic) then
minetest.log("warning","[mcl_structures] schematic file "..tostring(schematic).." does not exist.")
return end
local s = loadstring(minetest.serialize_schematic(schematic, "lua", {lua_use_comments = false, lua_num_indent_spaces = 0}) .. " return schematic")()
if s and s.size then
local x, z = s.size.x, s.size.z
if rotation then
if rotation == "random" and pr then
rotation = rotations[pr:next(1,#rotations)]
end
if rotation == "random" then
x = math.max(x, z)
z = x
elseif rotation == "90" or rotation == "270" then
x, z = z, x
end
end
local p1 = {x=pos.x , y=pos.y , z=pos.z }
local p2 = {x=pos.x+x-1, y=pos.y+s.size.y-1, z=pos.z+z-1}
minetest.log("verbose", "[mcl_structures] size=" ..minetest.pos_to_string(s.size) .. ", rotation=" .. tostring(rotation) .. ", emerge from "..minetest.pos_to_string(p1) .. " to " .. minetest.pos_to_string(p2))
local param = {pos=vector.new(pos), schematic=s, rotation=rotation, replacements=replacements, force_placement=force_placement, flags=flags, p1=p1, p2=p2, after_placement_callback = after_placement_callback, size=vector.new(s.size), pr=pr, callback_param=callback_param}
minetest.emerge_area(p1, p2, ecb_place, param)
return true
end
end
function mcl_structures.get_struct(file)
local localfile = modpath.."/schematics/"..file
local file, errorload = io.open(localfile, "rb")
if errorload then
minetest.log("error", "[mcl_structures] Could not open this struct: "..localfile)
return nil
end
local allnode = file:read("*a")
file:close()
return allnode
end
-- Call on_construct on pos.
-- Useful to init chests from formspec.
local function init_node_construct(pos)
local node = minetest.get_node(pos)
local def = minetest.registered_nodes[node.name]
if def and def.on_construct then
def.on_construct(pos)
return true
end
return false
end
mcl_structures.init_node_construct = init_node_construct
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
local function generate_loot(pos, def, pr)
local hl = def.sidelen
local p1 = vector.offset(pos,-hl,-hl,-hl)
local p2 = vector.offset(pos,hl,hl,hl)
if def.loot then mcl_structures.fill_chests(p1,p2,def.loot,pr) end
end
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
local function construct_nodes(pos,def,pr)
return mcl_structures.construct_nodes(vector.offset(pos,-def.sidelen/2,0,-def.sidelen/2),vector.offset(pos,def.sidelen/2,def.sidelen,def.sidelen/2),def.construct_nodes)
end
function mcl_structures.find_lowest_y(pp)
local y = 31000
for _,p in pairs(pp) do
if p.y < y then y = p.y end
end
return y
end
function mcl_structures.find_highest_y(pp)
local y = -31000
for _,p in pairs(pp) do
if p.y > y then y = p.y end
end
return y
end
local function smooth_cube(nn,pos,plane,amnt)
local r = {}
local amnt = amnt or 9
table.sort(nn,function(a, b)
if false or plane then
return vector.distance(vector.new(pos.x,0,pos.z), vector.new(a.x,0,a.z)) < vector.distance(vector.new(pos.x,0,pos.z), vector.new(b.x,0,b.z))
else
return vector.distance(pos, a) < vector.distance(pos, b)
end
end)
for i=1,math.max(1,#nn-amnt) do table.insert(r,nn[i]) end
return r
end
local function find_ground(pos,nn,gn)
local r = 0
for _,v in pairs(nn) do
local p=vector.new(v)
repeat
local n = minetest.get_node(p).name
p = vector.offset(p,0,-1,0)
until not n or n == "mcl_core:bedrock" or n == "ignore" or n == gn
--minetest.log(tostring(pos.y - p.y))
if pos.y - p.y > r then r = pos.y - p.y end
end
return r
end
local function get_foundation_nodes(ground_p1,ground_p2,pos,sidelen,node_stone)
local replace = {"air","group:liquid","mcl_core:snow","group:tree","group:leaves","group:plant","grass_block","group:dirt"}
local depth = find_ground(pos,minetest.find_nodes_in_area(ground_p1,ground_p2,replace),node_stone)
local nn = smooth_cube(minetest.find_nodes_in_area(vector.offset(ground_p1,0,-1,0),vector.offset(ground_p2,0,-depth,0),replace),vector.offset(pos,0,-depth,0),true,sidelen * 64)
local stone = {}
local filler = {}
local top = {}
local dust = {}
for l,v in pairs(nn) do
if v.y == ground_p1.y - 1 then
table.insert(filler,v)
table.insert(top,vector.offset(v,0,1,0))
table.insert(dust,vector.offset(v,0,2,0))
elseif v.y < ground_p1.y -1 and v.y > ground_p2.y -4 then table.insert(filler,v)
elseif v.y < ground_p2.y - 3 and v.y > ground_p2.y -5 then
if math.random(3) == 1 then
table.insert(filler,v)
else
table.insert(stone,v)
end
else
table.insert(stone,v)
end
end
return stone,filler,top,dust
end
local function foundation(ground_p1,ground_p2,pos,sidelen)
local node_stone = "mcl_core:stone"
local node_filler = "mcl_core:dirt"
local node_top = "mcl_core:dirt_with_grass" or minetest.get_node(ground_p1).name
local node_dust = nil
if mg_name ~= "v6" then
local b = minetest.registered_biomes[minetest.get_biome_name(minetest.get_biome_data(pos).biome)]
--minetest.log(dump(b.node_top))
if b then
if b.node_top then node_top = b.node_top end
if b.node_filler then node_filler = b.node_filler end
if b.node_stone then node_stone = b.node_stone end
if b.node_dust then node_dust = b.node_dust end
end
end
local stone,filler,top,dust = get_foundation_nodes(ground_p1,ground_p2,pos,sidelen,node_stone)
minetest.bulk_set_node(top,{name=node_top},node_stone)
if node_dust then
minetest.bulk_set_node(dust,{name=node_dust})
end
minetest.bulk_set_node(filler,{name=node_filler})
minetest.bulk_set_node(stone,{name=node_stone})
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
if not rot then rot = "random" end
local log_enabled = logging and not def.terrain_feature
local y_offset = 0
if type(def.y_offset) == "function" then
y_offset = def.y_offset(pr)
elseif def.y_offset then
y_offset = def.y_offset
end
local pp = vector.offset(pos,0,y_offset,0)
if def.solid_ground and def.sidelen then
local ground_p1 = vector.offset(pos,-def.sidelen/2,-1,-def.sidelen/2)
local ground_p2 = vector.offset(pos,def.sidelen/2,-1,def.sidelen/2)
local solid = minetest.find_nodes_in_area(ground_p1,ground_p2,{"group:solid"})
if #solid < ( def.sidelen * def.sidelen ) then
if def.make_foundation then
foundation(vector.offset(pos,-def.sidelen/2 - 3,-1,-def.sidelen/2 - 3),vector.offset(pos,def.sidelen/2 + 3,-1,def.sidelen/2 + 3),pos,def.sidelen)
else
if log_enabled then
minetest.log("warning","[mcl_structures] "..def.name.." at "..minetest.pos_to_string(pp).." not placed. No solid ground.")
end
return false
end
end
end
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(pp).." not placed. Conditions not satisfied.")
end
return false
end
if def.filenames then
if #def.filenames <= 0 then return false end
local r = pr:next(1,#def.filenames)
local file = def.filenames[r]
if file then
local rot = rotations[pr:next(1,#rotations)]
local ap = function(pos,def,pr,blockseed) end
if def.daughters then
ap = function(pos,def,pr,blockseed)
for _,d in pairs(def.daughters) do
local p = vector.add(pos,d.pos)
local rot = d.rot or 0
mcl_structures.place_schematic(p, d.files[pr:next(1,#d.files)], rot, nil, true, "place_center_x,place_center_z",function()
if def.loot then generate_loot(pp,def,pr,blockseed) end
if def.construct_nodes then construct_nodes(pp,def,pr,blockseed) end
if def.after_place then
def.after_place(pos,def,pr)
end
end,pr)
end
end
elseif def.after_place then
ap = def.after_place
end
mcl_structures.place_schematic(pp, file, rot, def.replacements, true, "place_center_x,place_center_z",function(p1, p2, size, rotation)
if not def.daughters then
if def.loot then generate_loot(pp,def,pr,blockseed) end
if def.construct_nodes then construct_nodes(pp,def,pr,blockseed) end
end
return ap(pp,def,pr,blockseed,p1,p2,size,rotation)
end,pr)
if log_enabled then
minetest.log("action","[mcl_structures] "..def.name.." placed at "..minetest.pos_to_string(pp))
end
return true
end
elseif 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.loot then generate_loot(pp,def,pr,blockseed) end
if def.construct_nodes then construct_nodes(pp,def,pr,blockseed) end
if log_enabled then
minetest.log("action","[mcl_structures] "..def.name.." placed at "..minetest.pos_to_string(pp))
end
return true
end
end
if log_enabled then
minetest.log("warning","[mcl_structures] placing "..def.name.." failed at "..minetest.pos_to_string(pos))
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
flags = def.flags or "place_center_x, place_center_z, force_placement"
def.name = name
if not nospawn and def.place_on then
minetest.register_on_mods_loaded(function() --make sure all previous decorations and biomes have been registered
def.deco = minetest.register_decoration({
name = "mcl_structures:deco_"..name,
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,
fill_ratio = def.fill_ratio,
noise_params = def.noise_params,
flags = flags,
biomes = def.biomes,
y_max = def.y_max,
y_min = def.y_min
})
def.deco_id = minetest.get_decoration_id("mcl_structures:deco_"..name)
minetest.set_gen_notify({decoration=true}, { def.deco_id })
--catching of gennotify happens in mcl_mapgen_core
end)
end
mcl_structures.registered_structures[name] = def
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

View File

@ -0,0 +1,71 @@
local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)
vl_structures.register_structure("campsite",{
place_on = {"group:grass_block"},
flags = "place_center_x, place_center_z",
chunk_probability = 50,
y_max = mcl_vars.mg_overworld_max,
y_min = 1,
prepare = { tolerance = 1, foundation = -2, clear_top = 1, padding = 1, corners = 1 }, -- low tolerance, perform little terraforming
filenames = {
modpath.."/schematics/campsite_1.mts"
},
loot = {
["mcl_chests:trapped_chest_small"] = {
{
stacks_min = 1,
stacks_max = 3,
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_farming:wheat_item", weight = 7, amount_min = 8, amount_max = 21 },
{ itemstring = "mcl_farming:carrot_item", weight = 7, amount_min = 4, amount_max = 8 },
{ itemstring = "mcl_farming:potato_item_poison", weight = 7, amount_min = 2, amount_max = 6 },
{ itemstring = "mcl_farming:potato_item", weight = 7, amount_min = 2, amount_max = 6 },
{ itemstring = "mcl_core:coal_lump", weight = 6, amount_min = 2, amount_max = 8 },
{ itemstring = "mcl_mobitems:rotten_flesh", weight = 5, amount_min = 5, amount_max = 24 },
{ itemstring = "mcl_farming:potato_item", weight = 3, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_armor:helmet_leather_enchanted", weight = 3, func = function(stack, _)
mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}) end },
{ itemstring = "mcl_armor:chestplate_leather_enchanted", weight = 3, func = function(stack, _)
mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}) end },
{ itemstring = "mcl_armor:leggings_leather_enchanted", weight = 3, func = function(stack, _)
mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}) end },
{ itemstring = "mcl_armor:boots_leather_enchanted", weight = 3, func = function(stack, _)
mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}) end },
{ itemstring = "mcl_bamboo:bamboo", weight = 2, amount_min = 1, amount_max = 3 },
{ itemstring = "mcl_farming:pumpkin", weight = 2, amount_min = 1, amount_max = 3 },
{ itemstring = "mcl_tnt:tnt", weight = 1, amount_min = 1, amount_max = 2 },
}
},
{
stacks_min = 1,
stacks_max = 2,
items = {
{ itemstring = "mcl_core:iron_ingot", weight = 90, amount_min = 1, amount_max = 2 },
{ itemstring = "mcl_core:iron_nugget", weight = 50, amount_min = 1, amount_max = 10 },
{ itemstring = "mcl_core:emerald", weight = 40, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_core:lapis", weight = 20, amount_min = 1, amount_max = 10 },
{ itemstring = "mcl_core:gold_ingot", weight = 10, amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_core:gold_nugget", weight = 10, amount_min = 1, amount_max = 4 },
{ itemstring = "mcl_experience:bottle", weight = 5, amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_core:diamond", weight = 5, amount_min = 1, amount_max = 1 },
}
},{
stacks_min = 1,
stacks_max = 1,
items = {
--{ itemstring = "FIXME TREASURE MAP", weight = 8, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_core:paper", weight = 20, amount_min = 1, amount_max = 10 },
{ itemstring = "mcl_mobitems:feather", weight = 10, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_books:book", weight = 5, 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 },
}
},
}
}
})

View File

@ -1,16 +1,12 @@
local modname = minetest.get_current_modname() local modname = minetest.get_current_modname()
local S = minetest.get_translator(modname)
local modpath = minetest.get_modpath(modname) local modpath = minetest.get_modpath(modname)
local function temple_placement_callback(pos,def, pr) local function temple_placement_callback(pos,def,pr,p1,p2)
local hl = def.sidelen / 2
local p1 = vector.offset(pos,-hl,-hl,-hl)
local p2 = vector.offset(pos,hl,hl,hl)
-- Delete cacti leftovers: -- Delete cacti leftovers:
local cactus_nodes = minetest.find_nodes_in_area_under_air(p1, p2, "mcl_core:cactus") local cactus_nodes = minetest.find_nodes_in_area_under_air(p1, p2, "mcl_core:cactus")
if cactus_nodes and #cactus_nodes > 0 then if cactus_nodes and #cactus_nodes > 0 then
for _, pos in pairs(cactus_nodes) do 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 if node_below and node_below.name == "mcl_core:sandstone" then
minetest.swap_node(pos, {name="air"}) minetest.swap_node(pos, {name="air"})
end end
@ -32,15 +28,12 @@ local function temple_placement_callback(pos,def, pr)
end end
end end
mcl_structures.register_structure("desert_temple",{ vl_structures.register_structure("desert_temple",{
place_on = {"group:sand"}, place_on = {"group:sand"},
fill_ratio = 0.01,
flags = "place_center_x, place_center_z", flags = "place_center_x, place_center_z",
solid_ground = true,
make_foundation = true,
sidelen = 18,
y_offset = -12, y_offset = -12,
chunk_probability = 300, prepare = { tolerance = 10, padding = 3, corners = 3, foundation = true, clear = false },
chunk_probability = 18,
y_max = mcl_vars.mg_overworld_max, y_max = mcl_vars.mg_overworld_max,
y_min = 1, y_min = 1,
biomes = { "Desert" }, biomes = { "Desert" },

View File

@ -0,0 +1,14 @@
local modname = minetest.get_current_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" },
})

View File

@ -1,42 +1,36 @@
local modname = minetest.get_current_modname() local modname = minetest.get_current_modname()
local S = minetest.get_translator(modname)
local modpath = minetest.get_modpath(modname) local modpath = minetest.get_modpath(modname)
local spawnon = {"mcl_end:purpur_block"} local spawnon = {"mcl_end:purpur_block"}
local function spawn_shulkers(pos,def,pr) local function spawn_shulkers(pos,def,pr,p1,p2)
local p1 = vector.offset(pos,-def.sidelen/2,-1,-def.sidelen/2) vl_structures.spawn_mobs("mobs_mc:shulker",spawnon,p1,p2,pr,1)
local p2 = vector.offset(pos,def.sidelen/2,def.sidelen,def.sidelen/2) local guard = minetest.find_nodes_in_area(p1,p2,{"mcl_itemframes:item_frame"})
mcl_structures.spawn_mobs("mobs_mc:shulker",spawnon,p1,p2,pr,1) if #guard > 0 then
minetest.add_entity(vector.offset(guard[1],0,-1.5,0),"mobs_mc:shulker")
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")
end end
end end
mcl_structures.register_structure("end_shipwreck",{ vl_structures.register_structure("end_shipwreck",{
place_on = {"mcl_end:end_stone"}, place_on = {"mcl_end:end_stone"},
fill_ratio = 0.001,
flags = "place_center_x, place_center_z, all_floors", 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(15,40) end,
chunk_probability = 800, force_placement = false,
prepare = { foundation = false, clear = false },
chunk_probability = 25,
--y_max = mcl_vars.mg_end_max, --y_max = mcl_vars.mg_end_max,
--y_min = mcl_vars.mg_end_min -100, --y_min = mcl_vars.mg_end_min -100,
biomes = { "End", "EndHighlands", "EndMidlands", "EndBarrens", "EndSmallIslands" }, biomes = { "End", "EndHighlands", "EndMidlands", "EndBarrens", "EndSmallIslands" },
sidelen = 32,
filenames = { filenames = {
modpath.."/schematics/mcl_structures_end_shipwreck_1.mts", 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"}, 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) after_place = function(pos,def,pr,p1,p2)
local fr = minetest.find_node_near(pos,def.sidelen,{"mcl_itemframes:item_frame"}) local fr = minetest.find_nodes_in_area(p1,p2,{"mcl_itemframes:item_frame"})
if fr then if #fr > 0 and mcl_itemframes then
if mcl_itemframes then mcl_itemframes.update_item_entity(fr[1],minetest.get_node(fr[1]))
mcl_itemframes.update_item_entity(fr,minetest.get_node(fr))
end
end end
return spawn_shulkers(pos,def,pr) return spawn_shulkers(pos,def,pr,p1,p2)
end, end,
loot = { loot = {
[ "mcl_itemframes:item_frame" ] ={{ [ "mcl_itemframes:item_frame" ] ={{
@ -53,7 +47,7 @@ mcl_structures.register_structure("end_shipwreck",{
{ itemstring = "mcl_mobitems:bone", weight = 20, amount_min = 4, amount_max=6 }, { 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_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_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:iron_ingot", weight = 15, amount_min = 4, amount_max = 8 },
{ itemstring = "mcl_core:diamond", weight = 3, amount_min = 2, amount_max = 7 }, { itemstring = "mcl_core:diamond", weight = 3, amount_min = 2, amount_max = 7 },
{ itemstring = "mcl_mobitems:saddle", weight = 3, }, { itemstring = "mcl_mobitems:saddle", weight = 3, },
@ -86,16 +80,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"}, place_on = {"mcl_end:end_stone"},
fill_ratio = 0.01,
flags = "place_center_x, place_center_z, all_floors", flags = "place_center_x, place_center_z, all_floors",
y_offset = function(pr) return pr:next(15,30) end, y_offset = function(pr) return pr:next(10,20) end,
chunk_probability = 900, force_placement = false,
prepare = { foundation = false, clear = false },
chunk_probability = 10,
--y_max = mcl_vars.mg_end_max, --y_max = mcl_vars.mg_end_max,
--y_min = mcl_vars.mg_end_min -100, --y_min = mcl_vars.mg_end_min -100,
biomes = { "End", "EndHighlands", "EndMidlands", "EndBarrens", "EndSmallIslands" }, biomes = { "End", "EndHighlands", "EndMidlands", "EndBarrens", "EndSmallIslands" },
sidelen = 20,
filenames = { filenames = {
modpath.."/schematics/mcl_structures_end_boat.mts", modpath.."/schematics/mcl_structures_end_boat.mts",
}, },
@ -134,7 +128,7 @@ mcl_structures.register_structure("end_boat",{
} }
}) })
mcl_structures.register_structure_spawn({ vl_structures.register_structure_spawn({
name = "mobs_mc:shulker", name = "mobs_mc:shulker",
y_min = mcl_vars.mg_end_min, y_min = mcl_vars.mg_end_min,
y_max = mcl_vars.mg_end_max, y_max = mcl_vars.mg_end_max,
@ -143,3 +137,52 @@ mcl_structures.register_structure_spawn({
limit = 6, limit = 6,
spawnon = spawnon, spawnon = spawnon,
}) })
vl_structures.register_structure("small_end_city",{
place_on = {"mcl_end:end_stone"},
flags = "place_center_x, place_center_z, all_floors",
y_offset = 0,
chunk_probability = 30,
prepare = { foundation = -1, tolerance = 15 },
biomes = { "End", "EndHighlands", "EndMidlands", "EndBarrens", "EndSmallIslands" },
sidelen = 20,
filenames = {
modpath.."/schematics/mcl_structures_end_city_simple.mts",
},
after_place = function(pos,def,pr,p1,p2)
-- not on roof
vl_structures.spawn_mobs("mobs_mc:shulker",spawnon,p1,vector.offset(p2,0,-2,0),pr,1)
end,
construct_nodes = {"mcl_chests:ender_chest_small","mcl_chests:ender_chest","mcl_brewing:stand_000","mcl_chests:violet_shulker_box_small"},
loot = {
[ "mcl_chests:chest_small" ] ={{
stacks_min = 2,
stacks_max = 6,
items = {
{ 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_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, },
{ itemstring = "mcl_core:emerald", weight = 2, amount_min = 1, amount_max = 3 },
{ itemstring = "mcl_tools:pick_iron_enchanted", weight = 3,func = function(stack, pr) mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}, pr) end },
{ itemstring = "mcl_tools:shovel_iron_enchanted", weight = 3,func = function(stack, pr) mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}, pr) end },
{ itemstring = "mcl_tools:sword_iron_enchanted", weight = 3,func = function(stack, pr) mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}, pr) end },
{ itemstring = "mcl_armor:helmet_iron_enchanted", weight = 3,func = function(stack, pr) mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}, pr) end },
{ itemstring = "mcl_armor:chestplate_iron_enchanted", weight = 3,func = function(stack, pr) mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}, pr) end },
{ itemstring = "mcl_armor:leggings_iron_enchanted", weight = 3,func = function(stack, pr) mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}, pr) end },
{ itemstring = "mcl_armor:boots_iron_enchanted", weight = 3,func = function(stack, pr) mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}, pr) end },
{ itemstring = "mcl_tools:pick_diamond_enchanted", weight = 3,func = function(stack, pr) mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}, pr) end },
{ itemstring = "mcl_tools:shovel_diamond_enchanted", weight = 3,func = function(stack, pr) mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}, pr) end },
{ itemstring = "mcl_armor:helmet_diamond_enchanted", weight = 3,func = function(stack, pr) mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}, pr) end },
{ itemstring = "mcl_armor:leggings_diamond_enchanted", weight = 3,func = function(stack, pr) mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}, pr) end },
{ itemstring = "mcl_armor:boots_diamond_enchanted", weight = 3,func = function(stack, pr) mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}, pr) end },
{ itemstring = "mcl_core:emerald", weight = 2, amount_min = 1, amount_max = 3 },
{ itemstring = "mcl_mobitems:iron_horse_armor", weight = 1, },
{ itemstring = "mcl_mobitems:gold_horse_armor", weight = 1, },
{ itemstring = "mcl_core:apple_gold_enchanted", weight = 2, },
}
}}
}
})

View File

@ -1,123 +1,101 @@
local modname = minetest.get_current_modname() local modname = minetest.get_current_modname()
local S = minetest.get_translator(modname)
local modpath = minetest.get_modpath(modname) local modpath = minetest.get_modpath(modname)
local worldseed = minetest.get_mapgen_setting("seed")
-- mcl_structures_end_exit_portal open is triggered by mods/ENTITIES/mobs_mc/ender_dragon.lua
mcl_structures.register_structure("end_spawn_obsidian_platform",{ mcl_structures.spawn_end_exit_portal = function(pos)
static_pos ={mcl_vars.mg_end_platform_pos}, local schematic = vl_structures.load_schematic(modpath.."/schematics/mcl_structures_end_exit_portal.mts")
place_func = function(pos,def,pr) vl_structures.place_schematic(pos, 0, schematic, "0", { name="end_exit_portal_open", prepare = false })
local obby = minetest.find_nodes_in_area(vector.offset(pos,-2,0,-2),vector.offset(pos,2,0,2),{"air","mcl_end:end_stone"})
local air = minetest.find_nodes_in_area(vector.offset(pos,-2,1,-2),vector.offset(pos,2,3,2),{"air","mcl_end:end_stone"})
minetest.bulk_set_node(obby,{name="mcl_core:obsidian"})
minetest.bulk_set_node(air,{name="air"})
return true
end,
})
mcl_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)
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"})
local obj = minetest.add_entity(vector.offset(pos,3, 11, 3), "mobs_mc:enderdragon")
if obj then
local dragon_entity = obj:get_luaentity()
dragon_entity._portal_pos = pos
if blockseed ~= -1 then
dragon_entity._initial = true
end
else
minetest.log("error", "[mcl_mapgen_core] ERROR! Ender dragon doesn't want to spawn")
end
minetest.fix_light(p1,p2)
end)
end
})
mcl_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)
minetest.fix_light(p1,p2)
end
})
mcl_structures.register_structure("end_gateway_portal",{
filenames = {
modpath.."/schematics/mcl_structures_end_gateway_portal.mts"
},
})
local function get_tower(p,h,tbl)
for i = 1,h do
table.insert(tbl,vector.offset(p,0,i,0))
end
end end
local function make_endspike(pos,width,height) -- mcl_structures_end_gateway_portal.mts: see mods/ITEMS/mcl_portals/portal_gateway.lua
local nn = minetest.find_nodes_in_area(vector.offset(pos,-width/2,0,-width/2),vector.offset(pos,width/2,0,width/2),{"air","group:solid"}) mcl_structures.spawn_end_gateway_portal = function(pos)
local schematic = vl_structures.load_schematic(modpath.."/schematics/mcl_structures_end_gateway_portal.mts")
vl_structures.place_schematic(pos, 0, schematic, "0", { name="end_gateway_portal", prepare = false })
end
local function make_endspike(pos, rad, height)
-- FIXME: why find_nodes, not just use the circle?
local nn = minetest.find_nodes_in_area(vector.offset(pos, -rad, 0, -rad), vector.offset(pos, rad, 0, rad), {"air", "group:solid"})
table.sort(nn,function(a, b) table.sort(nn,function(a, b)
return vector.distance(pos, a) < vector.distance(pos, b) return vector.distance(pos, a) < vector.distance(pos, b)
end) end)
local nodes = {} local nodes = {}
for i = 1,math.ceil(#nn*0.55) do for i = 1, math.ceil(#nn * 0.55) do
get_tower(nn[i],height,nodes) for j = 1, height do
table.insert(nodes, vector.offset(nn[i], 0, j, 0))
end
end end
minetest.bulk_set_node(nodes,{ name="mcl_core:obsidian"} ) minetest.bulk_swap_node(nodes, {name = "mcl_core:obsidian"})
return vector.offset(pos,0,height,0) return vector.offset(pos, 0, height, 0)
end end
function make_cage(pos,width) function make_cage(pos, rad)
if not xpanes then return end
local nodes = {} local nodes = {}
local nodes2 = {} local r = math.max(1, rad - 2)
local r = math.max(1,math.floor(width/2) - 2) for y = 0, rad * 2 do for xz = -r, r do
for x=-r,r do for y = 0,width do for z = -r,r do table.insert(nodes,vector.add(pos,vector.new(xz,y, r)))
if x == r or x == -r or z==r or z == -r then table.insert(nodes,vector.add(pos,vector.new(xz,y,-r)))
table.insert(nodes,vector.add(pos,vector.new(x,y,z))) table.insert(nodes,vector.add(pos,vector.new( r,y,xz)))
end table.insert(nodes,vector.add(pos,vector.new(-r,y,xz)))
end end end end end
if xpanes then minetest.bulk_swap_node(nodes, {name = "xpanes:bar_flat"} )
minetest.bulk_set_node(nodes,{ name="xpanes:bar_flat"} ) for _,p in pairs(nodes) do xpanes.update_pane(p) end
for _,p in pairs(nodes) do
xpanes.update_pane(p)
end
end
end end
local function get_points_on_circle(pos,r,n) local function get_points_on_circle(pos,r,n)
local rt = {} local rt, step = {}, 2 * math.pi / n
for i=1, n do 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)*step), 0, r * math.sin((i-1)*step)))
end end
return rt return rt
end end
mcl_structures.register_structure("end_spike",{ minetest.register_on_mods_loaded(function()
static_pos =get_points_on_circle(vector.offset(mcl_vars.mg_end_exit_portal_pos,0,-20,0),43,10), -- TODO: use LVM?
place_func = function(pos,def,pr) mcl_mapgen_core.register_generator("end structures", nil, function(minp, maxp, blockseed)
local d = pr:next(6,12) if maxp.y < mcl_vars.mg_end_min or minp.y > mcl_vars.mg_end_max then return end
local h = d * pr:next(4,6) -- end spawn obsidian platform
local p1 = vector.offset(pos, -d / 2, 0, -d / 2) local pos = mcl_vars.mg_end_platform_pos
local p2 = vector.offset(pos, d / 2, h + d, d / 2) if vector.in_area(pos, minp, maxp) then
minetest.emerge_area(p1, p2, function(blockpos, action, calls_remaining, param) local obby = minetest.find_nodes_in_area(vector.offset(pos,-2,0,-2),vector.offset(pos,2,0,2),{"air","mcl_end:end_stone"})
local air = minetest.find_nodes_in_area(vector.offset(pos,-2,1,-2),vector.offset(pos,2,3,2),{"air","mcl_end:end_stone"})
minetest.bulk_swap_node(obby,{name="mcl_core:obsidian"})
minetest.bulk_swap_node(air,{name="air"})
end
-- end exit portal and pillars
local pos = mcl_vars.mg_end_exit_portal_pos
if vector.in_area(pos, minp, maxp) then
local pr = PcgRandom(worldseed)
-- emerge pillars
local p1, p2 = vector.offset(pos, -43-6, -10, -43-6), vector.offset(pos, 43+6, 12*6-10, 43+6)
minetest.emerge_area(p1, p2, function(_, _, calls_remaining)
if calls_remaining ~= 0 then return end if calls_remaining ~= 0 then return end
local s = make_endspike(pos,d,h) for _, p in ipairs(get_points_on_circle(vector.offset(pos, 0, -10, 0), 43, 10)) do
minetest.set_node(vector.offset(s,0,1,0),{name="mcl_core:bedrock"}) local rad = pr:next(3,6)
minetest.add_entity(vector.offset(s,0,2,0),"mcl_end:crystal") local top = make_endspike(p, rad, rad * 2 * pr:next(4,6) - 10)
if pr:next(1,3) == 1 then minetest.swap_node(vector.offset(top, 0, 1, 0), {name = "mcl_core:bedrock"})
make_cage(vector.offset(s,0,1,0),d) minetest.add_entity(vector.offset(top, 0, 2, 0), "mcl_end:crystal")
if pr:next(1, 3) == 1 then make_cage(vector.offset(top, 0, 1, 0), rad) end
end end
end) end)
return true -- emerge end portal
end, local schematic = vl_structures.load_schematic(modpath.."/schematics/mcl_structures_end_exit_portal.mts")
}) vl_structures.place_schematic(pos, 0, schematic, "0", { name = "end portal", prepare = false,
after_place = function(pos,def,pr,pmin,pmax,size,rot)
-- spawn ender dragon
if minetest.settings:get_bool("only_peaceful_mobs", false) then return end
minetest.bulk_swap_node(minetest.find_nodes_in_area(pmin, pmax, {"mcl_portals:portal_end"}), { name="air" })
local obj = minetest.add_entity(vector.offset(pos, 3, 11, 3), "mobs_mc:enderdragon")
if obj then
local dragon_entity = obj:get_luaentity()
dragon_entity._portal_pos = pos
dragon_entity._initial = true
else
minetest.log("error", "[mcl_mapgen_core] ERROR! Ender dragon doesn't want to spawn")
end
end}, pr)
end
end, 100)
end)

View File

@ -0,0 +1,24 @@
local modname = minetest.get_current_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 = false,
chunk_probability = 15, -- was 25, FIXME: needs rebalancing
y_offset = function(pr) return pr:next(-32,-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
},
})

View File

@ -14,11 +14,11 @@ local function set_node_no_bedrock(pos, node)
return minetest.set_node(pos,node) return minetest.set_node(pos,node)
end end
local function makegeode(pos,def,pr) local function makegeode(pos, _, pr)
local size = pr:next(5,7) local size = pr:next(5,7)
local p1 = vector.offset(pos,-size,-size,-size) local p1 = vector.offset(pos,-size,-size,-size)
local p2 = vector.offset(pos,size,size,size) local p2 = vector.offset(pos,size,size,size)
minetest.emerge_area(p1, p2, function(blockpos, action, calls_remaining, param) minetest.emerge_area(p1, p2, function(_, _, calls_remaining)
if calls_remaining ~= 0 then return end if calls_remaining ~= 0 then return end
local calcite = {} local calcite = {}
local nn = minetest.find_nodes_in_area(p1,p2,{"group:material_stone","group:dirt","mcl_core:gravel"}) local nn = minetest.find_nodes_in_area(p1,p2,{"group:material_stone","group:dirt","mcl_core:gravel"})
@ -37,9 +37,9 @@ local function makegeode(pos,def,pr)
set_node_no_bedrock(nn[i],{name="mcl_amethyst:amethyst_block"}) set_node_no_bedrock(nn[i],{name="mcl_amethyst:amethyst_block"})
end end
for k,v in pairs(minetest.find_nodes_in_area(p1,p2,{"mcl_amethyst:amethyst_block"})) do for _, v in pairs(minetest.find_nodes_in_area(p1,p2,{"mcl_amethyst:amethyst_block"})) do
local all_amethyst = true local all_amethyst = true
for kk,vv in pairs(adjacents) do for _, vv in pairs(adjacents) do
local pp = vector.add(v,vv) local pp = vector.add(v,vv)
local an = minetest.get_node(pp) local an = minetest.get_node(pp)
if an.name ~= "mcl_amethyst:amethyst_block" then if an.name ~= "mcl_amethyst:amethyst_block" then
@ -58,13 +58,13 @@ local function makegeode(pos,def,pr)
if all_amethyst then set_node_no_bedrock(v,{name="air"}) end if all_amethyst then set_node_no_bedrock(v,{name="air"}) end
end end
for _,v in pairs(calcite) do for _, v in pairs(calcite) do
for _,vv in pairs(minetest.find_nodes_in_area(vector.offset(v,-1,-1,-1),vector.offset(v,1,1,1),{"group:material_stone"})) do for _, vv in pairs(minetest.find_nodes_in_area(vector.offset(v,-1,-1,-1),vector.offset(v,1,1,1),{"group:material_stone"})) do
set_node_no_bedrock(vv,{name="mcl_blackstone:basalt_smooth"}) set_node_no_bedrock(vv,{name="mcl_blackstone:basalt_smooth"})
end end
end end
for k,v in pairs(minetest.find_nodes_in_area_under_air(p1,p2,{"mcl_amethyst:amethyst_block","mcl_amethyst:budding_amethyst_block"})) do for _, v in pairs(minetest.find_nodes_in_area_under_air(p1,p2,{"mcl_amethyst:amethyst_block","mcl_amethyst:budding_amethyst_block"})) do
local r = pr:next(1,50) local r = pr:next(1,50)
if r < 10 then if r < 10 then
set_node_no_bedrock(vector.offset(v,0,1,0),{name="mcl_amethyst:amethyst_cluster",param2=1}) set_node_no_bedrock(vector.offset(v,0,1,0),{name="mcl_amethyst:amethyst_cluster",param2=1})
@ -75,7 +75,7 @@ local function makegeode(pos,def,pr)
return true return true
end end
mcl_structures.register_structure("geode",{ vl_structures.register_structure("geode",{
place_on = {"group:material_stone"}, place_on = {"group:material_stone"},
noise_params = { noise_params = {
offset = 0, offset = 0,

View File

@ -1,24 +1,11 @@
local modname = minetest.get_current_modname() local modname = minetest.get_current_modname()
local S = minetest.get_translator(modname)
local modpath = minetest.get_modpath(modname) local modpath = minetest.get_modpath(modname)
local S = minetest.get_translator(modname)
function mcl_structures.generate_igloo_top(pos, pr)
-- Furnace does ot work atm because apparently meta is not set. Need a bit of help with fixing this for furnaces, bookshelves, and brewing stands.
local newpos = {x=pos.x,y=pos.y-2,z=pos.z}
local path = modpath.."/schematics/mcl_structures_igloo_top.mts"
local rotation = tostring(pr:next(0,3)*90)
return mcl_structures.place_schematic(newpos, path, rotation, nil, true, nil, function()
local p1 = vector.offset(pos,-5,-5,-5)
local p2 = vector.offset(pos,5,5,5)
mcl_structures.construct_nodes(p1,p2,{"mcl_furnaces:furnace","mcl_books:bookshelf"})
end), rotation
end
local function spawn_mobs(p1,p2,vi,zv) local function spawn_mobs(p1,p2,vi,zv)
local mc = minetest.find_nodes_in_area_under_air(p1,p2,{"mcl_core:stonebrickmossy"}) local mc = minetest.find_nodes_in_area_under_air(p1,p2,{"mcl_core:stonebrickmossy"})
if #mc == 2 then if #mc == 2 then
local vp = mc[1] local vp, zp = mc[1], mc[2]
local zp = mc[2]
if not vi and zv and zv:get_pos() and vector.distance(mc[1],zv:get_pos()) < 2 then if not vi and zv and zv:get_pos() and vector.distance(mc[1],zv:get_pos()) < 2 then
vp = mc[2] vp = mc[2]
elseif not zv and vi and vi:get_pos() and vector.distance(mc[2],vi:get_pos()) < 2 then elseif not zv and vi and vi:get_pos() and vector.distance(mc[2],vi:get_pos()) < 2 then
@ -26,134 +13,102 @@ local function spawn_mobs(p1,p2,vi,zv)
elseif zv and vi then elseif zv and vi then
return return
end end
vi = minetest.add_entity(vector.offset(mc[1],0,1,0),"mobs_mc:villager") vi = minetest.add_entity(vector.offset(vp,0,1,0),"mobs_mc:villager")
zv = minetest.add_entity(vector.offset(mc[2],0,1,0),"mobs_mc:villager_zombie") zv = minetest.add_entity(vector.offset(zp,0,1,0),"mobs_mc:villager_zombie")
minetest.after(1,spawn_mobs,p1,p2,vi,zv) if vi and vi:get_pos() and zv and zv:get_pos() then
minetest.after(1,spawn_mobs,p1,p2,vi,zv)
end
end end
end end
function mcl_structures.generate_igloo_basement(pos, orientation, loot, pr) local function igloo_callback(cpos,def,pr,p1,p2,size,rotation)
-- TODO: Add monster eggs vl_structures.construct_nodes(p1, p2, {"mcl_furnaces:furnace","mcl_books:bookshelf"})
local path = modpath.."/schematics/mcl_structures_igloo_basement.mts"
mcl_structures.place_schematic(pos, path, orientation, nil, true, nil, function()
local p1 = vector.offset(pos,-5,-5,-5)
local p2 = vector.offset(pos,5,5,5)
mcl_structures.fill_chests(p1,p2,loot,pr)
mcl_structures.construct_nodes(p1,p2,{"mcl_brewing:stand_000","mcl_books:bookshelf"})
spawn_mobs(p1,p2)
end, pr)
end
function mcl_structures.generate_igloo(pos, def, pr)
-- Place igloo
local success, rotation = mcl_structures.generate_igloo_top(pos, pr)
-- Place igloo basement with 50% chance -- Place igloo basement with 50% chance
local r = pr:next(1,2) if pr:next(1,2) == 1 then return end
if r == 1 then local pos = p1 -- we use top left as reference
-- Select basement depth -- Select basement depth
local dim = mcl_worlds.pos_to_dimension(pos) local maxdepth = pos.y - (mcl_vars.mg_lava_overworld_max + 10)
--local buffer = pos.y - (mcl_vars.mg_lava_overworld_max + 10) if maxdepth <= 9 then return true end
local buffer local depth = pr:next(9, maxdepth)
if dim == "nether" then -- trapdoor position and orientation
buffer = pos.y - (mcl_vars.mg_lava_nether_max + 10) local tpos, dir, tdir
elseif dim == "end" then if rotation == "0" then
buffer = pos.y - (mcl_vars.mg_end_min + 1) dir = vector.new(-1, 0, 0)
elseif dim == "overworld" then tdir = vector.new(1, 0, 0)
buffer = pos.y - (mcl_vars.mg_lava_overworld_max + 10) tpos = vector.new(pos.x+7, pos.y, pos.z+3)
else elseif rotation == "90" then
return success dir = vector.new(0, 0, -1)
end tdir = vector.new(0, 0, -1)
if buffer <= 19 then tpos = vector.new(pos.x+3, pos.y, pos.z+1)
return success elseif rotation == "180" then
end dir = vector.new(1, 0, 0)
local depth = pr:next(19, buffer) tdir = vector.new(-1, 0, 0)
local bpos = {x=pos.x, y=pos.y-depth, z=pos.z} tpos = vector.new(pos.x+1, pos.y, pos.z+3)
-- trapdoor position elseif rotation == "270" then
local tpos dir = vector.new(0, 0, 1)
local dir, tdir tdir = vector.new(0, 0, 1)
if rotation == "0" then tpos = vector.new(pos.x+3, pos.y, pos.z+7)
dir = {x=-1, y=0, z=0} else
tdir = {x=1, y=0, z=0} minetest.log("bad rotation: "..tostring(rotation))
tpos = {x=pos.x+7, y=pos.y-2, z=pos.z+3} return false
elseif rotation == "90" then
dir = {x=0, y=0, z=-1}
tdir = {x=0, y=0, z=-1}
tpos = {x=pos.x+3, y=pos.y-2, z=pos.z+1}
elseif rotation == "180" then
dir = {x=1, y=0, z=0}
tdir = {x=-1, y=0, z=0}
tpos = {x=pos.x+1, y=pos.y-2, z=pos.z+3}
elseif rotation == "270" then
dir = {x=0, y=0, z=1}
tdir = {x=0, y=0, z=1}
tpos = {x=pos.x+3, y=pos.y-2, z=pos.z+7}
else
return success
end
local function set_brick(pos)
local c = pr:next(1, 3) -- cracked chance
local m = pr:next(1, 10) -- chance for monster egg
local brick
if m == 1 then
if c == 1 then
brick = "mcl_monster_eggs:monster_egg_stonebrickcracked"
else
brick = "mcl_monster_eggs:monster_egg_stonebrick"
end
else
if c == 1 then
brick = "mcl_core:stonebrickcracked"
else
brick = "mcl_core:stonebrick"
end
end
minetest.set_node(pos, {name=brick})
end
local ladder_param2 = minetest.dir_to_wallmounted(tdir)
local real_depth = 0
-- Check how deep we can actuall dig
for y=1, depth-5 do
real_depth = real_depth + 1
local node = minetest.get_node({x=tpos.x,y=tpos.y-y,z=tpos.z})
local def = 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
end
if real_depth <= 6 then
return success
end
-- Generate ladder to basement
for y=1, real_depth-1 do
set_brick({x=tpos.x-1,y=tpos.y-y,z=tpos.z })
set_brick({x=tpos.x+1,y=tpos.y-y,z=tpos.z })
set_brick({x=tpos.x ,y=tpos.y-y,z=tpos.z-1})
set_brick({x=tpos.x ,y=tpos.y-y,z=tpos.z+1})
minetest.set_node({x=tpos.x,y=tpos.y-y,z=tpos.z}, {name="mcl_core:ladder", param2=ladder_param2})
end
-- Place basement
mcl_structures.generate_igloo_basement(bpos, rotation, def.loot, pr)
-- Place hidden trapdoor
minetest.after(5, function(tpos, dir)
minetest.set_node(tpos, {name="mcl_doors:trapdoor", param2=20+minetest.dir_to_facedir(dir)}) -- TODO: more reliable param2
end, tpos, dir)
end end
return success local function set_brick(pos)
local c = pr:next(1, 3) -- cracked chance
local m = pr:next(1, 10) -- chance for monster egg
local brick
if m == 1 then
brick = (c == 1 and "mcl_monster_eggs:monster_egg_stonebrickcracked") or "mcl_monster_eggs:monster_egg_stonebrick"
else
brick = (c == 1 and "mcl_core:stonebrickcracked") or "mcl_core:stonebrick"
end
minetest.set_node(pos, {name=brick})
end
local real_depth = 2
-- Check how deep we can actually dig
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, 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 break end
end
local bpos = vector.new(cpos.x, pos.y-real_depth+1, cpos.z)
if real_depth <= 6 then
minetest.log("action", "Ground not deep enough for igloo basement: "..real_depth)
return false
end
local path = modpath.."/schematics/mcl_structures_igloo_basement.mts"
vl_structures.place_schematic(bpos, -1, path, rotation, {
force_placement = true,
prepare = { tolerance = -1, foundation = false, clear = false },
after_place = function(_, _, pr, p1, p2)
-- Generate ladder to basement
local ladder = {name="mcl_core:ladder", param2=minetest.dir_to_wallmounted(tdir)}
-- TODO: use voxelmanip?
minetest.set_node(tpos, {name="mcl_doors:trapdoor", param2=20+minetest.dir_to_facedir(dir)}) -- TODO: more reliable param2
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
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
}, pr)
end 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"}, place_on = {"mcl_core:snowblock","mcl_core:snow","group:grass_block_snow"},
fill_ratio = 0.01, chunk_probability = 15,
sidelen = 16, prepare = { tolerance = 3, padding = 1, corners = 1, foundation = -6, clear_top = -1 },
chunk_probability = 250,
solid_ground = true,
make_foundation = true,
y_max = mcl_vars.mg_overworld_max, y_max = mcl_vars.mg_overworld_max,
y_min = 0, y_min = 0,
y_offset = 0, y_offset = -1,
biomes = { "ColdTaiga", "IcePlainsSpikes", "IcePlains" }, biomes = { "ColdTaiga", "IcePlainsSpikes", "IcePlains" },
place_func = mcl_structures.generate_igloo, after_place = igloo_callback,
loot = { loot = {
["mcl_chests:chest_small"] = {{ ["mcl_chests:chest_small"] = {{
stacks_min = 1, stacks_min = 1,

View File

@ -1,135 +1,68 @@
local modname = minetest.get_current_modname() local modname = minetest.get_current_modname()
local S = minetest.get_translator(modname) local S = minetest.get_translator(modname)
local modpath = minetest.get_modpath(modname) local modpath = minetest.get_modpath(modname)
mcl_structures = {} mcl_structures = {}
dofile(modpath.."/api.lua") -- some legacy API adapters
dofile(modpath.."/shipwrecks.lua") 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?
mcl_structures.place_schematic = function(pos, schematic, rotation, replacements, force_placement, flags, after_placement_callback, pr, callback_param)
vl_structures.place_schematic(pos, yoffset, schematic, rotation, {
replacements = replacements,
force_placement = force_placement,
flags = flags,
after_place = after_placement_callback,
callback_param = callback_param
}, pr)
end
mcl_structures.place_structure = vl_structures.place_structure -- still compatible
mcl_structures.register_structure = function(name, def, nospawn)
-- nospawn: ignored, just pass no place_on!
if not def.solid_ground then def.prepare = def.prepare or {} end
vl_structures.register_structure(name, def)
end
dofile(modpath.."/campsite.lua")
dofile(modpath.."/desert_temple.lua") 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.."/jungle_temple.lua")
dofile(modpath.."/ocean_ruins.lua") dofile(modpath.."/ocean_ruins.lua")
dofile(modpath.."/witch_hut.lua") dofile(modpath.."/ocean_temple.lua")
dofile(modpath.."/igloo.lua")
dofile(modpath.."/woodland_mansion.lua")
dofile(modpath.."/ruined_portal.lua")
dofile(modpath.."/geode.lua")
dofile(modpath.."/pillager_outpost.lua") dofile(modpath.."/pillager_outpost.lua")
dofile(modpath.."/end_spawn.lua") dofile(modpath.."/ruined_portal.lua")
dofile(modpath.."/end_city.lua") dofile(modpath.."/shipwrecks.lua")
dofile(modpath.."/spider_cocoon.lua")
dofile(modpath.."/witch_hut.lua")
dofile(modpath.."/woodland_mansion.lua")
vl_structures.register_structure("boulder",{
mcl_structures.register_structure("desert_well",{ -- as they have no place_on, they will not be spawned by this mechanism. this is just for /spawnstruct
place_on = {"group:sand"},
fill_ratio = 0.01,
flags = "place_center_x, place_center_z",
not_near = { "desert_temple_new" },
solid_ground = true,
sidelen = 4,
chunk_probability = 600,
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"},
fill_ratio = 0.01,
flags = "place_center_x, place_center_z",
solid_ground = true,
sidelen = 13,
chunk_probability = 1000,
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",{
filenames = { filenames = {
-- small boulder 3x as likely
modpath.."/schematics/mcl_structures_boulder_small.mts", modpath.."/schematics/mcl_structures_boulder_small.mts",
modpath.."/schematics/mcl_structures_boulder_small.mts", modpath.."/schematics/mcl_structures_boulder_small.mts",
modpath.."/schematics/mcl_structures_boulder_small.mts", modpath.."/schematics/mcl_structures_boulder_small.mts",
modpath.."/schematics/mcl_structures_boulder.mts", modpath.."/schematics/mcl_structures_boulder.mts",
-- small boulder 3x as likely
}, },
},true) --is spawned as a normal decoration. this is just for /spawnstruct
mcl_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,
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 pr = PseudoRandom(pos.x+pos.y+pos.z)
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,math.random(),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 = "" vl_structures.register_structure("ice_spike_small",{
for n,_ in pairs(mcl_structures.registered_structures) do -- as they have no place_on, they will not be spawned by this mechanism. this is just for /spawnstruct
p = p .. " | "..n filenames = { modpath.."/schematics/mcl_structures_ice_spike_small.mts" },
end })
minetest.registered_chatcommands["spawnstruct"].params = minetest.registered_chatcommands["spawnstruct"].params .. p
end) vl_structures.register_structure("ice_spike_large",{
-- as they have no place_on, they will not be spawned by this mechanism. this is just for /spawnstruct
filenames = { modpath.."/schematics/mcl_structures_ice_spike_large.mts" },
})

View File

@ -2,18 +2,14 @@ local modname = minetest.get_current_modname()
local S = minetest.get_translator(modname) local S = minetest.get_translator(modname)
local modpath = minetest.get_modpath(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"}, place_on = {"group:grass_block","group:dirt","mcl_core:dirt_with_grass"},
fill_ratio = 0.01,
flags = "place_center_x, place_center_z", flags = "place_center_x, place_center_z",
solid_ground = true,
make_foundation = true,
y_offset = function(pr) return pr:next(-3,0) -5 end, y_offset = function(pr) return pr:next(-3,0) -5 end,
chunk_probability = 200, chunk_probability = 5,
y_max = mcl_vars.mg_overworld_max, y_max = mcl_vars.mg_overworld_max,
y_min = 1, y_min = 1,
biomes = { "Jungle" }, biomes = { "Jungle" },
sidelen = 18,
filenames = { filenames = {
modpath.."/schematics/mcl_structures_jungle_temple.mts", modpath.."/schematics/mcl_structures_jungle_temple.mts",
modpath.."/schematics/mcl_structures_jungle_temple_nice.mts", modpath.."/schematics/mcl_structures_jungle_temple_nice.mts",
@ -26,7 +22,7 @@ mcl_structures.register_structure("jungle_temple",{
{ itemstring = "mcl_mobitems:bone", weight = 20, amount_min = 4, amount_max=6 }, { itemstring = "mcl_mobitems:bone", weight = 20, amount_min = 4, amount_max=6 },
{ itemstring = "mcl_mobitems:rotten_flesh", weight = 16, amount_min = 3, amount_max=7 }, { itemstring = "mcl_mobitems:rotten_flesh", weight = 16, amount_min = 3, amount_max=7 },
{ itemstring = "mcl_core:gold_ingot", weight = 15, amount_min = 2, amount_max = 7 }, { 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 = 1, amount_max = 5 }, { itemstring = "mcl_core:iron_ingot", weight = 15, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_core:diamond", weight = 3, amount_min = 1, amount_max = 3 }, { itemstring = "mcl_core:diamond", weight = 3, amount_min = 1, amount_max = 3 },
{ itemstring = "mcl_mobitems:saddle", weight = 3, }, { itemstring = "mcl_mobitems:saddle", weight = 3, },

View File

@ -1,4 +1,4 @@
name = mcl_structures name = mcl_structures
author = Wuzzy, cora author = Wuzzy, cora, kno10
description = Structure placement for MCL2 description = Structure placement for MCL2
depends = mcl_init, mcl_loot depends = mcl_init, mcl_util, mcl_loot, vl_terraforming, vl_structures

View File

@ -1,6 +1,6 @@
local modname = minetest.get_current_modname() local modname = minetest.get_current_modname()
local S = minetest.get_translator(modname)
local modpath = minetest.get_modpath(modname) local modpath = minetest.get_modpath(modname)
local water_level = minetest.get_mapgen_setting("water_level")
local cold_oceans = { local cold_oceans = {
"RoofedForest_ocean", "RoofedForest_ocean",
@ -72,18 +72,15 @@ local warm_oceans = {
local cold = { local cold = {
place_on = {"group:sand","mcl_core:gravel","mcl_core:dirt","mcl_core:clay","group:material_stone"}, place_on = {"group:sand","mcl_core:gravel","mcl_core:dirt","mcl_core:clay","group:material_stone"},
spawn_by = {"mcl_core:water_source"}, spawn_by = {"group:water"},
num_spawn_by = 2, num_spawn_by = 2,
fill_ratio = 0.01, chunk_probability = 10, -- todo: 15?
flags = "place_center_x, place_center_z, force_placement",
solid_ground = true,
make_foundation = true,
y_offset = -1,
y_min = mcl_vars.mg_overworld_min,
y_max = -2,
biomes = cold_oceans, biomes = cold_oceans,
chunk_probability = 400, y_min = mcl_vars.mg_overworld_min,
sidelen = 20, y_max = water_level - 6,
y_offset = -1,
flags = "place_center_x, place_center_z, force_placement",
prepare = { foundation = -2, clear = false, mode="water" },
filenames = { filenames = {
modpath.."/schematics/mcl_structures_ocean_ruins_cold_1.mts", modpath.."/schematics/mcl_structures_ocean_ruins_cold_1.mts",
modpath.."/schematics/mcl_structures_ocean_ruins_cold_2.mts", modpath.."/schematics/mcl_structures_ocean_ruins_cold_2.mts",
@ -128,5 +125,5 @@ warm.filenames = {
modpath.."/schematics/mcl_structures_ocean_ruins_warm_4.mts", modpath.."/schematics/mcl_structures_ocean_ruins_warm_4.mts",
} }
mcl_structures.register_structure("cold_ocean_ruins",cold) vl_structures.register_structure("cold_ocean_ruins",cold)
mcl_structures.register_structure("warm_ocean_ruins",warm) vl_structures.register_structure("warm_ocean_ruins",warm)

View File

@ -0,0 +1,157 @@
local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(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",
},
flags = "force_placement",
force_placement = true,
prepare = { tolerance = 8, clear = false, foundation = 3, mode="water" },
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 = -1, --function(pr) return pr:next(-2,-1) end, -- fewer mobs if buried in sand
after_place = function(p, _, pr, p1, p2)
vl_structures.spawn_mobs("mobs_mc:guardian",spawnon,p1,p2,pr,5,true)
vl_structures.spawn_mobs("mobs_mc:guardian_elder",spawnon,p1,p2,pr,1,true)
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,
})

View File

@ -1,19 +1,14 @@
local modname = minetest.get_current_modname() local modname = minetest.get_current_modname()
local S = minetest.get_translator(modname)
local modpath = minetest.get_modpath(modname) local modpath = minetest.get_modpath(modname)
local peaceful = minetest.settings:get_bool("only_peaceful_mobs", false)
local spawnon = {"mcl_core:stripped_oak","mcl_stairs:slab_birchwood_top"} 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"}, place_on = {"group:grass_block","group:dirt","mcl_core:dirt_with_grass","group:sand"},
fill_ratio = 0.01,
flags = "place_center_x, place_center_z", flags = "place_center_x, place_center_z",
solid_ground = true, prepare = { padding = 3, corners = 4, foundation = -8, clear = true },
make_foundation = true,
sidelen = 32,
y_offset = 0, y_offset = 0,
chunk_probability = 600, chunk_probability = 15,
y_max = mcl_vars.mg_overworld_max, y_max = mcl_vars.mg_overworld_max,
y_min = 1, y_min = 1,
biomes = { "Desert", "Plains", "Savanna", "IcePlains", "Taiga" }, biomes = { "Desert", "Plains", "Savanna", "IcePlains", "Taiga" },
@ -62,22 +57,15 @@ mcl_structures.register_structure("pillager_outpost",{
} }
}} }}
}, },
after_place = function(p,def,pr) after_place = function(p,_,pr) -- fixme: use p1, p2 from the callback?
local p1 = vector.offset(p,-9,0,-9) local p1, p2 = vector.offset(p,-9,0,-9), vector.offset(p,9,32,9)
local p2 = vector.offset(p,9,32,9) vl_structures.spawn_mobs("mobs_mc:pillager",spawnon,p1,p2,pr,5)
mcl_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)
mcl_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)
mcl_structures.spawn_mobs("mobs_mc:iron_golem",{"mesecons_button:button_stone_off"},p1,p2,pr,1)
for _,n in pairs(minetest.find_nodes_in_area(p1,p2,{"group:wall"})) do
local def = minetest.registered_nodes[minetest.get_node(n).name:gsub("_%d+$","")]
if def and def.on_construct then
def.on_construct(n)
end
end
end end
}) })
mcl_structures.register_structure_spawn({ vl_structures.register_structure_spawn({
name = "mobs_mc:pillager", name = "mobs_mc:pillager",
y_min = mcl_vars.mg_overworld_min, y_min = mcl_vars.mg_overworld_min,
y_max = mcl_vars.mg_overworld_max, y_max = mcl_vars.mg_overworld_max,

View File

@ -4,7 +4,7 @@ local modpath = minetest.get_modpath(modname)
local function get_replacements(b,c,pr) local function get_replacements(b,c,pr)
local r = {} local r = {}
if not b then return r end if not b then return r end
for k,v in pairs(b) do for _, v in pairs(b) do
if pr:next(1,100) < c then table.insert(r,v) end if pr:next(1,100) < c then table.insert(r,v) end
end end
return r return r
@ -12,14 +12,11 @@ end
local def = { 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"}, place_on = {"group:grass_block","group:dirt","mcl_core:dirt_with_grass","group:grass_block","group:sand","group:grass_block_snow","mcl_core:snow"},
fill_ratio = 0.006,
flags = "place_center_x, place_center_z, all_floors", flags = "place_center_x, place_center_z, all_floors",
solid_ground = true, prepare = { padding = 0, corners = 3, tolerance = 15, foundation = true, clear = true, clear_top = 0 },
make_foundation = true, chunk_probability = 20,
chunk_probability = 800,
y_max = mcl_vars.mg_overworld_max, y_max = mcl_vars.mg_overworld_max,
y_min = 1, y_min = 1,
sidelen = 10,
y_offset = -5, y_offset = -5,
filenames = { filenames = {
modpath.."/schematics/mcl_structures_ruined_portal_1.mts", modpath.."/schematics/mcl_structures_ruined_portal_1.mts",
@ -27,25 +24,23 @@ local def = {
modpath.."/schematics/mcl_structures_ruined_portal_3.mts", modpath.."/schematics/mcl_structures_ruined_portal_3.mts",
modpath.."/schematics/mcl_structures_ruined_portal_4.mts", modpath.."/schematics/mcl_structures_ruined_portal_4.mts",
modpath.."/schematics/mcl_structures_ruined_portal_5.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", modpath.."/schematics/mcl_structures_ruined_portal_99.mts",
}, },
after_place = function(pos,def,pr) after_place = function(pos,def,pr,p1,p2)
local hl = def.sidelen / 2
local p1 = vector.offset(pos,-hl,-hl,-hl)
local p2 = vector.offset(pos,hl,hl,hl)
local gold = minetest.find_nodes_in_area(p1,p2,{"mcl_core:goldblock"}) 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 lava = minetest.find_nodes_in_area(p1,p2,{"mcl_core:lava_source"})
local rack = minetest.find_nodes_in_area(p1,p2,{"mcl_nether:netherrack"}) local rack = minetest.find_nodes_in_area(p1,p2,{"mcl_nether:netherrack"})
local brick = minetest.find_nodes_in_area(p1,p2,{"mcl_core:stonebrick"}) local brick = minetest.find_nodes_in_area(p1,p2,{"mcl_core:stonebrick"})
local obby = minetest.find_nodes_in_area(p1,p2,{"mcl_core:obsidian"}) local obby = minetest.find_nodes_in_area(p1,p2,{"mcl_core:obsidian"})
minetest.bulk_set_node(get_replacements(gold,30,pr),{name="air"}) minetest.bulk_swap_node(get_replacements(gold,30,pr),{name="air"})
minetest.bulk_set_node(get_replacements(lava,20,pr),{name="mcl_nether:magma"}) minetest.bulk_swap_node(get_replacements(lava,20,pr),{name="mcl_nether:magma"})
minetest.bulk_set_node(get_replacements(rack,7,pr),{name="mcl_nether:magma"}) minetest.bulk_swap_node(get_replacements(rack,7,pr),{name="mcl_nether:magma"})
minetest.bulk_set_node(get_replacements(obby,30,pr),{name="mcl_core:crying_obsidian"}) minetest.bulk_swap_node(get_replacements(obby,30,pr),{name="mcl_core:crying_obsidian"})
minetest.bulk_set_node(get_replacements(obby,10,pr),{name="air"}) minetest.bulk_swap_node(get_replacements(obby,10,pr),{name="air"})
minetest.bulk_set_node(get_replacements(brick,50,pr),{name="mcl_core:stonebrickcracked"}) minetest.bulk_swap_node(get_replacements(brick,50,pr),{name="mcl_core:stonebrickcracked"})
brick = minetest.find_nodes_in_area(p1,p2,{"mcl_core:stonebrick"}) brick = minetest.find_nodes_in_area(p1,p2,{"mcl_core:stonebrick"})
minetest.bulk_set_node(get_replacements(brick,50,pr),{name="mcl_core:stonebrickmossy"}) minetest.bulk_swap_node(get_replacements(brick,50,pr),{name="mcl_core:stonebrickmossy"})
end, end,
loot = { loot = {
["mcl_chests:chest_small" ] ={{ ["mcl_chests:chest_small" ] ={{
@ -102,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) local ndef = table.copy(def)
ndef.y_min=mcl_vars.mg_lava_nether_max +10 ndef.y_min=mcl_vars.mg_lava_nether_max +10
ndef.y_max=mcl_vars.mg_nether_max - 15 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"}, 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.

Binary file not shown.

View File

@ -1,19 +1,6 @@
local modname = minetest.get_current_modname() local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(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 water_level = minetest.get_mapgen_setting("water_level")
local pr = PseudoRandom(seed)
--schematics by chmodsayshello
local schems = {
modpath.."/schematics/mcl_structures_shipwreck_full_damaged.mts",
modpath.."/schematics/mcl_structures_shipwreck_full_normal.mts",
modpath.."/schematics/mcl_structures_shipwreck_full_back_damaged.mts",
modpath.."/schematics/mcl_structures_shipwreck_half_front.mts",
modpath.."/schematics/mcl_structures_shipwreck_half_back.mts",
}
local ocean_biomes = { local ocean_biomes = {
"RoofedForest_ocean", "RoofedForest_ocean",
@ -78,42 +65,27 @@ local ocean_biomes = {
"JungleM_ocean" "JungleM_ocean"
} }
local beach_biomes = { -- FIXME: integrate treasure maps from MCLA
"FlowerForest_beach",
"Forest_beach",
"StoneBeach",
"ColdTaiga_beach_water",
"Taiga_beach",
"Savanna_beach",
"Plains_beach",
"ExtremeHills_beach",
"ColdTaiga_beach",
"Swampland_shore",
"MushroomIslandShore",
"JungleM_shore",
"Jungle_shore"
}
mcl_structures.register_structure("shipwreck",{ vl_structures.register_structure("shipwreck",{
place_on = {"group:sand","mcl_core:gravel"}, place_on = {"group:sand","mcl_core:gravel"},
spawn_by = {"group:water"}, spawn_by = {"group:water"},
num_spawn_by = 4, num_spawn_by = 4,
noise_params = { chunk_probability = 15,
offset = 0,
scale = 0.000022,
spread = {x = 250, y = 250, z = 250},
seed = 3,
octaves = 3,
persist = 0.001,
flags = "absvalue",
},
sidelen = 16,
flags = "force_placement",
biomes = ocean_biomes, biomes = ocean_biomes,
y_max = water_level-4,
y_min = mcl_vars.mg_overworld_min, y_min = mcl_vars.mg_overworld_min,
filenames = schems, y_max = water_level-4,
y_offset = function(pr) return pr:next(-4,-2) end, y_offset = function(pr) return pr:next(-3,-1) end,
flags = "place_center_x, place_center_z, force_placement",
prepare = { tolerance = -1, clear = false, foundation = false, mode = "water" },
filenames = {
--schematics by chmodsayshello
modpath.."/schematics/mcl_structures_shipwreck_full_damaged.mts",
modpath.."/schematics/mcl_structures_shipwreck_full_normal.mts",
modpath.."/schematics/mcl_structures_shipwreck_full_back_damaged.mts",
modpath.."/schematics/mcl_structures_shipwreck_half_front.mts",
modpath.."/schematics/mcl_structures_shipwreck_half_back.mts",
},
loot = { loot = {
["mcl_chests:chest_small"] = { ["mcl_chests:chest_small"] = {
{ {
@ -130,13 +102,13 @@ mcl_structures.register_structure("shipwreck",{
{ itemstring = "mcl_core:coal_lump", weight = 6, amount_min = 2, amount_max = 8 }, { itemstring = "mcl_core:coal_lump", weight = 6, amount_min = 2, amount_max = 8 },
{ itemstring = "mcl_mobitems:rotten_flesh", weight = 5, amount_min = 5, amount_max = 24 }, { itemstring = "mcl_mobitems:rotten_flesh", weight = 5, amount_min = 5, amount_max = 24 },
{ itemstring = "mcl_farming:potato_item", weight = 3, amount_min = 1, amount_max = 5 }, { itemstring = "mcl_farming:potato_item", weight = 3, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_armor:helmet_leather_enchanted", weight = 3, func = function(stack, pr) { itemstring = "mcl_armor:helmet_leather_enchanted", weight = 3, func = function(stack, _)
mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}) end }, mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}) end },
{ itemstring = "mcl_armor:chestplate_leather_enchanted", weight = 3, func = function(stack, pr) { itemstring = "mcl_armor:chestplate_leather_enchanted", weight = 3, func = function(stack, _)
mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}) end }, mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}) end },
{ itemstring = "mcl_armor:leggings_leather_enchanted", weight = 3, func = function(stack, pr) { itemstring = "mcl_armor:leggings_leather_enchanted", weight = 3, func = function(stack, _)
mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}) end }, mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}) end },
{ itemstring = "mcl_armor:boots_leather_enchanted", weight = 3, func = function(stack, pr) { itemstring = "mcl_armor:boots_leather_enchanted", weight = 3, func = function(stack, _)
mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}) end }, mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}) end },
{ itemstring = "mcl_bamboo:bamboo", weight = 2, amount_min = 1, amount_max = 3 }, { itemstring = "mcl_bamboo:bamboo", weight = 2, amount_min = 1, amount_max = 3 },
{ itemstring = "mcl_farming:pumpkin", weight = 2, amount_min = 1, amount_max = 3 }, { itemstring = "mcl_farming:pumpkin", weight = 2, amount_min = 1, amount_max = 3 },
@ -174,93 +146,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,
})

View File

@ -0,0 +1,94 @@
local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)
vl_structures.register_structure("cocoon",{
place_on = {"group:material_stone"},
flags = "place_center_x, place_center_z, all_floors",
chunk_probability = 20,
y_max = -10,
y_min = mcl_vars.mg_overworld_min,
y_offset = 2,
spawn_by = "air",
check_offset = 1,
num_spawn_by = 6,
force_placement = false,
prepare = { foundation = false, clear = false, clear_top = 0, padding = -1, corners = 1 }, -- TODO: make clear/foundation not use grass
filenames = {
modpath.."/schematics/cocoon_1.mts"
},
after_place = function(p,def,pr,p1,p2)
if mcl_mobspawners then
local spawner = minetest.find_nodes_in_area(p1,p2,{"mcl_mobspawners:spawner"})
if #spawner > 0 then
mcl_mobspawners.setup_spawner(spawner[1], "mobs_mc:cave_spider", 0, 7, 4, 15, -3)
end
end
-- p2.y is the top slice only, not a typo, we look for the rope
local cs = minetest.find_nodes_in_area(vector.new(p1.x,p2.y,p1.z), p2, "mcl_wool:white")
local rope = {}
-- TODO: port to VoxelManip?
for _,c in pairs(cs) do
while true do
c = vector.offset(c,0,1,0)
local name = minetest.get_node(c).name
if name == "ignore" then break end
if name ~= "air" then break end
table.insert(rope,c)
end
end
minetest.bulk_swap_node(rope, {name = "mcl_wool:white", param2 = 2})
-- remove some of the spiderwebs to add variation
local ws = minetest.find_nodes_in_area(p1, p2, "mcl_core:cobweb")
local clear = {}
for i = 1,math.floor(#ws/4) do
if #ws == 0 then break end
local idx = pr:next(1,#ws)
table.insert(clear, ws[idx])
table.remove(ws, idx)
end
minetest.bulk_swap_node(clear, {name = "air"})
end,
loot = {
["mcl_chests:chest_small"] = {
{
stacks_min = 2,
stacks_max = 4,
items = {
{ itemstring = "mcl_mobitems:bone", weight = 10, amount_min = 2, amount_max = 4 },
{ itemstring = "mcl_farming:potato_item_poison", weight = 7, amount_min = 2, amount_max = 6 },
{ itemstring = "mcl_mobitems:rotten_flesh", weight = 5, amount_min = 5, amount_max = 24 },
{ itemstring = "mcl_farming:potato_item", weight = 3, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_tnt:tnt", weight = 1, amount_min = 1, amount_max = 2 },
}
},
{
stacks_min = 2,
stacks_max = 4,
items = {
{ itemstring = "mcl_core:iron_ingot", weight = 90, amount_min = 1, amount_max = 2 },
{ itemstring = "mcl_core:iron_nugget", weight = 50, amount_min = 1, amount_max = 10 },
{ itemstring = "mcl_core:emerald", weight = 40, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_core:lapis", weight = 20, amount_min = 1, amount_max = 10 },
{ itemstring = "mcl_core:gold_ingot", weight = 10, amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_core:gold_nugget", weight = 10, amount_min = 1, amount_max = 4 },
{ itemstring = "mcl_experience:bottle", weight = 5, amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_core:diamond", weight = 5, amount_min = 1, amount_max = 1 },
}
},{
stacks_min = 1,
stacks_max = 1,
items = {
--{ itemstring = "FIXME TREASURE MAP", weight = 8, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_core:paper", weight = 20, amount_min = 1, amount_max = 10 },
{ itemstring = "mcl_mobitems:bone", weight = 10, amount_min = 2, amount_max = 4 },
{ itemstring = "mcl_mobitems:rotten_flesh", weight = 5, amount_min = 3, amount_max = 8 },
{ itemstring = "mcl_books:book", weight = 5, amount_min = 1, amount_max = 1 },
{ 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 },
}
},
}
}
})

View File

@ -14,39 +14,46 @@ local function spawn_witch(p1,p2)
witch._home = c witch._home = c
witch.can_despawn = false witch.can_despawn = false
end end
local cat = minetest.add_entity(vector.offset(nn[math.random(#nn)],0,1,0),"mobs_mc:cat"):get_luaentity() local catobject = minetest.add_entity(vector.offset(nn[math.random(#nn)],0,1,0),"mobs_mc:cat")
cat.object:set_properties({textures = {"mobs_mc_cat_black.png"}}) if catobject and catobject:get_pos() then
cat.owner = "!witch!" --so it's not claimable by player local cat=catobject:get_luaentity()
cat._home = c cat.object:set_properties({textures = {"mobs_mc_cat_black.png"}})
cat.can_despawn = false cat.owner = "!witch!" --so it's not claimable by player
cat._home = c
cat.can_despawn = false
end
return return
end end
end end
local function hut_placement_callback(pos,def,pr) local function hut_placement_callback(pos,def,pr,p1,p2)
local hl = def.sidelen / 2 -- p1.y is the bottom slice only, not a typo, we look for the hut legs
local p1 = vector.offset(pos,-hl,-hl,-hl) local legs = minetest.find_nodes_in_area(p1,vector.new(p2.x,p1.y,p2.z), "mcl_core:tree")
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 tree = {} local tree = {}
-- TODO: port leg generation to VoxelManip?
for _,leg in pairs(legs) do 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) leg = vector.offset(leg,0,-1,0)
table.insert(tree,leg) table.insert(tree,leg)
end end
end end
minetest.bulk_set_node(tree, {name = "mcl_core:tree", param2 = 2}) minetest.bulk_swap_node(tree, {name = "mcl_core:tree", param2 = 2})
spawn_witch(p1,p2) spawn_witch(p1,p2)
end end
mcl_structures.register_structure("witch_hut",{ vl_structures.register_structure("witch_hut",{
place_on = {"mcl_core:water_source","mclx_core:river_water_source"}, place_on = {"mcl_core:water_source","group:sand","group:grass_block","group:dirt","mclx_core:river_water_source"},
fill_ratio = 0.01, spawn_by = {"mcl_core:water_source","mclx_core:river_water_source"},
flags = "place_center_x, place_center_z, liquid_surface, force_placement", check_offset = -1,
sidelen = 8, num_spawn_by = 3,
chunk_probability = 300, flags = "place_center_x, place_center_z, all_surfaces",
chunk_probability = 8,
prepare = { mode="under_air", tolerance=4, clear_bottom=3, padding=0, corners=1, foundation=false },
y_max = mcl_vars.mg_overworld_max, y_max = mcl_vars.mg_overworld_max,
y_min = -4, y_min = -5,
y_offset = 0, y_offset = 0,
biomes = { "Swampland", "Swampland_ocean", "Swampland_shore" }, biomes = { "Swampland", "Swampland_ocean", "Swampland_shore" },
filenames = { modpath.."/schematics/mcl_structures_witch_hut.mts" }, filenames = { modpath.."/schematics/mcl_structures_witch_hut.mts" },

View File

@ -1,32 +1,26 @@
local modname = minetest.get_current_modname() local modname = minetest.get_current_modname()
local S = minetest.get_translator(modname)
local modpath = minetest.get_modpath(modname) local modpath = minetest.get_modpath(modname)
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"} 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"}, place_on = {"group:grass_block","group:dirt","mcl_core:dirt_with_grass"},
fill_ratio = 0.01,
flags = "place_center_x, place_center_z", flags = "place_center_x, place_center_z",
solid_ground = true, prepare = { padding = 2, corners = 5, foundation = true, clear = true, clear_top = 2 },
make_foundation = true, force_placement = false,
chunk_probability = 800, chunk_probability = 20,
y_max = mcl_vars.mg_overworld_max, y_max = mcl_vars.mg_overworld_max,
y_min = 1, y_min = 1,
biomes = { "RoofedForest" }, biomes = { "RoofedForest" },
sidelen = 32,
filenames = { filenames = {
modpath.."/schematics/mcl_structures_woodland_cabin.mts", modpath.."/schematics/mcl_structures_woodland_cabin.mts",
modpath.."/schematics/mcl_structures_woodland_outpost.mts", modpath.."/schematics/mcl_structures_woodland_outpost.mts",
}, },
construct_nodes = {"mcl_barrels:barrel_closed","mcl_books:bookshelf"}, construct_nodes = {"mcl_barrels:barrel_closed","mcl_books:bookshelf"},
after_place = function(p,def,pr) after_place = function(p,def,pr,p1,p2)
local p1=vector.offset(p,-def.sidelen,-1,-def.sidelen) vl_structures.spawn_mobs("mobs_mc:vindicator",spawnon,p1,p2,pr,5)
local p2=vector.offset(p,def.sidelen,def.sidelen,def.sidelen) vl_structures.spawn_mobs("mobs_mc:evoker",spawnon,p1,p2,pr,1)
mcl_structures.spawn_mobs("mobs_mc:vindicator",spawnon,p1,p2,pr,5) vl_structures.spawn_mobs("mobs_mc:parrot",{"mcl_heads:wither_skeleton"},p1,p2,pr,1)
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)
end, end,
loot = { loot = {
["mcl_chests:chest_small" ] ={{ ["mcl_chests:chest_small" ] ={{
@ -69,7 +63,7 @@ mcl_structures.register_structure("woodland_cabin",{
} }
}) })
mcl_structures.register_structure_spawn({ vl_structures.register_structure_spawn({
name = "mobs_mc:vindicator", name = "mobs_mc:vindicator",
y_min = mcl_vars.mg_overworld_min, y_min = mcl_vars.mg_overworld_min,
y_max = mcl_vars.mg_overworld_max, y_max = mcl_vars.mg_overworld_max,
@ -79,7 +73,7 @@ mcl_structures.register_structure_spawn({
spawnon = spawnon, spawnon = spawnon,
}) })
mcl_structures.register_structure_spawn({ vl_structures.register_structure_spawn({
name = "mobs_mc:evoker", name = "mobs_mc:evoker",
y_min = mcl_vars.mg_overworld_min, y_min = mcl_vars.mg_overworld_min,
y_max = mcl_vars.mg_overworld_max, y_max = mcl_vars.mg_overworld_max,

View File

@ -10,19 +10,6 @@ local adjacents = {
vector.new(0,-1,0) vector.new(0,-1,0)
} }
local plane_adjacents = {
vector.new(1,0,0),
vector.new(-1,0,0),
vector.new(0,0,1),
vector.new(0,0,-1),
}
local function set_node_no_bedrock(pos,node)
local n = minetest.get_node(pos)
if n.name == "mcl_core:bedrock" then return end
return minetest.set_node(pos,node)
end
local function airtower(pos,tbl,h) local function airtower(pos,tbl,h)
for i=1,h do for i=1,h do
table.insert(tbl,vector.offset(pos,0,i,0)) table.insert(tbl,vector.offset(pos,0,i,0))
@ -32,7 +19,7 @@ end
local function makelake(pos,size,liquid,placein,border,pr,noair) local function makelake(pos,size,liquid,placein,border,pr,noair)
local p1, p2 = vector.offset(pos,-size,-1,-size), vector.offset(pos,size,-1,size) local p1, p2 = vector.offset(pos,-size,-1,-size), vector.offset(pos,size,-1,size)
local e1, e2 = vector.offset(pos,-size,-2,-size), vector.offset(pos,size,15,size) local e1, e2 = vector.offset(pos,-size,-2,-size), vector.offset(pos,size,15,size)
minetest.emerge_area(e1, e2, function(blockpos, action, calls_remaining, param) minetest.emerge_area(e1, e2, function(_, _, calls_remaining)
if calls_remaining ~= 0 then return end if calls_remaining ~= 0 then return end
local nn = minetest.find_nodes_in_area(p1,p2,placein) local nn = minetest.find_nodes_in_area(p1,p2,placein)
if not nn[1] then return end if not nn[1] then return end
@ -46,8 +33,8 @@ local function makelake(pos,size,liquid,placein,border,pr,noair)
airtower(nn[i],air,20) airtower(nn[i],air,20)
table.insert(lq,nn[i]) table.insert(lq,nn[i])
end end
minetest.bulk_set_node(lq,{name=liquid}) minetest.bulk_swap_node(lq,{name=liquid})
minetest.bulk_set_node(air,{name="air"}) minetest.bulk_swap_node(air,{name="air"})
air = {} air = {}
local br = {} local br = {}
for k,v in pairs(lq) do for k,v in pairs(lq) do
@ -74,8 +61,8 @@ local function makelake(pos,size,liquid,placein,border,pr,noair)
end end
end end
end end
minetest.bulk_set_node(br,{name=border}) minetest.bulk_swap_node(br,{name=border})
minetest.bulk_set_node(air,{name="air"}) minetest.bulk_swap_node(air,{name="air"})
return true return true
end) end)
return true return true
@ -83,57 +70,83 @@ end
local mushrooms = {"mcl_mushrooms:mushroom_brown","mcl_mushrooms:mushroom_red"} local mushrooms = {"mcl_mushrooms:mushroom_brown","mcl_mushrooms:mushroom_red"}
local function get_fallen_tree_schematic(pos,pr) local function place_fallen_tree(pos,def,pr)
local tree = minetest.find_node_near(pos,15,{"group:tree"}) local tree = minetest.find_node_near(pos,15,{"group:tree"})
if not tree then return end if not tree then return end
tree = minetest.get_node(tree).name tree = minetest.get_node(tree).name
local maxlen = 8 local minlen, maxlen = 3, 9
local minlen = 2 local vrate, mrate = 120, 160
local vprob = 120
local mprob = 160
local len = pr:next(minlen,maxlen) local len = pr:next(minlen,maxlen)
local schem = { local dir = pr:next(0,3)
size = {x = len + 2, y = 2, z = 3}, local dx, dy, dz, param2, w1, w2
data = { if dir == 0 then
{name = "air", prob=0}, dx, dy, dz, param2, w1, w2 = 1, 0, 0, 12, 5, 4
{name = "air", prob=0}, elseif dir == 1 then
} dx, dy, dz, param2, w1, w2 = -1, 0, 0, 12, 4, 5
} elseif dir == 2 then
for i = 1,len do dx, dy, dz, param2, w1, w2 = 0, 0, 1, 6, 3, 2
table.insert(schem.data,{name = "mcl_core:vine",param2=4, prob=vprob}) else -- if dir == 3 then
dx, dy, dz, param2, w1, w2 = 0, 0, -1, 6, 2, 3
end end
table.insert(schem.data,{name = "air", prob=0}) -- TODO: port this to voxel manipulators
table.insert(schem.data,{name = "air", prob=0}) -- ensure we have room for the tree
local minsupport, maxsupport = 99, 1
for i = 1,len do 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
-- 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
end end
if maxsupport - minsupport < minlen then return end
table.insert(schem.data,{name = tree, param2 = 0}) len = math.min(len, maxsupport - 1)
table.insert(schem.data,{name = "air", prob=0}) if len < minlen then return end
for i = 1,len do -- place the tree
table.insert(schem.data,{name = tree, param2 = 12}) minetest.swap_node(pos, {name = tree, param2 = 0})
-- some are hollow:
if vl_hollow_logs.logs and pr:next(1,20) == 1 then
local nam = string.sub(tree, string.find(tree, ":") + 1)
nam = "vl_hollow_logs:"..nam.."_hollow"
if minetest.registered_nodes[nam] then tree = nam end
end end
table.insert(schem.data,{name = "air", prob=0}) for i = 2,len do
table.insert(schem.data,{name = "air", prob=0}) minetest.swap_node(vector.offset(pos, dx * i, 0, dz * i), {name = tree, param2 = param2})
for i = 1,len do if pr:next(0,255) < vrate then
table.insert(schem.data,{name = mushrooms[pr:next(1,#mushrooms)], param2 = 12, prob=mprob}) 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
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 = "mcl_core:vine",param2=5, prob=vprob})
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 end
mcl_structures.register_structure("fallen_tree",{ vl_structures.register_structure("fallen_tree",{
priority = 1100, -- after regular trees
place_on = {"group:grass_block"}, place_on = {"group:grass_block"},
terrain_feature = true, terrain_feature = true,
noise_params = { noise_params = {
@ -145,27 +158,13 @@ mcl_structures.register_structure("fallen_tree",{
persist = 0.66 persist = 0.66
}, },
flags = "place_center_x, place_center_z", flags = "place_center_x, place_center_z",
sidelen = 18,
solid_ground = true, solid_ground = true,
y_max = mcl_vars.mg_overworld_max, y_max = mcl_vars.mg_overworld_max,
y_min = minetest.get_mapgen_setting("water_level"), y_min = minetest.get_mapgen_setting("water_level"),
on_place = function(pos,def,pr) place_func = place_fallen_tree
local air_p1 = vector.offset(pos,-def.sidelen/2,1,-def.sidelen/2)
local air_p2 = vector.offset(pos,def.sidelen/2,1,def.sidelen/2)
local air = minetest.find_nodes_in_area(air_p1,air_p2,{"air"})
if #air < ( def.sidelen * def.sidelen ) / 2 then
return false
end
return true
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
}) })
mcl_structures.register_structure("lavapool",{ vl_structures.register_structure("lavapool",{
place_on = {"group:sand", "group:dirt", "group:stone"}, place_on = {"group:sand", "group:dirt", "group:stone"},
terrain_feature = true, terrain_feature = true,
noise_params = { noise_params = {
@ -180,12 +179,12 @@ mcl_structures.register_structure("lavapool",{
flags = "place_center_x, place_center_z, all_floors", flags = "place_center_x, place_center_z, all_floors",
y_max = mcl_vars.mg_overworld_max, y_max = mcl_vars.mg_overworld_max,
y_min = minetest.get_mapgen_setting("water_level"), y_min = minetest.get_mapgen_setting("water_level"),
place_func = function(pos,def,pr) place_func = function(pos, _, pr)
return makelake(pos,5,"mcl_core:lava_source",{"group:material_stone", "group:sand", "group:dirt"},"mcl_core:stone",pr) return makelake(pos,5,"mcl_core:lava_source",{"group:material_stone", "group:sand", "group:dirt"},"mcl_core:stone",pr)
end end
}) })
mcl_structures.register_structure("water_lake",{ vl_structures.register_structure("water_lake",{
place_on = {"group:dirt","group:stone"}, place_on = {"group:dirt","group:stone"},
terrain_feature = true, terrain_feature = true,
noise_params = { noise_params = {
@ -200,12 +199,12 @@ mcl_structures.register_structure("water_lake",{
flags = "place_center_x, place_center_z, all_floors", flags = "place_center_x, place_center_z, all_floors",
y_max = mcl_vars.mg_overworld_max, y_max = mcl_vars.mg_overworld_max,
y_min = minetest.get_mapgen_setting("water_level"), y_min = minetest.get_mapgen_setting("water_level"),
place_func = function(pos,def,pr) place_func = function(pos, _, pr)
return makelake(pos,5,"mcl_core:water_source",{"group:material_stone", "group:sand", "group:dirt","group:grass_block"},"mcl_core:dirt_with_grass",pr) return makelake(pos,5,"mcl_core:water_source",{"group:material_stone", "group:sand", "group:dirt","group:grass_block"},"mcl_core:dirt_with_grass",pr)
end end
}) })
mcl_structures.register_structure("water_lake_mangrove_swamp",{ vl_structures.register_structure("water_lake_mangrove_swamp",{
place_on = {"mcl_mud:mud"}, place_on = {"mcl_mud:mud"},
biomes = { "MangroveSwamp" }, biomes = { "MangroveSwamp" },
terrain_feature = true, terrain_feature = true,
@ -221,12 +220,12 @@ mcl_structures.register_structure("water_lake_mangrove_swamp",{
flags = "place_center_x, place_center_z, all_floors", flags = "place_center_x, place_center_z, all_floors",
y_max = mcl_vars.mg_overworld_max, y_max = mcl_vars.mg_overworld_max,
y_min = minetest.get_mapgen_setting("water_level"), y_min = minetest.get_mapgen_setting("water_level"),
place_func = function(pos,def,pr) place_func = function(pos, _, pr)
return makelake(pos,3,"mcl_core:water_source",{"group:material_stone", "group:sand", "group:dirt","group:grass_block","mcl_mud:mud"},"mcl_mud:mud",pr,true) return makelake(pos,3,"mcl_core:water_source",{"group:material_stone", "group:sand", "group:dirt","group:grass_block","mcl_mud:mud"},"mcl_mud:mud",pr,true)
end end
}) })
mcl_structures.register_structure("basalt_column",{ vl_structures.register_structure("basalt_column",{
place_on = {"mcl_blackstone:blackstone","mcl_blackstone:basalt"}, place_on = {"mcl_blackstone:blackstone","mcl_blackstone:basalt"},
terrain_feature = true, terrain_feature = true,
spawn_by = {"air"}, spawn_by = {"air"},
@ -244,7 +243,7 @@ mcl_structures.register_structure("basalt_column",{
y_max = mcl_vars.mg_nether_max - 20, y_max = mcl_vars.mg_nether_max - 20,
y_min = mcl_vars.mg_lava_nether_max + 1, y_min = mcl_vars.mg_lava_nether_max + 1,
biomes = { "BasaltDelta" }, biomes = { "BasaltDelta" },
place_func = function(pos,def,pr) place_func = function(pos, _, pr)
local nn = minetest.find_nodes_in_area(vector.offset(pos,-5,-1,-5),vector.offset(pos,5,-1,5),{"air","mcl_blackstone:basalt","mcl_blackstone:blackstone"}) local nn = minetest.find_nodes_in_area(vector.offset(pos,-5,-1,-5),vector.offset(pos,5,-1,5),{"air","mcl_blackstone:basalt","mcl_blackstone:blackstone"})
table.sort(nn,function(a, b) table.sort(nn,function(a, b)
return vector.distance(vector.new(pos.x,0,pos.z), a) < vector.distance(vector.new(pos.x,0,pos.z), b) return vector.distance(vector.new(pos.x,0,pos.z), a) < vector.distance(vector.new(pos.x,0,pos.z), b)
@ -265,12 +264,12 @@ mcl_structures.register_structure("basalt_column",{
end end
end end
end end
minetest.bulk_set_node(magma,{name="mcl_nether:magma"}) minetest.bulk_swap_node(magma,{name="mcl_nether:magma"})
minetest.bulk_set_node(basalt,{name="mcl_blackstone:basalt"}) minetest.bulk_swap_node(basalt,{name="mcl_blackstone:basalt"})
return true return true
end end
}) })
mcl_structures.register_structure("basalt_pillar",{ vl_structures.register_structure("basalt_pillar",{
place_on = {"mcl_blackstone:blackstone","mcl_blackstone:basalt"}, place_on = {"mcl_blackstone:blackstone","mcl_blackstone:basalt"},
terrain_feature = true, terrain_feature = true,
noise_params = { noise_params = {
@ -286,7 +285,7 @@ mcl_structures.register_structure("basalt_pillar",{
y_max = mcl_vars.mg_nether_max-40, y_max = mcl_vars.mg_nether_max-40,
y_min = mcl_vars.mg_lava_nether_max + 1, y_min = mcl_vars.mg_lava_nether_max + 1,
biomes = { "BasaltDelta" }, biomes = { "BasaltDelta" },
place_func = function(pos,def,pr) place_func = function(pos, _, pr)
local nn = minetest.find_nodes_in_area(vector.offset(pos,-2,-1,-2),vector.offset(pos,2,-1,2),{"air","mcl_blackstone:basalt","mcl_blackstone:blackstone"}) local nn = minetest.find_nodes_in_area(vector.offset(pos,-2,-1,-2),vector.offset(pos,2,-1,2),{"air","mcl_blackstone:basalt","mcl_blackstone:blackstone"})
table.sort(nn,function(a, b) table.sort(nn,function(a, b)
return vector.distance(vector.new(pos.x,0,pos.z), a) < vector.distance(vector.new(pos.x,0,pos.z), b) return vector.distance(vector.new(pos.x,0,pos.z), a) < vector.distance(vector.new(pos.x,0,pos.z), b)
@ -306,13 +305,13 @@ mcl_structures.register_structure("basalt_pillar",{
end end
end end
end end
minetest.bulk_set_node(basalt,{name="mcl_blackstone:basalt"}) minetest.bulk_swap_node(basalt,{name="mcl_blackstone:basalt"})
minetest.bulk_set_node(magma,{name="mcl_nether:magma"}) minetest.bulk_swap_node(magma,{name="mcl_nether:magma"})
return true return true
end end
}) })
mcl_structures.register_structure("lavadelta",{ vl_structures.register_structure("lavadelta",{
place_on = {"mcl_blackstone:blackstone","mcl_blackstone:basalt"}, place_on = {"mcl_blackstone:blackstone","mcl_blackstone:basalt"},
spawn_by = {"mcl_blackstone:basalt","mcl_blackstone:blackstone"}, spawn_by = {"mcl_blackstone:basalt","mcl_blackstone:blackstone"},
num_spawn_by = 2, num_spawn_by = 2,
@ -330,7 +329,7 @@ mcl_structures.register_structure("lavadelta",{
y_max = mcl_vars.mg_nether_max, y_max = mcl_vars.mg_nether_max,
y_min = mcl_vars.mg_lava_nether_max + 1, y_min = mcl_vars.mg_lava_nether_max + 1,
biomes = { "BasaltDelta" }, biomes = { "BasaltDelta" },
place_func = function(pos,def,pr) place_func = function(pos, _, pr)
local nn = minetest.find_nodes_in_area_under_air(vector.offset(pos,-10,-1,-10),vector.offset(pos,10,-2,10),{"mcl_blackstone:basalt","mcl_blackstone:blackstone","mcl_nether:netherrack"}) local nn = minetest.find_nodes_in_area_under_air(vector.offset(pos,-10,-1,-10),vector.offset(pos,10,-2,10),{"mcl_blackstone:basalt","mcl_blackstone:blackstone","mcl_nether:netherrack"})
table.sort(nn,function(a, b) table.sort(nn,function(a, b)
return vector.distance(vector.new(pos.x,0,pos.z), a) < vector.distance(vector.new(pos.x,0,pos.z), b) return vector.distance(vector.new(pos.x,0,pos.z), a) < vector.distance(vector.new(pos.x,0,pos.z), b)
@ -340,7 +339,7 @@ mcl_structures.register_structure("lavadelta",{
for i=1,pr:next(1,#nn) do for i=1,pr:next(1,#nn) do
table.insert(lava,nn[i]) table.insert(lava,nn[i])
end end
minetest.bulk_set_node(lava,{name="mcl_nether:nether_lava_source"}) minetest.bulk_swap_node(lava,{name="mcl_nether:nether_lava_source"})
local basalt = {} local basalt = {}
local magma = {} local magma = {}
for _,v in pairs(lava) do for _,v in pairs(lava) do
@ -355,8 +354,8 @@ mcl_structures.register_structure("lavadelta",{
table.insert(magma,v) table.insert(magma,v)
end end
end end
minetest.bulk_set_node(basalt,{name="mcl_blackstone:basalt"}) minetest.bulk_swap_node(basalt,{name="mcl_blackstone:basalt"})
minetest.bulk_set_node(magma,{name="mcl_nether:magma"}) minetest.bulk_swap_node(magma,{name="mcl_nether:magma"})
return true return true
end end
}) })

View File

@ -0,0 +1,301 @@
# mcl_villages
When creating buildings or farms for use with this mod, you can prevent paths
from crossing areas by using the `mcl_villages:no_paths` block. You may need to
stack them 2 high to prevent all paths. After the village paths have be laid
this block will be replaced by air.
## Building Interfaces
### Parameter
All of the following functions take a table with the following keys.
#### Mandatory
name
: The name to use for the object.
mts
: The path to the mts format schema file.
#### Optional
yadjust
: Y axis adjustment when placing the schema. This can be positive to raise the
placement, or negative to lower it.
If your schema does not contain a ground layer then set this to 1.
no_ground_turnip
: If you don't want the foundation under the building modified, you can disable
the ground turnip by setting this to true.
Mainly useful for small thing such as lamps, planters, etc.
no_clearance
: If you don't want the area around and above the building modified, you can
disable the overground clearance by setting this to true.
Mainly useful for small thing such as lamps, planters, etc.
### mcl_villages.register_lamp(table)
Register a structure to use as a lamp. These will be added to the table used when
adding lamps to paths during village creation.
### mcl_villages.register_bell(table)
Register a structure to use as a bell. These will be added to the table used when
adding the bell during village creation.
There is 1 bell per village.
### mcl_villages.register_well(table)
Register a structure to use as a well. These will be added to the table used when
adding the wells during village creation.
The number of wells is calculated randomly based on the number of beds in the
village. Every 10 beds add 1 to the maximum number.
e.g. 8 beds == 1 well, 15 beds == 1 or 2 wells, 22 beds == 1 to 3 wells, etc.
### mcl_villages.register_building(table)
Register a building used for jobs, houses, or other uses.
The schema is parsed to work out how many jobs and beds are in it.
If you are adding a job site for a custom profession then ensure you call
```mobs_mc.register_villager_profession``` before you register a building using it.
If a building doesn't have any job sites or beds then it may get added during
the house placement phase. This will simply add another building to
the village and will not affect the number of jobs or beds.
#### Additional options
The ```mcl_villages.register_building``` call accepts the following optional
parameters in the table.
min_jobs
: A village will need at least this many jobs to have one of these buildings.
This is used to restrict buildings to bigger villages.
max_jobs
: A village will need less that or equal to (<=) this many jobs to have one of
these buildings.
This is used to restrict buildings to smaller villages.
num_others
: A village will need this many other job sites before you can have another of
these jobs sites.
This is used to influence the ratio of buildings in a village.
is_mandatory
: This ensures that each village will have at least one of these buildings.
### mobs_mc.register_villager_profession(title, table)
**TODO** this should be somewhere else.
This API call allows you to register professions for villagers.
It takes 2 arguments.
1. title - The title to use for the profession.
This mus be unique; the profession will be rejected if this title is already
used.
1. Record - a table containing the details of the profession, it contains the
following fields.
1. name: The name displayed for the profession in the UI.
1. texture: The texture to use for the profession
1. jobsite: the node or group name sued to flag blocks as job sites for this
profession
1. trades: a table containing trades with 1 entry for each trade level.
You can access the current profession and job site data in
```mobs_mc.professions``` and ```mobs_mc.jobsites```.
### mcl_villages.register_on_village_placed(func)
This function allows registering functions to be called after a village is
laid out.
Note that the village may not be completed as the building post processing is
non-deterministic to avoid overloading the server.
`settlement_info` is a table containing data for all the buildings in the
village. The bell is always the first entry in the table.
`blockseed` is the block seed for the chunk the village was generated for.
Villages can extend outside of this chunk.
```lua
local function my_village_hook(settlement_info, blockseed)
minetest.log("The village has " .. #settlement_info .. " buildings in it!")
end
mcl_villages.register_on_village_placed(my_village_hook)
```
### mcl_villages.register_on_villager_spawned(func)
This function allows registering functions to be called after a villager is
placed as part of village generation.
`villager_ent` is the entity created by `minetest.add_entity`.
`blockseed` is the block seed for the chunk the village was generated for.
Villages can extend outside of this chunk.
```lua
local function my_villager_hook(villager_ent, blockseed)
local l = villager_ent:get_luaentity()
minetest.log("The villager's id is " .. l._id)
end
mcl_villages.register_on_villager_spawned(my_villager_hook)
```
## Farm Interface
These functions aid creating crops for use use in farms placed during village
generation.
### mcl_villages.get_crop_types()
This allows checking what crop types are supported.
Currently they are: grain, root, gourd, flower, bush, tree.
Placement of gourds should take in to consideration the way they fruit.
### mcl_villages.get_crops()
Returns a table containing all registered crops.
### mcl_villages.get_weighted_crop(biome, crop_type, pr)
Gets a random crop for the biome and crop type.
### mcl_villages.register_crop(crop_def)
Registers a crop for use on farms.
crop_def is a table with the following fields:
* `node` the name of the crop node to place. e.g. `mcl_farming:wheat_1`.
* `crop_type` the type crop. e.g. `grain`
* `biomes` a table containing the weighting to give the crop.
* Supported biome values are:
* acacia
* bamboo
* desert
* jungle
* plains
* savanna
* spruce
* If you leave a biome out ot he definition then the crop will not be available in that biome.
e.g.
```lua
mcl_villages.register_crop({
type = "grain",
node = "mcl_farming:wheat_1",
biomes = {
acacia = 10,
bamboo = 10,
desert = 10,
jungle = 10,
plains = 10,
savanna = 10,
spruce = 10,
},
})
```
### Creating farms with replaceable crops
To create a farm that will utilize registered crops you follow the basic process
for creating a farm, but you leave out the crops.
Once you have your farm constructed then instead of placing crops you place blocks named `mcl_villages:crop_*` over the dirt in the farm.
Each crop type has 8 blocks that can be used for it. This allows, but does not
guarantee, variety of crops in a farm.
Each of the crop tiles has an image of a entity that it represents. This image
is representative, not explicit.
i.e. The root crop tiles have an image of a carrot on them, but they will be
swapped for a random root crop, not always carrots.
Each specific node will be replaced by a single item.
e.g. if you use `mcl_villages:crop_root_1` and `mcl_villages:crop_root_2` in your farm then all there will be at most 2 types of root crops on the farm.
It is random, so both types may get replaced by the same crop.
Remember that gourds affect 5 nodes when they crop; a good farmer won't plant
anything on the 4 nodes a fruit wil form and your farm should not do that
either.
Once you have saved the schema for your farm you register it with the building interface.
e.g.
```lua
mcl_villages.register_building({
name = "my_farm",
mts = schem_path .. "/my_farm.mts",
num_others = 3,
})
```
When a village is generated there will be a chance your farm will be placed, any
crop blocks will be replaced by biome appropriate crops.
If a crop cannot be found for a crop type in a biome, then a default will be
used. This ensure all farming blocks are full, ven if it's al the same crop.
The default is wheat.
## Village Layout
There are two methods for layout out villages, circle layout is more likely to be
used for small villages and grid for large villages.
The circle layout uses circles (surprise) to calculate if buildings overlap. It
creates fairly widely spaced layouts.
The grid layout uses a predetermined grid layout to positions buildings and uses
AreaStore to adjust building position if there are collisions.
The predetermined grid is below, position 0 is the bell, the other numbers are the order of placement.
||||||||
| -- | -- | -- | -- | -- | -- | -- |
|48|41|33|25|29|37|45|
|40|17|13| 9|11|15|43|
|32|19| 5| 1| 3|22|35|
|28|23| 7| 0| 8|24|27|
|36|21| 4| 2| 6|20|31|
|44|16|12|10|14|18|39|
|46|38|30|26|34|42|47|

View File

@ -21,7 +21,6 @@ Basic conversion of Settlements mod for compatibility with VoxeLibre, plus new s
Seed-based Village Generation, multi-threading, bugfixes: kay27 Seed-based Village Generation, multi-threading, bugfixes: kay27
========================= =========================
version: 0.1 alpha version: 0.1 alpha
@ -43,3 +42,5 @@ This mod is based on "ruins" by BlockMen
Completely new schematics for VoxeLibre: Completely new schematics for VoxeLibre:
MysticTempest - CC-BY-SA 4.0 MysticTempest - CC-BY-SA 4.0
New schematics and improvements in mineclonia by codiac.

View File

@ -0,0 +1,154 @@
mcl_villages.schematic_houses = {}
mcl_villages.schematic_jobs = {}
mcl_villages.schematic_lamps = {}
mcl_villages.schematic_bells = {}
mcl_villages.schematic_wells = {}
mcl_villages.on_village_placed = {}
mcl_villages.on_villager_placed = {}
mcl_villages.mandatory_buildings = {}
local S = minetest.get_translator(minetest.get_current_modname())
local function job_count(schem_lua)
local count = 0
for _, n in pairs(mobs_mc.jobsites) do
if string.find(n, "^group:") then
if n == "group:cauldron" then
count = count + select(2, string.gsub(schem_lua, '"mcl_cauldrons:cauldron', ""))
else
local name = string.sub(n, 6, -1)
local num = select(2, string.gsub(schem_lua, name, ""))
if num then
minetest.log("info", string.format("[mcl_villages] Guessing how to handle %s counting it as %d job sites", name, num))
count = count + num
else
minetest.log("warning", string.format("[mcl_villages] Don't know how to handle group %s counting it as 1 job site", n))
count = count + 1
end
end
else
count = count + select(2, string.gsub(schem_lua, '{name="' .. n .. '"', ""))
end
end
return count
end
local function load_schema(name, mts)
local schem_lua = minetest.serialize_schematic(mts, "lua", { lua_use_comments = false, lua_num_indent_spaces = 0 }) .. " return schematic"
-- MCLA node names to VL for import
if string.find(mts, "new_villages/") then
for _, sub in pairs(mcl_villages.mcla_to_vl) do
schem_lua = schem_lua:gsub(sub[1], sub[2])
end
end
local schematic = loadstring(schem_lua)()
return { name = name, size = schematic.size, schem_lua = schem_lua }
end
local all_optional = { "yadjust", "no_ground_turnip", "no_clearance", "rotation_offset" }
local function set_all_optional(record, data)
for _, field in ipairs(all_optional) do
if record[field] then data[field] = record[field] end
end
end
local function set_mandatory(record, type)
if record['is_mandatory'] then
if not mcl_villages.mandatory_buildings[type] then mcl_villages.mandatory_buildings[type] = {} end
table.insert(mcl_villages.mandatory_buildings[type], record["name"])
end
end
function mcl_villages.register_lamp(record)
local data = load_schema(record["name"], record["mts"])
set_all_optional(record, data)
table.insert(mcl_villages.schematic_lamps, data)
set_mandatory(record, 'lamps')
end
function mcl_villages.register_bell(record)
local data = load_schema(record["name"], record["mts"])
set_all_optional(record, data)
table.insert(mcl_villages.schematic_bells, data)
set_mandatory(record, 'bells')
end
function mcl_villages.register_well(record)
local data = load_schema(record["name"], record["mts"])
set_all_optional(record, data)
table.insert(mcl_villages.schematic_wells, data)
set_mandatory(record, 'wells')
end
local optional_fields = { "min_jobs", "max_jobs", "num_others", "is_mandatory" }
function mcl_villages.register_building(record)
local data = load_schema(record["name"], record["mts"])
set_all_optional(record, data)
for _, field in ipairs(optional_fields) do
if record[field] then data[field] = record[field] end
end
local str = data["schem_lua"]
local num_beds = select(2, string.gsub(str, '"mcl_beds:bed_[^"]+_bottom"', ""))
if num_beds > 0 then data["num_beds"] = num_beds end
local job_count = job_count(data["schem_lua"])
if job_count > 0 then
data["num_jobs"] = job_count
table.insert(mcl_villages.schematic_jobs, data)
set_mandatory(record, 'jobs')
else
table.insert(mcl_villages.schematic_houses, data)
set_mandatory(record, 'houses')
end
end
local crop_list = {}
function mcl_villages.register_crop(crop_def)
local crops = crop_list[crop_def.type] or {}
for biome, weight in pairs(crop_def.biomes) do
if crops[biome] == nil then crops[biome] = {} end
crops[biome][crop_def.node] = weight
end
crop_list[crop_def.type] = crops
end
function mcl_villages.get_crop_types()
local ret = {}
for k, _ in pairs(crop_list) do
table.insert(ret, k)
end
return ret
end
function mcl_villages.get_weighted_crop(biome, crop_type, pr)
local crops = crop_list[crop_type]
if not crops then return end -- unknown crop
local crops = crops[biome] or crops["plains"]
local total = 0
for _, weight in pairs(crops) do total = total + weight end
local rand = pr:next(0, 1e7) * 1e-7 * total
for node, weight in pairs(crops) do
if rand <= weight then
return node
end
rand = rand - weight
end
return
end
function mcl_villages.register_on_village_placed(func)
table.insert(mcl_villages.on_village_placed, func)
end
function mcl_villages.register_on_villager_spawned(func)
table.insert(mcl_villages.on_villager_placed, func)
end

View File

@ -1,344 +1,362 @@
--[[ local min_jobs = tonumber(minetest.settings:get("vl_villages_min_jobs")) or 2
------------------------------------------------------------------------------- local max_jobs = tonumber(minetest.settings:get("vl_villages_max_jobs")) or 14
-- build schematic, replace material, rotation local placement_priority = minetest.settings:get("vl_villages_placement_priority") or "houses" -- houses is safer for villagers at night
------------------------------------------------------------------------------- local max_height_difference = 40 -- at distance 40. In the center, half as much
function settlements.build_schematic(vm, data, va, pos, building, replace_wall, name)
-- get building node material for better integration to surrounding
local platform_material = mcl_vars.get_node(pos)
if not platform_material or (platform_material.name == "air" or platform_material.name == "ignore") then
return
end
platform_material = platform_material.name
-- pick random material
local material = wallmaterial[math.random(1,#wallmaterial)]
-- schematic conversion to lua
local schem_lua = minetest.serialize_schematic(building,
"lua",
{lua_use_comments = false, lua_num_indent_spaces = 0}).." return schematic"
-- replace material
if replace_wall == "y" then
schem_lua = schem_lua:gsub("mcl_core:cobble", material)
end
schem_lua = schem_lua:gsub("mcl_core:dirt_with_grass",
platform_material)
-- Disable special junglewood for now. local S = minetest.get_translator(minetest.get_current_modname())
-- special material for spawning npcs
-- schem_lua = schem_lua:gsub("mcl_core:junglewood",
-- "settlements:junglewood")
--
-- format schematic string local function add_building(settlement, building, count_buildings)
local schematic = loadstring(schem_lua)() if placement_priority == "jobs" then
-- build foundation for the building an make room above table.insert(settlement, building)
local width = schematic["size"]["x"]
local depth = schematic["size"]["z"]
local height = schematic["size"]["y"]
local possible_rotations = {"0", "90", "180", "270"}
local rotation = possible_rotations[ math.random( #possible_rotations ) ]
settlements.foundation(
pos,
width,
depth,
height,
rotation)
vm:set_data(data)
-- place schematic
minetest.place_schematic_on_vmanip(
vm,
pos,
schematic,
rotation,
nil,
true)
vm:write_to_map(true)
end]]
-------------------------------------------------------------------------------
-- initialize settlement_info
-------------------------------------------------------------------------------
function settlements.initialize_settlement_info(pr)
local count_buildings = {}
-- count_buildings table reset
for k,v in pairs(settlements.schematic_table) do
count_buildings[v["name"]] = 0
end
-- randomize number of buildings
local number_of_buildings = pr:next(10, 25)
local number_built = 1
settlements.debug("Village ".. number_of_buildings)
return count_buildings, number_of_buildings, number_built
end
-------------------------------------------------------------------------------
-- fill settlement_info
--------------------------------------------------------------------------------
function settlements.create_site_plan(maxp, minp, pr)
local settlement_info = {}
local building_all_info
local possible_rotations = {"0", "90", "180", "270"}
-- find center of chunk
local center = {
x=math.floor((minp.x+maxp.x)/2),
y=maxp.y,
z=math.floor((minp.z+maxp.z)/2)
}
-- find center_surface of chunk
local center_surface , surface_material = settlements.find_surface(center, true)
local chunks = {}
chunks[mcl_vars.get_chunk_number(center)] = true
-- go build settlement around center
if not center_surface then
minetest.log("action", "Cannot build village at: " .. minetest.pos_to_string(center))
return false
else else
minetest.log("action", "Village built.") table.insert(settlement, 1, building) -- insert "backwards" - todo: add table.reverse
--minetest.log("action", "Build village at: " .. minetest.pos_to_string(center) .. " with surface material: " .. surface_material)
end end
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
-- initialize all settlement_info table local function layout_town(vm, minp, maxp, pr, input_settlement)
local count_buildings, number_of_buildings, number_built = settlements.initialize_settlement_info(pr) local center = vector.new(pr:next(minp.x + 24, maxp.x - 24), maxp.y, pr:next(minp.z + 24, maxp.z - 24))
-- first building is townhall in the center minetest.log("action", "[mcl_villages] sudo make me a village at: " .. minetest.pos_to_string(minp).." - "..minetest.pos_to_string(maxp))
building_all_info = settlements.schematic_table[1] local possible_rotations = {"0", "90", "180", "270"}
local rotation = possible_rotations[ pr:next(1, #possible_rotations ) ] local center_surface
-- add to settlement info table
local index = 1 local settlement = {}
settlement_info[index] = {
pos = center_surface,
name = building_all_info["name"],
hsize = building_all_info["hsize"],
rotat = rotation,
surface_mat = surface_material
}
--increase index for following buildings
index = index + 1
-- now some buildings around in a circle, radius = size of town center -- now some buildings around in a circle, radius = size of town center
local x, z, r = center_surface.x, center_surface.z, building_all_info["hsize"] local x, y, z, r, lastr = center.x, center.y, center.z, 0, 99
-- draw j circles around center and increase radius by math.random(2,5) 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 for j = 1,20 do
-- set position on imaginary circle local steps = math.min(math.floor(math.pi * 2 * r / 2), 30) -- try up to 30 angles
for j = 0, 360, 15 do for a = 0, steps - 1 do
local angle = j * math.pi / 180 if #settlement == #input_settlement then break end -- everything placed
local ptx, ptz = x + r * math.cos( angle ), z + r * math.sin( angle ) local angle = a * 71 / steps * math.pi * 2 -- prime to increase randomness
ptx = settlements.round(ptx, 0) local cpos = vector.new(math.floor(x + r * math.cos(angle) + 0.5), y, math.floor(z - r * math.sin(angle) + 0.5))
ptz = settlements.round(ptz, 0) local building = table.copy(input_settlement[#settlement + 1])
local pos1 = { x=ptx, y=center_surface.y+50, z=ptz} local size = vector.copy(building.size)
local chunk_number = mcl_vars.get_chunk_number(pos1) --local rotation = possible_rotations[pr:next(1, #possible_rotations)]
local pos_surface, surface_material -- instead of random rotations, rotating doors to the center makes the village
if chunks[chunk_number] then -- more defensive and hence safer for the poor villagers, even though less random
pos_surface, surface_material = settlements.find_surface(pos1) -- case distinction is simpler and faster than trigonometry here:
local rotation = building.rotation_offset or 0
if math.abs(cpos.z-center.z) > math.abs(cpos.x-center.x) then
rotation = rotation + (cpos.z <= center.z and 0 or 2) -- zero indexed for modulo below
else else
chunks[chunk_number] = true rotation = rotation + (cpos.x <= center.x and 1 or 3) -- zero indexed for modulo below
pos_surface, surface_material = settlements.find_surface(pos1, true)
end end
if not pos_surface then break end rotation = possible_rotations[rotation % 4 + 1]
--minetest.log("action", building.name.." at "..minetest.pos_to_string(cpos).." rotation: "..rotation.." to "..minetest.pos_to_string(center).." "..minetest.pos_to_string(center-cpos))
if rotation == "90" or rotation == "270" then size.x, size.z = size.z, size.x end
local tlpos = vector.offset(cpos, -math.floor((size.x-1)/2), 0, -math.floor((size.z-1)/2))
local randomized_schematic_table = shuffle(settlements.schematic_table, pr) -- ensure we have 3 space for terraforming, and avoid problems with VoxelManip
-- pick schematic if tlpos.x - 3 >= minp.x and tlpos.x + size.x + 3 <= maxp.x
local size = #randomized_schematic_table and tlpos.z + 3 >= minp.z and tlpos.z + size.y + 3 <= maxp.z then
for i = size, 1, -1 do local pos, surface_material = vl_terraforming.find_level_vm(vm, cpos, size, 6)
-- already enough buildings of that type? if pos and pos.y + size.y > maxp.y then pos = nil end
if count_buildings[randomized_schematic_table[i]["name"]] < randomized_schematic_table[i]["max_num"]*number_of_buildings then -- check distance to other buildings. Note that we still want to add baseplates etc.
building_all_info = randomized_schematic_table[i] if pos and mcl_villages.surface_mat[surface_material.name] and mcl_villages.check_distance(settlement, cpos, size.x, size.z, mindist) then
-- check distance to other buildings -- use town bell as new reference point for placement height
local distance_to_other_buildings_ok = settlements.check_distance(settlement_info, pos_surface, building_all_info["hsize"]) if #settlement == 0 then
if distance_to_other_buildings_ok then center_surface, y = cpos, math.min(maxp.y, pos.y + max_height_difference + 1)
-- count built houses end
count_buildings[building_all_info["name"]] = count_buildings[building_all_info["name"]] +1 -- limit height differences to town center, but gradually allow more
rotation = possible_rotations[ pr:next(1, #possible_rotations ) ] if math.abs(pos.y - center_surface.y) <= max_height_difference * (0.5 + r/80) then
number_built = number_built + 1 local minp = vector.offset(pos, -math.floor((size.x-1)/2), building.yadjust, -math.floor((size.z-1)/2))
settlement_info[index] = { building.minp = minp
pos = pos_surface, building.maxp = vector.offset(minp, size.x, size.y, size.z)
name = building_all_info["name"], building.pos = pos
hsize = building_all_info["hsize"], building.size = size
rotat = rotation, building.rotation = rotation
surface_mat = surface_material building.surface_mat = surface_material
} table.insert(settlement, building)
index = index + 1 -- minetest.log("verbose", "[mcl_villages] Planning "..schema["name"].." at "..minetest.pos_to_string(pos))
break lastr = r
else
minetest.log("verbose", "Too large height difference "..math.abs(pos.y - center_surface.y).." at distance "..r)
end end
end end
end end
if number_of_buildings == number_built then
break
end
end end
if number_built >= number_of_buildings then r = r + pr:next(2,4)
if r > lastr + 20 then -- too disconnected
minetest.log("verbose", "Disconnected village "..r.." > "..lastr)
break break
end end
r = r + pr:next(2,5)
end end
settlements.debug("really ".. number_built) -- minetest.log("verbose", "Planned "..#input_settlement.." buildings, placed "..#settlement)
return settlement_info if #settlement < #input_settlement and #settlement < 6 then
end minetest.log("action", "[mcl_villages] Bad village location, could only place "..#settlement.." buildings at "..minetest.pos_to_string(center))
-------------------------------------------------------------------------------
-- evaluate settlement_info and place schematics
-------------------------------------------------------------------------------
-- Initialize node
local function construct_node(p1, p2, name)
local r = minetest.registered_nodes[name]
if r then
if r.on_construct then
local nodes = minetest.find_nodes_in_area(p1, p2, name)
for p=1, #nodes do
local pos = nodes[p]
r.on_construct(pos)
end
return nodes
end
minetest.log("warning", "[mcl_villages] No on_construct defined for node name " .. name)
return return
end end
minetest.log("warning", "[mcl_villages] Attempt to 'construct' inexistant nodes: " .. name) minetest.log("action", "[mcl_villages] village plan completed at " .. minetest.pos_to_string(center))
return settlement
end end
local function spawn_iron_golem(pos) function mcl_villages.create_site_plan(vm, minp, maxp, pr)
--minetest.log("action", "Attempt to spawn iron golem.") local settlement = {}
local p = minetest.find_node_near(pos,50,"mcl_core:grass_path")
if p then -- initialize all settlement_info table
local l=minetest.add_entity(p,"mobs_mc:iron_golem"):get_luaentity() local count_buildings = { num_jobs = 0, num_beds = 0, target_jobs = pr:next(min_jobs, max_jobs) }
if l then
l._home = p -- first building is townhall in the center
local bindex = pr:next(1, #mcl_villages.schematic_bells)
local bell_info = table.copy(mcl_villages.schematic_bells[bindex])
if mcl_villages.mandatory_buildings['jobs'] then
for _, bld_name in pairs(mcl_villages.mandatory_buildings['jobs']) do
local building_info = info_for_building(bld_name, mcl_villages.schematic_jobs)
add_building(settlement, building_info, count_buildings)
end end
end end
end
local function spawn_villagers(minp,maxp) while count_buildings.num_jobs < count_buildings.target_jobs do
--minetest.log("action", "Attempt to spawn villagers.") local rindex = pr:next(1, #mcl_villages.schematic_jobs)
local beds=minetest.find_nodes_in_area(vector.offset(minp,-20,-20,-20),vector.offset(maxp,20,20,20),{"mcl_beds:bed_red_bottom"}) local building_info = mcl_villages.schematic_jobs[rindex]
for _,bed in pairs(beds) do
local m = minetest.get_meta(bed) if
if m:get_string("villager") == "" then (building_info.min_jobs == nil or count_buildings.target_jobs >= building_info.min_jobs)
local v=minetest.add_entity(bed,"mobs_mc:villager") and (building_info.max_jobs == nil or count_buildings.target_jobs <= building_info.max_jobs)
if v then and (
local l=v:get_luaentity() building_info.num_others == nil
l._bed = bed or (count_buildings[building_info.group or building_info.name] or 0) == 0
m:set_string("villager",l._id) or building_info.num_others * (count_buildings[building_info.group or building_info.name] or 0) < count_buildings.num_jobs
end )
then
add_building(settlement, building_info, count_buildings)
end end
end end
end
local function fix_village_water(minp,maxp) if mcl_villages.mandatory_buildings['houses'] then
local palettenodes = minetest.find_nodes_in_area(vector.offset(minp,-20,-20,-20),vector.offset(maxp,20,20,20), "group:water_palette") for _, bld_name in pairs(mcl_villages.mandatory_buildings['houses']) do
for _, palettenodepos in pairs(palettenodes) do local building_info = info_for_building(bld_name, mcl_villages.schematic_houses)
local palettenode = minetest.get_node(palettenodepos) add_building(settlement, building_info, count_buildings)
minetest.set_node(palettenodepos, {name = palettenode.name})
end
end
local function init_nodes(p1, p2, size, rotation, pr)
construct_node(p1, p2, "mcl_itemframes:item_frame")
construct_node(p1, p2, "mcl_furnaces:furnace")
construct_node(p1, p2, "mcl_anvils:anvil")
construct_node(p1, p2, "mcl_smoker:smoker")
construct_node(p1, p2, "mcl_barrels:barrel_closed")
construct_node(p1, p2, "mcl_blast_furnace:blast_furnace")
construct_node(p1, p2, "mcl_brewing:stand_000")
local nodes = construct_node(p1, p2, "mcl_chests:chest")
if nodes and #nodes > 0 then
for p=1, #nodes do
local pos = nodes[p]
settlements.fill_chest(pos, pr)
end end
end end
while count_buildings.num_beds <= count_buildings.num_jobs do
local rindex = pr:next(1, #mcl_villages.schematic_houses)
local building_info = mcl_villages.schematic_houses[rindex]
if
(building_info.min_jobs == nil or count_buildings.target_jobs >= building_info.min_jobs)
and (building_info.max_jobs == nil or count_buildings.target_jobs <= building_info.max_jobs)
and (
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)
end
end
-- Based on number of villagers
local num_wells = pr:next(1, math.ceil(count_buildings.num_beds / 10))
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)
end
if placement_priority == "randomg" then
settlement = mcl_villages.shuffle(settlement, pr)
end
table.insert(settlement, 1, bell_info)
return layout_town(vm, minp, maxp, pr, settlement)
end end
function settlements.place_schematics(settlement_info, pr) local function init_nodes(p1, p2, pr)
local building_all_info vl_structures.construct_nodes(p1, p2, {
"mcl_itemframes:item_frame",
"mcl_itemframes:glow_item_frame",
"mcl_furnaces:furnace",
"mcl_anvils:anvil",
"mcl_books:bookshelf",
"mcl_armor_stand:armor_stand",
-- jobsite: "mcl_smoker:smoker",
-- jobsite: "mcl_barrels:barrel_closed",
-- jobsite: "mcl_blast_furnace:blast_furnace",
-- jobsite: "mcl_brewing:stand_000",
})
for i, built_house in ipairs(settlement_info) do -- Support mods with custom job sites
local is_last = i == #settlement_info local job_sites = minetest.find_nodes_in_area(p1, p2, mobs_mc.jobsites)
for _, v in pairs(job_sites) do vl_structures.init_node_construct(v) end
for j, schem in ipairs(settlements.schematic_table) do local nodes = vl_structures.construct_nodes(p1, p2, {"mcl_chests:chest_small", "mcl_chests:chest" })
if settlement_info[i]["name"] == schem["name"] then for _, n in pairs(nodes) do mcl_villages.fill_chest(n, pr) end
building_all_info = schem end
break
end
end
-- important: the vm will be written and then is outdated!
function mcl_villages.place_schematics(vm, settlement, blockseed, pr)
-- first building is always the bell
local bell_pos = vector.offset(settlement[1].minp, math.floor(settlement[1].size.x/2), 0, math.floor(settlement[1].size.z/2))
for i, building in ipairs(settlement) do
local minp, cpos, maxp, size, rotation = building.minp, building.pos, building.maxp, building.size, building.rotation
-- adjust the schema to match location and biome
local pos = settlement_info[i]["pos"] local surface_material = building.surface_mat or {name = "mcl_core:dirt" }
local rotation = settlement_info[i]["rotat"] local platform_material = building.platform_mat or building.surface_mat or {name = "mcl_core:stone" }
-- get building node material for better integration to surrounding local schem_lua = building.schem_lua
local platform_material = settlement_info[i]["surface_mat"] schem_lua = schem_lua:gsub('"mcl_core:dirt"', '"'..platform_material.name..'"')
--platform_material_name = minetest.get_name_from_content_id(platform_material) schem_lua = schem_lua:gsub('"mcl_core:dirt_with_grass"', '"'..surface_material.name..'"') -- also keeping param2 would be nicer, grass color
-- pick random material schem_lua = mcl_villages.substitute_materials(cpos, schem_lua, pr)
--local material = wallmaterial[pr:next(1,#wallmaterial)]
--
local building = building_all_info["mts"]
local replace_wall = building_all_info["rplc"]
-- schematic conversion to lua
local schem_lua = minetest.serialize_schematic(building,
"lua",
{lua_use_comments = false, lua_num_indent_spaces = 0}).." return schematic"
schem_lua = schem_lua:gsub("mcl_core:stonebrickcarved", "mcl_villages:stonebrickcarved")
-- replace material
if replace_wall then
--Note, block substitution isn't matching node names exactly; so nodes that are to be substituted that have the same prefixes cause bugs.
-- Example: Attempting to swap out 'mcl_core:stonebrick'; which has multiple, additional sub-variants: (carved, cracked, mossy). Will currently cause issues, so leaving disabled.
if platform_material == "mcl_core:snow" or platform_material == "mcl_core:dirt_with_grass_snow" or platform_material == "mcl_core:podzol" then
schem_lua = schem_lua:gsub("mcl_core:tree", "mcl_core:sprucetree")
schem_lua = schem_lua:gsub("mcl_core:wood", "mcl_core:sprucewood")
--schem_lua = schem_lua:gsub("mcl_fences:fence", "mcl_fences:spruce_fence")
--schem_lua = schem_lua:gsub("mcl_stairs:slab_wood_top", "mcl_stairs:slab_sprucewood_top")
--schem_lua = schem_lua:gsub("mcl_stairs:stair_wood", "mcl_stairs:stair_sprucewood")
--schem_lua = schem_lua:gsub("mesecons_pressureplates:pressure_plate_wood_off", "mesecons_pressureplates:pressure_plate_sprucewood_off")
elseif platform_material == "mcl_core:sand" or platform_material == "mcl_core:redsand" then
schem_lua = schem_lua:gsub("mcl_core:tree", "mcl_core:sandstonecarved")
schem_lua = schem_lua:gsub("mcl_core:cobble", "mcl_core:sandstone")
schem_lua = schem_lua:gsub("mcl_core:wood", "mcl_core:sandstonesmooth")
--schem_lua = schem_lua:gsub("mcl_fences:fence", "mcl_fences:birch_fence")
--schem_lua = schem_lua:gsub("mcl_stairs:slab_wood_top", "mcl_stairs:slab_birchwood_top")
--schem_lua = schem_lua:gsub("mcl_stairs:stair_wood", "mcl_stairs:stair_birchwood")
--schem_lua = schem_lua:gsub("mesecons_pressureplates:pressure_plate_wood_off", "mesecons_pressureplates:pressure_plate_birchwood_off")
--schem_lua = schem_lua:gsub("mcl_stairs:stair_stonebrick", "mcl_stairs:stair_redsandstone")
--schem_lua = schem_lua:gsub("mcl_core:stonebrick", "mcl_core:redsandstonesmooth")
schem_lua = schem_lua:gsub("mcl_core:brick_block", "mcl_core:redsandstone")
end
end
schem_lua = schem_lua:gsub("mcl_core:dirt_with_grass", platform_material)
--[[ Disable special junglewood for now.
-- special material for spawning npcs
schem_lua = schem_lua:gsub("mcl_core:junglewood", "settlements:junglewood")
--]]
schem_lua = schem_lua:gsub("mcl_stairs:stair_wood_outer", "mcl_stairs:slab_wood")
schem_lua = schem_lua:gsub("mcl_stairs:stair_stone_rough_outer", "air")
-- format schematic string
local schematic = loadstring(schem_lua)() local schematic = loadstring(schem_lua)()
local is_belltower = building_all_info["name"] == "belltower" -- the foundation and air space for the building was already built before
-- minetest.log("action", "placing schematics for "..building.name.." at "..minetest.pos_to_string(minp).." on "..surface_material.name)
minetest.place_schematic_on_vmanip(vm, minp, schematic, rotation, nil, true, { place_center_x = false, place_center_y = false, place_center_z = false })
mcl_villages.store_path_ends(vm, minp, maxp, cpos, blockseed, bell_pos)
mcl_villages.increase_no_paths(vm, minp, maxp) -- help the path finder
end
-- build foundation for the building an make room above local minp, maxp = vm:get_emerged_area() -- safe area for further processing
vm:write_to_map(true) -- for path finder and light
mcl_structures.place_schematic( -- Path planning and placement
pos, mcl_villages.paths(blockseed, minetest.get_biome_name(minetest.get_biome_data(bell_pos).biome), minp, maxp)
schematic, mcl_villages.clean_no_paths(minp, maxp)
rotation, -- Clean up paths and initialize nodes
nil, for i, building in ipairs(settlement) do
true, init_nodes(building.minp, building.maxp, pr)
nil, end
function(p1, p2, size, rotation, pr)
if is_belltower then -- Replace center block with a temporary block, which will be used run delayed actions
spawn_iron_golem(p1) local block_name = minetest.get_node(bell_pos).name -- to restore the node afterwards
minetest.swap_node(bell_pos, { name = "mcl_villages:village_block" })
local meta = minetest.get_meta(bell_pos)
meta:set_string("node_type", block_name)
meta:set_string("blockseed", blockseed)
meta:set_string("infotext", S("The timer for this village has not run yet!"))
minetest.get_node_timer(bell_pos):start(1.0)
end
minetest.register_node("mcl_villages:village_block", {
drawtype = "glasslike",
groups = { not_in_creative_inventory = 1 },
light_source = 14, -- This is a light source so that lamps don't get placed near it
-- Somethings don't work reliably when done in the map building
-- so we use a timer to run them later when they work more reliably
-- e.g. spawning mobs, running minetest.find_path
on_timer = function(pos, _)
local meta = minetest.get_meta(pos)
mcl_villages.post_process_village(meta:get_string("blockseed"))
-- not swap_node, to clear metadata afterwards
minetest.set_node(pos, { name = meta:get_string("node_type") })
return false
end,
})
function mcl_villages.post_process_village(blockseed)
local village_info = mcl_villages.get_village(blockseed)
if not village_info then return end
-- minetest.log("Postprocessing village")
local settlement_info = village_info.data
local jobs, beds = {}, {}
local bell_pos = vector.copy(settlement_info[1].pos)
local bell = vector.offset(bell_pos, 0, 2, 0)
local biome_name = minetest.get_biome_name(minetest.get_biome_data(bell_pos).biome)
-- Spawn Golem
local l = minetest.add_entity(bell, "mobs_mc:iron_golem"):get_luaentity()
if l then
l._home = bell
else
minetest.log("info", "Could not create a golem!")
end
-- Spawn cats
local sp = minetest.find_nodes_in_area_under_air(vector.offset(bell, -20, -10, -20),vector.offset(bell, 20, 10, 20), { "group:opaque" })
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 = bell_pos -- help them stay local
else
minetest.log("info", "Could not spawn a cat")
break
end
end
-- collect beds and job sites
for _, building in pairs(settlement_info) do
local minp, maxp = building.minp, building.maxp
if building.num_jobs then
local jobsites = minetest.find_nodes_in_area(minp, maxp, mobs_mc.jobsites)
for _, job_pos in pairs(jobsites) do table.insert(jobs, job_pos) end
end
if building.num_beds then
local bld_beds = minetest.find_nodes_in_area(minp, maxp, { "group:bed" })
for _, bed_pos in pairs(bld_beds) do
local bed_group = minetest.get_item_group(minetest.get_node(bed_pos).name, "bed")
-- We only spawn at bed bottoms, 1 is bottom, 2 is top
if bed_group == 1 then table.insert(beds, bed_pos) end
end
end
end
-- TODO: shuffle jobs?
-- minetest.log("beds: "..#beds.." jobsites: "..#jobs)
if beds then
for _, bed_pos in pairs(beds) do
minetest.forceload_block(bed_pos, true)
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(vector.offset(bed_pos, 0, 0.06, 0), "mobs_mc:villager")
if v then
local l = v:get_luaentity()
l._bed = bed_pos
l._bell = bell_pos
m:set_string("villager", l._id)
m:set_string("infotext", S("A villager sleeps here"))
local job_pos = table.remove(jobs, 1)
if job_pos then villager_employ(l, job_pos) end -- HACK: merge more MCLA villager job code?
for _, callback in pairs(mcl_villages.on_villager_placed) do callback(v, blockseed) end
else else
init_nodes(p1, p2, size, rotation, pr) minetest.log("info", "Could not create a villager!")
spawn_villagers(p1,p2)
fix_village_water(p1,p2)
end end
end, else
pr minetest.log("info", "bed already owned by " .. m:get_string("villager")) -- should not happen unless villages overlap
) end
end
end
end
-- Terraform for an entire village
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-1)/2), 0, -math.floor((size.z-1)/2))
-- TODO: allow different clearance for different buildings?
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
if not building.no_ground_turnip then
local pos, size = building.pos, building.size
local surface_mat = building.surface_mat
local platform_mat = building.platform_mat or { name = mcl_villages.foundation_materials[surface_mat.name] or "mcl_core:dirt" }
local stone_mat = building.stone_mat or { name = mcl_villages.stone_materials[surface_mat.name] or "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))
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
end end

View File

@ -1,77 +1,292 @@
-- switch for debugging -- legacy type in old schematics
function settlements.debug(message) minetest.register_alias("mcl_villages:stonebrickcarved", "mcl_core:stonebrickcarved")
-- minetest.chat_send_all(message)
-- minetest.log("warning", "[mcl_villages] "..message)
minetest.log("verbose", "[mcl_villages] "..message)
end
--[[ Manually set in 'buildings.lua'
-- material to replace cobblestone with
local wallmaterial = {
"mcl_core:junglewood",
"mcl_core:sprucewood",
"mcl_core:wood",
"mcl_core:birchwood",
"mcl_core:acaciawood",
"mcl_core:stonebrick",
"mcl_core:cobble",
"mcl_core:sandstonecarved",
"mcl_core:sandstone",
"mcl_core:sandstonesmooth2"
}
--]]
settlements.surface_mat = {}
-------------------------------------------------------------------------------
-- Set array to list
-- https://stackoverflow.com/questions/656199/search-for-an-item-in-a-lua-list
-------------------------------------------------------------------------------
function settlements.grundstellungen()
settlements.surface_mat = settlements.Set {
"mcl_core:dirt_with_grass",
--"mcl_core:dry_dirt_with_grass",
"mcl_core:dirt_with_grass_snow",
--"mcl_core:dirt_with_dry_grass",
"mcl_core:podzol",
"mcl_core:sand",
"mcl_core:redsand",
--"mcl_core:silver_sand",
"mcl_core:snow"
}
end
--
-- possible surfaces where buildings can be built -- possible surfaces where buildings can be built
-- mcl_villages.surface_mat = {}
mcl_villages.surface_mat["mcl_core:andesite"] = true
mcl_villages.surface_mat["mcl_core:diorite"] = true
mcl_villages.surface_mat["mcl_core:dirt"] = true
mcl_villages.surface_mat["mcl_core:dirt_with_grass"] = true
--mcl_villages.surface_mat["mcl_core:dirt_with_dry_grass"] = true
mcl_villages.surface_mat["mcl_core:dirt_with_grass_snow"] = true
--mcl_villages.surface_mat["mcl_core:dry_dirt_with_grass"] = true
mcl_villages.surface_mat["mcl_core:grass_path"] = true
mcl_villages.surface_mat["mcl_core:granite"] = true
mcl_villages.surface_mat["mcl_core:podzol"] = true
mcl_villages.surface_mat["mcl_core:redsand"] = true
mcl_villages.surface_mat["mcl_core:sand"] = true
mcl_villages.surface_mat["mcl_core:sandstone"] = true
mcl_villages.surface_mat["mcl_core:sandstonesmooth"] = true
mcl_villages.surface_mat["mcl_core:sandstonesmooth2"] = true
--mcl_villages.surface_mat["mcl_core:silver_sand"] = true
--mcl_villages.surface_mat["mcl_core:snow"] = true
mcl_villages.surface_mat["mcl_core:stone"] = true
mcl_villages.surface_mat["mcl_core:stone_with_coal"] = true
mcl_villages.surface_mat["mcl_core:stone_with_iron"] = true
mcl_villages.surface_mat["mcl_colorblocks:hardened_clay"] = true
mcl_villages.surface_mat["mcl_colorblocks:hardened_clay_orange"] = true
mcl_villages.surface_mat["mcl_colorblocks:hardened_clay_red"] = true
mcl_villages.surface_mat["mcl_colorblocks:hardened_clay_white"] = true
-- substitute foundation materials
mcl_villages.foundation_materials = {}
mcl_villages.foundation_materials["mcl_core:sand"] = "mcl_core:sandstone"
mcl_villages.foundation_materials["mcl_core:redsand"] = "mcl_core:redsandstone"
-- substitute stone materials in foundation
mcl_villages.stone_materials = {}
mcl_villages.default_crop = "mcl_farming:wheat_1"
-- --
-- path to schematics -- Biome based block substitutions
-- --
schem_path = settlements.modpath.."/schematics/" -- TODO maybe this should be in the biomes?
-- mcl_villages.biome_map = {
-- list of schematics BambooJungle = "bamboo",
-- BambooJungleEdge = "bamboo",
local basic_pseudobiome_villages = minetest.settings:get_bool("basic_pseudobiome_villages", true) BambooJungleEdgeM = "bamboo",
BambooJungleM = "bamboo",
settlements.schematic_table = { Jungle = "jungle",
{name = "belltower", mts = schem_path.."belltower.mts", hwidth = 5, hdepth = 5, hheight = 9, hsize = 14, max_num = 0 , rplc = basic_pseudobiome_villages }, JungleEdge = "jungle",
{name = "large_house", mts = schem_path.."large_house.mts", hwidth = 12, hdepth = 12, hheight = 9, hsize = 14, max_num = 0.08 , rplc = basic_pseudobiome_villages }, JungleEdgeM = "jungle",
{name = "blacksmith", mts = schem_path.."blacksmith.mts", hwidth = 8, hdepth = 11, hheight = 13, hsize = 13, max_num = 0.055, rplc = basic_pseudobiome_villages }, JungleM = "jungle",
{name = "butcher", mts = schem_path.."butcher.mts", hwidth = 12, hdepth = 8, hheight = 10, hsize = 14, max_num = 0.03 , rplc = basic_pseudobiome_villages },
{name = "church", mts = schem_path.."church.mts", hwidth = 13, hdepth = 13, hheight = 14, hsize = 15, max_num = 0.04 , rplc = basic_pseudobiome_villages }, Desert = "desert",
{name = "farm", mts = schem_path.."farm.mts", hwidth = 9, hdepth = 7, hheight = 13, hsize = 13, max_num = 0.1 , rplc = basic_pseudobiome_villages },
{name = "lamp", mts = schem_path.."lamp.mts", hwidth = 3, hdepth = 4, hheight = 13, hsize = 10, max_num = 0.1 , rplc = false }, Savanna = "acacia",
{name = "library", mts = schem_path.."library.mts", hwidth = 12, hdepth = 12, hheight = 8, hsize = 13, max_num = 0.04 , rplc = basic_pseudobiome_villages }, SavannaM = "acacia",
{name = "medium_house", mts = schem_path.."medium_house.mts", hwidth = 9, hdepth = 12, hheight = 8, hsize = 14, max_num = 0.08 , rplc = basic_pseudobiome_villages },
{name = "small_house", mts = schem_path.."small_house.mts", hwidth = 9, hdepth = 8, hheight = 8, hsize = 13, max_num = 0.7 , rplc = basic_pseudobiome_villages }, Mesa = "hardened_clay",
{name = "tavern", mts = schem_path.."tavern.mts", hwidth = 12, hdepth = 10, hheight = 10, hsize = 13, max_num = 0.050, rplc = basic_pseudobiome_villages }, MesaBryce = "hardened_clay ",
{name = "well", mts = schem_path.."well.mts", hwidth = 6, hdepth = 8, hheight = 6, hsize = 10, max_num = 0.045, rplc = basic_pseudobiome_villages }, MesaPlateauF = "hardened_clay",
MesaPlateauFM = "hardened_clay",
MangroveSwamp = "mangrove",
RoofedForest = "dark_oak",
BirchForest = "birch",
BirchForestM = "birch",
ColdTaiga = "spruce",
ExtremeHills = "spruce",
ExtremeHillsM = "spruce",
IcePlains = "spruce",
IcePlainsSpikes = "spruce",
MegaSpruceTaiga = "spruce",
MegaTaiga = "spruce",
Taiga = "spruce",
["ExtremeHills+"] = "spruce",
CherryGrove = "cherry",
-- no change
--FlowerForest = "oak",
--Forest = "oak",
--MushroomIsland = "",
--Plains = "oak",
--StoneBeach = "",
--SunflowerPlains = "oak",
--Swampland = "oak",
} }
-- mcl_villages.vl_to_mcla = {
-- maximum allowed difference in height for building a sttlement { '"mcl_core:tree"', '"mcl_trees:tree_oak"'},
-- { '"mcl_core:darktree"', '"mcl_trees:tree_dark_oak"'},
max_height_difference = 56 { '"mcl_core:wood"', '"mcl_trees:wood_oak"'},
-- { '"mcl_core:darkwood"', '"mcl_trees:wood_dark_oak"'},
-- { '"mcl_fences:fence', '"mcl_fences:oak_fence'},
-- { '"mcl_stairs:stair_wood"', '"mcl_stairs:stair_oak"'},
half_map_chunk_size = 40 { '"mcl_stairs:stair_wood_', '"mcl_stairs:stair_oak_'},
--quarter_map_chunk_size = 20 { '"mcl_stairs:slab_wood"', '"mcl_stairs:slab_oak"'},
{ '"mcl_stairs:slab_wood_', '"mcl_stairs:slab_oak_'},
{ '"mcl_doors:wooden_door_', '"mcl_doors:door_oak_'},
{ '"mcl_doors:trapdoor_', '"mcl_doors:trapdoor_oak_'},
{ '"xpanes:bar', '"mcl_panes:bar' },
{ '"xpanes:pane', '"mcl_panes:pane' },
{ '"mcl_itemframes:item_frame"', '"mcl_itemframes:frame"' },
{ '"mesecons_pressureplates:pressure_plate_wood_', '"mesecons_pressureplates:pressure_plate_oak_'},
-- tree types
{ '"mcl_core:([a-z]*)tree"', '"mcl_trees:tree_%1"'},
{ '"mcl_core:([a-z]*)wood"', '"mcl_trees:wood_%1"'},
{ '"mcl_stairs:stair_darkwood"', '"mcl_stairs:stair_dark_oak"'},
{ '"mcl_stairs:stair_([a-z]*)wood"', '"mcl_stairs:stair_%1"'},
{ '"mcl_bamboo:bamboo_fence', '"mcl_fences:bamboo_fence'},
{ '"mcl_bamboo:bamboo_plank', '"mcl_core:bamboowood'},
{ '"mcl_bamboo:bamboo_block', '"mcl_core:bambootree'},
{ '"mcl_stairs:stair_bamboo_plank', '"mcl_stairs:stair_bamboo'},
{ '"mcl_bamboo:pressure_plate_bamboo_wood_', '"mesecons_pressureplates:pressure_plate_bamboo_'},
{ '"mcl_bamboo:bamboo_trapdoor', '"mcl_doors:trapdoor_bamboo'},
{ '"mcl_bamboo:bamboo_door', '"mcl_doors:door_bamboo'},
}
mcl_villages.mcla_to_vl = {
-- bidirectional
{ '"mcl_trees:tree_oak"', '"mcl_core:tree"'},
{ '"mcl_trees:tree_dark_oak"', '"mcl_core:darktree"'},
{ '"mcl_trees:wood_oak"', '"mcl_core:wood"'},
{ '"mcl_trees:wood_dark_oak"', '"mcl_core:darkwood"'},
{ '"mcl_fences:oak_fence', '"mcl_fences:fence'},
{ '"mcl_stairs:stair_oak"', '"mcl_stairs:stair_wood"'},
{ '"mcl_stairs:stair_oak_bark', '"mcl_stairs:stair_tree_bark'},
{ '"mcl_stairs:stair_oak_', '"mcl_stairs:stair_wood_'},
{ '"mcl_stairs:slab_oak"', '"mcl_stairs:slab_wood"'},
{ '"mcl_stairs:slab_oak_', '"mcl_stairs:slab_wood_'},
{ '"mcl_doors:door_oak_', '"mcl_doors:wooden_door_'},
{ '"mcl_doors:trapdoor_oak_', '"mcl_doors:trapdoor_'},
{ '"mcl_panes:bar', '"xpanes:bar'},
{ '"mcl_panes:pane', '"xpanes:pane'},
{ '"mcl_itemframes:frame"', '"mcl_itemframes:item_frame"'},
{ '"mesecons_pressureplates:pressure_plate_oak_', '"mesecons_pressureplates:pressure_plate_wood_'},
-- tree types
{ '"mcl_trees:tree_([a-z]*)"', '"mcl_core:%1tree"'},
{ '"mcl_trees:wood_([a-z]*)"', '"mcl_core:%1wood"'},
{ '"mcl_stairs:stair_birch(["_])', '"mcl_stairs:stair_birchwood%1'},
{ '"mcl_stairs:stair_spruce(["_])', '"mcl_stairs:stair_sprucewood%1'},
{ '"mcl_stairs:stair_dark_oak(["_])', '"mcl_stairs:stair_darkwood%1'},
{ '"mcl_stairs:stair_jungle(["_])', '"mcl_stairs:stair_junglewood%1'},
{ '"mcl_stairs:stair_acacia(["_])', '"mcl_stairs:stair_acaciawood%1'},
{ '"mcl_fences:bamboo_fence', '"mcl_bamboo:bamboo_fence'},
{ '"mcl_core:bamboowood', '"mcl_bamboo:bamboo_plank'},
{ '"mcl_core:bambootree', '"mcl_bamboo:bamboo_block'},
{ '"mcl_stairs:stair_bamboo', '"mcl_stairs:stair_bamboo_plank'},
{ '"mesecons_pressureplates:pressure_plate_bamboo_', '"mcl_bamboo:pressure_plate_bamboo_wood_'},
{ '"mcl_doors:trapdoor_bamboo', '"mcl_bamboo:bamboo_trapdoor'},
{ '"mcl_doors:door_bamboo', '"mcl_bamboo:bamboo_door'},
}
mcl_villages.material_substitions = {
desert = {
{ '"mcl_stairs:slab_oak([^"]*)"', '"mcl_stairs:slab_sandstonesmooth2%1"' }, -- divert from MCLA, no version 1?
{
'"mesecons_pressureplates:pressure_plate_oak_([^"]+)"',
'"mesecons_pressureplates:pressure_plate_birchwood_%1"',
},
{ '"mcl_doors:trapdoor_oak([^"]*)"', '"mcl_doors:birch_trapdoor%1"' },
{ '"mcl_doors:door_oak([^"]*)"', '"mcl_doors:birch_door%1"' },
{ "mcl_core:cobble", "mcl_core:sandstone" },
{ '"mcl_stairs:stair_cobble([^"]*)"', '"mcl_stairs:stair_sandstone%1"' },
{ '"mcl_walls:cobble([^"]*)"', '"mcl_walls:sandstone%1"' },
{ '"mcl_stairs:slab_cobble([^"]*)"', '"mcl_stairs:slab_sandstone%1"' },
{ '"mcl_core:stonebrick"', '"mcl_core:redsandstone"' },
{ '"mcl_core:stonebrick_([^"]+)"', '"mcl_core:redsandstone_%1"' },
{ '"mcl_walls:stonebrick([^"]*)"', '"mcl_walls:redsandstone%1"' },
{ '"mcl_stairs:stair_stonebrick"', '"mcl_stairs:stair_redsandstone"' },
{ '"mcl_stairs:stair_stonebrick_([^"]+)"', '"mcl_stairs:stair_redsandstone_%1"' },
{ '"mcl_stairs:slab_brick_block([^"]*)"', '"mcl_core:redsandstonesmooth2%1"' },
{ '"mcl_core:brick_block"', '"mcl_core:redsandstonesmooth2"' },
{ "mcl_trees:tree_oak", "mcl_core:redsandstonecarved" },
{ "mcl_trees:wood_oak", "mcl_core:redsandstonesmooth" },
{ '"mcl_fences:oak_fence([^"]*)"', '"mcl_fences:birch_fence%1"' },
{ '"mcl_stairs:stair_oak_bark([^"]*)"', '"mcl_stairs:stair_sandstonesmooth2%1"' },
{ '"mcl_stairs:stair_oak([^"]*)"', '"mcl_stairs:stair_sandstonesmooth2%1"' }, -- divert from MCLA, no version 1?
{ '"mcl_core:leaves"', '"air"' }, -- addition to MCLA
},
spruce = {
{ '"mcl_stairs:slab_oak([^"]*)"', '"mcl_stairs:slab_sprucewood%1"' },
{
'"mesecons_pressureplates:pressure_plate_oak_([^"]+)"',
'"mesecons_pressureplates:pressure_plate_sprucewood_%1"',
},
{ '"mcl_doors:trapdoor_oak([^"]*)"', '"mcl_doors:spruce_trapdoor%1"' },
{ '"mcl_doors:door_oak([^"]*)"', '"mcl_doors:spruce_door%1"' },
{ "mcl_trees:tree_oak", "mcl_trees:tree_spruce" },
{ "mcl_trees:wood_oak", "mcl_trees:wood_spruce" },
{ '"mcl_fences:oak_fence([^"]*)"', '"mcl_fences:spruce_fence%1"' },
{ '"mcl_stairs:stair_oak([^"]*)"', '"mcl_stairs:stair_spruce%1"' },
{ '"mcl_core:leaves"', '"mcl_core:spruceleaves"' }, -- addition to MCLA
},
birch = {
{ '"mcl_stairs:slab_oak([^"]*)"', '"mcl_stairs:slab_birchwood%1"' },
{
'"mesecons_pressureplates:pressure_plate_oak_([^"]+)"',
'"mesecons_pressureplates:pressure_plate_birchwood_%1"',
},
{ '"mcl_doors:trapdoor_oak([^"]*)"', '"mcl_doors:birch_trapdoor%1"' },
{ '"mcl_doors:door_oak([^"]*)"', '"mcl_doors:birch_door%1"' },
{ "mcl_trees:tree_oak", "mcl_core:stripped_birch" }, -- divert from MCLA, use stripped birch, what is the name in MCLA?
{ "mcl_trees:wood_oak", "mcl_trees:wood_birch" },
{ '"mcl_fences:oak_fence([^"]*)"', '"mcl_fences:birch_fence%1"' },
{ '"mcl_stairs:stair_oak([^"]*)"', '"mcl_stairs:stair_birch%1"' },
{ '"mcl_core:leaves"', '"mcl_core:birchleaves"' }, -- addition to MCLA
},
acacia = {
{ '"mcl_stairs:slab_oak([^"]*)"', '"mcl_stairs:slab_acaciawood%1"' },
{
'"mesecons_pressureplates:pressure_plate_oak_([^"]+)"',
'"mesecons_pressureplates:pressure_plate_acaciawood_%1"',
},
{ '"mcl_doors:trapdoor_oak([^"]*)"', '"mcl_doors:acacia_trapdoor%1"' },
{ '"mcl_doors:door_oak([^"]*)"', '"mcl_doors:acacia_door%1"' },
{ "mcl_trees:tree_oak", "mcl_trees:tree_acacia" },
{ "mcl_trees:wood_oak", "mcl_trees:wood_acacia" },
{ '"mcl_fences:oak_fence([^"]*)"', '"mcl_fences:acacia_fence%1"' },
{ '"mcl_stairs:stair_oak([^"]*)"', '"mcl_stairs:stair_acacia%1"' },
{ '"mcl_core:leaves"', '"mcl_core:acacialeaves"' }, -- addition to MCLA
},
dark_oak = {
{ '"mcl_stairs:slab_oak([^"]*)"', '"mcl_stairs:slab_darkwood%1"' },
{
'"mesecons_pressureplates:pressure_plate_oak_([^"]+)"',
'"mesecons_pressureplates:pressure_plate_darkwood_%1"',
},
{ '"mcl_doors:trapdoor_oak([^"]*)"', '"mcl_doors:dark_oak_trapdoor%1"' },
{ '"mcl_doors:door_oak([^"]*)"', '"mcl_doors:dark_oak_door%1"' },
{ "mcl_trees:tree_oak", "mcl_trees:tree_dark_oak" },
{ "mcl_trees:wood_oak", "mcl_trees:wood_dark_oak" },
{ '"mcl_fences:oak_fence([^"]*)"', '"mcl_fences:dark_oak_fence%1"' },
{ '"mcl_stairs:stair_oak([^"]*)"', '"mcl_stairs:stair_dark_oak%1"' },
{ '"mcl_core:leaves"', '"mcl_core:darkleaves"' }, -- addition to MCLA
},
jungle = {
{ '"mcl_stairs:slab_oak([^"]*)"', '"mcl_stairs:slab_junglewood%1"' },
{
'"mesecons_pressureplates:pressure_plate_oak_([^"]+)"',
'"mesecons_pressureplates:pressure_plate_junglewood_%1"',
},
{ '"mcl_doors:trapdoor_oak([^"]*)"', '"mcl_doors:jungle_trapdoor%1"' },
{ '"mcl_doors:door_oak([^"]*)"', '"mcl_doors:jungle_door%1"' },
{ "mcl_trees:tree_oak", "mcl_trees:tree_jungle" },
{ "mcl_trees:wood_oak", "mcl_trees:wood_jungle" },
{ '"mcl_fences:oak_fence([^"]*)"', '"mcl_fences:jungle_fence%1"' },
{ '"mcl_stairs:stair_oak([^"]*)"', '"mcl_stairs:stair_jungle%1"' },
{ '"mcl_core:leaves"', '"mcl_core:jungleleaves"' }, -- addition to MCLA
},
bamboo = {
{ '"mcl_stairs:slab_oak([^"]*)"', '"mcl_stairs:slab_bamboo_plank%1"' }, -- divert from MCLA
{
'"mesecons_pressureplates:pressure_plate_oak_([^"]+)"',
'"mesecons_pressureplates:pressure_plate_bamboo_%1"',
},
{ '"mcl_doors:trapdoor_oak([^"]*)"', '"mcl_doors:trapdoor_bamboo%1"' },
{ '"mcl_doors:door_oak([^"]*)"', '"mcl_doors:door_bamboo%1"' },
{ "mcl_core:cobble", "mcl_core:andesite" },
{ '"mcl_stairs:stair_cobble([^"]*)"', '"mcl_stairs:stair_andesite%1"' },
{ '"mcl_walls:cobble([^"]*)"', '"mcl_walls:andesite%1"' },
{ '"mcl_stairs:slab_cobble([^"]*)"', '"mcl_stairs:slab_andesite%1"' },
{ "mcl_trees:tree_oak", "mcl_trees:tree_bamboo" },
{ "mcl_trees:wood_oak", "mcl_trees:wood_bamboo" },
{ '"mcl_fences:oak_fence([^"]*)"', '"mcl_fences:bamboo_fence%1"' },
{ '"mcl_stairs:stair_oak([^"]*)"', '"mcl_stairs:stair_bamboo%1"' },
{ '"mcl_core:leaves"', '"air"' }, -- addition to MCLA
},
cherry = {
{ '"mcl_stairs:slab_oak([^"]*)"', '"mcl_stairs:slab_cherry_blossom%1"' },
{
'"mesecons_pressureplates:pressure_plate_oak_([^"]+)"',
'"mesecons_pressureplates:pressure_plate_cherry_blossom_%1"',
},
{ '"mcl_doors:trapdoor_oak([^"]*)"', '"mcl_doors:trapdoor_cherry_blossom%1"' },
{ '"mcl_doors:door_oak([^"]*)"', '"mcl_doors:door_cherry_blossom%1"' },
{ "mcl_trees:tree_oak", "mcl_trees:tree_cherry_blossom" },
{ "mcl_trees:wood_oak", "mcl_trees:wood_cherry_blossom" },
{ '"mcl_fences:oak_fence([^"]*)"', '"mcl_fences:cherry_blossom_fence%1"' },
{ '"mcl_stairs:stair_oak([^"]*)"', '"mcl_stairs:stair_cherry_blossom%1"' },
{ '"mcl_core:leaves"', '"mcl_core:leaves"' }, -- addition to MCLA
},
}

View File

@ -1,90 +0,0 @@
local function mcl_log (message)
mcl_util.mcl_log (message, "[Village - Foundation]")
end
local foundation_materials = {}
foundation_materials["mcl_core:sand"] = "mcl_core:sandstone"
--"mcl_core:sandstonecarved"
-------------------------------------------------------------------------------
-- function to fill empty space below baseplate when building on a hill
-------------------------------------------------------------------------------
function settlements.ground(pos, pr, platform_material) -- role model: Wendelsteinkircherl, Brannenburg
local p2 = vector.new(pos)
local cnt = 0
local mat = "mcl_core:dirt"
if not platform_material then
mat = "mcl_core:dirt"
else
mat = platform_material
end
p2.y = p2.y-1
while true do
cnt = cnt+1
if cnt > 20 then break end
if cnt>pr:next(2,4) then
if not platform_material then
mat = "mcl_core:stone"
end
end
minetest.swap_node(p2, {name=mat})
p2.y = p2.y-1
end
end
-------------------------------------------------------------------------------
-- function clear space above baseplate
-------------------------------------------------------------------------------
function settlements.terraform(settlement_info, pr)
local fheight, fwidth, fdepth, schematic_data
for i, built_house in ipairs(settlement_info) do
-- pick right schematic_info to current built_house
for j, schem in ipairs(settlements.schematic_table) do
if settlement_info[i]["name"] == schem["name"] then
schematic_data = schem
break
end
end
local pos = settlement_info[i]["pos"]
if settlement_info[i]["rotat"] == "0" or settlement_info[i]["rotat"] == "180" then
fwidth = schematic_data["hwidth"]
fdepth = schematic_data["hdepth"]
else
fwidth = schematic_data["hdepth"]
fdepth = schematic_data["hwidth"]
end
--fheight = schematic_data["hheight"] * 3 -- remove trees and leaves above
fheight = schematic_data["hheight"] -- remove trees and leaves above
local surface_mat = settlement_info[i]["surface_mat"]
mcl_log("Surface material: " .. tostring(surface_mat))
local platform_mat = foundation_materials[surface_mat]
mcl_log("Foundation material: " .. tostring(platform_mat))
--
-- now that every info is available -> create platform and clear space above
--
for xi = 0,fwidth-1 do
for zi = 0,fdepth-1 do
for yi = 0,fheight *3 do
if yi == 0 then
local p = {x=pos.x+xi, y=pos.y, z=pos.z+zi}
-- Pass in biome info and make foundations of same material (seed: apple for desert)
settlements.ground(p, pr, platform_mat)
else
-- write ground
-- local p = {x=pos.x+xi, y=pos.y+yi, z=pos.z+zi}
-- local node = mcl_vars.get_node(p)
-- if node and node.name ~= "air" then
-- minetest.swap_node(p,{name="air"})
-- end
minetest.swap_node({x=pos.x+xi, y=pos.y+yi, z=pos.z+zi},{name="air"})
end
end
end
end
end
end

View File

@ -1,79 +1,31 @@
settlements = {} mcl_villages = {}
settlements.modpath = minetest.get_modpath(minetest.get_current_modname()) mcl_villages.modpath = minetest.get_modpath(minetest.get_current_modname())
dofile(settlements.modpath.."/const.lua") local village_boost = tonumber(minetest.settings:get("vl_villages_boost")) or 1
dofile(settlements.modpath.."/utils.lua")
dofile(settlements.modpath.."/foundation.lua") dofile(mcl_villages.modpath.."/const.lua")
dofile(settlements.modpath.."/buildings.lua") dofile(mcl_villages.modpath.."/utils.lua")
dofile(settlements.modpath.."/paths.lua") dofile(mcl_villages.modpath.."/buildings.lua")
--dofile(settlements.modpath.."/convert_lua_mts.lua") dofile(mcl_villages.modpath.."/paths.lua")
-- dofile(mcl_villages.modpath.."/api.lua")
-- load settlements on server
--
settlements.grundstellungen()
local S = minetest.get_translator(minetest.get_current_modname()) local S = minetest.get_translator(minetest.get_current_modname())
local villagegen={}
--
-- register block for npc spawn
--
minetest.register_node("mcl_villages:stonebrickcarved", {
description = S("Chiseled Stone Village Bricks"),
_doc_items_longdesc = doc.sub.items.temp.build,
tiles = {"mcl_core_stonebrick_carved.png"},
drop = "mcl_core:stonebrickcarved",
groups = {pickaxey=1, stone=1, stonebrick=1, building_block=1, material_stone=1},
sounds = mcl_sounds.node_sound_stone_defaults(),
is_ground_content = false,
_mcl_blast_resistance = 6,
_mcl_hardness = 1.5,
})
minetest.register_node("mcl_villages:structblock", {drawtype="airlike",groups = {not_in_creative_inventory=1},})
--[[ Enable for testing, but use MineClone2's own spawn code if/when merging.
--
-- register inhabitants
--
if minetest.get_modpath("mobs_mc") then
mcl_mobs:register_spawn("mobs_mc:villager", --name
{"mcl_core:stonebrickcarved"}, --nodes
15, --max_light
0, --min_light
20, --chance
7, --active_object_count
31000, --max_height
nil) --day_toggle
end
--]]
--
-- on map generation, try to build a settlement
--
local function build_a_settlement(minp, maxp, blockseed)
local pr = PseudoRandom(blockseed)
-- fill settlement_info with buildings and their data
local settlement_info = settlements.create_site_plan(maxp, minp, pr)
if not settlement_info then return end
-- evaluate settlement_info and prepair terrain
settlements.terraform(settlement_info, pr)
-- evaluate settlement_info and build paths between buildings
settlements.paths(settlement_info)
-- evaluate settlement_info and place schematics
settlements.place_schematics(settlement_info, pr)
end
local function ecb_village(blockpos, action, calls_remaining, param) local function ecb_village(blockpos, action, calls_remaining, param)
if calls_remaining >= 1 then return end if calls_remaining >= 1 then return end
local minp, maxp, blockseed = param.minp, param.maxp, param.blockseed if mcl_villages.village_exists(param.blockseed) then return end
build_a_settlement(minp, maxp, blockseed) local pr = PcgRandom(param.blockseed)
local vm = VoxelManip(param.minp, param.maxp)
local settlement = mcl_villages.create_site_plan(vm, param.minp, param.maxp, pr)
if not settlement then return false, false end
-- all foundations first, then all buildings, to avoid damaging very close buildings
mcl_villages.terraform(vm, settlement, pr)
mcl_villages.place_schematics(vm, settlement, param.blockseed, pr)
mcl_villages.add_village(param.blockseed, settlement)
--lvm:write_to_map(true) -- destorys paths as of now, as they are placed afterwards
for _, on_village_placed_callback in pairs(mcl_villages.on_village_placed) do
on_village_placed_callback(settlement, param.blockseed)
end
end end
-- Disable natural generation in singlenode. -- Disable natural generation in singlenode.
@ -81,61 +33,430 @@ local mg_name = minetest.get_mapgen_setting("mg_name")
if mg_name ~= "singlenode" then if mg_name ~= "singlenode" then
mcl_mapgen_core.register_generator("villages", nil, function(minp, maxp, blockseed) mcl_mapgen_core.register_generator("villages", nil, function(minp, maxp, blockseed)
if maxp.y < 0 then return end if maxp.y < 0 then return end
if village_boost == 0 then return end
-- randomly try to build settlements local pr = PcgRandom(blockseed)
if blockseed % 77 ~= 17 then return end if pr:next(0,1e9) * 100e-9 >= village_boost then return end
--minetest.log("Rng good. Generate attempt") if village_boost < 25 then -- otherwise, this tends to transitively emerge too much
minp, maxp = vector.offset(minp, -16, 0, -16), vector.offset(maxp, 16, 0, 16)
-- needed for manual and automated settlement building
-- don't build settlements on (too) uneven terrain
local n=minetest.get_node_or_nil(minp)
if n and n.name == "mcl_villages:structblock" then return end
--minetest.log("No existing village attempt here")
if villagegen[minetest.pos_to_string(minp)] ~= nil then return end
--minetest.log("Not in village gen. Put down placeholder: " .. minetest.pos_to_string(minp) .. " || " .. minetest.pos_to_string(maxp))
minetest.set_node(minp,{name="mcl_villages:structblock"})
local height_difference = settlements.evaluate_heightmap()
if not height_difference or height_difference > max_height_difference then
minetest.log("action", "Do not spawn village here as heightmap not good")
return
end end
--minetest.log("Build me a village: " .. minetest.pos_to_string(minp) .. " || " .. minetest.pos_to_string(maxp)) minetest.emerge_area(minp, maxp, ecb_village, { minp = minp, maxp = maxp, blockseed = blockseed })
villagegen[minetest.pos_to_string(minp)]={minp=vector.new(minp), maxp=vector.new(maxp), blockseed=blockseed}
end) end)
end end
-- Handle legacy structblocks that are not fully emerged yet.
minetest.register_node("mcl_villages:structblock", {drawtype="airlike",groups = {not_in_creative_inventory=1}})
minetest.register_lbm({ minetest.register_lbm({
name = "mcl_villages:structblock", name = "mcl_villages:structblock",
run_at_every_load = true, run_at_every_load = true,
nodenames = {"mcl_villages:structblock"}, nodenames = {"mcl_villages:structblock"},
action = function(pos, node) action = function(pos, node)
minetest.set_node(pos, {name = "air"}) minetest.set_node(pos, {name = "air"})
if not villagegen[minetest.pos_to_string(pos)] then return end local minp, maxp = vector.offset(pos, -40, -40, -40), vector.offset(pos, 40, 40, 40)
local minp=villagegen[minetest.pos_to_string(pos)].minp local blockseed = PcgRandom(minetest.hash_node_position(pos)):next()
local maxp=villagegen[minetest.pos_to_string(pos)].maxp minetest.emerge_area(minp, maxp, ecb_village, { minp = minp, maxp = maxp, blockseed = blockseed})
minetest.emerge_area(minp, maxp, ecb_village, villagegen[minetest.pos_to_string(minp)])
villagegen[minetest.pos_to_string(minp)]=nil
end end
}) })
-- manually place villages -- manually place villages
if minetest.is_creative_enabled("") then if minetest.is_creative_enabled("") then
minetest.register_craftitem("mcl_villages:tool", { minetest.register_craftitem("mcl_villages:tool", {
description = S("mcl_villages build tool"), description = S("mcl_villages build tool"),
inventory_image = "default_tool_woodshovel.png", inventory_image = "default_tool_woodshovel.png",
-- build ssettlement -- build settlement
on_place = function(itemstack, placer, pointed_thing) on_place = function(itemstack, placer, pointed_thing)
if not pointed_thing.under then return end if not pointed_thing.under then return end
if not minetest.check_player_privs(placer, "server") then if not minetest.check_player_privs(placer, "server") then
minetest.chat_send_player(placer:get_player_name(), S("Placement denied. You need the “server” privilege to place villages.")) minetest.chat_send_player(placer:get_player_name(), S("Placement denied. You need the “server” privilege to place villages."))
return return
end end
local minp = vector.subtract( pointed_thing.under, half_map_chunk_size) local pos = pointed_thing.under
local maxp = vector.add( pointed_thing.under, half_map_chunk_size) local minp, maxp = vector.offset(pos, -40, -40, -40), vector.offset(pos, 40, 40, 40)
build_a_settlement(minp, maxp, math.random(0,32767)) local blockseed = PcgRandom(minetest.hash_node_position(pos)):next()
minetest.emerge_area(minp, maxp, ecb_village, { minp = minp, maxp = maxp, blockseed = blockseed })
end end
}) })
mcl_wip.register_experimental_item("mcl_villages:tool") mcl_wip.register_experimental_item("mcl_villages:tool")
end end
-- This makes the temporary node invisble unless in creative mode
local drawtype = minetest.is_creative_enabled("") and "glasslike" or "airlike"
-- Special node for schematics editing: no path on this place
minetest.register_node("mcl_villages:no_paths", {
description = S("Prevent paths from being placed during villager generation. Replaced by air after village path generation"),
paramtype = "light",
drawtype = drawtype,
inventory_image = "mcl_core_barrier.png",
wield_image = "mcl_core_barrier.png",
tiles = { "mcl_core_barrier.png" },
is_ground_content = false,
groups = { creative_breakable = 1, not_solid = 1, not_in_creative_inventory = 1 },
})
-- Special node for schematics editing: path endpoint
minetest.register_node("mcl_villages:path_endpoint", {
description = S("Mark the node as a good place for paths to connect to"),
is_ground_content = false,
tiles = { "wool_white.png" },
wield_image = "wool_white.png",
wield_scale = { x = 1, y = 1, z = 0.5 },
groups = { handy = 1, supported_node = 1, not_in_creative_inventory = 1 },
sounds = mcl_sounds.node_sound_wool_defaults(),
paramtype = "light",
sunlight_propagates = true,
drawtype = "nodebox",
node_box = { type = "fixed", fixed = { { -0.5, -0.5, -0.5, 0.5, -0.45, 0.5 } } },
_mcl_hardness = 0.1,
_mcl_blast_resistance = 0.1,
})
local schem_path = mcl_villages.modpath .. "/schematics/"
mcl_villages.register_bell({ name = "belltower", mts = schem_path .. "new_villages/belltower.mts", yadjust = 1 })
mcl_villages.register_well({
name = "well",
mts = schem_path .. "new_villages/well.mts",
yadjust = -1,
rotation_offset = 3, -- lamp is east
})
for i = 1, 6 do
mcl_villages.register_lamp({
name = "lamp",
mts = schem_path .. "new_villages/lamp_" .. i .. ".mts",
yadjust = 1,
no_ground_turnip = true,
no_clearance = true,
})
end
mcl_villages.register_building({
name = "house_big",
mts = schem_path .. "new_villages/house_4_bed.mts",
min_jobs = 6,
max_jobs = 99,
yadjust = 1,
})
mcl_villages.register_building({
name = "house_large",
mts = schem_path .. "new_villages/house_3_bed.mts",
min_jobs = 4,
max_jobs = 99,
yadjust = 1,
})
mcl_villages.register_building({
name = "house_medium",
mts = schem_path .. "new_villages/house_2_bed.mts",
min_jobs = 2,
max_jobs = 99,
yadjust = 1,
})
mcl_villages.register_building({
name = "house_chimney",
mts = schem_path .. "haeuschen2.mts",
min_jobs = 2,
max_jobs = 99,
yadjust = 1,
rotation_offset = 1, -- entrance is west
})
mcl_villages.register_building({
name = "house_small",
mts = schem_path .. "new_villages/house_1_bed.mts",
min_jobs = 1,
max_jobs = 99,
yadjust = 1,
})
mcl_villages.register_building({
name = "blacksmith",
mts = schem_path .. "new_villages/blacksmith.mts",
num_others = 8,
yadjust = 1,
rotation_offset = 1, -- entrance is west
})
mcl_villages.register_building({
name = "butcher",
mts = schem_path .. "new_villages/butcher.mts",
num_others = 8,
yadjust = 1,
rotation_offset = 0, -- entrance is north
})
mcl_villages.register_building({
name = "farm",
mts = schem_path .. "new_villages/farm.mts",
num_others = 3,
yadjust = 1,
rotation_offset = 3, -- composters are east
})
mcl_villages.register_building({
name = "fish_farm",
mts = schem_path .. "new_villages/fishery.mts",
num_others = 8,
yadjust = -2,
rotation_offset = 2, -- entrances are east and west
})
mcl_villages.register_building({
name = "fletcher_tiny",
group = "g:fletcher",
mts = schem_path .. "bogner.mts",
num_others = 8,
max_jobs = 6,
yadjust = 0,
rotation_offset = 1, -- entrance is west
})
mcl_villages.register_building({
name = "fletcher",
group = "g:fletcher",
mts = schem_path .. "new_villages/fletcher.mts",
num_others = 8,
min_jobs = 7,
yadjust = 1,
rotation_offset = 3, -- entrance is east
})
mcl_villages.register_building({
name = "library",
group = "g:library",
mts = schem_path .. "new_villages/library.mts",
min_jobs = 12,
max_jobs = 99,
num_others = 15,
yadjust = 1,
})
mcl_villages.register_building({
name = "librarian",
group = "g:library",
mts = schem_path .. "schreiber.mts",
min_jobs = 1,
max_jobs = 11,
yadjust = 0,
rotation_offset = 1, -- entrance is west
})
mcl_villages.register_building({
name = "map_shop",
mts = schem_path .. "new_villages/cartographer.mts",
num_others = 15,
yadjust = 1,
rotation_offset = 1, -- entrance is west
})
mcl_villages.register_building({
name = "mason",
mts = schem_path .. "new_villages/mason.mts",
num_others = 8,
yadjust = 1,
rotation_offset = 1, -- entrance is west
})
mcl_villages.register_building({
name = "mill",
mts = schem_path .. "new_villages/mill.mts",
num_others = 8,
yadjust = 1,
})
mcl_villages.register_building({
name = "tannery",
mts = schem_path .. "new_villages/leather_worker.mts",
num_others = 8,
yadjust = 1,
rotation_offset = 1, -- entrance is west
})
mcl_villages.register_building({
name = "tool_smith",
mts = schem_path .. "new_villages/toolsmith.mts",
num_others = 8,
yadjust = 1,
rotation_offset = 1, -- entrance is west
})
mcl_villages.register_building({
name = "weapon_smith",
mts = schem_path .. "new_villages/weaponsmith.mts",
num_others = 8,
yadjust = 1,
rotation_offset = 0, -- entrance is north
})
mcl_villages.register_building({
name = "chapel",
group = "g:church",
mts = schem_path .. "new_villages/chapel.mts",
num_others = 8,
min_jobs = 1,
max_jobs = 9,
yadjust = 1,
})
mcl_villages.register_building({
name = "church_european",
group = "g:church",
mts = schem_path .. "kirche.mts",
num_others = 20,
min_jobs = 8,
max_jobs = 99,
yadjust = 0,
rotation_offset = 2, -- entrance is west, but tower is south
})
mcl_villages.register_building({
name = "church",
group = "g:church",
mts = schem_path .. "new_villages/church.mts",
num_others = 20,
min_jobs = 8,
max_jobs = 99,
yadjust = 1,
})
mcl_villages.register_building({
name = "farm_small",
mts = schem_path .. "new_villages/farm_small_1.mts",
num_others = 3,
yadjust = 1,
rotation_offset = 3, -- composters are south west?
})
mcl_villages.register_building({
name = "farm_small2",
mts = schem_path .. "new_villages/farm_small_2.mts",
num_others = 3,
yadjust = 1,
rotation_offset = 3, -- composters are south west?
})
mcl_villages.register_building({
name = "farm_large",
mts = schem_path .. "new_villages/farm_large_1.mts",
num_others = 6,
yadjust = 1,
rotation_offset = 3, -- composters are east
})
mcl_villages.register_crop({
type = "grain",
node = "mcl_farming:wheat_1",
biomes = {
acacia = 10,
bamboo = 10,
desert = 10,
jungle = 10,
plains = 10,
savanna = 10,
spruce = 10,
},
})
mcl_villages.register_crop({
type = "root",
node = "mcl_farming:carrot_1",
biomes = {
acacia = 10,
bamboo = 6,
desert = 10,
jungle = 6,
plains = 6,
spruce = 10,
},
})
mcl_villages.register_crop({
type = "root",
node = "mcl_farming:potato_1",
biomes = {
acacia = 6,
bamboo = 10,
desert = 6,
jungle = 10,
plains = 10,
spruce = 6,
},
})
mcl_villages.register_crop({
type = "root",
node = "mcl_farming:beetroot_0",
biomes = {
acacia = 3,
bamboo = 3,
desert = 3,
jungle = 3,
plains = 3,
spruce = 3,
},
})
mcl_villages.register_crop({
type = "gourd",
node = "mcl_farming:melontige_1",
biomes = {
bamboo = 10,
jungle = 10,
},
})
mcl_villages.register_crop({
type = "gourd",
node = "mcl_farming:pumpkin_1",
biomes = {
acacia = 10,
bamboo = 5,
desert = 10,
jungle = 5,
plains = 10,
spruce = 10,
},
})
-- TODO: make flowers biome-specific
for name, def in pairs(minetest.registered_nodes) do
if def.groups.flower and not def.groups.double_plant and name ~= "mcl_flowers:wither_rose" then
mcl_villages.register_crop({
type = "flower",
node = name,
biomes = {
acacia = 10,
bamboo = 6,
desert = 10,
jungle = 6,
plains = 6,
spruce = 10,
},
})
end
end
-- Crop placeholder nodes at different growth stages, for designing schematics
for _, crop_type in ipairs(mcl_villages.get_crop_types()) do
for count = 1, 8 do
local tile = crop_type .. "_" .. count .. ".png"
minetest.register_node("mcl_villages:crop_" .. crop_type .. "_" .. count, {
description = S("A place to plant @1 crops", crop_type),
is_ground_content = false,
tiles = { tile },
wield_image = tile,
wield_scale = { x = 1, y = 1, z = 0.5 },
groups = { handy = 1, supported_node = 1, not_in_creative_inventory = 1 },
paramtype = "light",
sunlight_propagates = true,
drawtype = "nodebox",
node_box = { type = "fixed", fixed = { { -0.5, -0.5, -0.5, 0.5, -0.45, 0.5 } } },
_mcl_hardness = 0.1,
_mcl_blast_resistance = 0.1,
})
end
end

View File

@ -1,5 +1,5 @@
name = mcl_villages name = mcl_villages
author = Rochambeau author = Rochambeau, kno10
description = This mod adds settlements on world generation. description = This mod adds settlements on world generation.
depends = mcl_util, mcl_mapgen_core, mcl_structures, mcl_core, mcl_loot depends = mcl_core, mcl_util, mcl_mapgen_core, mcl_structures, mcl_loot, mobs_mc, vl_terraforming
optional_depends = mcl_farming, mobs_mc optional_depends = mcl_farming

View File

@ -1,91 +1,373 @@
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- generate paths between buildings -- generate paths between buildings
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
function settlements.paths(settlement_info) local light_threshold = tonumber(minetest.settings:get("mcl_villages_light_threshold")) or 5
local starting_point
local end_point
local distance
--for k,v in pairs(settlement_info) do
starting_point = settlement_info[1]["pos"]
for o,p in pairs(settlement_info) do
end_point = settlement_info[o]["pos"] -- This ends up being a nested table.
if starting_point ~= end_point -- 1st level is the blockseed which is the village
then -- 2nd is the distance of the building from the bell
-- loop until end_point is reched (distance == 0) -- 3rd is the pos of the end points
while true do local path_ends = {}
-- define surrounding pos to starting_point -- note: not using LVM here, as this runs after the pathfinder
local north_p = {x=starting_point.x+1, y=starting_point.y, z=starting_point.z} -- simple function to increase "no_paths" walls
local south_p = {x=starting_point.x-1, y=starting_point.y, z=starting_point.z} function mcl_villages.clean_no_paths(minp, maxp)
local west_p = {x=starting_point.x, y=starting_point.y, z=starting_point.z+1} local no_paths_nodes = minetest.find_nodes_in_area(minp, maxp, { "mcl_villages:no_paths" })
local east_p = {x=starting_point.x, y=starting_point.y, z=starting_point.z-1} if #no_paths_nodes > 0 then
-- measure distance to end_point minetest.bulk_swap_node(no_paths_nodes, { name = "air" })
local dist_north_p_to_end = math.sqrt( end
((north_p.x - end_point.x)*(north_p.x - end_point.x))+ end
((north_p.z - end_point.z)*(north_p.z - end_point.z))
) -- this can still run in LVM
local dist_south_p_to_end = math.sqrt( -- simple function to increase "no_paths" walls
((south_p.x - end_point.x)*(south_p.x - end_point.x))+ function mcl_villages.increase_no_paths(vm, minp, maxp)
((south_p.z - end_point.z)*(south_p.z - end_point.z)) local p = vector.zero()
) for z = minp.z, maxp.z do
local dist_west_p_to_end = math.sqrt( p.z = z
((west_p.x - end_point.x)*(west_p.x - end_point.x))+ for x = minp.x, maxp.x do
((west_p.z - end_point.z)*(west_p.z - end_point.z)) p.x = x
) for y = minp.y, maxp.y - 1 do
local dist_east_p_to_end = math.sqrt( p.y = y
((east_p.x - end_point.x)*(east_p.x - end_point.x))+ local n = vm:get_node_at(p)
((east_p.z - end_point.z)*(east_p.z - end_point.z)) if n and n.name == "mcl_villages:no_paths" then
) p.y = y + 1
-- evaluate which pos is closer to the end_point n = vm:get_node_at(p)
if dist_north_p_to_end <= dist_south_p_to_end and if n and n.name == "air" then
dist_north_p_to_end <= dist_west_p_to_end and vm:set_node_at(p, {name = "mcl_villages:no_paths" })
dist_north_p_to_end <= dist_east_p_to_end end
then end
starting_point = north_p end
distance = dist_north_p_to_end end
end
elseif dist_south_p_to_end <= dist_north_p_to_end and end
dist_south_p_to_end <= dist_west_p_to_end and
dist_south_p_to_end <= dist_east_p_to_end -- Insert end points in to the nested tables
then function mcl_villages.store_path_ends(vm, minp, maxp, pos, blockseed, bell_pos)
starting_point = south_p -- We store by distance because we create paths far away from the bell first
distance = dist_south_p_to_end local dist = vector.distance(bell_pos, pos)
local id = "block_" .. blockseed -- cannot use integers as keys
elseif dist_west_p_to_end <= dist_north_p_to_end and -- TODO: benchmark best way
dist_west_p_to_end <= dist_south_p_to_end and local tab = {}
dist_west_p_to_end <= dist_east_p_to_end local v = vector.zero()
then for zi = minp.z, maxp.z do
starting_point = west_p v.z = zi
distance = dist_west_p_to_end for yi = minp.y, maxp.y do
v.y = yi
elseif dist_east_p_to_end <= dist_north_p_to_end and for xi = minp.x, maxp.x do
dist_east_p_to_end <= dist_south_p_to_end and v.x = xi
dist_east_p_to_end <= dist_west_p_to_end local n = vm:get_node_at(v)
then if n and n.name == "mcl_villages:path_endpoint" then
starting_point = east_p table.insert(tab, vector.copy(v))
distance = dist_east_p_to_end vm:set_node_at(v, { name = "air" })
end end
-- find surface of new starting point end
local surface_point, surface_mat = settlements.find_surface(starting_point) end
-- replace surface node with mcl_core:grass_path end
if surface_point if not path_ends[id] then path_ends[id] = {} end
then table.insert(path_ends[id], {dist, minetest.pos_to_string(pos), tab})
if surface_mat == "mcl_core:sand" or surface_mat == "mcl_core:redsand" then end
minetest.swap_node(surface_point,{name="mcl_core:sandstonesmooth2"})
else local function place_lamp(pos, pr)
minetest.swap_node(surface_point,{name="mcl_core:grass_path"}) local lamp_index = pr:next(1, #mcl_villages.schematic_lamps)
end local schema = mcl_villages.schematic_lamps[lamp_index]
-- don't set y coordinate, surface might be too low or high local schem_lua = mcl_villages.substitute_materials(pos, schema.schem_lua, pr)
starting_point.x = surface_point.x local schematic = loadstring(schem_lua)()
starting_point.z = surface_point.z
end minetest.place_schematic(vector.offset(pos, 0, schema.yadjust or 0, 0), schematic, "0",
if distance <= 1 or {["air"] = "ignore"}, -- avoid destroying stairs etc.
starting_point == end_point true,
then { place_center_x = true, place_center_y = false, place_center_z = true }
break )
end end
end
end -- TODO: port this to lvm.
end local function smooth_path(path, passes, minp, maxp)
-- bridge over water/laver
for i = 2, #path - 1 do
while true do
local cur = path[i]
local node = minetest.get_node(cur).name
if node == "air" and vector.in_area(cur, minp, maxp) then
local under = minetest.get_node(vector.offset(path[i], 0, -1, 0)).name
local udef = minetest.registered_nodes[under]
-- do not build paths over leaves
if udef and udef.groups.leaves then
minetest.swap_node(path[i], {name="mcl_villages:no_paths"})
return -- bad path
end
break
else
local ndef = minetest.registered_nodes[node]
if not ndef then break end -- ignore
if (ndef.groups.water or 0) > 0 or (ndef.groups.lava or 0) > 0 then
cur.y = cur.y + 1
else
break
end
end
end
end
-- Smooth out bumps in path to reduce weird stairs
local any_changed = false
for pass = 1, passes do
local changed = false
for i = 2, #path - 1 do
local prev_y = path[i - 1].y
local y = path[i].y
local next_y = path[i + 1].y
local bump = minetest.get_node(path[i]).name
local bdef = minetest.registered_nodes[bump]
-- TODO: also replace bamboo underneath with dirt here?
if bdef and ((bdef.groups.water or 0) > 0 or (bdef.groups.lava or 0) > 0) then
-- ignore in this pass
elseif (y > next_y + 1 and y <= prev_y) -- large step
or (y > prev_y + 1 and y <= next_y) -- large step
or (y > prev_y and y > next_y) then
-- Remove peaks to flatten path
path[i].y = math.max(prev_y, next_y)
minetest.swap_node(path[i], { name = "air" })
changed = true
elseif (y < next_y - 1 and y >= prev_y) -- large step
or (y < prev_y - 1 and y >= next_y) -- large step
or (y < prev_y and y < next_y) then
-- Fill in dips to flatten path
path[i].y = math.min(prev_y, next_y) - 1 -- to replace below first
minetest.swap_node(path[i], { name = "mcl_core:dirt" }) -- todo: use sand/sandstone in desert?, use slabs?
path[i].y = path[i].y + 1 -- above dirt
changed = true
end
end
if changed then any_changed = true else break end
end
-- by delaying this, we allow making bridges over deep dips:
--[[
if any_changed then
-- we may not yet have filled a gap
for i = 2, #path - 1 do
local below = vector.offset(path[y], 0, -1, 0)
local bdef = minetest.registered_nodes[minetest.get_node(path[i]).name]
if bdef and not bdef.walkable then
minetest.swap_node(path[i], { name = "mcl_core:dirt" }) -- todo: use sand/sandstone in desert?, use slabs?
end
end
end]]
return path
end
-- TODO: port this to lvm.
local function place_path(path, pr, stair, slab)
-- find water/lava below
for i = 2, #path - 1 do
local prev_y = path[i - 1].y
local y = path[i].y
local next_y = path[i + 1].y
local bump = minetest.get_node(path[i]).name
local bdef = minetest.registered_nodes[bump]
if bdef and ((bdef.groups.water or 0) > 0 or (bdef.groups.lava or 0) > 0) then
-- Find air
local up_pos = vector.copy(path[i])
while true do
up_pos.y = up_pos.y + 1
local up_node = minetest.get_node(up_pos).name
local udef = minetest.registered_nodes[up_node]
if udef and (udef.groups.water or 0) == 0 and (udef.groups.lava or 0) == 0 then
minetest.swap_node(up_pos, { name = "air" })
path[i] = up_pos
break
elseif not udef then break end -- ignore node encountered
end
end
end
for i, pos in ipairs(path) do
local n0 = minetest.get_node(pos).name
if n0 ~= "air" then minetest.swap_node(pos, { name = "air" }) end
local under_pos = vector.offset(pos, 0, -1, 0)
local n = minetest.get_node(under_pos).name
local ndef = minetest.registered_nodes[n]
local groups = ndef and ndef.groups or {}
local done = false
if i > 1 and pos.y > path[i - 1].y then
-- stairs up
if not groups.stair then
done = true
local param2 = minetest.dir_to_facedir(vector.subtract(pos, path[i - 1]))
minetest.swap_node(under_pos, { name = stair, param2 = param2 })
end
elseif i < #path-1 and pos.y > path[i + 1].y then
-- stairs down
if not groups.stair then
done = true
local param2 = minetest.dir_to_facedir(vector.subtract(pos, path[i + 1]))
minetest.swap_node(under_pos, { name = stair, param2 = param2 })
end
elseif not groups.stair and i > 1 and pos.y < path[i - 1].y then
-- stairs down
local n2 = minetest.get_node(vector.offset(path[i - 1], 0, -1, 0)).name
if not minetest.get_item_group(n2, "stair") then
done = true
local param2 = minetest.dir_to_facedir(vector.subtract(path[i - 1], pos))
if i < #path - 1 then -- uglier, but easier to walk up?
param2 = minetest.dir_to_facedir(vector.subtract(pos, path[i + 1]))
end
minetest.add_node(pos, { name = stair, param2 = param2 })
pos.y = pos.y + 1
end
elseif not groups.stair and i < #path-1 and pos.y < path[i + 1].y then
-- stairs up
local n2 = minetest.get_node(vector.offset(path[i + 1], 0, -1, 0)).name
if not minetest.get_item_group(n2, "stair") then
done = true
local param2 = minetest.dir_to_facedir(vector.subtract(path[i + 1], pos))
if i > 1 then -- uglier, but easier to walk up?
param2 = minetest.dir_to_facedir(vector.subtract(pos, path[i - 1]))
end
minetest.add_node(pos, { name = stair, param2 = param2 })
pos.y = pos.y + 1
end
end
-- flat
if not done then
if groups.water then
minetest.add_node(under_pos, { name = slab })
elseif groups.lava then
minetest.add_node(under_pos, { name = "mcl_stairs:slab_stone" })
elseif groups.sand then
minetest.swap_node(under_pos, { name = "mcl_core:sandstonesmooth2" })
elseif groups.soil and not groups.dirtifies_below_solid then
minetest.swap_node(under_pos, { name = "mcl_core:grass_path" })
end
end
-- Clear space for villagers to walk
for j = 1, 2 do
local over_pos = vector.offset(pos, 0, j, 0)
if minetest.get_node(over_pos).name ~= "air" then
minetest.swap_node(over_pos, { name = "air" })
end
end
end
-- Do lamps afterwards so we don't put them where a path will be laid
for _, pos in ipairs(path) do
if minetest.get_node_light(pos, 0) < light_threshold then
local nn = minetest.find_nodes_in_area_under_air(vector.offset(pos, -1, -1, -1), vector.offset(pos, 1, 1, 1),
{ "group:material_sand", "group:material_stone", "group:grass_block", "group:wood_slab" }
)
-- todo: shuffle nn?
for _, npos in ipairs(nn) do
local node = minetest.get_node(npos).name
if node ~= "mcl_core:grass_path" and minetest.get_item_group(node, "stair") == 0 then
if minetest.get_item_group(node, "wood_slab") ~= 0 then
minetest.add_node(vector.offset(npos, 0, 1, 0), { name = "mcl_torches:torch", param2 = 1 })
else
place_lamp(npos, pr)
end
break
end
end
end
end
end
-- FIXME: ugly
function get_biome_stair_slab(biome_name)
-- Use the same stair and slab throughout the entire village
-- The quotes are necessary to be matched as JSON strings
local stair, slab = '"mcl_stairs:stair_oak"', '"mcl_stairs:slab_oak_top"'
-- Change stair and slab for biome
if mcl_villages.biome_map[biome_name] and mcl_villages.material_substitions[mcl_villages.biome_map[biome_name]] then
for _, sub in pairs(mcl_villages.material_substitions[mcl_villages.biome_map[biome_name]]) do
stair = stair:gsub(sub[1], sub[2])
slab = slab:gsub(sub[1], sub[2])
end
end
-- translate MCLA values to VL
for _, sub in pairs(mcl_villages.mcla_to_vl) do
stair = stair:gsub(sub[1], sub[2])
slab = slab:gsub(sub[1], sub[2])
end
-- The quotes are to match what is in JSON schemas, but we don't want them now
return stair:gsub('"', ""), slab:gsub('"', "")
end
-- Work out which end points should be connected
-- works from the outside of the village in
function mcl_villages.paths(blockseed, biome_name, minp, maxp)
local pr = PcgRandom(blockseed)
local pathends = path_ends["block_" .. blockseed]
if pathends == nil then
minetest.log("warning", string.format("[mcl_villages] Tried to set paths for block seed that doesn't exist %d", blockseed))
return
end
-- Stair and slab style of the village
local stair, slab = get_biome_stair_slab(biome_name)
table.sort(pathends, function(a, b) return a[1] > b[1] end)
--minetest.log("action", "path ends: "..dump(pathends,""))
-- find ways to connect
local connected, to_place = {}, {}
for _, tmp in ipairs(pathends) do
local from, from_eps = tmp[2], tmp[3]
-- ep == end_point
for _, from_ep_pos in ipairs(from_eps) do
-- TODO: add back some logic as before that ensures some longer paths, too?
local cand = {}
for _, tmp in ipairs(pathends) do
local to, to_eps = tmp[2], tmp[3]
if from ~= to and not connected[from .. "-" .. to] and not connected[to .. "-" .. from] then
for _, to_ep_pos in ipairs(to_eps) do
local dist = vector.distance(from_ep_pos, to_ep_pos)
table.insert(cand, {dist, from, from_ep_pos, to, to_ep_pos})
end
end
end
table.sort(cand, function(a,b) return a[1] < b[1] end)
--minetest.log("action", "candidates: "..dump(cand,""))
for _, pair in ipairs(cand) do
local dist, from, from_ep_pos, to, to_ep_pos = unpack(pair)
local path = minetest.find_path(from_ep_pos, to_ep_pos, 10, 4, 4)
if path then smooth_path(path, 3, minp, maxp) end
path = minetest.find_path(from_ep_pos, to_ep_pos, 10, 2, 2)
if path then smooth_path(path, 1, minp, maxp) end
path = minetest.find_path(from_ep_pos, to_ep_pos, 12, 1, 1)
if path then
--minetest.log("path "..from.." to "..to.." len "..tostring(#path))
path = smooth_path(path, 1, minp, maxp)
if path then
connected[from .. "-" .. to] = 1
table.insert(to_place, pair)
goto continue -- add only one path per building
end
end
end
end
::continue::
end
--minetest.log("action", "to_place: "..dump(to_place,""))
-- now lay the actual paths
for _, cand in ipairs(to_place) do
local dist, from, from_ep_pos, to, to_ep_pos = unpack(cand)
local path = minetest.find_path(from_ep_pos, to_ep_pos, 12, 1, 1)
if path then
path = place_path(path, pr, stair, slab)
else
minetest.log("warning",
string.format(
"[mcl_villages] No good path from %s to %s, distance %d",
minetest.pos_to_string(from_ep_pos),
minetest.pos_to_string(to_ep_pos),
dist
)
)
end
end
path_ends["block_" .. blockseed] = nil
end end

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More