From 89e55e9065508febdc96d9c64b28c9024de7dbb3 Mon Sep 17 00:00:00 2001 From: kay27 Date: Mon, 22 Feb 2021 03:15:32 +0400 Subject: [PATCH] Add sub-map generators queue, fix https://git.minetest.land/MineClone2/MineClone2/issues/993 and https://git.minetest.land/MineClone2/MineClone2/issues/1060 --- mods/CORE/mcl_init/init.lua | 38 +- mods/CORE/mcl_util/init.lua | 50 -- mods/MAPGEN/mcl_biomes/depends.txt | 1 + mods/MAPGEN/mcl_biomes/init.lua | 2 +- mods/MAPGEN/mcl_dungeons/init.lua | 600 ++++++++++------------ mods/MAPGEN/mcl_mapgen_core/init.lua | 523 ++++++++++++------- mods/MAPGEN/mcl_strongholds/init.lua | 18 +- mods/MAPGEN/mcl_structures/init.lua | 268 ++++++---- mods/MAPGEN/mcl_villages/buildings.lua | 97 ++-- mods/MAPGEN/mcl_villages/depends.txt | 1 + mods/MAPGEN/mcl_villages/foundation.lua | 11 +- mods/MAPGEN/mcl_villages/init.lua | 32 +- mods/MAPGEN/mcl_villages/utils.lua | 99 ++-- mods/MAPGEN/tsm_railcorridors/depends.txt | 1 + mods/MAPGEN/tsm_railcorridors/init.lua | 4 +- 15 files changed, 944 insertions(+), 801 deletions(-) diff --git a/mods/CORE/mcl_init/init.lua b/mods/CORE/mcl_init/init.lua index ebbfd559..884ebfae 100644 --- a/mods/CORE/mcl_init/init.lua +++ b/mods/CORE/mcl_init/init.lua @@ -46,6 +46,42 @@ local numcmax = math.max(math.floor((mapgen_limit_max - ccfmax) / chunk_size_in_ mcl_vars.mapgen_edge_min = central_chunk_min_pos - numcmin * chunk_size_in_nodes mcl_vars.mapgen_edge_max = central_chunk_max_pos + numcmax * chunk_size_in_nodes +local function coordinate_to_block(x) + return math.floor(x / mcl_vars.MAP_BLOCKSIZE) +end + +local function coordinate_to_chunk(x) + return math.floor((coordinate_to_block(x) + central_chunk_offset) / mcl_vars.chunksize) +end + +function mcl_vars.pos_to_block(pos) + return { + x = coordinate_to_block(pos.x), + y = coordinate_to_block(pos.y), + z = coordinate_to_block(pos.z) + } +end + +function mcl_vars.pos_to_chunk(pos) + return { + x = coordinate_to_chunk(pos.x), + y = coordinate_to_chunk(pos.y), + z = coordinate_to_chunk(pos.z) + } +end + +local k_positive = math.ceil(mcl_vars.MAX_MAP_GENERATION_LIMIT / chunk_size_in_nodes) +local k_positive_z = k_positive * 2 +local k_positive_y = k_positive_z * k_positive_z + +function mcl_vars.get_chunk_number(pos) -- unsigned int + local c = mcl_vars.pos_to_chunk(pos) + return + (c.y + k_positive) * k_positive_y + + (c.z + k_positive) * k_positive_z + + c.x + k_positive +end + if not superflat and not singlenode then -- Normal mode --[[ Realm stacking (h is for height) @@ -91,7 +127,7 @@ else mcl_vars.mg_bedrock_is_rough = false end -mcl_vars.mg_overworld_max = 31000 +mcl_vars.mg_overworld_max = mcl_vars.mapgen_edge_max -- The Nether (around Y = -29000) mcl_vars.mg_nether_min = -29067 -- Carefully chosen to be at a mapchunk border diff --git a/mods/CORE/mcl_util/init.lua b/mods/CORE/mcl_util/init.lua index aedff5ef..6c63c21a 100644 --- a/mods/CORE/mcl_util/init.lua +++ b/mods/CORE/mcl_util/init.lua @@ -405,53 +405,3 @@ function mcl_util.get_object_center(obj) pos.y = pos.y + (ymax - ymin) / 2.0 return pos end - -local get_node_emerge_queue = {} -local function ecb_get_far_node(blockpos, action, calls_remaining, param) - if calls_remaining <= 0 and param then - minetest.log("verbose","[mcl_util] ecb done for param = "..param.." node.name="..minetest.get_node(minetest.string_to_pos(param)).name) - get_node_emerge_queue[param] = nil - end -end - -function mcl_util.get_far_node(pos, force) - local node = minetest.get_node(pos) - if node.name ~= "ignore" then - return node - end - - minetest.get_voxel_manip():read_from_map(pos, pos) - node = minetest.get_node(pos) - if node.name ~= "ignore" or not force then - return node - end - - local blockpos = vector.multiply(vector.floor(vector.divide(pos, mcl_vars.MAP_BLOCKSIZE)), mcl_vars.MAP_BLOCKSIZE) - local key = minetest.pos_to_string(blockpos) - - for i=1,2 do -- give engine 2 chances to emerge the data - if not get_node_emerge_queue[key] then - get_node_emerge_queue[key] = 1 - minetest.log("verbose","[mcl_util] emerge during get_far_node("..minetest.pos_to_string(pos).."), key="..key..", blockpos="..minetest.pos_to_string(blockpos)) - minetest.emerge_area(blockpos, vector.add(blockpos, mcl_vars.MAP_BLOCKSIZE-1), ecb_get_far_node, key) - end - - while not get_node_emerge_queue[key] do end - minetest.log("verbose","[mcl_util] emerge finished for node "..minetest.pos_to_string(pos)..", key="..key..", blockpos="..minetest.pos_to_string(blockpos)..", node.name="..mcl_util.get_far_node(pos).name) - - node = minetest.get_node(pos) - if node.name ~= "ignore" then - return node - end - - minetest.get_voxel_manip():read_from_map(pos, pos) - node = minetest.get_node(pos) - if node.name ~= "ignore" or not force then - return node - end - end - - node.name = "air" -- engine continuously returns "ignore" - probably it is a bug - minetest.swap_node(pos, node) -- engine continuously returns "ignore" - probably it is a bug - return node -- engine continuously returns "ignore" - probably it is a bug -end diff --git a/mods/MAPGEN/mcl_biomes/depends.txt b/mods/MAPGEN/mcl_biomes/depends.txt index dd21ef64..50f84ae9 100644 --- a/mods/MAPGEN/mcl_biomes/depends.txt +++ b/mods/MAPGEN/mcl_biomes/depends.txt @@ -1,4 +1,5 @@ mcl_init +mcl_mapgen_core mcl_core mcl_worlds mcl_farming diff --git a/mods/MAPGEN/mcl_biomes/init.lua b/mods/MAPGEN/mcl_biomes/init.lua index dc4905dd..768b99ef 100644 --- a/mods/MAPGEN/mcl_biomes/init.lua +++ b/mods/MAPGEN/mcl_biomes/init.lua @@ -3967,7 +3967,7 @@ if mg_name ~= "singlenode" then -- Overworld decorations for v6 are handled in mcl_mapgen_core if deco_id_chorus_plant then - minetest.register_on_generated(function(minp, maxp, blockseed) + mcl_mapgen_core.register_generator("chorus_grow", nil, function(minp, maxp, blockseed) local gennotify = minetest.get_mapgen_object("gennotify") local poslist = {} for _, pos in ipairs(gennotify["decoration#"..deco_id_chorus_plant] or {}) do diff --git a/mods/MAPGEN/mcl_dungeons/init.lua b/mods/MAPGEN/mcl_dungeons/init.lua index 1ce1556b..7c5f52b0 100644 --- a/mods/MAPGEN/mcl_dungeons/init.lua +++ b/mods/MAPGEN/mcl_dungeons/init.lua @@ -1,17 +1,38 @@ -- FIXME: Chests may appear at openings local mg_name = minetest.get_mapgen_setting("mg_name") -local pr = PseudoRandom(os.time()) -- Are dungeons disabled? -if mcl_vars.mg_dungeons == false then +if mcl_vars.mg_dungeons == false or mg_name == "singlenode" then return end -if mg_name ~= "singlenode" then --- Get loot for dungeon chests -local get_loot = function() - local loottable = { +local min_y = math.max(mcl_vars.mg_overworld_min, mcl_vars.mg_bedrock_overworld_max) + 1 +local max_y = mcl_vars.mg_overworld_max - 1 + +local dungeonsizes = { + { x=5, y=4, z=5}, + { x=5, y=4, z=7}, + { x=7, y=4, z=5}, + { x=7, y=4, z=7}, +} + +local dirs = { + { x= 1, y=0, z= 0 }, + { x= 0, y=0, z= 1 }, + { x=-1, y=0, z= 0 }, + { x= 0, y=0, z=-1 }, +} + +local surround_vectors = { + { x=-1, y=0, z=0 }, + { x=1, y=0, z=0 }, + { x=0, y=0, z=-1 }, + { x=0, y=0, z=1 }, +} + +local loottable = +{ { stacks_min = 1, stacks_max = 3, @@ -54,347 +75,278 @@ local get_loot = function() { itemstring = "mcl_mobitems:string", weight = 10, amount_min = 1, amount_max = 8 }, }, } +} + +-- Bonus loot for v6 mapgen: Otherwise unobtainable saplings. +if mg_name == "v6" then + table.insert(loottable, { + stacks_min = 1, + stacks_max = 3, + items = { + { itemstring = "mcl_core:birchsapling", weight = 1, amount_min = 1, amount_max = 2 }, + { itemstring = "mcl_core:acaciasapling", weight = 1, amount_min = 1, amount_max = 2 }, + { itemstring = "", weight = 6 }, + }, + }) +end + + +-- Calculate the number of dungeon spawn attempts +-- In Minecraft, there 8 dungeon spawn attempts Minecraft chunk (16*256*16 = 65536 blocks). +-- Minetest chunks don't have this size, so scale the number accordingly. +local attempts = math.ceil(((mcl_vars.chunksize * mcl_vars.MAP_BLOCKSIZE) ^ 3) / 8192) -- 63 = 80*80*80/8192 + +local function ecb_spawn_dungeon(blockpos, action, calls_remaining, param) + if calls_remaining >= 1 then return end + + local p1, p2, dim, pr = param.p1, param.p2, param.dim, param.pr + local x, y, z = p1.x, p1.y, p1.z + + -- Check floor and ceiling: Must be *completely* solid + local y_floor = y + local y_ceiling = y + dim.y + 1 + for tx = x, x + dim.x do for tz = z, z + dim.z do + if not minetest.registered_nodes[mcl_mapgen_core.get_node({x = tx, y = y_floor , z = tz}).name].walkable + or not minetest.registered_nodes[mcl_mapgen_core.get_node({x = tx, y = y_ceiling, z = tz}).name].walkable then return false end + end end + + -- Check for air openings (2 stacked air at ground level) in wall positions + local openings_counter = 0 + -- Store positions of openings; walls will not be generated here + local openings = {} + -- Corners are stored because a corner-only opening needs to be increased, + -- so entities can get through. + local corners = {} + + local walls = { + -- walls along x axis (contain corners) + { x, x+dim.x+1, "x", "z", z }, + { x, x+dim.x+1, "x", "z", z+dim.z+1 }, + -- walls along z axis (exclude corners) + { z+1, z+dim.z, "z", "x", x }, + { z+1, z+dim.z, "z", "x", x+dim.x+1 }, } - -- Bonus loot for v6 mapgen: Otherwise unobtainable saplings. - if mg_name == "v6" then - table.insert(loottable, { - stacks_min = 1, - stacks_max = 3, - items = { - { itemstring = "mcl_core:birchsapling", weight = 1, amount_min = 1, amount_max = 2 }, - { itemstring = "mcl_core:acaciasapling", weight = 1, amount_min = 1, amount_max = 2 }, - { itemstring = "", weight = 6 }, - }, - }) - end - local items = mcl_loot.get_multi_loot(loottable, pr) + for w=1, #walls do + local wall = walls[w] + for iter = wall[1], wall[2] do + local pos = {} + pos[wall[3]] = iter + pos[wall[4]] = wall[5] + pos.y = y+1 - return items -end + if openings[pos.x] == nil then openings[pos.x] = {} end + local doorname1 = mcl_mapgen_core.get_node(pos).name + pos.y = y+2 + local doorname2 = mcl_mapgen_core.get_node(pos).name + if doorname1 == "air" and doorname2 == "air" then + openings_counter = openings_counter + 1 + openings[pos.x][pos.z] = true - --- Buffer for LuaVoxelManip -local lvm_buffer = {} - --- Below the bedrock, generate air/void -minetest.register_on_generated(function(minp, maxp) - if maxp.y < mcl_vars.mg_overworld_min or minp.y > mcl_vars.mg_overworld_max then - return + -- Record corners + if wall[3] == "x" and (iter == wall[1] or iter == wall[2]) then + table.insert(corners, {x=pos.x, z=pos.z}) + end + end + end end - local vm, emin, emax = minetest.get_mapgen_object("voxelmanip") - local data = vm:get_data(lvm_buffer) - local area = VoxelArea:new({MinEdge=emin, MaxEdge=emax}) - local lvm_used = false + -- If all openings are only at corners, the dungeon can't be accessed yet. + -- This code extends the openings of corners so they can be entered. + if openings_counter >= 1 and openings_counter == #corners then + for c=1, #corners do + -- Prevent creating too many openings because this would lead to dungeon rejection + if openings_counter >= 5 then + break + end + -- A corner is widened by adding openings to both neighbors + local cx, cz = corners[c].x, corners[c].z + local cxn, czn = cx, cz + if x == cx then + cxn = cxn + 1 + else + cxn = cxn - 1 + end + if z == cz then + czn = czn + 1 + else + czn = czn - 1 + end + openings[cx][czn] = true + openings_counter = openings_counter + 1 + if openings_counter < 5 then + openings[cxn][cz] = true + openings_counter = openings_counter + 1 + end + end + end - local c_air = minetest.get_content_id("air") - local c_cobble = minetest.get_content_id("mcl_core:cobble") - local c_mossycobble = minetest.get_content_id("mcl_core:mossycobble") + -- Check conditions. If okay, start generating + if openings_counter < 1 or openings_counter > 5 then return end + + minetest.log("action","[mcl_dungeons] Placing new dungeon at "..minetest.pos_to_string({x=x,y=y,z=z})) + -- Okay! Spawning starts! -- Remember spawner chest positions to set metadata later - local chest_posses = {} + local chests = {} local spawner_posses = {} - -- Calculate the number of dungeon spawn attempts - local sizevector = vector.subtract(maxp, minp) - sizevector = vector.add(sizevector, 1) - local chunksize = sizevector.x * sizevector.y * sizevector.z + -- First prepare random chest positions. + -- Chests spawn at wall - -- In Minecraft, there 8 dungeon spawn attempts Minecraft chunk (16*256*16 = 65536 blocks). - -- Minetest chunks don't have this size, so scale the number accordingly. - local attempts = math.ceil(chunksize / 65536 * 8) - - for a=1, attempts do - local x, y, z - local b = 7 -- buffer - x = math.random(minp.x+b, maxp.x-b) - - local ymin = math.min(mcl_vars.mg_overworld_max, math.max(minp.y, mcl_vars.mg_bedrock_overworld_max) + 7) - local ymax = math.min(mcl_vars.mg_overworld_max, math.max(maxp.y, mcl_vars.mg_bedrock_overworld_max) - 4) - - y = math.random(ymin, ymax) - z = math.random(minp.z+b, maxp.z-b) - - local dungeonsizes = { - { x=5, y=4, z=5}, - { x=5, y=4, z=7}, - { x=7, y=4, z=5}, - { x=7, y=4, z=7}, - } - local dim = dungeonsizes[math.random(1, #dungeonsizes)] - - -- Check floor and ceiling: Must be *completely* solid - local ceilingfloor_ok = true - for tx = x, x+dim.x do - for tz = z, z+dim.z do - local floor = minetest.get_name_from_content_id(data[area:index(tx, y, tz)]) - local ceiling = minetest.get_name_from_content_id(data[area:index(tx, y+dim.y+1, tz)]) - if (not minetest.registered_nodes[floor].walkable) or (not minetest.registered_nodes[ceiling].walkable) then - ceilingfloor_ok = false - break - end - end - if not ceilingfloor_ok then break end + -- We assign each position at the wall a number and each chest gets one of these numbers randomly + local totalChests = 2 -- this code strongly relies on this number being 2 + local totalChestSlots = (dim.x-1) * (dim.z-1) + local chestSlots = {} + -- There is a small chance that both chests have the same slot. + -- In that case, we give a 2nd chance for the 2nd chest to get spawned. + -- If it failed again, tough luck! We stick with only 1 chest spawned. + local lastRandom + local secondChance = true -- second chance is still available + for i=1, totalChests do + local r = pr:next(1, totalChestSlots) + if r == lastRandom and secondChance then + -- Oops! Same slot selected. Try again. + r = pr:next(1, totalChestSlots) + secondChance = false end + lastRandom = r + table.insert(chestSlots, r) + end + table.sort(chestSlots) + local currentChest = 1 - -- Check for air openings (2 stacked air at ground level) in wall positions - local openings_counter = 0 - -- Store positions of openings; walls will not be generated here - local openings = {} - -- Corners are stored because a corner-only opening needs to be increased, - -- so entities can get through. - local corners = {} - if ceilingfloor_ok then - - local walls = { - -- walls along x axis (contain corners) - { x, x+dim.x+1, "x", "z", z }, - { x, x+dim.x+1, "x", "z", z+dim.z+1 }, - -- walls along z axis (exclude corners) - { z+1, z+dim.z, "z", "x", x }, - { z+1, z+dim.z, "z", "x", x+dim.x+1 }, - } - - for w=1, #walls do - local wall = walls[w] - for iter = wall[1], wall[2] do - local pos = {} - pos[wall[3]] = iter - pos[wall[4]] = wall[5] - pos.y = y+1 - - if openings[pos.x] == nil then openings[pos.x] = {} end - - local door1 = area:index(pos.x, pos.y, pos.z) - pos.y = y+2 - local door2 = area:index(pos.x, pos.y, pos.z) - local doorname1 = minetest.get_name_from_content_id(data[door1]) - local doorname2 = minetest.get_name_from_content_id(data[door2]) - if doorname1 == "air" and doorname2 == "air" then - openings_counter = openings_counter + 1 - openings[pos.x][pos.z] = true - - -- Record corners - if wall[3] == "x" and (iter == wall[1] or iter == wall[2]) then - table.insert(corners, {x=pos.x, z=pos.z}) - end - end - end - end - - end - - -- If all openings are only at corners, the dungeon can't be accessed yet. - -- This code extends the openings of corners so they can be entered. - if openings_counter >= 1 and openings_counter == #corners then - for c=1, #corners do - -- Prevent creating too many openings because this would lead to dungeon rejection - if openings_counter >= 5 then - break - end - -- A corner is widened by adding openings to both neighbors - local cx, cz = corners[c].x, corners[c].z - local cxn, czn = cx, cz - if x == cx then - cxn = cxn + 1 - else - cxn = cxn - 1 - end - if z == cz then - czn = czn + 1 - else - czn = czn - 1 - end - openings[cx][czn] = true - openings_counter = openings_counter + 1 - if openings_counter < 5 then - openings[cxn][cz] = true - openings_counter = openings_counter + 1 - end - end - end - - -- Check conditions. If okay, start generating - if ceilingfloor_ok and openings_counter >= 1 and openings_counter <= 5 then - -- Okay! Spawning starts! - - -- First prepare random chest positions. - -- Chests spawn at wall - - -- We assign each position at the wall a number and each chest gets one of these numbers randomly - local totalChests = 2 -- this code strongly relies on this number being 2 - local totalChestSlots = (dim.x-1) * (dim.z-1) - local chestSlots = {} - -- There is a small chance that both chests have the same slot. - -- In that case, we give a 2nd chance for the 2nd chest to get spawned. - -- If it failed again, tough luck! We stick with only 1 chest spawned. - local lastRandom - local secondChance = true -- second chance is still available - for i=1, totalChests do - local r = math.random(1, totalChestSlots) - if r == lastRandom and secondChance then - -- Oops! Same slot selected. Try again. - r = math.random(1, totalChestSlots) - secondChance = false - end - lastRandom = r - table.insert(chestSlots, r) - end - table.sort(chestSlots) - local currentChest = 1 - - -- Calculate the mob spawner position, to be re-used for later - local spawner_pos = {x = x + math.ceil(dim.x/2), y = y+1, z = z + math.ceil(dim.z/2)} - table.insert(spawner_posses, spawner_pos) - - -- Generate walls and floor - local maxx, maxy, maxz = x+dim.x+1, y+dim.y, z+dim.z+1 - local chestSlotCounter = 1 - for tx = x, maxx do - for tz = z, maxz do - for ty = y, maxy do - local p_pos = area:index(tx, ty, tz) - - -- Do not overwrite nodes with is_ground_content == false (e.g. bedrock) - -- Exceptions: cobblestone and mossy cobblestone so neighborings dungeons nicely connect to each other - local name = minetest.get_name_from_content_id(data[p_pos]) - if name == "mcl_core:cobble" or name == "mcl_core:mossycobble" or minetest.registered_nodes[name].is_ground_content then - -- Floor - if ty == y then - if math.random(1,4) == 1 then - data[p_pos] = c_cobble - else - data[p_pos] = c_mossycobble - end - - -- Generate walls - --[[ Note: No additional cobblestone ceiling is generated. This is intentional. - The solid blocks above the dungeon are considered as the “ceiling”. - It is possible (but rare) for a dungeon to generate below sand or gravel. ]] - - elseif ty > y and (tx == x or tx == maxx or (tz == z or tz == maxz)) then - -- Check if it's an opening first - if (not openings[tx][tz]) or ty == maxy then - -- Place wall or ceiling - data[p_pos] = c_cobble - elseif ty < maxy - 1 then - -- Normally the openings are already clear, but not if it is a corner - -- widening. Make sure to clear at least the bottom 2 nodes of an opening. - data[p_pos] = c_air - elseif ty == maxy - 1 and data[p_pos] ~= c_air then - -- This allows for variation between 2-node and 3-node high openings. - data[p_pos] = c_cobble - end - -- If it was an opening, the lower 3 blocks are not touched at all - - -- Room interiour - else - local forChest = ty==y+1 and (tx==x+1 or tx==maxx-1 or tz==z+1 or tz==maxz-1) - - -- Place next chest at the wall (if it was its chosen wall slot) - if forChest and (currentChest < totalChests + 1) and (chestSlots[currentChest] == chestSlotCounter) then - table.insert(chest_posses, {x=tx, y=ty, z=tz}) - currentChest = currentChest + 1 - else - data[p_pos] = c_air - end - if forChest then - chestSlotCounter = chestSlotCounter + 1 - end - end - end - - end - end - end - end - - lvm_used = true + -- Calculate the mob spawner position, to be re-used for later + local sp = {x = x + math.ceil(dim.x/2), y = y+1, z = z + math.ceil(dim.z/2)} + local rn = minetest.registered_nodes[mcl_mapgen_core.get_node(sp).name] + if rn and rn.is_ground_content then + table.insert(spawner_posses, sp) end - if lvm_used then - local chest_param2 = {} - -- Determine correct chest rotation (must pointi outwards) - for c=1, #chest_posses do - local cpos = chest_posses[c] + -- Generate walls and floor + local maxx, maxy, maxz = x+dim.x+1, y+dim.y, z+dim.z+1 + local chestSlotCounter = 1 + for tx = x, maxx do + for tz = z, maxz do + for ty = y, maxy do + local p = {x = tx, y=ty, z=tz} - -- Check surroundings of chest to determine correct rotation - local surround_vectors = { - { x=-1, y=0, z=0 }, - { x=1, y=0, z=0 }, - { x=0, y=0, z=-1 }, - { x=0, y=0, z=1 }, - } - local surroundings = {} - - for s=1, #surround_vectors do - -- Detect the 4 horizontal neighbors - local spos = vector.add(cpos, surround_vectors[s]) - local wpos = vector.subtract(cpos, surround_vectors[s]) - local p_pos = area:index(spos.x, spos.y, spos.z) - local p_pos2 = area:index(wpos.x, wpos.y, wpos.z) - - local nodename = minetest.get_name_from_content_id(data[p_pos]) - local nodename2 = minetest.get_name_from_content_id(data[p_pos2]) - local nodedef = minetest.registered_nodes[nodename] - local nodedef2 = minetest.registered_nodes[nodename2] - -- The chest needs an open space in front of it and a walkable node (except chest) behind it - if nodedef and nodedef.walkable == false and nodedef2 and nodedef2.walkable == true and nodename2 ~= "mcl_chests:chest" then - table.insert(surroundings, spos) + -- Do not overwrite nodes with is_ground_content == false (e.g. bedrock) + -- Exceptions: cobblestone and mossy cobblestone so neighborings dungeons nicely connect to each other + local name = mcl_mapgen_core.get_node(p).name + if name == "mcl_core:cobble" or name == "mcl_core:mossycobble" or minetest.registered_nodes[name].is_ground_content then + -- Floor + if ty == y then + if pr:next(1,4) == 1 then + minetest.swap_node(p, {name = "mcl_core:cobble"}) + else + minetest.swap_node(p, {name = "mcl_core:mossycobble"}) end - end - -- Set param2 (=facedir) of this chest - local facedir - if #surroundings <= 0 then - -- Fallback if chest ended up in the middle of a room for some reason - facedir = math.random(0, 0) + + -- Generate walls + --[[ Note: No additional cobblestone ceiling is generated. This is intentional. + The solid blocks above the dungeon are considered as the “ceiling”. + It is possible (but rare) for a dungeon to generate below sand or gravel. ]] + + elseif ty > y and (tx == x or tx == maxx or (tz == z or tz == maxz)) then + -- Check if it's an opening first + if (not openings[tx][tz]) or ty == maxy then + -- Place wall or ceiling + minetest.swap_node(p, {name = "mcl_core:cobble"}) + elseif ty < maxy - 1 then + -- Normally the openings are already clear, but not if it is a corner + -- widening. Make sure to clear at least the bottom 2 nodes of an opening. + minetest.swap_node(p, {name = "air"}) + elseif ty == maxy - 1 and mcl_mapgen_core.get_node(p).name ~= "air" then + -- This allows for variation between 2-node and 3-node high openings. + minetest.swap_node(p, {name = "mcl_core:cobble"}) + end + -- If it was an opening, the lower 3 blocks are not touched at all + + -- Room interiour else - -- 1 or multiple possible open directions: Choose random facedir - local face_to = surroundings[math.random(1, #surroundings)] - facedir = minetest.dir_to_facedir(vector.subtract(cpos, face_to)) - end - chest_param2[c] = facedir - end + local forChest = ty==y+1 and (tx==x+1 or tx==maxx-1 or tz==z+1 or tz==maxz-1) - -- Finally generate the dungeons all at once (except the chests and the spawners) - vm:set_data(data) - vm:calc_lighting() - vm:update_liquids() - vm:write_to_map() - - -- Chests are placed seperately - for c=1, #chest_posses do - local cpos = chest_posses[c] - minetest.set_node(cpos, {name="mcl_chests:chest", param2=chest_param2[c]}) - local meta = minetest.get_meta(cpos) - local inv = meta:get_inventory() - local items = get_loot() - mcl_loot.fill_inventory(inv, "main", items) - end - - -- Mob spawners are placed seperately, too - -- We don't want to destroy non-ground nodes - for s=1, #spawner_posses do - local sp = spawner_posses[s] - local n = minetest.get_name_from_content_id(data[area:index(sp.x,sp.y,sp.z)]) - if minetest.registered_nodes[n].is_ground_content then - - -- ... and place it and select a random mob - minetest.set_node(sp, {name = "mcl_mobspawners:spawner"}) - local mobs = { - "mobs_mc:zombie", - "mobs_mc:zombie", - "mobs_mc:spider", - "mobs_mc:skeleton", - } - local spawner_mob = mobs[math.random(1, #mobs)] - - mcl_mobspawners.setup_spawner(sp, spawner_mob, 0, 7) + -- Place next chest at the wall (if it was its chosen wall slot) + if forChest and (currentChest < totalChests + 1) and (chestSlots[currentChest] == chestSlotCounter) then + currentChest = currentChest + 1 + table.insert(chests, {x=tx, y=ty, z=tz}) + else + minetest.swap_node(p, {name = "air"}) + end + if forChest then + chestSlotCounter = chestSlotCounter + 1 + end end end + end end end + for c=#chests, 1, -1 do + local pos = chests[c] + + local surroundings = {} + for s=1, #surround_vectors do + -- Detect the 4 horizontal neighbors + local spos = vector.add(pos, surround_vectors[s]) + local wpos = vector.subtract(pos, surround_vectors[s]) + local nodename = minetest.get_node(spos).name + local nodename2 = minetest.get_node(wpos).name + local nodedef = minetest.registered_nodes[nodename] + local nodedef2 = minetest.registered_nodes[nodename2] + -- The chest needs an open space in front of it and a walkable node (except chest) behind it + if nodedef and nodedef.walkable == false and nodedef2 and nodedef2.walkable == true and nodename2 ~= "mcl_chests:chest" then + table.insert(surroundings, spos) + end + end + -- Set param2 (=facedir) of this chest + local facedir + if #surroundings <= 0 then + -- Fallback if chest ended up in the middle of a room for some reason + facedir = pr:next(0, 0) + else + -- 1 or multiple possible open directions: Choose random facedir + local face_to = surroundings[pr:next(1, #surroundings)] + facedir = minetest.dir_to_facedir(vector.subtract(pos, face_to)) + end + + minetest.set_node(pos, {name="mcl_chests:chest", param2=facedir}) + local meta = minetest.get_meta(pos) + mcl_loot.fill_inventory(meta:get_inventory(), "main", mcl_loot.get_multi_loot(loottable, pr)) end -end) + -- Mob spawners are placed seperately, too + -- We don't want to destroy non-ground nodes + for s=#spawner_posses, 1, -1 do + local sp = spawner_posses[s] + -- ... and place it and select a random mob + minetest.set_node(sp, {name = "mcl_mobspawners:spawner"}) + local mobs = { + "mobs_mc:zombie", + "mobs_mc:zombie", + "mobs_mc:spider", + "mobs_mc:skeleton", + } + local spawner_mob = mobs[pr:next(1, #mobs)] + + mcl_mobspawners.setup_spawner(sp, spawner_mob, 0, 7) + end end + +local function dungeons_nodes(minp, maxp, blockseed) + local ymin, ymax = math.max(min_y, minp.y), math.min(max_y, maxp.y) + if ymax < ymin then return false end + local pr = PseudoRandom(blockseed) + for a=1, attempts do + local dim = dungeonsizes[pr:next(1, #dungeonsizes)] + local x = pr:next(minp.x, maxp.x-dim.x-2) + local y = pr:next(ymin , ymax -dim.y-2) + local z = pr:next(minp.z, maxp.z-dim.z-2) + local p1 = {x=x,y=y,z=z} + local p2 = {x = x+dim.x+1, y = y+dim.y+1, z = 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)) + local param = {p1=p1, p2=p2, dim=dim, pr=pr} + minetest.emerge_area(p1, p2, ecb_spawn_dungeon, param) + end +end + +mcl_mapgen_core.register_generator("dungeons", nil, dungeons_nodes, 999999) diff --git a/mods/MAPGEN/mcl_mapgen_core/init.lua b/mods/MAPGEN/mcl_mapgen_core/init.lua index 8e75de91..a6f9b9d6 100644 --- a/mods/MAPGEN/mcl_mapgen_core/init.lua +++ b/mods/MAPGEN/mcl_mapgen_core/init.lua @@ -1,3 +1,46 @@ +mcl_mapgen_core = {} +mcl_mapgen_core.registered_generators = {} + +local lvm, nodes, param2 = 0, 0, 0 + +local generating = {} -- generating chunks +local chunks = {} -- intervals of chunks generated +local function 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] = {n, n} +end +function mcl_mapgen_core.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 + -- -- Aliases for map generator outputs -- @@ -1190,12 +1233,14 @@ local perlin_structures local perlin_vines, perlin_vines_fine, perlin_vines_upwards, perlin_vines_length, perlin_vines_density local perlin_clay -local function generate_clay(minp, maxp, seed, voxelmanip_data, voxelmanip_area, lvm_used) +local function generate_clay(minp, maxp, blockseed, voxelmanip_data, voxelmanip_area, lvm_used) -- TODO: Make clay generation reproducible for same seed. if maxp.y < -5 or minp.y > 0 then return lvm_used end + local pr = PseudoRandom(blockseed) + perlin_clay = perlin_clay or minetest.get_perlin({ offset = 0.5, scale = 0.2, @@ -1212,18 +1257,18 @@ local function generate_clay(minp, maxp, seed, voxelmanip_data, voxelmanip_area, for divx=0+1,divs-2 do for divz=0+1,divs-2 do -- Get position and shift it a bit randomly so the clay do not obviously appear in a grid - local cx = minp.x + math.floor((divx+0.5)*divlen) + math.random(-1,1) - local cz = minp.z + math.floor((divz+0.5)*divlen) + math.random(-1,1) + local cx = minp.x + math.floor((divx+0.5)*divlen) + pr:next(-1,1) + local cz = minp.z + math.floor((divz+0.5)*divlen) + pr:next(-1,1) local water_pos = voxelmanip_area:index(cx, y+1, cz) local waternode = voxelmanip_data[water_pos] local surface_pos = voxelmanip_area:index(cx, y, cz) local surfacenode = voxelmanip_data[surface_pos] - local genrnd = math.random(1, 20) + local genrnd = pr:next(1, 20) if genrnd == 1 and perlin_clay:get_3d({x=cx,y=y,z=cz}) > 0 and waternode == c_water and (surfacenode == c_dirt or minetest.get_item_group(minetest.get_name_from_content_id(surfacenode), "sand") == 1) then - local diamondsize = math.random(1, 3) + local diamondsize = pr:next(1, 3) for x1 = -diamondsize, diamondsize do for z1 = -(diamondsize - math.abs(x1)), diamondsize - math.abs(x1) do local ccpos = voxelmanip_area:index(cx+x1, y, cz+z1) @@ -1242,37 +1287,34 @@ local function generate_clay(minp, maxp, seed, voxelmanip_data, voxelmanip_area, end -- TODO: Try to use more efficient structure generating code -local function generate_structures(minp, maxp, seed, biomemap) +local function generate_structures(minp, maxp, blockseed, biomemap) local chunk_has_desert_well = false local chunk_has_desert_temple = false local chunk_has_igloo = false - local struct_min, struct_max = -3, 64 + local struct_min, struct_max = -3, 111 --64 + if maxp.y >= struct_min and minp.y <= struct_max then -- Generate structures - + local pr = PcgRandom(blockseed) perlin_structures = perlin_structures or minetest.get_perlin(329, 3, 0.6, 100) -- Assume X and Z lengths are equal local divlen = 5 - local divs = (maxp.x-minp.x)/divlen+1; - for divx=0,divs-1 do - for divz=0,divs-1 do - local x0 = minp.x + math.floor((divx+0)*divlen) - local z0 = minp.z + math.floor((divz+0)*divlen) - local x1 = minp.x + math.floor((divx+1)*divlen) - local z1 = minp.z + math.floor((divz+1)*divlen) + for x0 = minp.x, maxp.x, divlen do for z0 = minp.z, maxp.z, divlen do -- Determine amount from perlin noise local amount = math.floor(perlin_structures:get_2d({x=x0, y=z0}) * 9) -- Find random positions based on this random - local pr = PseudoRandom(seed+1) + local p, ground_y for i=0, amount do - local x = pr:next(x0, x1) - local z = pr:next(z0, z1) + p = {x = pr:next(x0, x0+divlen-1), y = 0, z = pr:next(z0, z0+divlen-1)} -- Find ground level - local ground_y = nil + ground_y = nil + local nn for y = struct_max, struct_min, -1 do - local checknode = minetest.get_node_or_nil({x=x,y=y,z=z}) + p.y = y + local checknode = minetest.get_node(p) if checknode then - local def = minetest.registered_nodes[checknode.name] + nn = checknode.name + local def = minetest.registered_nodes[nn] if def and def.walkable then ground_y = y break @@ -1281,21 +1323,17 @@ local function generate_structures(minp, maxp, seed, biomemap) end if ground_y then - local p = {x=x,y=ground_y+1,z=z} - local nn = minetest.get_node(p).name + p.y = ground_y+1 + local nn0 = minetest.get_node(p).name -- Check if the node can be replaced - if minetest.registered_nodes[nn] and - minetest.registered_nodes[nn].buildable_to then - nn = minetest.get_node({x=x,y=ground_y,z=z}).name - local struct = false - + if minetest.registered_nodes[nn0] and minetest.registered_nodes[nn0].buildable_to then -- Desert temples and desert wells if nn == "mcl_core:sand" or (nn == "mcl_core:sandstone") then if not chunk_has_desert_temple and not chunk_has_desert_well and ground_y > 3 then -- Spawn desert temple -- TODO: Check surface - if math.random(1,12000) == 1 then - mcl_structures.call_struct(p, "desert_temple") + if pr:next(1,12000) == 1 then + mcl_structures.call_struct(p, "desert_temple", nil, pr) chunk_has_desert_temple = true end end @@ -1303,11 +1341,11 @@ local function generate_structures(minp, maxp, seed, biomemap) local desert_well_prob = minecraft_chunk_probability(1000, minp, maxp) -- Spawn desert well - if math.random(1, desert_well_prob) == 1 then + if pr:next(1, desert_well_prob) == 1 then -- Check surface local surface = minetest.find_nodes_in_area({x=p.x,y=p.y-1,z=p.z}, {x=p.x+5, y=p.y-1, z=p.z+5}, "mcl_core:sand") if #surface >= 25 then - mcl_structures.call_struct(p, "desert_well") + mcl_structures.call_struct(p, "desert_well", nil, pr) chunk_has_desert_well = true end end @@ -1315,13 +1353,13 @@ local function generate_structures(minp, maxp, seed, biomemap) -- Igloos elseif not chunk_has_igloo and (nn == "mcl_core:snowblock" or nn == "mcl_core:snow" or (minetest.get_item_group(nn, "grass_block_snow") == 1)) then - if math.random(1, 4400) == 1 then + if pr:next(1, 4400) == 1 then -- Check surface local floor = {x=p.x+9, y=p.y-1, z=p.z+9} local surface = minetest.find_nodes_in_area({x=p.x,y=p.y-1,z=p.z}, floor, "mcl_core:snowblock") local surface2 = minetest.find_nodes_in_area({x=p.x,y=p.y-1,z=p.z}, floor, "mcl_core:dirt_with_grass_snow") if #surface + #surface2 >= 63 then - mcl_structures.call_struct(p, "igloo") + mcl_structures.call_struct(p, "igloo", nil, pr) chunk_has_igloo = true end end @@ -1331,16 +1369,16 @@ local function generate_structures(minp, maxp, seed, biomemap) if nn == "mcl_core:sandstone" or nn == "mcl_core:sand" and not chunk_has_desert_temple and ground_y > 3 then local fossil_prob = minecraft_chunk_probability(64, minp, maxp) - if math.random(1, fossil_prob) == 1 then + if pr:next(1, fossil_prob) == 1 then -- Spawn fossil below desert surface between layers 40 and 49 - local p1 = {x=p.x, y=math.random(mcl_worlds.layer_to_y(40), mcl_worlds.layer_to_y(49)), z=p.z} + local p1 = {x=p.x, y=pr:next(mcl_worlds.layer_to_y(40), mcl_worlds.layer_to_y(49)), z=p.z} -- Very rough check of the environment (we expect to have enough stonelike nodes). -- Fossils may still appear partially exposed in caves, but this is O.K. local p2 = vector.add(p1, 4) local nodes = minetest.find_nodes_in_area(p1, p2, {"mcl_core:sandstone", "mcl_core:stone", "mcl_core:diorite", "mcl_core:andesite", "mcl_core:granite", "mcl_core:stone_with_coal", "mcl_core:dirt", "mcl_core:gravel"}) if #nodes >= 100 then -- >= 80% - mcl_structures.call_struct(p1, "fossil") + mcl_structures.call_struct(p1, "fossil", nil, pr) end end end @@ -1348,7 +1386,7 @@ local function generate_structures(minp, maxp, seed, biomemap) -- Witch hut if ground_y <= 0 and nn == "mcl_core:dirt" then local prob = minecraft_chunk_probability(48, minp, maxp) - if math.random(1, prob) == 1 then + if pr:next(1, prob) == 1 then local swampland = minetest.get_biome_id("Swampland") local swampland_shore = minetest.get_biome_id("Swampland_shore") @@ -1370,7 +1408,7 @@ local function generate_structures(minp, maxp, seed, biomemap) end if here_be_witches then - local r = tostring(math.random(0, 3) * 90) -- "0", "90", "180" or 270" + local r = tostring(pr:next(0, 3) * 90) -- "0", "90", "180" or 270" local p1 = {x=p.x-1, y=WITCH_HUT_HEIGHT+2, z=p.z-1} local size if r == "0" or r == "180" then @@ -1389,7 +1427,7 @@ local function generate_structures(minp, maxp, seed, biomemap) -- FIXME: For some mysterious reason (black magic?) this -- function does sometimes NOT spawn the witch hut. One can only see the -- oak wood nodes in the water, but no hut. :-/ - mcl_structures.call_struct(place, "witch_hut", r) + mcl_structures.call_struct(place, "witch_hut", r, pr) -- TODO: Spawn witch in or around hut when the mob sucks less. @@ -1451,7 +1489,7 @@ local function generate_structures(minp, maxp, seed, biomemap) -- Ice spikes in v6 -- In other mapgens, ice spikes are generated as decorations. if mg_name == "v6" and not chunk_has_igloo and nn == "mcl_core:snowblock" then - local spike = math.random(1, 58000) + local spike = pr:next(1,58000) if spike < 3 then -- Check surface local floor = {x=p.x+4, y=p.y-1, z=p.z+4} @@ -1460,7 +1498,7 @@ local function generate_structures(minp, maxp, seed, biomemap) local spruce_collisions = minetest.find_nodes_in_area({x=p.x+1,y=p.y+2,z=p.z+1}, {x=p.x+4, y=p.y+6, z=p.z+4}, {"mcl_core:sprucetree", "mcl_core:spruceleaves"}) if #surface >= 9 and #spruce_collisions == 0 then - mcl_structures.call_struct(p, "ice_spike_large") + mcl_structures.call_struct(p, "ice_spike_large", nil, pr) end elseif spike < 100 then -- Check surface @@ -1471,7 +1509,7 @@ local function generate_structures(minp, maxp, seed, biomemap) local spruce_collisions = minetest.find_nodes_in_area({x=p.x+1,y=p.y+1,z=p.z+1}, {x=p.x+6, y=p.y+6, z=p.z+6}, {"mcl_core:sprucetree", "mcl_core:spruceleaves"}) if #surface >= 25 and #spruce_collisions == 0 then - mcl_structures.call_struct(p, "ice_spike_small") + mcl_structures.call_struct(p, "ice_spike_small", nil, pr) end end end @@ -1479,36 +1517,31 @@ local function generate_structures(minp, maxp, seed, biomemap) end end - end - end + end end -- End exit portal - elseif minp.y <= END_EXIT_PORTAL_POS.y and maxp.y >= END_EXIT_PORTAL_POS.y and - minp.x <= END_EXIT_PORTAL_POS.x and maxp.x >= END_EXIT_PORTAL_POS.x and - minp.z <= END_EXIT_PORTAL_POS.z and maxp.z >= END_EXIT_PORTAL_POS.z then - local built = false + elseif minp.y <= END_EXIT_PORTAL_POS.y and maxp.y >= END_EXIT_PORTAL_POS.y and + minp.x <= END_EXIT_PORTAL_POS.x and maxp.x >= END_EXIT_PORTAL_POS.x and + minp.z <= END_EXIT_PORTAL_POS.z and maxp.z >= END_EXIT_PORTAL_POS.z then for y=maxp.y, minp.y, -1 do local p = {x=END_EXIT_PORTAL_POS.x, y=y, z=END_EXIT_PORTAL_POS.z} if minetest.get_node(p).name == "mcl_end:end_stone" then mcl_structures.call_struct(p, "end_exit_portal") - built = true - break + return end end - if not built then - mcl_structures.call_struct(END_EXIT_PORTAL_POS, "end_exit_portal") - end + mcl_structures.call_struct(END_EXIT_PORTAL_POS, "end_exit_portal") end end -- Buffers for LuaVoxelManip -local lvm_buffer = {} -local lvm_buffer_param2 = {} +-- local lvm_buffer = {} +-- local lvm_buffer_param2 = {} -- Generate tree decorations in the bounding box. This adds: -- * Cocoa at jungle trees -- * Jungle tree vines -- * Oak vines in swamplands -local function generate_tree_decorations(minp, maxp, seed, data, param2_data, area, biomemap, lvm_used) +local function generate_tree_decorations(minp, maxp, seed, data, param2_data, area, biomemap, lvm_used, pr) if maxp.y < 0 then return lvm_used end @@ -1576,7 +1609,7 @@ local function generate_tree_decorations(minp, maxp, seed, data, param2_data, ar if minetest.find_node_near(pos, 1, {"mcl_core:jungleleaves"}) then - dir = math.random(1, cocoachance) + dir = pr:next(1, cocoachance) if dir == 1 then pos.z = pos.z + 1 @@ -1594,7 +1627,7 @@ local function generate_tree_decorations(minp, maxp, seed, data, param2_data, ar if dir < 5 and data[p_pos] == c_air and l ~= nil and l > 12 then - local c = math.random(1, 3) + local c = pr:next(1, 3) if c == 1 then data[p_pos] = c_cocoa_1 elseif c == 2 then @@ -1810,125 +1843,206 @@ local generate_nether_decorations = function(minp, maxp, seed) end --- Below the bedrock, generate air/void -minetest.register_on_generated(function(minp, maxp, seed) - local vm, emin, emax = minetest.get_mapgen_object("voxelmanip") - local data = vm:get_data(lvm_buffer) - local param2_data = vm:get_param2_data(lvm_buffer_param2) - local area = VoxelArea:new({MinEdge=emin, MaxEdge=emax}) - local aream = VoxelArea:new({MinEdge={x=minp.x, y=0, z=minp.z}, MaxEdge={x=maxp.x, y=0, z=maxp.z}}) - local lvm_used = false - local biomemap +minetest.register_on_generated(function(minp, maxp, blockseed) + add_chunk(minp) + 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 + local lvm_used, shadow = false, false + local lb = {} -- buffer + local lb2 = {} -- param2 + 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 data2 + local data = vm:get_data(lb) + if param2 > 0 then + data2 = vm:get_param2_data(lb2) + end + local area = VoxelArea:new({MinEdge=e1, MaxEdge=e2}) - local ymin, ymax + for _, rec in pairs(mcl_mapgen_core.registered_generators) do + if rec.vf then + local lvm_used0, shadow0 = rec.vf(vm, data, data2, e1, e2, area, p1, p2, blockseed) + if lvm_used0 then + lvm_used = true + end + if shadow0 then + shadow = true + end + end + end - -- Generate basic layer-based nodes: void, bedrock, realm barrier, lava seas, etc. - -- Also perform some basic node replacements. + if lvm_used then + -- Write stuff + vm:set_data(data) + if param2 > 0 then + vm:set_param2_data(data2) + end + vm:calc_lighting(p1, p2, shadow) + vm:write_to_map() + vm:update_liquids() + end + end - -- Helper function to set all nodes in the layers between min and max. - -- content_id: Node to set - -- check: optional. - -- If content_id, node will be set only if it is equal to check. - -- 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 - -- 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 - local function set_layers(content_id, check, min, max, minp, maxp, lvm_used) - if (maxp.y >= min and minp.y <= max) then - for y = math.max(min, minp.y), math.min(max, maxp.y) do - for x = minp.x, maxp.x do - for z = minp.z, maxp.z do - local p_pos = area:index(x, y, z) - if check then - if type(check) == "function" and check({x=x,y=y,z=z}, data[p_pos]) then - data[p_pos] = content_id - lvm_used = true - elseif check == data[p_pos] then - data[p_pos] = content_id - lvm_used = true - end - else + if nodes > 0 then + for _, rec in pairs(mcl_mapgen_core.registered_generators) do + if rec.nf then + rec.nf(p1, p2, blockseed) + end + end + end + +-- add_chunk(minp) +end) + +minetest.register_on_generated=function(node_function) + mcl_mapgen_core.register_generator("mod_"..tostring(#mcl_mapgen_core.registered_generators+1), nil, node_function) +end + +function mcl_mapgen_core.register_generator(id, lvm_function, node_function, priority, needs_param2) + if not id then return end + + local priority = priority or 5000 + + if lvm_function then lvm = lvm + 1 end + if lvm_function then nodes = nodes + 1 end + if needs_param2 then param2 = param2 + 1 end + + local new_record = { + i = priority, + vf = lvm_function, + nf = node_function, + needs_param2 = needs_param2, + } + + mcl_mapgen_core.registered_generators[id] = new_record + table.sort( + mcl_mapgen_core.registered_generators, + function(a, b) + return (a.i < b.i) or ((a.i == b.i) and (a.vf ~= nil) and (b.vf == nil)) + end) +end + +function mcl_mapgen_core.unregister_generator(id) + if not mcl_mapgen_core.registered_generators[id] then return end + local rec = mcl_mapgen_core.registered_generators[id] + mcl_mapgen_core.registered_generators[id] = nil + if rec.vf then lvm = lvm - 1 end + if rev.nf then nodes = nodes - 1 end + if rec.needs_param2 then param2 = param2 - 1 end + if rec.needs_level0 then level0 = level0 - 1 end +end + +-- Generate basic layer-based nodes: void, bedrock, realm barrier, lava seas, etc. +-- Also perform some basic node replacements. + +local bedrock_check +if mcl_vars.mg_bedrock_is_rough then + bedrock_check = function(pos, _, pr) + local y = pos.y + -- Bedrock layers with increasing levels of roughness, until a perfecly flat bedrock later at the bottom layer + -- This code assumes a bedrock height of 5 layers. + + local diff = mcl_vars.mg_bedrock_overworld_max - y -- Overworld bedrock + local ndiff1 = mcl_vars.mg_bedrock_nether_bottom_max - y -- Nether bedrock, bottom + local ndiff2 = mcl_vars.mg_bedrock_nether_top_max - y -- Nether bedrock, ceiling + + local top + if diff == 0 or ndiff1 == 0 or ndiff2 == 4 then + -- 50% bedrock chance + top = 2 + elseif diff == 1 or ndiff1 == 1 or ndiff2 == 3 then + -- 66.666...% + top = 3 + elseif diff == 2 or ndiff1 == 2 or ndiff2 == 2 then + -- 75% + top = 4 + elseif diff == 3 or ndiff1 == 3 or ndiff2 == 1 then + -- 90% + top = 10 + elseif diff == 4 or ndiff1 == 4 or ndiff2 == 0 then + -- 100% + return true + else + -- Not in bedrock layer + return false + end + + return pr:next(1, top) <= top-1 + end +end + + +-- Helper function to set all nodes in the layers between min and max. +-- content_id: Node to set +-- check: optional. +-- If content_id, node will be set only if it is equal to check. +-- 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 +-- 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 +local function set_layers(data, area, content_id, check, min, max, minp, maxp, lvm_used, pr) + if (maxp.y >= min and minp.y <= max) then + for y = math.max(min, minp.y), math.min(max, maxp.y) do + for x = minp.x, maxp.x do + for z = minp.z, maxp.z do + local p_pos = area:index(x, y, z) + if check then + if type(check) == "function" and check({x=x,y=y,z=z}, data[p_pos], pr) then + data[p_pos] = content_id + lvm_used = true + elseif check == data[p_pos] then data[p_pos] = content_id lvm_used = true end + else + data[p_pos] = content_id + lvm_used = true end end end end - return lvm_used end + return lvm_used +end + +-- Below the bedrock, generate air/void +local function basic(vm, data, data2, emin, emax, area, minp, maxp, blockseed) + local biomemap, ymin, ymax + local lvm_used = false + local pr = PseudoRandom(blockseed) -- The Void - lvm_used = set_layers(c_void, nil, -31000, mcl_vars.mg_nether_min-1, minp, maxp, lvm_used) - lvm_used = set_layers(c_void, nil, mcl_vars.mg_nether_max+1, mcl_vars.mg_end_min-1, minp, maxp, lvm_used) - lvm_used = set_layers(c_void, nil, mcl_vars.mg_end_max+1, mcl_vars.mg_realm_barrier_overworld_end_min-1, minp, maxp, lvm_used) - lvm_used = set_layers(c_void, nil, mcl_vars.mg_realm_barrier_overworld_end_max+1, mcl_vars.mg_overworld_min-1, minp, maxp, lvm_used) - + 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.mg_nether_max +1, mcl_vars.mg_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, lvm_used, pr) -- Realm barrier between the Overworld void and the End - lvm_used = set_layers(c_realm_barrier, nil, mcl_vars.mg_realm_barrier_overworld_end_min, mcl_vars.mg_realm_barrier_overworld_end_max, minp, maxp, lvm_used) + 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_void , nil, mcl_vars.mg_realm_barrier_overworld_end_max+1, mcl_vars.mg_overworld_min -1, minp, maxp, lvm_used, pr) if mg_name ~= "singlenode" then -- Bedrock - local bedrock_check - if mcl_vars.mg_bedrock_is_rough then - bedrock_check = function(pos) - local y = pos.y - -- Bedrock layers with increasing levels of roughness, until a perfecly flat bedrock later at the bottom layer - -- This code assumes a bedrock height of 5 layers. - - local diff = mcl_vars.mg_bedrock_overworld_max - y -- Overworld bedrock - local ndiff1 = mcl_vars.mg_bedrock_nether_bottom_max - y -- Nether bedrock, bottom - local ndiff2 = mcl_vars.mg_bedrock_nether_top_max - y -- Nether bedrock, ceiling - - local top - if diff == 0 or ndiff1 == 0 or ndiff2 == 4 then - -- 50% bedrock chance - top = 2 - elseif diff == 1 or ndiff1 == 1 or ndiff2 == 3 then - -- 66.666...% - top = 3 - elseif diff == 2 or ndiff1 == 2 or ndiff2 == 2 then - -- 75% - top = 4 - elseif diff == 3 or ndiff1 == 3 or ndiff2 == 1 then - -- 90% - top = 10 - elseif diff == 4 or ndiff1 == 4 or ndiff2 == 0 then - -- 100% - return true - else - -- Not in bedrock layer - return false - end - - return math.random(1, top) <= top-1 - end - else - bedrock_check = nil - end - - lvm_used = set_layers(c_bedrock, bedrock_check, mcl_vars.mg_bedrock_overworld_min, mcl_vars.mg_bedrock_overworld_max, minp, maxp, lvm_used) - lvm_used = set_layers(c_bedrock, bedrock_check, mcl_vars.mg_bedrock_nether_bottom_min, mcl_vars.mg_bedrock_nether_bottom_max, minp, maxp, lvm_used) - lvm_used = set_layers(c_bedrock, bedrock_check, mcl_vars.mg_bedrock_nether_top_min, mcl_vars.mg_bedrock_nether_top_max, minp, maxp, lvm_used) + 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_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_top_min, mcl_vars.mg_bedrock_nether_top_max, minp, maxp, lvm_used, pr) -- Flat Nether if mg_name == "flat" then - lvm_used = set_layers(c_air, nil, mcl_vars.mg_flat_nether_floor, mcl_vars.mg_flat_nether_ceiling, minp, maxp, lvm_used) + 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) end -- Big lava seas by replacing air below a certain height if mcl_vars.mg_lava then - lvm_used = set_layers(c_lava, c_air, mcl_vars.mg_overworld_min, mcl_vars.mg_lava_overworld_max, emin, emax, lvm_used) - lvm_used = set_layers(c_nether_lava, c_air, mcl_vars.mg_nether_min, mcl_vars.mg_lava_nether_max, emin, emax, lvm_used) + lvm_used = set_layers(data, area, c_lava, c_air, mcl_vars.mg_overworld_min, mcl_vars.mg_lava_overworld_max, emin, emax, 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, emin, emax, lvm_used, pr) end -- Clay, vines, cocoas - lvm_used = generate_clay(minp, maxp, seed, data, area, lvm_used) + lvm_used = generate_clay(minp, maxp, blockseed, data, area, lvm_used) biomemap = minetest.get_mapgen_object("biomemap") - lvm_used = generate_tree_decorations(minp, maxp, seed, data, param2_data, area, biomemap, lvm_used) + lvm_used = generate_tree_decorations(minp, maxp, blockseed, data, data2, area, biomemap, lvm_used, pr) ----- Interactive block fixing section ----- ----- The section to perform basic block overrides of the core mapgen generated world. ----- @@ -1974,19 +2088,21 @@ minetest.register_on_generated(function(minp, maxp, seed) -- Set param2 (=color) of grass blocks. -- Clear snowy grass blocks without snow above to ensure consistency. local nodes = minetest.find_nodes_in_area(minp, maxp, {"mcl_core:dirt_with_grass", "mcl_core:dirt_with_grass_snow"}) + + -- 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 aream = VoxelArea:new({MinEdge={x=minp.x, y=0, z=minp.z}, MaxEdge={x=maxp.x, y=0, z=maxp.z}}) for n=1, #nodes do - local p_pos = area:index(nodes[n].x, nodes[n].y, nodes[n].z) - local p_pos_above = area:index(nodes[n].x, nodes[n].y+1, nodes[n].z) - local p_pos_below = area:index(nodes[n].x, nodes[n].y-1, nodes[n].z) - local b_pos = aream:index(nodes[n].x, 0, nodes[n].z) + local n = nodes[n] + local p_pos = area:index(n.x, n.y, n.z) + local p_pos_above = area:index(n.x, n.y+1, n.z) + local p_pos_below = area:index(n.x, n.y-1, n.z) + local b_pos = aream:index(n.x, 0, n.z) local bn = minetest.get_biome_name(biomemap[b_pos]) if bn then local biome = minetest.registered_biomes[bn] - if biome then - if biome._mcl_biome_type then - param2_data[p_pos] = biome._mcl_palette_index - lvm_used = true - end + if biome and biome._mcl_biome_type then + data2[p_pos] = biome._mcl_palette_index + lvm_used = true end end if data[p_pos] == c_dirt_with_grass_snow and p_pos_above and data[p_pos_above] ~= c_top_snow and data[p_pos_above] ~= c_snow_block then @@ -1994,6 +2110,7 @@ minetest.register_on_generated(function(minp, maxp, seed) lvm_used = true end end + end -- Nether block fixes: @@ -2025,27 +2142,28 @@ minetest.register_on_generated(function(minp, maxp, seed) -- * Remove stone, sand, dirt in v6 so our End map generator works in v6. -- * Generate spawn platform (End portal destination) elseif emin.y <= mcl_vars.mg_end_max and emax.y >= mcl_vars.mg_end_min then - local nodes + local nodes, node if mg_name == "v6" then nodes = minetest.find_nodes_in_area(emin, emax, {"mcl_core:water_source", "mcl_core:stone", "mcl_core:sand", "mcl_core:dirt"}) else nodes = minetest.find_nodes_in_area(emin, emax, {"mcl_core:water_source"}) end - for n=1, #nodes do - local y = nodes[n].y - local p_pos = area:index(nodes[n].x, y, nodes[n].z) - - if data[p_pos] == c_water or data[p_pos] == c_stone or data[p_pos] == c_dirt or data[p_pos] == c_sand then - data[p_pos] = c_air - lvm_used = true + if #nodes > 0 then + lvm_used = true + for n=1, #nodes do + node = nodes[n] + data[area:index(node.x, node.y, node.z)] = c_air end - end -- Obsidian spawn platform if minp.y <= mcl_vars.mg_end_platform_pos.y and maxp.y >= mcl_vars.mg_end_platform_pos.y and - minp.x <= mcl_vars.mg_end_platform_pos.x and maxp.x >= mcl_vars.mg_end_platform_pos.z and - minp.z <= mcl_vars.mg_end_platform_pos.z and maxp.z >= mcl_vars.mg_end_platform_pos.z then + minp.x <= mcl_vars.mg_end_platform_pos.x and maxp.x >= mcl_vars.mg_end_platform_pos.z and + minp.z <= mcl_vars.mg_end_platform_pos.z and maxp.z >= mcl_vars.mg_end_platform_pos.z then + + local pos1 = {x = math.max(minp.x, mcl_vars.mg_end_platform_pos.x-2), y = math.max(minp.y, mcl_vars.mg_end_platform_pos.y), z = math.max(minp.z, mcl_vars.mg_end_platform_pos.z-2)} + local pos2 = {x = math.min(maxp.x, mcl_vars.mg_end_platform_pos.x+2), y = math.min(maxp.y, mcl_vars.mg_end_platform_pos.y+2), z = math.min(maxp.z, mcl_vars.mg_end_platform_pos.z+2)} + for x=math.max(minp.x, mcl_vars.mg_end_platform_pos.x-2), math.min(maxp.x, mcl_vars.mg_end_platform_pos.x+2) do for z=math.max(minp.z, mcl_vars.mg_end_platform_pos.z-2), math.min(maxp.z, mcl_vars.mg_end_platform_pos.z+2) do for y=math.max(minp.y, mcl_vars.mg_end_platform_pos.y), math.min(maxp.y, mcl_vars.mg_end_platform_pos.y+2) do @@ -2065,7 +2183,7 @@ minetest.register_on_generated(function(minp, maxp, seed) -- Final hackery: Set sun light level in the End. -- -26912 is at a mapchunk border. - local shadow + local shadow = true if minp.y >= -26912 and maxp.y <= mcl_vars.mg_end_max then vm:set_lighting({day=15, night=15}) lvm_used = true @@ -2075,21 +2193,60 @@ minetest.register_on_generated(function(minp, maxp, seed) lvm_used = true end - -- Write stuff - if lvm_used then - vm:set_data(data) - vm:set_param2_data(param2_data) - vm:calc_lighting(nil, nil, shadow) - vm:write_to_map() - vm:update_liquids() - end - if mg_name ~= "singlenode" then -- Generate special decorations - generate_underground_mushrooms(minp, maxp, seed) - generate_nether_decorations(minp, maxp, seed) - generate_structures(minp, maxp, seed, biomemap) + generate_underground_mushrooms(minp, maxp, blockseed) + generate_nether_decorations(minp, maxp, blockseed) + generate_structures(minp, maxp, blockseed, biomemap) end -end) + return lvm_used, shadow +end +mcl_mapgen_core.register_generator("main", basic, nil, 1, true) + +-- "Trivial" (actually NOT) function to just read the node and some stuff to not just return "ignore", like 5.3.0 does. +-- p: Position, if it's wrong, {name="error"} node will return. +-- force: optional (default: false) - Do the maximum to still read the node within us_timeout. +-- us_timeout: optional (default: 244 = 0.000244 s = 1/80/80/80), set it at least to 3000000 to let mapgen to finish its job. +-- +-- returns node definition, eg. {name="air"}. Unfortunately still can return {name="ignore"}. +function mcl_mapgen_core.get_node(p, force, us_timeout) + -- check initial circumstances + if not p or not p.x or not p.y or not p.z then return {name="error"} end + + -- try common way + local node = minetest.get_node(p) + if node.name ~= "ignore" then + return node + end + + -- copy table to get sure it won't changed by other threads + local pos = {x=p.x,y=p.y,z=p.z} + + -- try LVM + minetest.get_voxel_manip():read_from_map(pos, pos) + node = minetest.get_node(pos) + if node.name ~= "ignore" or not force then + return node + end + + -- all ways failed - need to emerge (or forceload if generated) + local us_timeout = us_timeout or 244 + if mcl_mapgen_core.is_generated(pos) then + minetest.forceload_block(pos) + else + minetest.emerge_area(pos, pos) + end + + local t = minetest.get_us_time() + + node = minetest.get_node(pos) + + while (not node or node.name == "ignore") and (minetest.get_us_time() - t < us_timeout) do + node = minetest.get_node(pos) + end + + return node + -- it still can return "ignore", LOL, even if force = true, but only after time out +end diff --git a/mods/MAPGEN/mcl_strongholds/init.lua b/mods/MAPGEN/mcl_strongholds/init.lua index 92313bee..e465b2e4 100644 --- a/mods/MAPGEN/mcl_strongholds/init.lua +++ b/mods/MAPGEN/mcl_strongholds/init.lua @@ -67,7 +67,8 @@ local init_strongholds = function() end -- Stronghold generation for register_on_generated. -local generate_strongholds = function(minp, maxp) +local generate_strongholds = function(minp, maxp, blockseed) + local pr = PseudoRandom(blockseed) for s=1, #strongholds do if not strongholds[s].generated then local pos = strongholds[s].pos @@ -80,6 +81,12 @@ local generate_strongholds = function(minp, maxp) if pos.x + 6 > maxp.x then pos.x = maxp.x - 7 end + if pos.y - 4 < minp.y then + pos.y = minp.y + 5 + end + if pos.y + 4 > maxp.y then + pos.y = maxp.y - 5 + end if pos.z - 6 < minp.z then pos.z = minp.z + 7 end @@ -87,7 +94,7 @@ local generate_strongholds = function(minp, maxp) pos.z = maxp.z - 7 end - mcl_structures.call_struct(pos, "end_portal_shrine") + mcl_structures.call_struct(pos, "end_portal_shrine", nil, pr) strongholds[s].generated = true end end @@ -96,9 +103,4 @@ end init_strongholds() ---[[ Note this mod depends on mcl_mapgen_core to make sure the core mapgen runs FIRST. -This is important because we need this to make sure the stronghold isn't instantly -overwritten by the core mapgen (since it uses LuaVoxelManip). ]] -minetest.register_on_generated(function(minp, maxp, blockseed) - generate_strongholds(minp, maxp) -end) +mcl_mapgen_core.register_generator("strongholds", nil, generate_strongholds, 999999) diff --git a/mods/MAPGEN/mcl_structures/init.lua b/mods/MAPGEN/mcl_structures/init.lua index bc3b1b1a..31b85e15 100644 --- a/mods/MAPGEN/mcl_structures/init.lua +++ b/mods/MAPGEN/mcl_structures/init.lua @@ -1,24 +1,58 @@ local S = minetest.get_translator("mcl_structures") mcl_structures ={} +local rotations = { + "0", + "90", + "180", + "270" +} + +mcl_structures.minetest_place_schematic = minetest.place_schematic +local function ecb_place(blockpos, action, calls_remaining, param) + if calls_remaining >= 1 then return end + mcl_structures.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) + end +end +minetest.place_schematic = function(pos, schematic, rotation, replacements, force_placement, flags, after_placement_callback, pr) + 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} + minetest.emerge_area(p1, p2, ecb_place, param) + end +end +mcl_structures.place_schematic = minetest.place_schematic -- for direct usage mcl_structures.get_struct = function(file) local localfile = minetest.get_modpath("mcl_structures").."/schematics/"..file local file, errorload = io.open(localfile, "rb") if errorload ~= nil then - minetest.log("error", '[mcl_structures] Could not open this struct: ' .. localfile) - return nil + minetest.log("error", '[mcl_structures] Could not open this struct: ' .. localfile) + return nil end - local allnode = file:read("*a") - file:close() + local allnode = file:read("*a") + file:close() - return allnode + return allnode end -local mapseed = tonumber(minetest.get_mapgen_setting("seed")) --- Random number generator for all generated structures -local pr = PseudoRandom(mapseed) - -- Call on_construct on pos. -- Useful to init chests from formspec. local init_node_construct = function(pos) @@ -32,16 +66,17 @@ local init_node_construct = function(pos) end -- The call of Struct -mcl_structures.call_struct = function(pos, struct_style, rotation) +mcl_structures.call_struct = function(pos, struct_style, rotation, pr) + minetest.log("action","[mcl_structures] call_struct " .. struct_style.." at "..minetest.pos_to_string(pos)) if not rotation then rotation = "random" end if struct_style == "desert_temple" then - return mcl_structures.generate_desert_temple(pos, rotation) + return mcl_structures.generate_desert_temple(pos, rotation, pr) elseif struct_style == "desert_well" then return mcl_structures.generate_desert_well(pos, rotation) elseif struct_style == "igloo" then - return mcl_structures.generate_igloo(pos, rotation) + return mcl_structures.generate_igloo(pos, rotation, pr) elseif struct_style == "witch_hut" then return mcl_structures.generate_witch_hut(pos, rotation) elseif struct_style == "ice_spike_small" then @@ -49,13 +84,13 @@ mcl_structures.call_struct = function(pos, struct_style, rotation) elseif struct_style == "ice_spike_large" then return mcl_structures.generate_ice_spike_large(pos, rotation) elseif struct_style == "boulder" then - return mcl_structures.generate_boulder(pos, rotation) + return mcl_structures.generate_boulder(pos, rotation, pr) elseif struct_style == "fossil" then - return mcl_structures.generate_fossil(pos, rotation) + return mcl_structures.generate_fossil(pos, rotation, pr) elseif struct_style == "end_exit_portal" then return mcl_structures.generate_end_exit_portal(pos, rotation) elseif struct_style == "end_portal_shrine" then - return mcl_structures.generate_end_portal_shrine(pos, rotation) + return mcl_structures.generate_end_portal_shrine(pos, rotation, pr) end end @@ -65,12 +100,12 @@ mcl_structures.generate_desert_well = function(pos) return minetest.place_schematic(newpos, path, "0", nil, true) end -mcl_structures.generate_igloo = function(pos) +mcl_structures.generate_igloo = function(pos, rotation, pr) -- Place igloo - local success, rotation = mcl_structures.generate_igloo_top(pos) + local success, rotation = mcl_structures.generate_igloo_top(pos, pr) -- Place igloo basement with 50% chance - local r = math.random(1,2) - if success and r == 1 then + local r = pr:next(1,2) + if r == 1 then -- Select basement depth local dim = mcl_worlds.pos_to_dimension(pos) local buffer = pos.y - (mcl_vars.mg_lava_overworld_max + 10) @@ -86,7 +121,7 @@ mcl_structures.generate_igloo = function(pos) if buffer <= 19 then return success end - local depth = math.random(19, buffer) + local depth = pr:next(19, buffer) local bpos = {x=pos.x, y=pos.y-depth, z=pos.z} -- trapdoor position local tpos @@ -111,8 +146,8 @@ mcl_structures.generate_igloo = function(pos) return success end local set_brick = function(pos) - local c = math.random(1, 3) -- cracked chance - local m = math.random(1, 10) -- chance for monster egg + 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 @@ -155,75 +190,74 @@ mcl_structures.generate_igloo = function(pos) 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) + mcl_structures.generate_igloo_basement(bpos, rotation, pr) end return success end -mcl_structures.generate_igloo_top = function(pos) +mcl_structures.generate_igloo_top = function(pos, pr) -- FIXME: This spawns bookshelf instead of furnace. Fix this! -- Furnace does ot work atm because apparently meta is not set. :-( local newpos = {x=pos.x,y=pos.y-1,z=pos.z} local path = minetest.get_modpath("mcl_structures").."/schematics/mcl_structures_igloo_top.mts" - local rotation = tostring(math.random(0,3)*90) + local rotation = tostring(pr:next(0,3)*90) return minetest.place_schematic(newpos, path, rotation, nil, true), rotation end -mcl_structures.generate_igloo_basement = function(pos, orientation) +local function igloo_placement_callback(p1, p2, size, orientation, pr) + local chest_offset + if orientation == "0" then + chest_offset = {x=5, y=1, z=5} + elseif orientation == "90" then + chest_offset = {x=5, y=1, z=3} + elseif orientation == "180" then + chest_offset = {x=3, y=1, z=1} + elseif orientation == "270" then + chest_offset = {x=1, y=1, z=5} + else + return + end + local size = {x=9,y=5,z=7} + local lootitems = mcl_loot.get_multi_loot({ + { + stacks_min = 1, + stacks_max = 1, + items = { + { itemstring = "mcl_core:apple_gold", weight = 1 }, + } + }, + { + stacks_min = 2, + stacks_max = 8, + items = { + { itemstring = "mcl_core:coal_lump", weight = 15, amount_min = 1, amount_max = 4 }, + { itemstring = "mcl_core:apple", weight = 15, amount_min = 1, amount_max = 3 }, + { itemstring = "mcl_farming:wheat_item", weight = 10, amount_min = 2, amount_max = 3 }, + { itemstring = "mcl_core:gold_nugget", weight = 10, amount_min = 1, amount_max = 3 }, + { itemstring = "mcl_mobitems:rotten_flesh", weight = 10 }, + { itemstring = "mcl_tools:axe_stone", weight = 2 }, + { itemstring = "mcl_core:emerald", weight = 1 }, + } + }}, pr) + + local chest_pos = vector.add(p1, chest_offset) + init_node_construct(chest_pos) + local meta = minetest.get_meta(chest_pos) + local inv = meta:get_inventory() + mcl_loot.fill_inventory(inv, "main", lootitems) +end + +mcl_structures.generate_igloo_basement = function(pos, orientation, pr) -- TODO: Add brewing stand -- TODO: Add monster eggs -- TODO: Spawn villager and zombie villager local path = minetest.get_modpath("mcl_structures").."/schematics/mcl_structures_igloo_basement.mts" - - local success = minetest.place_schematic(pos, path, orientation, nil, true) - if success then - local chest_offset - if orientation == "0" then - chest_offset = {x=5, y=1, z=5} - elseif orientation == "90" then - chest_offset = {x=5, y=1, z=3} - elseif orientation == "180" then - chest_offset = {x=3, y=1, z=1} - elseif orientation == "270" then - chest_offset = {x=1, y=1, z=5} - else - return success - end - local size = {x=9,y=5,z=7} - local lootitems = mcl_loot.get_multi_loot({ - { - stacks_min = 1, - stacks_max = 1, - items = { - { itemstring = "mcl_core:apple_gold", weight = 1 }, - } - }, - { - stacks_min = 2, - stacks_max = 8, - items = { - { itemstring = "mcl_core:coal_lump", weight = 15, amount_min = 1, amount_max = 4 }, - { itemstring = "mcl_core:apple", weight = 15, amount_min = 1, amount_max = 3 }, - { itemstring = "mcl_farming:wheat_item", weight = 10, amount_min = 2, amount_max = 3 }, - { itemstring = "mcl_core:gold_nugget", weight = 10, amount_min = 1, amount_max = 3 }, - { itemstring = "mcl_mobitems:rotten_flesh", weight = 10 }, - { itemstring = "mcl_tools:axe_stone", weight = 2 }, - { itemstring = "mcl_core:emerald", weight = 1 }, - } - }}, pr) - - local chest_pos = vector.add(pos, chest_offset) - init_node_construct(chest_pos) - local meta = minetest.get_meta(chest_pos) - local inv = meta:get_inventory() - mcl_loot.fill_inventory(inv, "main", lootitems) - end - return success + mcl_structures.place_schematic(pos, path, orientation, nil, true, nil, igloo_placement_callback, pr) end -mcl_structures.generate_boulder = function(pos) +mcl_structures.generate_boulder = function(pos, rotation, pr) -- Choose between 2 boulder sizes (2×2×2 or 3×3×3) - local r = math.random(1, 10) + local r = pr:next(1, 10) local path if r <= 3 then path = minetest.get_modpath("mcl_structures").."/schematics/mcl_structures_boulder_small.mts" @@ -235,9 +269,20 @@ mcl_structures.generate_boulder = function(pos) return minetest.place_schematic(newpos, path) end +local function hut_placement_callback(p1, p2, size, orientation, pr) + if not p1 or not p2 then return end + local legs = minetest.find_nodes_in_area(p1, p2, "mcl_core:tree") + for i = 1, #legs do + while minetest.get_item_group(mcl_mapgen_core.get_node({x=legs[i].x, y=legs[i].y-1, z=legs[i].z}, true, 333333).name, "water") ~= 0 do + legs[i].y = legs[i].y - 1 + minetest.swap_node(legs[i], {name = "mcl_core:tree", param2 = 2}) + end + end +end + mcl_structures.generate_witch_hut = function(pos, rotation) local path = minetest.get_modpath("mcl_structures").."/schematics/mcl_structures_witch_hut.mts" - return minetest.place_schematic(pos, path, rotation, nil, true) + mcl_structures.place_schematic(pos, path, rotation, nil, true, nil, hut_placement_callback, pr) end mcl_structures.generate_ice_spike_small = function(pos) @@ -250,7 +295,7 @@ mcl_structures.generate_ice_spike_large = function(pos) return minetest.place_schematic(pos, path, "random", nil, false) end -mcl_structures.generate_fossil = function(pos) +mcl_structures.generate_fossil = function(pos, rotation, pr) -- Generates one out of 8 possible fossil pieces local newpos = {x=pos.x,y=pos.y-1,z=pos.z} local fossils = { @@ -263,7 +308,7 @@ mcl_structures.generate_fossil = function(pos) "mcl_structures_fossil_spine_3.mts", -- 7×4×13 "mcl_structures_fossil_spine_4.mts", -- 8×5×13 } - local r = math.random(1, #fossils) + local r = pr:next(1, #fossils) local path = minetest.get_modpath("mcl_structures").."/schematics/"..fossils[r] return minetest.place_schematic(newpos, path, "random", nil, true) end @@ -273,24 +318,16 @@ mcl_structures.generate_end_exit_portal = function(pos) return minetest.place_schematic(pos, path, "0", nil, true) end -local generate_end_portal_shrine_no_delay = function(newpos) - local path = minetest.get_modpath("mcl_structures").."/schematics/mcl_structures_end_portal_room_simple.mts" - local size = {x=13, y=8, z=13} - local ret = minetest.place_schematic(newpos, path, "0", nil, true) - if ret == nil then - return ret - end - - local area_start, area_end = newpos, vector.add(newpos, size) +local function shrine_placement_callback(p1, p2, size, rotation, pr) -- Find and setup spawner with silverfish - local spawners = minetest.find_nodes_in_area(area_start, area_end, "mcl_mobspawners:spawner") + local spawners = minetest.find_nodes_in_area(p1, p2, "mcl_mobspawners:spawner") for s=1, #spawners do local meta = minetest.get_meta(spawners[s]) mcl_mobspawners.setup_spawner(spawners[s], "mobs_mc:silverfish") end -- Shuffle stone brick types - local bricks = minetest.find_nodes_in_area(area_start, area_end, "mcl_core:stonebrick") + local bricks = minetest.find_nodes_in_area(p1, p2, "mcl_core:stonebrick") for b=1, #bricks do local r_bricktype = pr:next(1, 100) local r_infested = pr:next(1, 100) @@ -317,7 +354,7 @@ local generate_end_portal_shrine_no_delay = function(newpos) end -- Also replace stairs - local stairs = minetest.find_nodes_in_area(area_start, area_end, {"mcl_stairs:stair_stonebrick", "mcl_stairs:stair_stonebrick_outer", "mcl_stairs:stair_stonebrick_inner"}) + local stairs = minetest.find_nodes_in_area(p1, p2, {"mcl_stairs:stair_stonebrick", "mcl_stairs:stair_stonebrick_outer", "mcl_stairs:stair_stonebrick_inner"}) for s=1, #stairs do local stair = minetest.get_node(stairs[s]) local r_type = pr:next(1, 100) @@ -344,7 +381,7 @@ local generate_end_portal_shrine_no_delay = function(newpos) end -- Randomly add ender eyes into end portal frames, but never fill the entire frame - local frames = minetest.find_nodes_in_area(area_start, area_end, "mcl_portals:end_portal_frame") + local frames = minetest.find_nodes_in_area(p1, p2, "mcl_portals:end_portal_frame") local eyes = 0 for f=1, #frames do local r_eye = pr:next(1, 10) @@ -357,40 +394,34 @@ local generate_end_portal_shrine_no_delay = function(newpos) end end end - - return ret end -local function ecb_generate_end_portal_shrine(blockpos, action, calls_remaining, param) - if calls_remaining <= 0 then - generate_end_portal_shrine_no_delay({x=param.x, y=param.y, z=param.z}) - end -end - -mcl_structures.generate_end_portal_shrine = function(pos) - local offset = {x=6, y=8, z=6} +mcl_structures.generate_end_portal_shrine = function(pos, rotation, pr) + local offset = {x=6, y=4, z=6} local size = {x=13, y=8, z=13} local newpos = { x = pos.x - offset.x, y = pos.y, z = pos.z - offset.z } - minetest.emerge_area(vector.subtract(newpos,10), vector.add(vector.add(newpos, size),10), ecb_generate_end_portal_shrine, {x=newpos.x, y=newpos.y, z=newpos.z}) + + local path = minetest.get_modpath("mcl_structures").."/schematics/mcl_structures_end_portal_room_simple.mts" + mcl_structures.place_schematic(newpos, path, "0", nil, true, nil, shrine_placement_callback, pr) end -mcl_structures.generate_desert_temple = function(pos) - -- No Generating for the temple ... Why using it ? No Change - local path = minetest.get_modpath("mcl_structures").."/schematics/mcl_structures_desert_temple.mts" - local newpos = {x=pos.x,y=pos.y-12,z=pos.z} - local size = {x=22, y=24, z=22} - if newpos == nil then - return - end - local ret = minetest.place_schematic(newpos, path, "random", nil, true) - if ret == nil then - return ret +local function temple_placement_callback(p1, p2, size, rotation, pr) + + -- Delete cacti leftovers: + local cactus_nodes = minetest.find_nodes_in_area_under_air(p1, p2, "mcl_core:cactus") + if cactus_nodes and #cactus_nodes > 0 then + for _, pos in pairs(cactus_nodes) do + local node_below = minetest.get_node({x=pos.x, y=pos.y-1, z=pos.z}) + if node_below and node_below.name == "mcl_core:sandstone" then + minetest.swap_node(pos, {name="air"}) + end + end end -- Find chests. -- FIXME: Searching this large area just for the chets is not efficient. Need a better way to find the chests; -- probably let's just infer it from newpos because the schematic always the same. - local chests = minetest.find_nodes_in_area({x=newpos.x-size.x, y=newpos.y, z=newpos.z-size.z}, vector.add(newpos, size), "mcl_chests:chest") + local chests = minetest.find_nodes_in_area(p1, p2, "mcl_chests:chest") -- Add desert temple loot into chests for c=1, #chests do @@ -436,7 +467,7 @@ mcl_structures.generate_desert_temple = function(pos) end -- Initialize pressure plates and randomly remove up to 5 plates - local pplates = minetest.find_nodes_in_area({x=newpos.x-size.x, y=newpos.y, z=newpos.z-size.z}, vector.add(newpos, size), "mesecons_pressureplates:pressure_plate_stone_off") + local pplates = minetest.find_nodes_in_area(p1, p2, "mesecons_pressureplates:pressure_plate_stone_off") local pplates_remove = 5 for p=1, #pplates do if pplates_remove > 0 and pr:next(1, 100) >= 50 then @@ -448,8 +479,17 @@ mcl_structures.generate_desert_temple = function(pos) minetest.registered_nodes["mesecons_pressureplates:pressure_plate_stone_off"].on_construct(pplates[p]) end end +end - return ret +mcl_structures.generate_desert_temple = function(pos, rotation, pr) + -- No Generating for the temple ... Why using it ? No Change + local path = minetest.get_modpath("mcl_structures").."/schematics/mcl_structures_desert_temple.mts" + local newpos = {x=pos.x,y=pos.y-12,z=pos.z} + local size = {x=22, y=24, z=22} + if newpos == nil then + return + end + minetest.place_schematic(newpos, path, "random", nil, true, nil, temple_placement_callback, pr) end local registered_structures = {} diff --git a/mods/MAPGEN/mcl_villages/buildings.lua b/mods/MAPGEN/mcl_villages/buildings.lua index 0d58739e..cf1111bb 100644 --- a/mods/MAPGEN/mcl_villages/buildings.lua +++ b/mods/MAPGEN/mcl_villages/buildings.lua @@ -4,8 +4,8 @@ ------------------------------------------------------------------------------- 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_util.get_far_node(pos, true) - if not platform_material then + local platform_material = mcl_mapgen_core.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 @@ -81,12 +81,15 @@ function settlements.create_site_plan(maxp, minp, pr) local possible_rotations = {"0", "90", "180", "270"} -- find center of chunk local center = { - x=maxp.x-half_map_chunk_size, + x=math.floor((minp.x+maxp.x)/2), y=maxp.y, - z=maxp.z-half_map_chunk_size + z=math.floor((minp.z+maxp.z)/2) } -- find center_surface of chunk - local center_surface , surface_material = settlements.find_surface(center) + 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 return false end @@ -114,49 +117,57 @@ function settlements.create_site_plan(maxp, minp, pr) local x, z, r = center_surface.x, center_surface.z, building_all_info["hsize"] -- draw j circles around center and increase radius by math.random(2,5) for j = 1,20 do - if number_built < number_of_buildings then - -- set position on imaginary circle - for j = 0, 360, 15 do - local angle = j * math.pi / 180 - local ptx, ptz = x + r * math.cos( angle ), z + r * math.sin( angle ) - ptx = settlements.round(ptx, 0) - ptz = settlements.round(ptz, 0) - local pos1 = { x=ptx, y=center_surface.y+50, z=ptz} - local pos_surface, surface_material = settlements.find_surface(pos1) - if not pos_surface then break end + -- set position on imaginary circle + for j = 0, 360, 15 do + local angle = j * math.pi / 180 + local ptx, ptz = x + r * math.cos( angle ), z + r * math.sin( angle ) + ptx = settlements.round(ptx, 0) + ptz = settlements.round(ptz, 0) + local pos1 = { x=ptx, y=center_surface.y+50, z=ptz} + local chunk_number = mcl_vars.get_chunk_number(pos1) + local pos_surface, surface_material + if chunks[chunk_number] then + pos_surface, surface_material = settlements.find_surface(pos1) + else + chunks[chunk_number] = true + pos_surface, surface_material = settlements.find_surface(pos1, true) + end + if not pos_surface then break end - local randomized_schematic_table = shuffle(settlements.schematic_table, pr) - -- pick schematic - local size = #randomized_schematic_table - for i = size, 1, -1 do - -- already enough buildings of that type? - if count_buildings[randomized_schematic_table[i]["name"]] < randomized_schematic_table[i]["max_num"]*number_of_buildings then - building_all_info = randomized_schematic_table[i] - -- check distance to other buildings - local distance_to_other_buildings_ok = settlements.check_distance(settlement_info, pos_surface, building_all_info["hsize"]) - if distance_to_other_buildings_ok then - -- count built houses - count_buildings[building_all_info["name"]] = count_buildings[building_all_info["name"]] +1 - rotation = possible_rotations[ pr:next(1, #possible_rotations ) ] - number_built = number_built + 1 - settlement_info[index] = { - pos = pos_surface, - name = building_all_info["name"], - hsize = building_all_info["hsize"], - rotat = rotation, - surface_mat = surface_material - } - index = index + 1 - break - end + local randomized_schematic_table = shuffle(settlements.schematic_table, pr) + -- pick schematic + local size = #randomized_schematic_table + for i = size, 1, -1 do + -- already enough buildings of that type? + if count_buildings[randomized_schematic_table[i]["name"]] < randomized_schematic_table[i]["max_num"]*number_of_buildings then + building_all_info = randomized_schematic_table[i] + -- check distance to other buildings + local distance_to_other_buildings_ok = settlements.check_distance(settlement_info, pos_surface, building_all_info["hsize"]) + if distance_to_other_buildings_ok then + -- count built houses + count_buildings[building_all_info["name"]] = count_buildings[building_all_info["name"]] +1 + rotation = possible_rotations[ pr:next(1, #possible_rotations ) ] + number_built = number_built + 1 + settlement_info[index] = { + pos = pos_surface, + name = building_all_info["name"], + hsize = building_all_info["hsize"], + rotat = rotation, + surface_mat = surface_material + } + index = index + 1 + break end end - if number_of_buildings == number_built then - break - end end - r = r + pr:next(2,5) + if number_of_buildings == number_built then + break + end end + if number_built >= number_of_buildings then + break + end + r = r + pr:next(2,5) end settlements.debug("really ".. number_built) return settlement_info diff --git a/mods/MAPGEN/mcl_villages/depends.txt b/mods/MAPGEN/mcl_villages/depends.txt index e9d14ad9..d8a90ad4 100644 --- a/mods/MAPGEN/mcl_villages/depends.txt +++ b/mods/MAPGEN/mcl_villages/depends.txt @@ -1,4 +1,5 @@ mcl_util +mcl_mapgen_core mcl_core mcl_loot mcl_farming? diff --git a/mods/MAPGEN/mcl_villages/foundation.lua b/mods/MAPGEN/mcl_villages/foundation.lua index 98726966..67a2385f 100644 --- a/mods/MAPGEN/mcl_villages/foundation.lua +++ b/mods/MAPGEN/mcl_villages/foundation.lua @@ -51,11 +51,12 @@ function settlements.terraform(settlement_info, pr) settlements.ground(p, pr) else -- write ground - local p = {x=pos.x+xi, y=pos.y+yi, z=pos.z+zi} - local node = mcl_util.get_far_node(p, true) - if node and node.name ~= "air" then - minetest.swap_node(p,{name="air"}) - end +-- local p = {x=pos.x+xi, y=pos.y+yi, z=pos.z+zi} +-- local node = mcl_mapgen_core.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 diff --git a/mods/MAPGEN/mcl_villages/init.lua b/mods/MAPGEN/mcl_villages/init.lua index 287b4c9b..584155d9 100644 --- a/mods/MAPGEN/mcl_villages/init.lua +++ b/mods/MAPGEN/mcl_villages/init.lua @@ -52,7 +52,7 @@ end -- -- on map generation, try to build a settlement -- -local function build_a_settlement_no_delay(minp, maxp, blockseed) +local function build_a_settlement(minp, maxp, blockseed) local pr = PseudoRandom(blockseed) -- fill settlement_info with buildings and their data @@ -72,30 +72,28 @@ local function build_a_settlement_no_delay(minp, maxp, blockseed) settlements.initialize_nodes(settlement_info, pr) end -local function ecb_build_a_settlement(blockpos, action, calls_remaining, param) - if calls_remaining <= 0 then - build_a_settlement_no_delay(param.minp, param.maxp, param.blockseed) - end +local function ecb_village(blockpos, action, calls_remaining, param) + if calls_remaining >= 1 then return end + local minp, maxp, blockseed = param.minp, param.maxp, param.blockseed + build_a_settlement(minp, maxp, blockseed) end -- Disable natural generation in singlenode. local mg_name = minetest.get_mapgen_setting("mg_name") if mg_name ~= "singlenode" then - minetest.register_on_generated(function(minp, maxp, blockseed) - -- needed for manual and automated settlement building - local heightmap = minetest.get_mapgen_object("heightmap") - - -- randomly try to build settlements - if blockseed % 77 ~= 17 then return end - + mcl_mapgen_core.register_generator("villages", nil, function(minp, maxp, blockseed) -- don't build settlement underground if maxp.y < 0 then return end - + -- randomly try to build settlements + if blockseed % 77 ~= 17 then return end + -- needed for manual and automated settlement building -- don't build settlements on (too) uneven terrain - local height_difference = settlements.evaluate_heightmap(minp, maxp) + local heightmap = minetest.get_mapgen_object("heightmap") + local height_difference = settlements.evaluate_heightmap() if height_difference > max_height_difference then return end - -- we need 'minetest.after' here to exit from emerging thread we probably currently in: - minetest.after(0.1, build_a_settlement_no_delay, vector.new(minp), vector.new(maxp), blockseed) + + local param={minp=vector.new(minp), maxp=vector.new(maxp), blockseed=blockseed} + minetest.emerge_area(minp, maxp, ecb_village, param) end) end -- manually place villages @@ -108,7 +106,7 @@ if minetest.is_creative_enabled("") then if not pointed_thing.under then return end local minp = vector.subtract( pointed_thing.under, half_map_chunk_size) local maxp = vector.add( pointed_thing.under, half_map_chunk_size) - build_a_settlement_no_delay(minp, maxp, math.random(0,32767)) + build_a_settlement(minp, maxp, math.random(0,32767)) end }) end diff --git a/mods/MAPGEN/mcl_villages/utils.lua b/mods/MAPGEN/mcl_villages/utils.lua index c539e7dc..dfde4089 100644 --- a/mods/MAPGEN/mcl_villages/utils.lua +++ b/mods/MAPGEN/mcl_villages/utils.lua @@ -45,31 +45,27 @@ end -- function to find surface block y coordinate -- returns surface postion ------------------------------------------------------------------------------- -function settlements.find_surface(pos) +function settlements.find_surface(pos, wait) local p6 = vector.new(pos) local cnt = 0 - local itter -- count up or down + local itter = 1 -- count up or down local cnt_max = 200 -- check, in which direction to look for surface - local surface_node = mcl_util.get_far_node(p6, true) - if surface_node and string.find(surface_node.name,"air") then - itter = -1 + local surface_node + if wait then + surface_node = mcl_mapgen_core.get_node(p6, true, 10000000) else - itter = 1 + surface_node = mcl_mapgen_core.get_node(p6) + end + if surface_node.name=="air" or surface_node.name=="ignore" then + itter = -1 end -- go through nodes an find surface while cnt < cnt_max do - cnt = cnt+1 - surface_node = mcl_util.get_far_node(p6, true) - if surface_node.name == "ignore" then - settlements.debug("find_surface1: nil or ignore") - return nil - end - -- Check Surface_node and Node above -- if settlements.surface_mat[surface_node.name] then - local surface_node_plus_1 = mcl_util.get_far_node({ x=p6.x, y=p6.y+1, z=p6.z}, true) + local surface_node_plus_1 = mcl_mapgen_core.get_node({ x=p6.x, y=p6.y+1, z=p6.z}) if surface_node_plus_1 and surface_node and (string.find(surface_node_plus_1.name,"air") or string.find(surface_node_plus_1.name,"snow") or @@ -93,6 +89,8 @@ function settlements.find_surface(pos) settlements.debug("find_surface4: y<0") return nil end + cnt = cnt+1 + surface_node = mcl_mapgen_core.get_node(p6) end settlements.debug("find_surface5: cnt_max overflow") return nil @@ -241,7 +239,7 @@ function settlements.initialize_nodes(settlement_info, pr) for xi = 0,width do for zi = 0,depth do local ptemp = {x=p.x+xi, y=p.y+yi, z=p.z+zi} - local node = mcl_util.get_far_node(ptemp, true) + local node = mcl_mapgen_core.get_node(ptemp) if node.name == "mcl_furnaces:furnace" or node.name == "mcl_chests:chest" or node.name == "mcl_anvils:anvil" then @@ -272,44 +270,39 @@ end -- evaluate heightmap ------------------------------------------------------------------------------- function settlements.evaluate_heightmap() - local heightmap = minetest.get_mapgen_object("heightmap") - -- max height and min height, initialize with impossible values for easier first time setting - local max_y = -50000 - local min_y = 50000 - -- only evaluate the center square of heightmap 40 x 40 - local square_start = 1621 - local square_end = 1661 - for j = 1 , 40, 1 do - for i = square_start, square_end, 1 do - -- skip buggy heightmaps, return high value - if heightmap[i] == -31000 or - heightmap[i] == 31000 - then - return max_height_difference + 1 - end - if heightmap[i] < min_y - then - min_y = heightmap[i] - end - if heightmap[i] > max_y - then - max_y = heightmap[i] - end - end - -- set next line - square_start = square_start + 80 - square_end = square_end + 80 - end - -- return the difference between highest and lowest pos in chunk - local height_diff = max_y - min_y - -- filter buggy heightmaps - if height_diff <= 1 - then - return max_height_difference + 1 - end - -- debug info - settlements.debug("heightdiff ".. height_diff) - return height_diff + local heightmap = minetest.get_mapgen_object("heightmap") + -- max height and min height, initialize with impossible values for easier first time setting + local max_y = -50000 + local min_y = 50000 + -- only evaluate the center square of heightmap 40 x 40 + local square_start = 1621 + local square_end = 1661 + for j = 1 , 40, 1 do + for i = square_start, square_end, 1 do + -- skip buggy heightmaps, return high value + if heightmap[i] == -31000 or heightmap[i] == 31000 then + return max_height_difference + 1 + end + if heightmap[i] < min_y then + min_y = heightmap[i] + end + if heightmap[i] > max_y then + max_y = heightmap[i] + end + end + -- set next line + square_start = square_start + 80 + square_end = square_end + 80 + end + -- return the difference between highest and lowest pos in chunk + local height_diff = max_y - min_y + -- filter buggy heightmaps + if height_diff <= 1 then + return max_height_difference + 1 + end + -- debug info + settlements.debug("heightdiff ".. height_diff) + return height_diff end ------------------------------------------------------------------------------- -- Set array to list diff --git a/mods/MAPGEN/tsm_railcorridors/depends.txt b/mods/MAPGEN/tsm_railcorridors/depends.txt index a22565d7..560b68a4 100644 --- a/mods/MAPGEN/tsm_railcorridors/depends.txt +++ b/mods/MAPGEN/tsm_railcorridors/depends.txt @@ -1,6 +1,7 @@ mcl_init mcl_worlds mcl_core +mcl_mapgen_core mcl_loot mcl_tnt mcl_farming diff --git a/mods/MAPGEN/tsm_railcorridors/init.lua b/mods/MAPGEN/tsm_railcorridors/init.lua index f2e02d99..483a79f4 100644 --- a/mods/MAPGEN/tsm_railcorridors/init.lua +++ b/mods/MAPGEN/tsm_railcorridors/init.lua @@ -1089,7 +1089,7 @@ local function create_corridor_system(main_cave_coords) end -- The rail corridor algorithm starts here -minetest.register_on_generated(function(minp, maxp, blockseed) +mcl_mapgen_core.register_generator("railcorridors", nil, function(minp, maxp, blockseed, _pr) -- We re-init the randomizer for every mapchunk as we start generating in the middle of each mapchunk. -- We can't use the mapgen seed as this would make the algorithm depending on the order the mapchunk generate. InitRandomizer(blockseed) @@ -1115,4 +1115,4 @@ minetest.register_on_generated(function(minp, maxp, blockseed) end end end -end) +end, 10)