From 764d666a7009d818c83a9215f65b4158dbbd0b07 Mon Sep 17 00:00:00 2001 From: kno10 Date: Fri, 23 Aug 2024 10:55:30 +0200 Subject: [PATCH] Huge update of new terraforming, structures, and village code --- mods/ENTITIES/mobs_mc/ender_dragon.lua | 2 +- mods/ITEMS/mcl_end/end_crystal.lua | 2 +- mods/ITEMS/mcl_end/eye_of_ender.lua | 2 +- mods/ITEMS/mcl_portals/portal_gateway.lua | 2 +- mods/ITEMS/mcl_portals/portal_nether.lua | 119 ++-- mods/MAPGEN/mcl_biomes/init.lua | 116 ++-- mods/MAPGEN/mcl_dungeons/init.lua | 17 +- mods/MAPGEN/mcl_mapgen_core/api.lua | 13 +- mods/MAPGEN/mcl_mapgen_core/v6.lua | 13 +- mods/MAPGEN/mcl_nether_fortresses/init.lua | 152 ++--- mods/MAPGEN/mcl_strongholds/init.lua | 7 +- mods/MAPGEN/mcl_structures/API.md | 38 +- mods/MAPGEN/mcl_structures/api.lua | 467 ---------------- mods/MAPGEN/mcl_structures/desert_temple.lua | 12 +- mods/MAPGEN/mcl_structures/desert_well.lua | 15 + mods/MAPGEN/mcl_structures/end_city.lua | 44 +- mods/MAPGEN/mcl_structures/end_spawn.lua | 34 +- mods/MAPGEN/mcl_structures/fossil.lua | 25 + mods/MAPGEN/mcl_structures/foundation.lua | 334 ----------- mods/MAPGEN/mcl_structures/geode.lua | 2 +- mods/MAPGEN/mcl_structures/igloo.lua | 170 +++--- mods/MAPGEN/mcl_structures/init.lua | 139 +---- mods/MAPGEN/mcl_structures/jungle_temple.lua | 4 +- mods/MAPGEN/mcl_structures/mod.conf | 2 +- mods/MAPGEN/mcl_structures/ocean_ruins.lua | 6 +- mods/MAPGEN/mcl_structures/ocean_temple.lua | 157 ++++++ .../mcl_structures/pillager_outpost.lua | 14 +- mods/MAPGEN/mcl_structures/ruined_portal.lua | 15 +- .../mcl_structures_ruined_portal_6.mts | Bin 0 -> 946 bytes mods/MAPGEN/mcl_structures/shipwrecks.lua | 97 +--- mods/MAPGEN/mcl_structures/witch_hut.lua | 28 +- .../mcl_structures/woodland_mansion.lua | 20 +- mods/MAPGEN/mcl_terrain_features/init.lua | 127 +++-- mods/MAPGEN/mcl_villages/buildings.lua | 158 ++---- mods/MAPGEN/mcl_villages/mod.conf | 2 +- .../mcl_villages/schematics/belltower.mts | Bin 211 -> 0 bytes .../mcl_villages/schematics/blacksmith.mts | Bin 996 -> 0 bytes .../mcl_villages/schematics/butcher.mts | Bin 806 -> 0 bytes .../MAPGEN/mcl_villages/schematics/church.mts | Bin 949 -> 0 bytes mods/MAPGEN/mcl_villages/schematics/farm.mts | Bin 379 -> 0 bytes mods/MAPGEN/mcl_villages/schematics/lamp.mts | Bin 176 -> 0 bytes .../mcl_villages/schematics/large_house.mts | Bin 1187 -> 0 bytes .../mcl_villages/schematics/library.mts | Bin 802 -> 0 bytes .../mcl_villages/schematics/medium_house.mts | Bin 807 -> 0 bytes .../mcl_villages/schematics/small_house.mts | Bin 578 -> 0 bytes .../MAPGEN/mcl_villages/schematics/tavern.mts | Bin 1009 -> 0 bytes mods/MAPGEN/mcl_villages/schematics/well.mts | Bin 444 -> 0 bytes mods/MAPGEN/mcl_villages/utils.lua | 76 --- mods/MAPGEN/modpack.conf | 2 +- mods/MAPGEN/tsm_railcorridors/init.lua | 3 +- mods/MAPGEN/vl_structures/API.md | 37 ++ mods/MAPGEN/vl_structures/api.lua | 518 ++++++++++++++++++ mods/MAPGEN/vl_structures/init.lua | 49 ++ mods/MAPGEN/vl_structures/mod.conf | 4 + mods/MAPGEN/vl_structures/util.lua | 132 +++++ mods/MAPGEN/vl_terraforming/API.md | 99 ++++ mods/MAPGEN/vl_terraforming/clearance.lua | 174 ++++++ mods/MAPGEN/vl_terraforming/foundation.lua | 117 ++++ mods/MAPGEN/vl_terraforming/init.lua | 8 + mods/MAPGEN/vl_terraforming/level.lua | 205 +++++++ mods/MAPGEN/vl_terraforming/mod.conf | 3 + mods/MAPGEN/vl_terraforming/util.lua | 63 +++ settingtypes.txt | 3 + 63 files changed, 2157 insertions(+), 1691 deletions(-) delete mode 100644 mods/MAPGEN/mcl_structures/api.lua create mode 100644 mods/MAPGEN/mcl_structures/desert_well.lua create mode 100644 mods/MAPGEN/mcl_structures/fossil.lua delete mode 100644 mods/MAPGEN/mcl_structures/foundation.lua create mode 100644 mods/MAPGEN/mcl_structures/ocean_temple.lua create mode 100644 mods/MAPGEN/mcl_structures/schematics/mcl_structures_ruined_portal_6.mts delete mode 100644 mods/MAPGEN/mcl_villages/schematics/belltower.mts delete mode 100644 mods/MAPGEN/mcl_villages/schematics/blacksmith.mts delete mode 100644 mods/MAPGEN/mcl_villages/schematics/butcher.mts delete mode 100644 mods/MAPGEN/mcl_villages/schematics/church.mts delete mode 100644 mods/MAPGEN/mcl_villages/schematics/farm.mts delete mode 100644 mods/MAPGEN/mcl_villages/schematics/lamp.mts delete mode 100644 mods/MAPGEN/mcl_villages/schematics/large_house.mts delete mode 100644 mods/MAPGEN/mcl_villages/schematics/library.mts delete mode 100644 mods/MAPGEN/mcl_villages/schematics/medium_house.mts delete mode 100644 mods/MAPGEN/mcl_villages/schematics/small_house.mts delete mode 100644 mods/MAPGEN/mcl_villages/schematics/tavern.mts delete mode 100644 mods/MAPGEN/mcl_villages/schematics/well.mts create mode 100644 mods/MAPGEN/vl_structures/API.md create mode 100644 mods/MAPGEN/vl_structures/api.lua create mode 100644 mods/MAPGEN/vl_structures/init.lua create mode 100644 mods/MAPGEN/vl_structures/mod.conf create mode 100644 mods/MAPGEN/vl_structures/util.lua create mode 100644 mods/MAPGEN/vl_terraforming/API.md create mode 100644 mods/MAPGEN/vl_terraforming/clearance.lua create mode 100644 mods/MAPGEN/vl_terraforming/foundation.lua create mode 100644 mods/MAPGEN/vl_terraforming/init.lua create mode 100644 mods/MAPGEN/vl_terraforming/level.lua create mode 100644 mods/MAPGEN/vl_terraforming/mod.conf create mode 100644 mods/MAPGEN/vl_terraforming/util.lua diff --git a/mods/ENTITIES/mobs_mc/ender_dragon.lua b/mods/ENTITIES/mobs_mc/ender_dragon.lua index d06ed104b..562b6113d 100644 --- a/mods/ENTITIES/mobs_mc/ender_dragon.lua +++ b/mods/ENTITIES/mobs_mc/ender_dragon.lua @@ -128,7 +128,7 @@ mcl_mobs.register_mob("mobs_mc:enderdragon", { on_die = function(self, pos, cmi_cause) if self._portal_pos then mcl_portals.spawn_gateway_portal() - mcl_structures.place_structure(self._portal_pos,mcl_structures.registered_structures["end_exit_portal_open"],PseudoRandom(minetest.get_mapgen_setting("seed"))) + vl_structures.place_structure(self._portal_pos,vl_structures.registered_structures["end_exit_portal_open"],PseudoRandom(minetest.get_mapgen_setting("seed"))) if self._initial then mcl_experience.throw_xp(pos, 11500) -- 500 + 11500 = 12000 minetest.set_node(vector.add(self._portal_pos, vector.new(0, 5, 0)), {name = "mcl_end:dragon_egg"}) diff --git a/mods/ITEMS/mcl_end/end_crystal.lua b/mods/ITEMS/mcl_end/end_crystal.lua index 78fe658b4..86aa736f5 100644 --- a/mods/ITEMS/mcl_end/end_crystal.lua +++ b/mods/ITEMS/mcl_end/end_crystal.lua @@ -80,7 +80,7 @@ local function spawn_crystal(pos) crystal_explode(crystal) end local portal_pos = vector.add(portal_center, vector.new(0, -1, 0)) - mcl_structures.place_structure(portal_pos,mcl_structures.registered_structures["end_exit_portal"],PseudoRandom(minetest.get_mapgen_setting("seed")),-1) + vl_structures.place_structure(portal_pos,vl_structures.registered_structures["end_exit_portal"],PseudoRandom(minetest.get_mapgen_setting("seed")),-1) end minetest.register_entity("mcl_end:crystal", { diff --git a/mods/ITEMS/mcl_end/eye_of_ender.lua b/mods/ITEMS/mcl_end/eye_of_ender.lua index 788107601..59148aafb 100644 --- a/mods/ITEMS/mcl_end/eye_of_ender.lua +++ b/mods/ITEMS/mcl_end/eye_of_ender.lua @@ -89,7 +89,7 @@ minetest.register_craftitem("mcl_end:ender_eye", { local player_name = user:get_player_name() local origin = user:get_pos() origin.y = origin.y + 1.5 - local strongholds = mcl_structures.registered_structures["end_shrine"].static_pos + local strongholds = vl_structures.registered_structures["end_shrine"].static_pos local dim = mcl_worlds.pos_to_dimension(origin) local is_creative = minetest.is_creative_enabled(player_name) diff --git a/mods/ITEMS/mcl_portals/portal_gateway.lua b/mods/ITEMS/mcl_portals/portal_gateway.lua index bf95b4477..124487971 100644 --- a/mods/ITEMS/mcl_portals/portal_gateway.lua +++ b/mods/ITEMS/mcl_portals/portal_gateway.lua @@ -29,7 +29,7 @@ local gateway_positions = { local path_gateway_portal = minetest.get_modpath("mcl_structures").."/schematics/mcl_structures_end_gateway_portal.mts" local function spawn_gateway_portal(pos, dest_str) - return mcl_structures.place_schematic(vector.add(pos, vector.new(-1, -2, -1)), 0, nil, nil, path_gateway_portal, "0", nil, true, nil, nil, nil, + return vl_structures.place_schematic(vector.offset(pos, -1, -2, -1), 0, nil, nil, path_gateway_portal, "0", nil, true, nil, nil, nil, dest_str and function() minetest.get_meta(pos):set_string("mcl_portals:gateway_destination", dest_str) end) diff --git a/mods/ITEMS/mcl_portals/portal_nether.lua b/mods/ITEMS/mcl_portals/portal_nether.lua index d261b4bd3..e45f1ec3c 100644 --- a/mods/ITEMS/mcl_portals/portal_nether.lua +++ b/mods/ITEMS/mcl_portals/portal_nether.lua @@ -183,12 +183,12 @@ local dimension_to_teleport = { nether = "overworld", overworld = "nether" } local limits = { nether = { - pmin = {x=LIM_MIN, y = N_Y_MIN, z = LIM_MIN}, - pmax = {x=LIM_MAX, y = N_Y_MAX, z = LIM_MAX}, + pmin = vector.new(LIM_MIN, N_Y_MIN, LIM_MIN), + pmax = vector.new(LIM_MAX, N_Y_MAX, LIM_MAX), }, overworld = { - pmin = {x=LIM_MIN, y = O_Y_MIN, z = LIM_MIN}, - pmax = {x=LIM_MAX, y = O_Y_MAX, z = LIM_MAX}, + pmin = vector.new(LIM_MIN, O_Y_MIN, LIM_MIN), + pmax = vector.new(LIM_MAX, O_Y_MAX, LIM_MAX), }, } @@ -206,12 +206,10 @@ end -- for old portals, so that players don't get surprises. New portals, or portals that lost -- node storage due to destruction should use the lookup table. local function get_portal_pos(pos) - local p1 = vector.offset(pos,-5,-1,-5) - local p2 = vector.offset(pos,5,5,5) - local nn = find_nodes_in_area(p1,p2,{"mcl_portals:portal"}) + local nn = find_nodes_in_area(vector.offset(pos,-5,-1,-5), vector.offset(pos,5,5,5), {"mcl_portals:portal"}) for _,p in pairs(nn) do local m = minetest.get_meta(p):get_string("target_portal") - if m and m ~= "" and mcl_vars.get_node(p).name == "mcl_portals:portal" then + if m and m ~= "" and minetest.get_node(p).name == "mcl_portals:portal" then return minetest.get_position_from_hash(m) end end @@ -229,15 +227,13 @@ end local function add_exit(p) local retval = {key=false, new=false} - if not p or not p.y or not p.z or not p.x then - return retval - end + if not p or not p.y or not p.z or not p.x then return retval end local x, y, z = floor(p.x), floor(p.y), floor(p.z) - local p = {x = x, y = y, z = z} + local p = vector.new(x, y, z) - if get_node({x=x,y=y-1,z=z}).name ~= OBSIDIAN + if get_node(vector.new(x, y-1, z)).name ~= OBSIDIAN or get_node(p).name ~= PORTAL - or get_node({x=x,y=y+1,z=z}).name ~= PORTAL + or get_node(vector.new(x, y+1, z)).name ~= PORTAL then return retval end @@ -301,7 +297,7 @@ local function remove_exit(p) end local x, y, z = floor(p.x), floor(p.y), floor(p.z) - local p = {x = x, y = y, z = z} + local p = vector.new(x, y, z) local k = get_exit_key(p) if not exits[k] then @@ -529,14 +525,14 @@ local function destroy_nether_portal(pos, node) while i <= #nodes do pos = nodes[i] if orientation == 0 then - check_remove({x = pos.x - 1, y = pos.y, z = pos.z}) - check_remove({x = pos.x + 1, y = pos.y, z = pos.z}) + check_remove(vector.offset(pos, -1, 0, 0)) + check_remove(vector.offset(pos, 1, 0, 0)) else - check_remove({x = pos.x, y = pos.y, z = pos.z - 1}) - check_remove({x = pos.x, y = pos.y, z = pos.z + 1}) + check_remove(vector.offset(pos, 0, 0, -1)) + check_remove(vector.offset(pos, 0, 0, 1)) end - check_remove({x = pos.x, y = pos.y - 1, z = pos.z}) - check_remove({x = pos.x, y = pos.y + 1, z = pos.z}) + check_remove(vector.offset(pos, 0, -1, 0)) + check_remove(vector.offset(pos, 0, 1, 0)) remove_exits({pos}) i = i + 1 end @@ -624,7 +620,7 @@ local function build_and_light_frame(x1, y1, z1, x2, y2, z2, name) else set_node(pos, {name = PORTAL, param2 = orientation}) add_exits({ - {x=pos.x, y=pos.y-1, z=pos.z} + vector.new(pos.x, pos.y-1, pos.z) }) end end @@ -701,8 +697,8 @@ function build_nether_portal(cube_pos1, width, height, orientation, name, clear_ -- Build obsidian platform: for x = pos.x - orientation, pos.x + orientation + (width_inner - 1) * (1 - orientation), 1 + orientation do for z = pos.z - 1 + orientation, pos.z + 1 - orientation + (width_inner - 1) * orientation, 2 - orientation do - local pp = {x = x, y = pos.y - 1, z = z} - local pp_1 = {x = x, y = pos.y - 2, z = z} + local pp = vector.new(x, pos.y - 1, z) + local pp_1 = vector.new(x, pos.y - 2, z) local nn = get_node(pp).name local nn_1 = get_node(pp_1).name if ((nn=="air" and nn_1 == "air") or not registered_nodes[nn].is_ground_content) and not is_protected(pp, name) then @@ -823,7 +819,7 @@ local function finalize_teleport(obj, exit) local _, dim = mcl_worlds.y_to_layer(exit.y) -- If player stands, player is at ca. something+0.5 which might cause precision problems, so we used ceil for objpos.y - objpos = {x = floor(objpos.x+0.5), y = ceil(objpos.y), z = floor(objpos.z+0.5)} + objpos = vector.new(floor(objpos.x+0.5), ceil(objpos.y), floor(objpos.z+0.5)) if get_node(objpos).name ~= PORTAL then log("action", "Entity no longer standing in portal") return @@ -1085,19 +1081,19 @@ local function search_for_build_location(blockpos, action, calls_remaining, para if nc2 >= (W_MIN*(H_MIN-1)*W_MIN) - ACCEPTABLE_PORTAL_REPLACES then -- We have sorted the candidates by distance, this is the best location. distance = distance0 - pos0 = {x=node.x, y=node.y, z=node.z} + pos0 = vector.new(node.x, node.y, node.z) log("verbose", "Found acceptable location at "..pos_to_string(pos0)..", distance "..distance0..", air nodes "..nc2) break elseif not most_airy_pos or nc2>most_airy_count then -- Remember the cube with the most amount of air as a fallback. most_airy_count = nc2 most_airy_distance = distance0 - most_airy_pos = {x=node.x, y=node.y, z=node.z} + most_airy_pos = vector.new(node.x, node.y, node.z) log("verbose", "Found fallback location at "..pos_to_string(most_airy_pos)..", distance "..distance0..", air nodes "..nc2) elseif most_airy_pos and nc2==most_airy_count and distance0 0 do @@ -1247,22 +1242,22 @@ local function check_and_light_shape(pos, orientation) return false end node_counter = node_counter + 1 - node_list[node_counter] = {x = x, y = y, z = z} + node_list[node_counter] = vector.new(x, y, z) index_list[k] = true stack[i].y = y - 1 - stack[i + 1] = {x = x, y = y + 1, z = z} + stack[i + 1] = vector.new(x, y + 1, z) if orientation == 0 then - stack[i + 2] = {x = x - 1, y = y, z = z} - stack[i + 3] = {x = x + 1, y = y, z = z} + stack[i + 2] = vector.new(x - 1, y, z) + stack[i + 3] = vector.new(x + 1, y, z) else - stack[i + 2] = {x = x, y = y, z = z - 1} - stack[i + 3] = {x = x, y = y, z = z + 1} + stack[i + 2] = vector.new(x, y, z - 1) + stack[i + 3] = vector.new(x, y, z + 1) end if (y < pos1.y) or (y == pos1.y and (x < pos1.x or z < pos1.z)) then - pos1 = {x = x, y = y, z = z} + pos1 = vector.new(x, y, z) end if (x > pos2.x or z > pos2.z) or (x == pos2.x and z == pos2.z and y > pos2.y) then - pos2 = {x = x, y = y, z = z} + pos2 = vector.new(x, y, z) end end end @@ -1344,7 +1339,7 @@ local function check_portal_then_teleport(obj, origin, exit) remove_exits({exit}) -- Also remove from structure storage, otherwise ABM will try the same bad exit again. local objpos = obj:get_pos() - delete_portal_pos({x = floor(objpos.x+0.5), y = ceil(objpos.y), z = floor(objpos.z+0.5)}) + delete_portal_pos(vector.new(floor(objpos.x+0.5), ceil(objpos.y), floor(objpos.z+0.5))) origin_flush(origin, nil) return @@ -1367,7 +1362,7 @@ local function teleport_no_delay(obj, portal_pos) local target_dim = dimension_to_teleport[current_dim] -- If player stands, player is at ca. something+0.5 which might cause precision problems, so we used ceil for objpos.y - origin = {x = floor(objpos.x+0.5), y = ceil(objpos.y), z = floor(objpos.z+0.5)} + origin = vector.new(floor(objpos.x+0.5), ceil(objpos.y), floor(objpos.z+0.5)) if get_node(origin).name ~= PORTAL then return end local target = get_target(origin) @@ -1450,8 +1445,8 @@ local function animation(player, playername) end minetest.add_particlespawner({ amount = 1, - minpos = {x = pos.x - 0.1, y = pos.y + 1.4, z = pos.z - 0.1}, - maxpos = {x = pos.x + 0.1, y = pos.y + 1.6, z = pos.z + 0.1}, + minpos = vector.offset(pos, -0.1, 1.4, -0.1), + maxpos = vector.offset(pos, 0.1, 1.6, 0.1), minvel = 0, maxvel = 0, minacc = 0, @@ -1496,11 +1491,11 @@ minetest.register_abm({ local time = random() * 1.9 + 0.5 local velocity, acceleration if o == 1 then - velocity = {x = random() * 0.7 + 0.3, y = random() - 0.5, z = random() - 0.5} - acceleration = {x = random() * 1.1 + 0.3, y = random() - 0.5, z = random() - 0.5} + velocity = vector.new(random() * 0.7 + 0.3, random() - 0.5, random() - 0.5) + acceleration = vector.new(random() * 1.1 + 0.3, random() - 0.5, random() - 0.5) else - velocity = {x = random() - 0.5, y = random() - 0.5, z = random() * 0.7 + 0.3} - acceleration = {x = random() - 0.5, y = random() - 0.5, z = random() * 1.1 + 0.3} + velocity = vector.new(random() - 0.5, random() - 0.5, random() * 0.7 + 0.3) + acceleration = vector.new(random() - 0.5, random() - 0.5, random() * 1.1 + 0.3) end local distance = add(mul(velocity, time), mul(acceleration, time * time / 2)) if d == 1 then @@ -1566,12 +1561,12 @@ minetest.override_item(OBSIDIAN, { end -- check each of 6 sides of it and destroy every portal - check_remove({x = pos.x - 1, y = pos.y, z = pos.z}) - check_remove({x = pos.x + 1, y = pos.y, z = pos.z}) - check_remove({x = pos.x, y = pos.y, z = pos.z - 1}) - check_remove({x = pos.x, y = pos.y, z = pos.z + 1}) - check_remove({x = pos.x, y = pos.y - 1, z = pos.z}) - check_remove({x = pos.x, y = pos.y + 1, z = pos.z}) + check_remove(vector.offset(pos, -1, 0, 0)) + check_remove(vector.offset(pos, 1, 0, 0)) + check_remove(vector.offset(pos, 0, 0, -1)) + check_remove(vector.offset(pos, 0, 0, 1)) + check_remove(vector.offset(pos, 0, -1, 0)) + check_remove(vector.offset(pos, 0, 1, 0)) end, _on_ignite = function(user, pointed_thing) @@ -1579,16 +1574,16 @@ minetest.override_item(OBSIDIAN, { -- Check empty spaces around obsidian and light all frames found. -- Permit igniting of portals that are partly protected to maintain integrity. local portals_placed = - mcl_portals.light_nether_portal({x = x - 1, y = y, z = z}) or mcl_portals.light_nether_portal({x = x + 1, y = y, z = z}) or - mcl_portals.light_nether_portal({x = x, y = y - 1, z = z}) or mcl_portals.light_nether_portal({x = x, y = y + 1, z = z}) or - mcl_portals.light_nether_portal({x = x, y = y, z = z - 1}) or mcl_portals.light_nether_portal({x = x, y = y, z = z + 1}) + mcl_portals.light_nether_portal(vector.new(x - 1, y, z)) or mcl_portals.light_nether_portal(vector.new(x + 1, y, z)) or + mcl_portals.light_nether_portal(vector.new(x, y - 1, z)) or mcl_portals.light_nether_portal(vector.new(x, y + 1, z)) or + mcl_portals.light_nether_portal(vector.new(x, y, z - 1)) or mcl_portals.light_nether_portal(vector.new(x, y, z + 1)) if portals_placed then - log("verbose", "Nether portal activated at "..pos_to_string({x=x,y=y,z=z})..".") + log("verbose", "Nether portal activated at "..pos_to_string(vector.new(x, y, z))..".") if minetest.get_modpath("doc") then doc.mark_entry_as_revealed(user:get_player_name(), "nodes", PORTAL) -- Achievement for finishing a Nether portal TO the Nether - local dim = mcl_worlds.pos_to_dimension({x=x, y=y, z=z}) + local dim = mcl_worlds.pos_to_dimension(vector.new(x, y, z)) if minetest.get_modpath("awards") and dim ~= "nether" and user:is_player() then awards.unlock(user:get_player_name(), "mcl:buildNetherPortal") end @@ -1600,13 +1595,13 @@ minetest.override_item(OBSIDIAN, { end, }) -mcl_structures.register_structure("nether_portal",{ +vl_structures.register_structure("nether_portal",{ nospawn = true, filenames = { modpath.."/schematics/mcl_portals_nether_portal.mts" } }) -mcl_structures.register_structure("nether_portal_open",{ +vl_structures.register_structure("nether_portal_open",{ nospawn = true, filenames = { modpath.."/schematics/mcl_portals_nether_portal_open.mts" diff --git a/mods/MAPGEN/mcl_biomes/init.lua b/mods/MAPGEN/mcl_biomes/init.lua index ab9a2bec0..be2141924 100644 --- a/mods/MAPGEN/mcl_biomes/init.lua +++ b/mods/MAPGEN/mcl_biomes/init.lua @@ -26,6 +26,9 @@ local mod_mcl_crimson = minetest.get_modpath("mcl_crimson") local mod_mcl_blackstone = minetest.get_modpath("mcl_blackstone") local mod_mcl_mangrove = minetest.get_modpath("mcl_mangrove") +-- these are registered asynchronously +local deco_ids_fungus = {} +local deco_ids_trees = {} local deco_id_chorus_plant -- @@ -3988,7 +3991,11 @@ local function register_decorations() schematic = mod_mcl_mangrove .. "/schematics/mcl_mangrove_tree_1.mts", flags = "place_center_x, place_center_z, force_placement", rotation = "random", - }) + }, function() + local f = minetest.get_decoration_id("mcl_biomes:mangrove_tree_1") + table.insert(deco_ids_trees, f) + minetest.set_gen_notify({decoration = true}, {f}) + end) mcl_mapgen_core.register_decoration({ name = "mcl_biomes:mangrove_tree_2", deco_type = "schematic", @@ -4001,7 +4008,11 @@ local function register_decorations() schematic = mod_mcl_mangrove .. "/schematics/mcl_mangrove_tree_2.mts", flags = "place_center_x, place_center_z, force_placement", rotation = "random", - }) + }, function() + local f = minetest.get_decoration_id("mcl_biomes:mangrove_tree_2") + table.insert(deco_ids_trees, f) + minetest.set_gen_notify({decoration = true}, {f}) + end) mcl_mapgen_core.register_decoration({ name = "mcl_biomes:mangrove_tree_3", deco_type = "schematic", @@ -4014,7 +4025,11 @@ local function register_decorations() schematic = mod_mcl_mangrove .. "/schematics/mcl_mangrove_tree_3.mts", flags = "place_center_x, place_center_z, force_placement", rotation = "random", - }) + }, function() + local f = minetest.get_decoration_id("mcl_biomes:mangrove_tree_3") + table.insert(deco_ids_trees, f) + minetest.set_gen_notify({decoration = true}, {f}) + end) mcl_mapgen_core.register_decoration({ name = "mcl_biomes:mangrove_tree_4", deco_type = "schematic", @@ -4027,7 +4042,11 @@ local function register_decorations() schematic = mod_mcl_mangrove .. "/schematics/mcl_mangrove_tree_4.mts", flags = "place_center_x, place_center_z, force_placement", rotation = "random", - }) + }, function() + local f = minetest.get_decoration_id("mcl_biomes:mangrove_tree_4") + table.insert(deco_ids_trees, f) + minetest.set_gen_notify({decoration = true}, {f}) + end) mcl_mapgen_core.register_decoration({ name = "mcl_biomes:mangrove_tree_5", deco_type = "schematic", @@ -4040,7 +4059,11 @@ local function register_decorations() schematic = mod_mcl_mangrove .. "/schematics/mcl_mangrove_tree_5.mts", flags = "place_center_x, place_center_z, force_placement", rotation = "random", - }) + }, function() + local f = minetest.get_decoration_id("mcl_biomes:mangrove_tree_5") + table.insert(deco_ids_trees, f) + minetest.set_gen_notify({decoration = true}, {f}) + end) mcl_mapgen_core.register_decoration({ name = "mcl_biomes:mangrove_bee_nest", deco_type = "schematic", @@ -4063,7 +4086,11 @@ local function register_decorations() rotation = "random", spawn_by = "group:flower", rank = 1550, - }) + }, function() + local f = minetest.get_decoration_id("mcl_biomes:mangrove_bee_nest") + table.insert(deco_ids_trees, f) + minetest.set_gen_notify({decoration = true}, {f}) + end) mcl_mapgen_core.register_decoration({ deco_type = "simple", place_on = {"mcl_mud:mud"}, @@ -5577,7 +5604,11 @@ local function register_dimension_decorations() schematic = mod_mcl_crimson .. "/schematics/warped_fungus_1.mts", size = vector.new(5, 11, 5), rotation = "random", - }) + }, function() + local f = minetest.get_decoration_id("mcl_biomes:warped_tree1") + table.insert(deco_ids_fungus, f) + minetest.set_gen_notify({decoration = true}, {f}) + end) mcl_mapgen_core.register_decoration({ deco_type = "schematic", name = "mcl_biomes:warped_tree2", @@ -5591,7 +5622,11 @@ local function register_dimension_decorations() schematic = mod_mcl_crimson .. "/schematics/warped_fungus_2.mts", size = vector.new(5, 6, 5), rotation = "random", - }) + }, function() + local f = minetest.get_decoration_id("mcl_biomes:warped_tree2") + table.insert(deco_ids_fungus, f) + minetest.set_gen_notify({decoration = true}, {f}) + end) mcl_mapgen_core.register_decoration({ deco_type = "schematic", name = "mcl_biomes:warped_tree3", @@ -5605,7 +5640,11 @@ local function register_dimension_decorations() schematic = mod_mcl_crimson .. "/schematics/warped_fungus_3.mts", size = vector.new(5, 12, 5), rotation = "random", - }) + }, function() + local f = minetest.get_decoration_id("mcl_biomes:warped_tree3") + table.insert(deco_ids_fungus, f) + minetest.set_gen_notify({decoration = true}, {f}) + end) mcl_mapgen_core.register_decoration({ deco_type = "simple", place_on = {"mcl_crimson:warped_nylium", "mcl_crimson:twisting_vines"}, @@ -5653,7 +5692,7 @@ local function register_dimension_decorations() }) mcl_mapgen_core.register_decoration({ deco_type = "schematic", - name = "mcl_biomes:crimson_tree", + name = "mcl_biomes:crimson_tree1", place_on = {"mcl_crimson:crimson_nylium"}, sidelen = 16, fill_ratio = 0.008, @@ -5664,7 +5703,12 @@ local function register_dimension_decorations() schematic = mod_mcl_crimson .. "/schematics/crimson_fungus_1.mts", size = vector.new(5, 8, 5), rotation = "random", - }) + }, function() + local f = minetest.get_decoration_id("mcl_biomes:crimson_tree1") + table.insert(deco_ids_fungus, f) + minetest.set_gen_notify({decoration = true}, {f}) + end) + minetest.register_alias("mcl_biomes:crimson_tree", "mcl_biomes:crimson_tree1") -- legacy inconsistency, fixed 08/2024 mcl_mapgen_core.register_decoration({ deco_type = "schematic", name = "mcl_biomes:crimson_tree2", @@ -5678,7 +5722,11 @@ local function register_dimension_decorations() schematic = mod_mcl_crimson .. "/schematics/crimson_fungus_2.mts", size = vector.new(5, 12, 5), rotation = "random", - }) + }, function() + local f = minetest.get_decoration_id("mcl_biomes:crimson_tree2") + table.insert(deco_ids_fungus, f) + minetest.set_gen_notify({decoration = true}, {f}) + end) mcl_mapgen_core.register_decoration({ deco_type = "schematic", name = "mcl_biomes:crimson_tree3", @@ -5692,7 +5740,11 @@ local function register_dimension_decorations() schematic = mod_mcl_crimson .. "/schematics/crimson_fungus_3.mts", size = vector.new(7, 13, 7), rotation = "random", - }) + }, function() + local f = minetest.get_decoration_id("mcl_biomes:crimson_tree3") + table.insert(deco_ids_fungus, f) + minetest.set_gen_notify({decoration = true}, {f}) + end) mcl_mapgen_core.register_decoration({ deco_type = "simple", place_on = {"mcl_crimson:warped_nylium", "mcl_crimson:weeping_vines", "mcl_nether:netherrack"}, @@ -5903,13 +5955,11 @@ local function register_dimension_decorations() decoration = "mcl_end:chorus_flower", height = 1, biomes = {"End", "EndMidlands", "EndHighlands", "EndBarrens", "EndSmallIslands"}, - }) - - deco_id_chorus_plant = minetest.get_decoration_id("mcl_biomes:chorus_plant") - minetest.set_gen_notify({decoration = true}, {deco_id_chorus_plant}) - + },function() + deco_id_chorus_plant = minetest.get_decoration_id("mcl_biomes:chorus_plant") + minetest.set_gen_notify({decoration = true}, {deco_id_chorus_plant}) + end) -- TODO: End cities - end @@ -5943,30 +5993,6 @@ if mg_name ~= "singlenode" then register_dimension_decorations() -- Overworld decorations for v6 are handled in mcl_mapgen_core - - local deco_ids_fungus = { - minetest.get_decoration_id("mcl_biomes:crimson_tree1"), - minetest.get_decoration_id("mcl_biomes:crimson_tree2"), - minetest.get_decoration_id("mcl_biomes:crimson_tree3"), - minetest.get_decoration_id("mcl_biomes:warped_tree1"), - minetest.get_decoration_id("mcl_biomes:warped_tree2"), - minetest.get_decoration_id("mcl_biomes:warped_tree3") - } - local deco_ids_trees = { - minetest.get_decoration_id("mcl_biomes:mangrove_tree_1"), - minetest.get_decoration_id("mcl_biomes:mangrove_tree_2"), - minetest.get_decoration_id("mcl_biomes:mangrove_tree_3"), - minetest.get_decoration_id("mcl_biomes:mangrove_tree_4"), - minetest.get_decoration_id("mcl_biomes:mangrove_tree_5"), - minetest.get_decoration_id("mcl_biomes:mangrove_bee_nest"), - } - for _, f in pairs(deco_ids_fungus) do - minetest.set_gen_notify({decoration = true}, {f}) - end - for _, f in pairs(deco_ids_trees) do - minetest.set_gen_notify({decoration = true}, {f}) - end - local function mangrove_roots_gen(gennotify, pr) for _, f in pairs(deco_ids_trees) do for _, pos in ipairs(gennotify["decoration#" .. f] or {}) do @@ -6020,9 +6046,7 @@ if mg_name ~= "singlenode" then local biomemap = minetest.get_mapgen_object("biomemap") local swamp_biome_id = minetest.get_biome_id("MangroveSwamp") local swamp_shore_id = minetest.get_biome_id("MangroveSwamp_shore") - local is_swamp = table.indexof(biomemap, swamp_biome_id) ~= -1 - local is_swamp_shore = table.indexof(biomemap, swamp_shore_id) ~= -1 - if is_swamp or is_swamp_shore then + if biomemap and (table.indexof(biomemap, swamp_biome_id) ~= -1 or table.indexof(biomemap, swamp_shore_id) ~= -1) then mangrove_roots_gen(gennotify, pr) end end diff --git a/mods/MAPGEN/mcl_dungeons/init.lua b/mods/MAPGEN/mcl_dungeons/init.lua index 24b5c6b4a..29201789d 100644 --- a/mods/MAPGEN/mcl_dungeons/init.lua +++ b/mods/MAPGEN/mcl_dungeons/init.lua @@ -2,6 +2,7 @@ mcl_dungeons = {} +local logging = minetest.settings:get_bool("mcl_logging_dungeons", false) local mg_name = minetest.get_mapgen_setting("mg_name") -- Are dungeons disabled? if mcl_vars.mg_dungeons == false or mg_name == "singlenode" then return end @@ -234,7 +235,9 @@ local function ecb_spawn_dungeon(blockpos, action, calls_remaining, param) -- Check conditions. If okay, start generating if check and (openings_counter < 1 or openings_counter > 5) then return end - minetest.log("action","[mcl_dungeons] Placing new dungeon at "..minetest.pos_to_string(vector_new(x, y, z))) + if logging then + minetest.log("action","[mcl_dungeons] Placing new dungeon at "..minetest.pos_to_string(vector_new(x, y, z))) + end -- Okay! Spawning starts! -- Remember spawner chest positions to set metadata later @@ -369,7 +372,9 @@ local function ecb_spawn_dungeon(blockpos, action, calls_remaining, param) set_node(pos, {name="mcl_chests:chest", param2=facedir}) local meta = get_meta(pos) - minetest.log("action", "[mcl_dungeons] Filling chest " .. tostring(c) .. " at " .. minetest.pos_to_string(pos)) + if logging then + minetest.log("action", "[mcl_dungeons] Filling chest " .. tostring(c) .. " at " .. minetest.pos_to_string(pos)) + end mcl_loot.fill_inventory(meta:get_inventory(), "main", mcl_loot.get_multi_loot(loottable, pr), pr) end @@ -404,7 +409,9 @@ local function dungeons_nodes(minp, maxp, blockseed) local z = pr:next(minp.z, maxp.z-dim.z-1) local p1 = vector_new(x, y, z) local p2 = vector_new(x+dim.x+1, y+dim.y+1, z+dim.z+1) - minetest.log("verbose","[mcl_dungeons] size=" ..minetest.pos_to_string(dim) .. ", emerge from "..minetest.pos_to_string(p1) .. " to " .. minetest.pos_to_string(p2)) + if logging then + minetest.log("verbose","[mcl_dungeons] size=" ..minetest.pos_to_string(dim) .. ", emerge from "..minetest.pos_to_string(p1) .. " to " .. minetest.pos_to_string(p2)) + end emerge_area(p1, p2, ecb_spawn_dungeon, {p1=p1, p2=p2, dim=dim, pr=pr}) end end @@ -414,7 +421,9 @@ function mcl_dungeons.spawn_dungeon(p1, _, pr) if not p1 or not pr or not p1.x or not p1.y or not p1.z then return end local dim = dungeonsizes[pr:next(1, #dungeonsizes)] local p2 = vector_new(p1.x+dim.x+1, p1.y+dim.y+1, p1.z+dim.z+1) - minetest.log("verbose","[mcl_dungeons] size=" ..minetest.pos_to_string(dim) .. ", emerge from "..minetest.pos_to_string(p1) .. " to " .. minetest.pos_to_string(p2)) + if logging then + minetest.log("verbose","[mcl_dungeons] size=" ..minetest.pos_to_string(dim) .. ", emerge from "..minetest.pos_to_string(p1) .. " to " .. minetest.pos_to_string(p2)) + end emerge_area(p1, p2, ecb_spawn_dungeon, {p1=p1, p2=p2, dim=dim, pr=pr, dontcheck=true}) end diff --git a/mods/MAPGEN/mcl_mapgen_core/api.lua b/mods/MAPGEN/mcl_mapgen_core/api.lua index 26d1109ff..0f15cfa64 100644 --- a/mods/MAPGEN/mcl_mapgen_core/api.lua +++ b/mods/MAPGEN/mcl_mapgen_core/api.lua @@ -1,11 +1,9 @@ -local registered_generators = {} - -local lvm, nodes, param2 = 0, 0, 0 -local lvm_buffer, lvm_buffer2 = {}, {} - local logging = minetest.settings:get_bool("mcl_logging_mapgen", false) local log_timing = minetest.settings:get_bool("mcl_logging_mapgen_timing", false) -- detailed, for performance debugging +local registered_generators = {} +local lvm, nodes, param2 = 0, 0, 0 + local function run_generators(minp, maxp, blockseed) if nodes == 0 then return end for _, rec in ipairs(registered_generators) do @@ -24,8 +22,8 @@ minetest.register_on_generated(function(minp, maxp, blockseed) if lvm > 0 then local vm, emin, emax = minetest.get_mapgen_object("voxelmanip") local area = VoxelArea(emin, emax) - local data = vm:get_data(lvm_buffer) - local data2 = param2 > 0 and vm:get_param2_data(lvm_buffer2) + local data = vm:get_data() + local data2 = param2 > 0 and vm:get_param2_data() if log_timing then minetest.log("action", string.format("[mcl_mapgen_core] %-20s %s ... %s %8.2fms", "get_data", minetest.pos_to_string(minp), minetest.pos_to_string(maxp), (os.clock() - t1)*1000)) end @@ -188,7 +186,6 @@ local function sort_decorations() end table.sort(keys) for _, key in ipairs(keys) do - -- minetest.log("action", "Deco: "..key) -- dump the resulting order minetest.register_decoration(map[key]) if map[key].callback then map[key].callback() end end diff --git a/mods/MAPGEN/mcl_mapgen_core/v6.lua b/mods/MAPGEN/mcl_mapgen_core/v6.lua index 46d0ed441..0be727187 100644 --- a/mods/MAPGEN/mcl_mapgen_core/v6.lua +++ b/mods/MAPGEN/mcl_mapgen_core/v6.lua @@ -509,7 +509,7 @@ local function generate_mgv6_structures() local surface = minetest.find_nodes_in_area({x=p.x,y=p.y-1,z=p.z}, floor, "mcl_core:snowblock") local surface2 = minetest.find_nodes_in_area({x=p.x,y=p.y-1,z=p.z}, floor, "mcl_core:dirt_with_grass_snow") if #surface + #surface2 >= 63 then - mcl_structures.call_struct(p, "igloo", nil, pr) + vl_structures.call_struct(p, "igloo", nil, pr) chunk_has_igloo = true end end @@ -528,7 +528,7 @@ local function generate_mgv6_structures() local nodes = minetest.find_nodes_in_area(p1, p2, {"mcl_core:sandstone", "mcl_core:stone", "mcl_core:diorite", "mcl_core:andesite", "mcl_core:granite", "mcl_core:stone_with_coal", "mcl_core:dirt", "mcl_core:gravel"}) if #nodes >= 100 then -- >= 80% - mcl_structures.call_struct(p1, "fossil", nil, pr) + vl_structures.call_struct(p1, "fossil", nil, pr) end end end @@ -565,10 +565,7 @@ local function generate_mgv6_structures() if #free_nodes >= ((size.x+1)*(size.y+1)*(size.z+1)) then local place = {x=p.x, y=WITCH_HUT_HEIGHT-1, z=p.z} - -- FIXME: For some mysterious reason (black magic?) this - -- function does sometimes NOT spawn the witch hut. One can only see the - -- oak wood nodes in the water, but no hut. :-/ - mcl_structures.place_structure(place,mcl_structures.registered_structures["witch_hut"],pr) + vl_structures.place_structure(place,vl_structures.registered_structures["witch_hut"],pr) local function place_tree_if_free(pos, prev_result) local nn = minetest.get_node(pos).name @@ -637,7 +634,7 @@ local function generate_mgv6_structures() local spruce_collisions = minetest.find_nodes_in_area({x=p.x+1,y=p.y+2,z=p.z+1}, {x=p.x+4, y=p.y+6, z=p.z+4}, {"mcl_core:sprucetree", "mcl_core:spruceleaves"}) if #surface >= 9 and #spruce_collisions == 0 then - mcl_structures.place_structure(p,mcl_structures.registered_structures["ice_spike_large"],pr) + vl_structures.place_structure(p,vl_structures.registered_structures["ice_spike_large"],pr) end elseif spike < 100 then -- Check surface @@ -648,7 +645,7 @@ local function generate_mgv6_structures() local spruce_collisions = minetest.find_nodes_in_area({x=p.x+1,y=p.y+1,z=p.z+1}, {x=p.x+6, y=p.y+6, z=p.z+6}, {"mcl_core:sprucetree", "mcl_core:spruceleaves"}) if #surface >= 25 and #spruce_collisions == 0 then - mcl_structures.place_structure(p,mcl_structures.registered_structures["ice_spike_small"],pr) + vl_structures.place_structure(p,vl_structures.registered_structures["ice_spike_small"],pr) end end end diff --git a/mods/MAPGEN/mcl_nether_fortresses/init.lua b/mods/MAPGEN/mcl_nether_fortresses/init.lua index d9910a302..c8c3087b7 100644 --- a/mods/MAPGEN/mcl_nether_fortresses/init.lua +++ b/mods/MAPGEN/mcl_nether_fortresses/init.lua @@ -5,20 +5,18 @@ local peaceful = minetest.settings:get_bool("only_peaceful_mobs", false) local BLAZE_SPAWNER_MAX_LIGHT = 11 -mcl_structures.register_structure("nether_outpost",{ +vl_structures.register_structure("nether_outpost",{ place_on = {"mcl_nether:netherrack","mcl_crimson:crimson_nylium","mcl_crimson:warped_nylium","mcl_blackstone:basalt","mcl_blackstone:soul_soil","mcl_blackstone:blackstone","mcl_nether:soul_sand"}, chunk_probability = 23, - flags = "all_floors", + flags = "place_center_x, place_center_y, all_floors", biomes = {"Nether","SoulsandValley","WarpedForest","CrimsonForest","BasaltDelta"}, - sidelen = 24, - solid_ground = true, - prepare = { tolerance=20, padding=2, corners=5, foundation=true, clearance=true }, + prepare = { tolerance = 20, padding = 4, corners = 5, foundation = true, clear = true, clear_top = 4 }, y_min = mcl_vars.mg_lava_nether_max - 1, y_max = mcl_vars.mg_nether_max - 30, filenames = { modpath.."/schematics/mcl_nether_fortresses_nether_outpost.mts" }, y_offset = 0, - after_place = function(pos) - local sp = minetest.find_nodes_in_area(pos,vector.offset(pos,0,20,0),{"mcl_mobspawners:spawner"}) + after_place = function(pos,def,pr,p1,p2) + local sp = minetest.find_nodes_in_area(p1,p2,{"mcl_mobspawners:spawner"}) if not sp[1] then return end mcl_mobspawners.setup_spawner(sp[1], "mobs_mc:blaze", 0, BLAZE_SPAWNER_MAX_LIGHT, 10, 8, 0) end @@ -29,85 +27,101 @@ local nbridges = { modpath.."/schematics/mcl_nether_fortresses_nether_bridge_3.mts", modpath.."/schematics/mcl_nether_fortresses_nether_bridge_4.mts", } -mcl_structures.register_structure("nether_bridge",{ +vl_structures.register_structure("nether_bridge",{ place_on = {"mcl_nether:nether_lava_source","mcl_nether:netherrack","mcl_crimson:crimson_nylium","mcl_crimson:warped_nylium","mcl_blackstone:basalt","mcl_blackstone:soul_soil","mcl_blackstone:blackstone","mcl_nether:soul_sand"}, - chunk_probability = 5, -- because of the small height allowed, these are quite rare otherwise - flags = "all_floors, liquid_surface", - prepare = { tolerance=-1, clearance = 6 }, + chunk_probability = 8, -- because of the y restriction these are quite rare + flags = "place_center_x, place_center_y, all_floors", + prepare = { tolerance = 50, padding = -1, corners = 0, clear_bottom = 8, clear_top = 6 }, -- asymmetric padding would be nice to have force_placement = true, - sidelen = 38, - solid_ground = false, - y_min = mcl_vars.mg_lava_nether_max - 5, - y_max = mcl_vars.mg_lava_nether_max + 15, + y_min = mcl_vars.mg_lava_nether_max, + y_max = mcl_vars.mg_lava_nether_max + 25, -- otherwise, we may see some very long legs filenames = nbridges, - y_offset = function(pr) return pr:next(-12, -5) end, - after_place = function(pos,def,pr) - local p1 = vector.offset(pos,-14,0,-14) - local p2 = vector.offset(pos,14,24,14) - mcl_structures.spawn_mobs("mobs_mc:witherskeleton",{"mcl_blackstone:blackstone_chiseled_polished"},p1,p2,pr,5) + y_offset = function(pr) return pr:next(-8, -5) end, + after_place = function(pos,def,pr,p1,p2) + vl_structures.spawn_mobs("mobs_mc:witherskeleton",{"mcl_blackstone:blackstone_chiseled_polished"},p1,p2,pr,5) + -- p1.y is not a typo, we want to lowest level only + local legs = minetest.find_nodes_in_area(vector.new(p1.x,p1.y,p1.z),vector.new(p2.x,p1.y,p2.z), "mcl_nether:nether_brick") + local bricks = {} + -- TODO: port leg generation to voxel manipulators? + for _,leg in pairs(legs) do + while true do + leg = vector.offset(leg,0,-1,0) + local nodename = minetest.get_node(leg).name + if nodename == "ignore" then break end + if nodename ~= "air" and nodename ~= "mcl_core:lava_source" and minetest.get_item_group(nodename, "solid") ~= 0 then break end + table.insert(bricks,leg) + end + end + minetest.bulk_set_node(bricks, {name = "mcl_nether:nether_brick", param2 = 2}) end }) -mcl_structures.register_structure("nether_outpost_with_bridges",{ +vl_structures.register_structure("nether_outpost_with_bridges",{ place_on = {"mcl_nether:netherrack","mcl_crimson:crimson_nylium","mcl_crimson:warped_nylium","mcl_blackstone:basalt","mcl_blackstone:soul_soil","mcl_blackstone:blackstone","mcl_nether:soul_sand","mcl_nether:nether_lava_source"}, - chunk_probability = 33, - flags = "all_floors", + chunk_probability = 10, -- because of the y restriction, it will still be rare + flags = "place_center_x, place_center_y, all_floors", biomes = {"Nether","SoulsandValley","WarpedForest","CrimsonForest","BasaltDelta"}, - sidelen = 24, - solid_ground = true, - prepare = { tolerance=30, padding=4, corners=5, foundation=true, clearance=true }, + prepare = { tolerance = 20, padding = 4, corners = 5, foundation = true, clear_top = 3 }, y_min = mcl_vars.mg_lava_nether_max - 1, - y_max = mcl_vars.mg_nether_max - 30, + y_max = mcl_vars.mg_lava_nether_max + 40, + -- todo: spawn_by a lot of air? filenames = { modpath.."/schematics/mcl_nether_fortresses_nether_outpost.mts" }, - daughters = {{ - files = { nbridges[1] }, - pos = vector.new(0,-3,-25), - rot = 180, - prepare = { tolerance = -1, foundation = false, clearance = 14, padding = -2, corners=2 }, - }, + emerge_padding = { vector.new(-38,-8,-38), vector.new(38,0,38) }, + daughters = { { - files = { nbridges[1] }, + filenames = { nbridges[1], nbridges[2] }, pos = vector.new(0,-3,24), - rot = 0, + rotation= 0, no_level = true, - prepare = { tolerance = -1, foundation = false, clearance = 14, padding = -2, corners=2 }, + prepare = { tolerance = -1, foundation = false, clear = true, clear_bottom = 16, clear_top = 2, padding = 1, corners = 4 }, }, { - files = { nbridges[1] }, - pos = vector.new(-25,-3,0), - rot = 270, - no_level = true, - prepare = { tolerance = -1, foundation = false, clearance = 14, padding = -2, corners=2 }, - }, - { - files = { nbridges[1] }, + filenames = { nbridges[1], nbridges[2] }, pos = vector.new(24,-3,0), - rot = 90, + rotation = 90, no_level = true, - prepare = { tolerance = -1, foundation = false, clearance = 14, padding = -2, corners=2 }, + prepare = { tolerance = -1, foundation = false, clear = true, clear_bottom = 16, clear_top = 2, padding = 1, corners = 4 }, + }, + { + filenames = { nbridges[1], nbridges[2] }, + pos = vector.new(0,-3,-25), + rotation = 180, + no_level = true, + prepare = { tolerance = -1, foundation = false, clear = true, clear_bottom = 16, clear_top = 2, padding = 1, corners = 4 }, + }, + { + filenames = { nbridges[1], nbridges[2] }, + pos = vector.new(-25,-3,0), + rotation = 270, + no_level = true, + prepare = { tolerance = -1, foundation = false, clear = true, clear_bottom = 16, clear_top = 2, padding = 1, corners = 4 }, }, }, - after_place = function(pos,def,pr) - local sp = minetest.find_nodes_in_area(pos,vector.offset(pos,0,20,0),{"mcl_mobspawners:spawner"}) + after_place = function(pos,def,pr,p1,p2) + local sp = minetest.find_nodes_in_area(p1,p2,{"mcl_mobspawners:spawner"}) if not sp[1] then return end mcl_mobspawners.setup_spawner(sp[1], "mobs_mc:blaze", 0, BLAZE_SPAWNER_MAX_LIGHT, 10, 8, 0) - - local legs = minetest.find_nodes_in_area(vector.offset(pos,-45,-2,-45),vector.offset(pos,45,0,45), "mcl_nether:nether_brick") + -- the -3 offset needs to be carefully aligned with the bridges above + local legs = minetest.find_nodes_in_area(vector.offset(pos,-45,-3,-45),vector.offset(pos,45,-3,45), "mcl_nether:nether_brick") local bricks = {} + -- TODO: port leg generation to voxel manipulators? for _,leg in pairs(legs) do - while minetest.get_item_group(mcl_vars.get_node(vector.offset(leg,0,-1,0), true, 333333).name, "solid") == 0 do + while true do leg = vector.offset(leg,0,-1,0) + local nodename = minetest.get_node(leg).name + if nodename == "ignore" then break end + if nodename ~= "air" and nodename ~= "mcl_core:lava_source" and minetest.get_item_group(nodename, "solid") ~= 0 then break end table.insert(bricks,leg) end end minetest.bulk_set_node(bricks, {name = "mcl_nether:nether_brick", param2 = 2}) local p1, p2 = vector.offset(pos,-45,12,-45), vector.offset(pos,45,22,45) - mcl_structures.spawn_mobs("mobs_mc:witherskeleton",{"mcl_blackstone:blackstone_chiseled_polished"},p1,p2,pr,5) + vl_structures.spawn_mobs("mobs_mc:witherskeleton",{"mcl_blackstone:blackstone_chiseled_polished"},p1,p2,pr,5) end }) -mcl_structures.register_structure_spawn({ +vl_structures.register_structure_spawn({ name = "mobs_mc:witherskeleton", y_min = mcl_vars.mg_lava_nether_max, y_max = mcl_vars.mg_nether_max, @@ -117,14 +131,12 @@ mcl_structures.register_structure_spawn({ spawnon = { "mcl_blackstone:blackstone_chiseled_polished" }, }) -mcl_structures.register_structure("nether_bulwark",{ +vl_structures.register_structure("nether_bulwark",{ place_on = {"mcl_nether:netherrack","mcl_crimson:crimson_nylium","mcl_crimson:warped_nylium","mcl_blackstone:basalt","mcl_blackstone:soul_soil","mcl_blackstone:blackstone","mcl_nether:soul_sand"}, chunk_probability = 29, - flags = "all_floors", + flags = "place_center_x, place_center_y, all_floors", biomes = {"Nether","SoulsandValley","WarpedForest","CrimsonForest"}, - sidelen = 36, - solid_ground = true, - prepare = { tolerance=15, padding=4, corners=4, foundation=true, clearance=true }, + prepare = { tolerance=10, padding=4, corners=5, foundation=-5, clear_top=0 }, y_min = mcl_vars.mg_lava_nether_max - 1, y_max = mcl_vars.mg_nether_max - 30, filenames = { @@ -134,24 +146,24 @@ mcl_structures.register_structure("nether_bulwark",{ modpath.."/schematics/mcl_nether_fortresses_nether_bulwark_4.mts", }, daughters = {{ - files = { + filenames = { modpath.."/schematics/mcl_nether_fortresses_nether_bulwark_interior_1.mts", modpath.."/schematics/mcl_nether_fortresses_nether_bulwark_interior_2.mts", modpath.."/schematics/mcl_nether_fortresses_nether_bulwark_interior_3.mts", modpath.."/schematics/mcl_nether_fortresses_nether_bulwark_interior_4.mts", }, pos = vector.new(0,1,0), - force_place = true, - prepare = { tolerance = -1, foundation = false, clearance = false }, + rotation = "random", + force_placement = true, + prepare = { tolerance = -1, foundation = false, clear = false }, }, }, y_offset = 0, construct_nodes = {"group:wall"}, - after_place = function(pos,def,pr) - local p1, p2 = vector.offset(pos,-14,0,-14), vector.offset(pos,14,24,14) - mcl_structures.spawn_mobs("mobs_mc:piglin",{"mcl_blackstone:blackstone_brick_polished","mcl_stairs:slab_blackstone_polished"},p1,p2,pr,5) - mcl_structures.spawn_mobs("mobs_mc:piglin_brute",{"mcl_blackstone:blackstone_brick_polished","mcl_stairs:slab_blackstone_polished"},p1,p2,pr) - mcl_structures.spawn_mobs("mobs_mc:hoglin",{"mcl_blackstone:nether_gold"},p1,p2,pr,4) + after_place = function(pos,def,pr,p1,p2) + vl_structures.spawn_mobs("mobs_mc:piglin",{"mcl_blackstone:blackstone_brick_polished","mcl_stairs:slab_blackstone_polished"},p1,p2,pr,5) + vl_structures.spawn_mobs("mobs_mc:piglin_brute",{"mcl_blackstone:blackstone_brick_polished","mcl_stairs:slab_blackstone_polished"},p1,p2,pr) + vl_structures.spawn_mobs("mobs_mc:hoglin",{"mcl_blackstone:nether_gold"},p1,p2,pr,4) end, loot = { ["mcl_chests:chest_small" ] ={ @@ -198,22 +210,22 @@ mcl_structures.register_structure("nether_bulwark",{ }, }) -mcl_structures.register_structure_spawn({ +vl_structures.register_structure_spawn({ name = "mobs_mc:piglin", y_min = mcl_vars.mg_nether_min, y_max = mcl_vars.mg_nether_max, chance = 10, interval = 60, limit = 9, - spawnon = {"mcl_blackstone:blackstone_brick_polished","mcl_stairs:slab_blackstone_polished"}, + spawnon = {"mcl_blackstone:blackstone_brick_polished", "mcl_stairs:slab_blackstone_polished"}, }) -mcl_structures.register_structure_spawn({ +vl_structures.register_structure_spawn({ name = "mobs_mc:piglin_brute", y_min = mcl_vars.mg_nether_min, y_max = mcl_vars.mg_nether_max, chance = 20, interval = 60, limit = 4, - spawnon = {"mcl_blackstone:blackstone_brick_polished","mcl_stairs:slab_blackstone_polished"}, + spawnon = {"mcl_blackstone:blackstone_brick_polished", "mcl_stairs:slab_blackstone_polished"}, }) diff --git a/mods/MAPGEN/mcl_strongholds/init.lua b/mods/MAPGEN/mcl_strongholds/init.lua index d5bc87963..028b680b0 100644 --- a/mods/MAPGEN/mcl_strongholds/init.lua +++ b/mods/MAPGEN/mcl_strongholds/init.lua @@ -83,19 +83,20 @@ local function generate_strongholds(minp, maxp, blockseed) pos.z = maxp.z - 7 end - --mcl_structures.call_struct(pos, "end_portal_shrine", nil, pr) + --vl_structures.call_struct(pos, "end_portal_shrine", nil, pr) strongholds[s].generated = true end end end end -mcl_structures.register_structure("end_shrine",{ +vl_structures.register_structure("end_shrine",{ static_pos = init_strongholds(), + prepare = { tolerance = -1, foundation = false, clear = false }, filenames = { minetest.get_modpath("mcl_structures").."/schematics/mcl_structures_end_portal_room_simple.mts" }, - after_place = function(pos,def,pr,blockseed,p1,p2,size,rotation) + after_place = function(pos,def,pr,p1,p2,size,rotation) local p1 = vector.subtract(pos,size) local p2 = vector.add(pos,size) local spawners = minetest.find_nodes_in_area(p1, p2, "mcl_mobspawners:spawner") diff --git a/mods/MAPGEN/mcl_structures/API.md b/mods/MAPGEN/mcl_structures/API.md index 1e89aa627..3e2308da7 100644 --- a/mods/MAPGEN/mcl_structures/API.md +++ b/mods/MAPGEN/mcl_structures/API.md @@ -1,39 +1,7 @@ # mcl_structures -Structure placement API for MCL2. +VoxeLibre structures -## mcl_structures.register_structure(name,structure definition,nospawn) -If nospawn is truthy the structure will not be placed by mapgen and the decoration parameters can be omitted. This is intended for secondary structures the placement of which gets triggered by the placement of other structures. It can also be used to register testing structures so they can be used with /spawnstruct. +This module contains standard VoxeLibre structures such as nether portals. -### structure definition -{ - fill_ratio = OR noise = {}, - biomes = {}, - y_min =, - y_max =, - place_on = {}, - spawn_by = {}, - num_spawn_by =, - flags = (default: "place_center_x, place_center_z, force_placement") - (same as decoration def) - y_offset =, --can be a number or a function returning a number - filenames = {} OR place_func = function(pos,def,pr) - -- filenames can be a list of any schematics accepted by mcl_structures.place_schematic / minetest.place_schematic - on_place = function(pos,def,pr) end, - -- called before placement. denies placement when returning falsy. - after_place = function(pos,def,pr) - -- executed after successful placement - rank = int, -- decorations are generated by increasing rank. Default for structures is 100, terrain features 900 - sidelen = int, --length of one side of the structure. used for foundations. - solid_ground = bool, -- structure requires solid ground - make_foundation = bool, -- a foundation is automatically built for the structure. needs the sidelen param - loot = , - --a table of loot tables for mcl_loot indexed by node names - -- e.g. { ["mcl_chests:chest_small"] = {loot},... } -} -## mcl_structures.registered_structures -Table of the registered structure defintions indexed by name. +The API has been redesigned and moved to the vl_structures module. -## mcl_structures.place_structure(pos, def, pr) -Places a structure using the mapgen placement function - -## mcl_structures.place_schematic(pos, schematic, rotation, replacements, force_placement, flags, after_placement_callback, pr, callback_param) diff --git a/mods/MAPGEN/mcl_structures/api.lua b/mods/MAPGEN/mcl_structures/api.lua deleted file mode 100644 index 11f040bb1..000000000 --- a/mods/MAPGEN/mcl_structures/api.lua +++ /dev/null @@ -1,467 +0,0 @@ -mcl_structures.registered_structures = {} - -local peaceful = minetest.settings:get_bool("only_peaceful_mobs", false) -local mob_cap_player = tonumber(minetest.settings:get("mcl_mob_cap_player")) or 75 -local mob_cap_animal = tonumber(minetest.settings:get("mcl_mob_cap_animal")) or 10 -local structure_boost = tonumber(minetest.settings:get("mcl_structures_boost")) or 1 -local worldseed = minetest.get_mapgen_setting("seed") -local RANDOM_SEED_OFFSET = 959 -- random constant that should be unique across each library - -local logging = minetest.settings:get_bool("mcl_logging_structures", true) - -local mg_name = minetest.get_mapgen_setting("mg_name") - -local disabled_structures = minetest.settings:get("mcl_disabled_structures") -if disabled_structures then disabled_structures = disabled_structures:split(",") -else disabled_structures = {} end -function mcl_structures.is_disabled(structname) - return table.indexof(disabled_structures,structname) ~= -1 -end - -local ROTATIONS = { "0", "90", "180", "270" } -function mcl_structures.parse_rotation(rotation, pr) - if rotation == "random" and pr then return ROTATIONS[pr:next(1,#ROTATIONS)] end - return rotation -end - ---- Get the size after rotation. --- @param size vector: Size information --- @param rotation string or number: only 0, 90, 180, 270 are allowed --- @return vector: new vector, for safety -function mcl_structures.size_rotated(size, rotation) - if rotation == "90" or rotation == "270" or rotation == 90 or rotation == 270 then - return vector.new(size.z, size.y, size.x) - end - return vector.copy(size) -end - ---- Get top left position after apply centering flags and padding. --- @param pos vector: Placement position --- @param[opt] size vector: Size information --- @param[opt] flags string or table: as in minetest.place_schematic, place_center_x, place_center_y --- @param[opt] padding number: optional margin (integer) --- @return vector: new vector, for safety -function mcl_structures.top_left_from_flags(pos, size, flags, padding) - local dx, dy, dz = 0, 0, 0 - -- must match src/mapgen/mg_schematic.cpp to be consistent - if type(flags) == "table" then - if flags["place_center_x"] ~= nil then dx = -math.floor((size.x-1)*0.5) end - if flags["place_center_y"] ~= nil then dy = -math.floor((size.y-1)*0.5) end - if flags["place_center_z"] ~= nil then dz = -math.floor((size.z-1)*0.5) end - elseif type(flags) == "string" then - if string.find(flags, "place_center_x") then dx = -math.floor((size.x-1)*0.5) end - if string.find(flags, "place_center_y") then dy = -math.floor((size.y-1)*0.5) end - if string.find(flags, "place_center_z") then dz = -math.floor((size.z-1)*0.5) end - end - if padding then - dx = dx - padding - dz = dz - padding - end - return vector.offset(pos, dx, dy, dz) -end - --- Expected contents of param: --- pos vector: position (center.x, base.y, center.z) -- flags NOT supported --- size vector: structure size after rotation (!) --- yoffset number: relative to base.y, typically <= 0 --- y_min number: minimum y range permitted --- y_max number: maximum y range permitted --- schematic string or schematic: as in minetest.place_schematic --- rotation string: as in minetest.place_schematic --- replacement table: as in minetest.place_schematic --- force_placement boolean: as in minetest.place_schematic --- prepare table: instructions for preparation (usually from definition) --- tolerance number: tolerable ground unevenness, -1 to disable, default 10 --- foundation boolean or number: level ground underneath structure (true is a minimum depth of -3) --- clearance boolean or string or number: clear overhead area (offset, or "top" to begin over the structure only) --- padding number: additional padding to increase the area, default 1 --- corners number: corner smoothing of foundation and clearance, default 1 --- pr PcgRandom: random generator --- name string: for logging -local function emerge_schematic_vm(vm, param) - local pos, size, prepare, surface_mat = param.pos, param.size, param.prepare, nil - -- adjust ground to a move level position - if pos and size and prepare and (prepare.tolerance or 10) >= 0 then - pos, surface_mat = mcl_structures.find_level(vm, pos, size, prepare.tolerance) - if not pos then - minetest.log("warning", "[mcl_structures] Not spawning "..tostring(param.name or param.schematic.name).." at "..minetest.pos_to_string(param.pos).." because ground is too uneven.") - return nil - end - if param.y_max and pos.y > param.y_max then pos.y = param.y_max end - if param.y_min and pos.y < param.y_min then pos.y = param.y_min end - end - -- Prepare the environment - if prepare and (prepare.clearance or prepare.foundation) then - -- Get materials from biome: - local b = mg_name ~= "v6" and minetest.registered_biomes[minetest.get_biome_name(minetest.get_biome_data(pos).biome)] - local node_top = b and b.node_top or (surface_mat and surface_mat.name) or "mcl_core:dirt_with_grass" - local node_filler = b and b.node_filler or "mcl_core:dirt" - local node_stone = b and b.node_stone or "mcl_core:stone" - -- FIXME: not yet used: local node_dust = b and b.node_dust - local node_top_param2 = node_top == "mcl_core:dirt_with_grass" and b._mcl_grass_palette_index or 0 -- grass color, also other materials? - - local corners, padding, depth = prepare.corners or 1, prepare.padding or 1, (type(prepare.foundation) == "number" and prepare.foundation) or -4 - local gp = vector.offset(pos, -math.floor((size.x-1)*0.5) - padding, 0, -math.floor((size.z-1)*0.5)-padding) - local gs = vector.offset(size, padding*2, depth, padding*2) - if prepare.clearance then - -- minetest.log("action", "[mcl_structures] clearing air "..minetest.pos_to_string(gp).." +"..minetest.pos_to_string(gs).." corners "..corners) - -- TODO: add more parameters? - local yoff, height = 0, size.y + (param.yoffset or 0) - if prepare.clearance == "top" or prepare.clearance == "above" then - yoff, height = height, 0 - elseif type(prepare.clearance) == "number" then - yoff, height = prepare.clearance, height - prepare.clearance - end - mcl_structures.clearance(vm, gp.x, gp.y + yoff, gp.z, gs.x, height, gs.z, corners, {name=node_top, param2=node_top_param2}, param.pr) - end - if prepare.foundation then - -- minetest.log("action", "[mcl_structures] fill foundation "..minetest.pos_to_string(gp).." +"..minetest.pos_to_string(gs).." corners "..corners) - local depth = (type(prepare.foundation) == "number" and prepare.foundation) or -3 - mcl_structures.foundation(vm, gp.x, gp.y - 1, gp.z, gs.x, depth, gs.z, corners, - {name=node_top, param2=node_top_param2}, {name=node_filler}, {name=node_stone}, param.pr) - end - end - -- place the actual schematic - pos.y = pos.y + (param.yoffset or 0) - minetest.place_schematic_on_vmanip(vm, pos, param.schematic, param.rotation, param.replacements, param.force_placement, "place_center_x,place_center_z") - return pos -end - --- Additional parameters: --- emin vector: emerge area minimum --- emax vector: emerge area maximum --- after_placement_callback function: callback after placement, (pmin, pmax, size, rotation, pr, param) --- callback_param table: additional parameters to callback function -local function emerge_schematic(blockpos, action, calls_remaining, param) - if calls_remaining >= 1 then return end - local vm = VoxelManip() - vm:read_from_map(param.emin, param.emax) - local pos = emerge_schematic_vm(vm, param) - vm:write_to_map(true) - if not pos then return end - -- repair walls (TODO: port to vmanip? but no "vm.find_nodes_in_area" yet) - local pmin = vector.offset(pos, -math.floor((param.size.x-1)*0.5), 0, -math.floor((param.size.z-1)*0.5)) - local pmax = vector.offset(pmin, param.size.x-1, param.size.y-1, param.size.z-1) - if pmin and pmax and mcl_walls then - for _, n in pairs(minetest.find_nodes_in_area(pmin, pmax, { "group:wall" })) do - mcl_walls.update_wall(n) - end - end - if pmin and pmax and param.after_placement_callback then - param.after_placement_callback(pmin, pmax, param.size, param.rotation, param.pr, param.callback_param) - end -end - -local DEFAULT_PREPARE = { tolerance = 8, foundation = -3, clearance = false, padding = 1, corners = 1 } -local DEFAULT_FLAGS = "place_center_x,place_center_z" -function mcl_structures.place_schematic(pos, yoffset, y_min, y_max, schematic, rotation, replacements, force_placement, flags, prepare, pr, after_placement_callback, callback_param) - if schematic and not schematic.size then -- e.g., igloo still passes filenames - schematic = loadstring(minetest.serialize_schematic(schematic, "lua", {lua_use_comments = false, lua_num_indent_spaces = 0}) .. " return schematic")() - end - rotation = mcl_structures.parse_rotation(rotation, pr) - local size = mcl_structures.size_rotated(schematic.size, rotation) - -- area to emerge; note that alignment flags could be non-center, although we almost always use place_center_x,place_center_z - local pmin = mcl_structures.top_left_from_flags(pos, flags or DEFAULT_FLAGS) - local ppos = vector.offset(pmin, math.floor((size.x-1)*0.5), 0, math.floor((size.z-1)*0.5)) -- center - local pmax = vector.offset(pmin, size.x - 1, size.y - 1, size.z - 1) - if prepare == nil or prepare == true then prepare = DEFAULT_PREPARE end - if prepare == false then prepare = {} end - -- area to emerge. Add some margin to allow for finding better suitable ground etc. - local emin, emax = vector.offset(pmin, -1, -5, -1), vector.offset(pmax, 1, 5, 1) - if prepare then emin.y = emin.y - (prepare.tolerance or 10) end - -- if we need to generate a foundation, we need to emerge a larger area: - if prepare.foundation or prepare.clearance then - -- these functions need some extra margins - local padding, depth, height = (prepare.padding or 0) + 3, (prepare.depth or -4) - 15, size.y * 2 + 6 - emin = vector.offset(pmin, -padding, depth + math.min(yoffset or 0, 0), -padding) - emax = vector.offset(pmax, padding, height + math.max(yoffset or 0, 0), padding) - end - minetest.emerge_area(emin, emax, emerge_schematic, { - emin=emin, emax=emax, name=schematic.name or (type(schematic)=="string" and schematic), - pos=ppos, size=size, yoffset=yoffset, y_min=y_min, y_max=y_max, - schematic=schematic, rotation=rotation, replacements=replacements, force_placement=force_placement, - prepare=prepare, pr=pr, - after_placement_callback=after_placement_callback, callback_param=callback_param - }) -end - --- Call all on_construct handlers --- also called from mcl_villages for job sites -function mcl_structures.init_node_construct(pos) - local node = minetest.get_node(pos) - local def = node and minetest.registered_nodes[node.name] - if def and def.on_construct then def.on_construct(pos) end -end - --- Find nodes to call on_construct handlers for -function mcl_structures.construct_nodes(p1,p2,nodes) - local nn = minetest.find_nodes_in_area(p1,p2,nodes) - for _,p in pairs(nn) do mcl_structures.init_node_construct(p) end -end - -function mcl_structures.fill_chests(p1,p2,loot,pr) - for it,lt in pairs(loot) do - local nodes = minetest.find_nodes_in_area(p1, p2, it) - for _,p in pairs(nodes) do - local lootitems = mcl_loot.get_multi_loot(lt, pr) - mcl_structures.init_node_construct(p) - local meta = minetest.get_meta(p) - local inv = meta:get_inventory() - mcl_loot.fill_inventory(inv, "main", lootitems, pr) - end - end -end - -function mcl_structures.spawn_mobs(mob,spawnon,p1,p2,pr,n,water) - n = n or 1 - local sp = {} - if water then - local nn = minetest.find_nodes_in_area(p1,p2,spawnon) - for k,v in pairs(nn) do - if minetest.get_item_group(minetest.get_node(vector.offset(v,0,1,0)).name,"water") > 0 then - table.insert(sp,v) - end - end - else - sp = minetest.find_nodes_in_area_under_air(p1,p2,spawnon) - end - table.shuffle(sp) - local count = 0 - local mob_def = minetest.registered_entities[mob] - local enabled = (not peaceful) or (mob_def and mob_def.spawn_class ~= "hostile") - for _,node in pairs(sp) do - if enabled and count < n and minetest.add_entity(vector.offset(node, 0, 1, 0), mob) then - count = count + 1 - end - minetest.get_meta(node):set_string("spawnblock", "yes") -- note: also in peaceful mode! - end -end - -function mcl_structures.place_structure(pos, def, pr, blockseed, rot) - if not def then return end - local log_enabled = logging and not def.terrain_feature - -- currently only used by fallen_tree, to check for sufficient empty space to fall - if def.on_place and not def.on_place(pos,def,pr,blockseed) then - if log_enabled then - minetest.log("warning","[mcl_structures] "..def.name.." at "..minetest.pos_to_string(pos).." not placed. on_place conditions not satisfied.") - end - return false - end - -- Apply vertical offset for schematic - local yoffset = (type(def.y_offset) == "function" and def.y_offset(pr)) or def.y_offset or 0 - if def.schematics and #def.schematics > 0 then - local schematic = def.schematics[pr:next(1,#def.schematics)] - rot = mcl_structures.parse_rotation(rot or "random", pr) - if not def.daughters then - mcl_structures.place_schematic(pos, yoffset, def.y_min, def.y_max, schematic, rot, def.replacements, def.force_placement, "place_center_x,place_center_z", def.prepare, pr, - function(p1, p2, size, rotation) - if def.loot then mcl_structures.fill_chests(p1,p2,def.loot,pr) end - if def.construct_nodes then mcl_structures.construct_nodes(p1,p2,def.construct_nodes) end - if def.after_place then def.after_place(pos,def,pr,p1,p2,size,rotation) end - if log_enabled then - minetest.log("action", "[mcl_structures] "..def.name.." spawned at "..minetest.pos_to_string(pos)) - end - end) - else -- currently only nether bulwarks + nether outpost with bridges? - -- FIXME: this really needs to be run in a single emerge! - mcl_structures.place_schematic(pos, yoffset, def.y_min, def.y_max, schematic, rot, def.replacements, def.force_placement, "place_center_x,place_center_z", def.prepare, pr, - function(p1, p2, size, rotation) - for i,d in pairs(def.daughters) do - local ds = d.files[pr:next(1,#d.files)] - -- Daughter schematics are not loaded yet. - if ds and not ds.size then - ds = loadstring(minetest.serialize_schematic(ds, "lua", {lua_use_comments = false, lua_num_indent_spaces = 0}) .. " return schematic")() - end - -- FIXME: apply centering, apply parent rotation. - local rot = d.rot or 0 - local dsize = mcl_structures.size_rotated(ds.size, rot) - local p = vector.new(math.floor((p1.x+p2.x)*0.5) + d.pos.x - math.floor((dsize.x-1)*0.5), p1.y + (yoffset or 0) + d.pos.y, math.floor((p1.z+p2.z)*0.5) + d.pos.z - math.floor((dsize.z-1)*0.5)) - local callback = nil - if i == #def.daughters then - callback = function() - -- Note: deliberately pos, p1 and p2 from the parent, as these are calls to the parent. - if def.loot then mcl_structures.fill_chests(p1,p2,def.loot,pr) end - if def.construct_nodes then mcl_structures.construct_nodes(p1,p2,def.construct_nodes) end - if def.after_place then def.after_place(pos,def,pr,p1,p2,size,rotation) end - if log_enabled then - minetest.log("action", "[mcl_structures] "..def.name.." spawned at "..minetest.pos_to_string(pos)) - end - end - end - mcl_structures.place_schematic(p, yoffset, d.y_min or def.y_min, d.y_max or def.y_max, ds, rot, nil, true, "place_center_x,place_center_y", d.prepare, pr, callback) - end - end) - end - if log_enabled then - minetest.log("verbose", "[mcl_structures] "..def.name.." to be placed at "..minetest.pos_to_string(pos)) - end - return true - end - if not def.place_func then - minetest.log("warning","[mcl_structures] no schematics and no place_func for schematic "..def.name) - return false - end - if def.solid_ground and def.sidelen and not def.prepare then - -- TODO: this assumes place_center, make padding configurable, use actual size? - local ground_p1 = vector.offset(pos,-math.floor(def.sidelen/2),-1,-math.floor(def.sidelen/2)) - local ground_p2 = vector.offset(ground_p1,def.sidelen-1,0,def.sidelen-1) - local solid = minetest.find_nodes_in_area(ground_p1,ground_p2,{"group:solid"}) - if #solid < def.sidelen * def.sidelen then - if log_enabled then - minetest.log("warning", "[mcl_structures] "..def.name.." at "..minetest.pos_to_string(pos).." not placed. No solid ground.") - end - return false - end - end - local pp = yoffset ~= 0 and vector.offset(pos, 0, yoffset, 0) or pos - if def.place_func and def.place_func(pp,def,pr,blockseed) then - if not def.after_place or (def.after_place and def.after_place(pp,def,pr,blockseed)) then - if def.prepare then - minetest.log("warning", "[mcl_structures] needed prepare for "..def.name.." placed at "..minetest.pos_to_string(pp).." but did not have size information") - end - if def.sidelen then - local p1, p2 = vector.offset(pos,-def.sidelen,-def.sidelen,-def.sidelen), vector.offset(pos,def.sidelen,def.sidelen,def.sidelen) - if def.loot then mcl_structures.fill_chests(p1,p2,def.loot,pr) end - if def.construct_nodes then mcl_structures.construct_nodes(p1,p2,def.construct_nodes) end - end - if log_enabled then - minetest.log("action","[mcl_structures] "..def.name.." placed at "..minetest.pos_to_string(pp)) - end - return true - else - minetest.log("warning","[mcl_structures] after_place failed for schematic "..def.name) - return false - end - elseif log_enabled then - minetest.log("warning","[mcl_structures] place_func failed for schematic "..def.name) - end -end - -local EMPTY_SCHEMATIC = { size = {x = 0, y = 0, z = 0}, data = { } } -function mcl_structures.register_structure(name,def,nospawn) --nospawn means it will not be placed by mapgen decoration mechanism - if mcl_structures.is_disabled(name) then return end - def.name = name - def.prepare = def.prepare or (type(def.make_foundation) == table and def.make_foundation) - def.flags = def.flags or "place_center_x, place_center_z, force_placement" - if def.filenames then - if #def.filenames == 0 then - minetest.log("warning","[mcl_structures] schematic "..name.." has an empty list of filenames.") - end - def.schematics = def.schematics or {} - for _, filename in ipairs(def.filenames) do - if not mcl_util.file_exists(filename) then - minetest.log("warning","[mcl_structures] schematic "..name.." is missing file "..tostring(filename)) - else - - -- load, and ensure we have size information - local s = nil --minetest.read_schematic(filename) - if not s or not s.size then - s = loadstring(minetest.serialize_schematic(filename, "lua", {lua_use_comments = false, lua_num_indent_spaces = 0}) .. " return schematic")() - end - if not s then - minetest.log("warning", "[mcl_structures] failed to load schematic "..tostring(filename)) - elseif not s.size then - minetest.log("warning", "[mcl_structures] no size information for schematic "..tostring(filename)) - else - if logging then - minetest.log("verbose", "[mcl_structures] loaded schematic "..tostring(filename).." size "..minetest.pos_to_string(s.size)) - end - if not s.name then s.name = name or filename end - table.insert(def.schematics, s) - end - end - end - end - if not def.noise_params and def.chunk_probability and not def.fill_ratio then - def.fill_ratio = 1.1/80/80 -- 1 per chunk, controlled by chunk probability only - end - mcl_structures.registered_structures[name] = def - if nospawn then return end -- ice column, boulder - if def.place_on then - minetest.register_on_mods_loaded(function() --make sure all previous decorations and biomes have been registered - mcl_mapgen_core.register_decoration({ - name = "mcl_structures:"..name, - rank = def.rank or (def.terrain_feature and 900) or 100, -- run before regular decorations - deco_type = "schematic", - schematic = EMPTY_SCHEMATIC, - place_on = def.place_on, - spawn_by = def.spawn_by, - num_spawn_by = def.num_spawn_by, - sidelen = 80, -- no def.sidelen subdivisions for now - fill_ratio = def.fill_ratio, - noise_params = def.noise_params, - flags = def.flags, - biomes = def.biomes, - y_max = def.y_max, - y_min = def.y_min - }, - function() - def.deco_id = minetest.get_decoration_id("mcl_structures:"..name) - minetest.set_gen_notify({decoration=true}, { def.deco_id }) - --catching of gennotify happens in mcl_mapgen_core - end - ) - end) - end -end - -local structure_spawns = {} -function mcl_structures.register_structure_spawn(def) - --name,y_min,y_max,spawnon,biomes,chance,interval,limit - minetest.register_abm({ - label = "Spawn "..def.name, - nodenames = def.spawnon, - min_y = def.y_min or -31000, - max_y = def.y_max or 31000, - interval = def.interval or 60, - chance = def.chance or 5, - action = function(pos, node, active_object_count, active_object_count_wider) - local limit = def.limit or 7 - if active_object_count_wider > limit + mob_cap_animal then return end - if active_object_count_wider > mob_cap_player then return end - local p = vector.offset(pos,0,1,0) - local pname = minetest.get_node(p).name - if def.type_of_spawning == "water" then - if pname ~= "mcl_core:water_source" and pname ~= "mclx_core:river_water_source" then return end - else - if pname ~= "air" then return end - end - if minetest.get_meta(pos):get_string("spawnblock") == "" then return end - if mg_name ~= "v6" and mg_name ~= "singlenode" and def.biomes then - if table.indexof(def.biomes,minetest.get_biome_name(minetest.get_biome_data(p).biome)) == -1 then - return - end - end - local mobdef = minetest.registered_entities[def.name] - if mobdef.can_spawn and not mobdef.can_spawn(p) then return end - minetest.add_entity(p,def.name) - end, - }) -end - --- To avoid a cyclic dependency, run this when modules have finished loading -minetest.register_on_mods_loaded(function() -mcl_mapgen_core.register_generator("structures", nil, function(minp, maxp, blockseed) - local gennotify = minetest.get_mapgen_object("gennotify") - for _,struct in pairs(mcl_structures.registered_structures) do - if struct.deco_id then - for _, pos in pairs(gennotify["decoration#"..struct.deco_id] or {}) do - local pr = PcgRandom(minetest.hash_node_position(pos) + blockseed + RANDOM_SEED_OFFSET) - if struct.chunk_probability == nil or pr:next(1, struct.chunk_probability) == 1 then - mcl_structures.place_structure(vector.offset(pos,0,1,0),struct,pr,blockseed) - if struct.chunk_probability then break end -- one (attempt) per chunk only - end - end - elseif struct.static_pos then - local pr = PcgRandom(blockseed + RANDOM_SEED_OFFSET) - for _, pos in pairs(struct.static_pos) do - if vector.in_area(pos, minp, maxp) then - mcl_structures.place_structure(pos, struct, pr, blockseed) - end - end - end - end - return false, false, false -end, 100, true) -end) - diff --git a/mods/MAPGEN/mcl_structures/desert_temple.lua b/mods/MAPGEN/mcl_structures/desert_temple.lua index 298dfc058..f00939259 100644 --- a/mods/MAPGEN/mcl_structures/desert_temple.lua +++ b/mods/MAPGEN/mcl_structures/desert_temple.lua @@ -2,15 +2,12 @@ local modname = minetest.get_current_modname() local S = minetest.get_translator(modname) local modpath = minetest.get_modpath(modname) -local function temple_placement_callback(pos,def, pr) - local hl = def.sidelen / 2 - local p1 = vector.offset(pos,-hl,-hl,-hl) - local p2 = vector.offset(pos,hl,hl,hl) +local function temple_placement_callback(pos,def,pr,p1,p2) -- Delete cacti leftovers: local cactus_nodes = minetest.find_nodes_in_area_under_air(p1, p2, "mcl_core:cactus") if cactus_nodes and #cactus_nodes > 0 then for _, pos in pairs(cactus_nodes) do - local node_below = minetest.get_node({x=pos.x, y=pos.y-1, z=pos.z}) + local node_below = minetest.get_node(vector.offset(pos,0,-1,0)) if node_below and node_below.name == "mcl_core:sandstone" then minetest.swap_node(pos, {name="air"}) end @@ -32,12 +29,11 @@ local function temple_placement_callback(pos,def, pr) end end -mcl_structures.register_structure("desert_temple",{ +vl_structures.register_structure("desert_temple",{ place_on = {"group:sand"}, flags = "place_center_x, place_center_z", - solid_ground = true, - sidelen = 18, y_offset = -12, + prepare = { padding = 3, corners = 3, foundation = true, clear = false }, chunk_probability = 8, y_max = mcl_vars.mg_overworld_max, y_min = 1, diff --git a/mods/MAPGEN/mcl_structures/desert_well.lua b/mods/MAPGEN/mcl_structures/desert_well.lua new file mode 100644 index 000000000..964c891b9 --- /dev/null +++ b/mods/MAPGEN/mcl_structures/desert_well.lua @@ -0,0 +1,15 @@ +local modname = minetest.get_current_modname() +--local S = minetest.get_translator(modname) +local modpath = minetest.get_modpath(modname) + +vl_structures.register_structure("desert_well",{ + place_on = {"group:sand"}, + flags = "place_center_x, place_center_z", + chunk_probability = 15, + y_max = mcl_vars.mg_overworld_max, + y_min = 1, + y_offset = -2, + biomes = { "Desert" }, + filenames = { modpath.."/schematics/mcl_structures_desert_well.mts" }, +}) + diff --git a/mods/MAPGEN/mcl_structures/end_city.lua b/mods/MAPGEN/mcl_structures/end_city.lua index 12967b234..deb805ae8 100644 --- a/mods/MAPGEN/mcl_structures/end_city.lua +++ b/mods/MAPGEN/mcl_structures/end_city.lua @@ -4,38 +4,34 @@ local modpath = minetest.get_modpath(modname) local spawnon = {"mcl_end:purpur_block"} -local function spawn_shulkers(pos,def,pr) - local p1 = vector.offset(pos,-def.sidelen/2,-1,-def.sidelen/2) - local p2 = vector.offset(pos,def.sidelen/2,def.sidelen,def.sidelen/2) - mcl_structures.spawn_mobs("mobs_mc:shulker",spawnon,p1,p2,pr,1) - - local guard = minetest.find_node_near(pos,def.sidelen,{"mcl_itemframes:item_frame"}) - if guard then - minetest.add_entity(vector.offset(guard,0,-1.5,0),"mobs_mc:shulker") +local function spawn_shulkers(pos,def,pr,p1,p2) + vl_structures.spawn_mobs("mobs_mc:shulker",spawnon,p1,p2,pr,1) + local guard = minetest.find_nodes_in_area(p1,p2,{"mcl_itemframes:item_frame"}) + if #guard > 0 then + minetest.add_entity(vector.offset(guard[1],0,-1.5,0),"mobs_mc:shulker") end end -mcl_structures.register_structure("end_shipwreck",{ +vl_structures.register_structure("end_shipwreck",{ place_on = {"mcl_end:end_stone"}, flags = "place_center_x, place_center_z, all_floors", - y_offset = function(pr) return pr:next(-50,-20) end, + y_offset = function(pr) return pr:next(20,50) end, + force_placement = false, + prepare = { foundation = false, clear = false }, chunk_probability = 25, --y_max = mcl_vars.mg_end_max, --y_min = mcl_vars.mg_end_min -100, biomes = { "End", "EndHighlands", "EndMidlands", "EndBarrens", "EndSmallIslands" }, - sidelen = 32, filenames = { modpath.."/schematics/mcl_structures_end_shipwreck_1.mts", }, construct_nodes = {"mcl_chests:ender_chest_small","mcl_chests:ender_chest","mcl_brewing:stand_000","mcl_chests:violet_shulker_box_small"}, - after_place = function(pos,def,pr) - local fr = minetest.find_node_near(pos,def.sidelen,{"mcl_itemframes:item_frame"}) - if fr then - if mcl_itemframes then - mcl_itemframes.update_item_entity(fr,minetest.get_node(fr)) - end + after_place = function(pos,def,pr,p1,p2) + local fr = minetest.find_nodes_in_area(p1,p2,{"mcl_itemframes:item_frame"}) + if #fr > 0 and mcl_itemframes then + mcl_itemframes.update_item_entity(fr[1],minetest.get_node(fr)) end - return spawn_shulkers(pos,def,pr) + return spawn_shulkers(pos,def,pr,p1,p2) end, loot = { [ "mcl_itemframes:item_frame" ] ={{ @@ -52,7 +48,7 @@ mcl_structures.register_structure("end_shipwreck",{ { itemstring = "mcl_mobitems:bone", weight = 20, amount_min = 4, amount_max=6 }, { itemstring = "mcl_farming:beetroot_seeds", weight = 16, amount_min = 1, amount_max=10 }, { itemstring = "mcl_core:gold_ingot", weight = 15, amount_min = 2, amount_max = 7 }, - --{ itemstring = "mcl_bamboo:bamboo", weight = 15, amount_min = 1, amount_max=3 }, --FIXME BAMBOO + { itemstring = "mcl_bamboo:bamboo", weight = 15, amount_min = 1, amount_max=3 }, { itemstring = "mcl_core:iron_ingot", weight = 15, amount_min = 4, amount_max = 8 }, { itemstring = "mcl_core:diamond", weight = 3, amount_min = 2, amount_max = 7 }, { itemstring = "mcl_mobitems:saddle", weight = 3, }, @@ -85,16 +81,16 @@ mcl_structures.register_structure("end_shipwreck",{ } }) -mcl_structures.register_structure("end_boat",{ +vl_structures.register_structure("end_boat",{ place_on = {"mcl_end:end_stone"}, - fill_ratio = 0.01, flags = "place_center_x, place_center_z, all_floors", y_offset = function(pr) return pr:next(15,30) end, - chunk_probability = 900, + force_placement = false, + prepare = { foundation = false, clear = false }, + chunk_probability = 10, --y_max = mcl_vars.mg_end_max, --y_min = mcl_vars.mg_end_min -100, biomes = { "End", "EndHighlands", "EndMidlands", "EndBarrens", "EndSmallIslands" }, - sidelen = 20, filenames = { modpath.."/schematics/mcl_structures_end_boat.mts", }, @@ -133,7 +129,7 @@ mcl_structures.register_structure("end_boat",{ } }) -mcl_structures.register_structure_spawn({ +vl_structures.register_structure_spawn({ name = "mobs_mc:shulker", y_min = mcl_vars.mg_end_min, y_max = mcl_vars.mg_end_max, diff --git a/mods/MAPGEN/mcl_structures/end_spawn.lua b/mods/MAPGEN/mcl_structures/end_spawn.lua index 5769ac487..2e4bf34aa 100644 --- a/mods/MAPGEN/mcl_structures/end_spawn.lua +++ b/mods/MAPGEN/mcl_structures/end_spawn.lua @@ -2,8 +2,7 @@ local modname = minetest.get_current_modname() local S = minetest.get_translator(modname) local modpath = minetest.get_modpath(modname) - -mcl_structures.register_structure("end_spawn_obsidian_platform",{ +vl_structures.register_structure("end_spawn_obsidian_platform",{ static_pos ={mcl_vars.mg_end_platform_pos}, place_func = function(pos,def,pr) local obby = minetest.find_nodes_in_area(vector.offset(pos,-2,0,-2),vector.offset(pos,2,0,2),{"air","mcl_end:end_stone"}) @@ -14,17 +13,13 @@ mcl_structures.register_structure("end_spawn_obsidian_platform",{ end, }) -mcl_structures.register_structure("end_exit_portal",{ +vl_structures.register_structure("end_exit_portal",{ static_pos = { mcl_vars.mg_end_exit_portal_pos }, filenames = { modpath.."/schematics/mcl_structures_end_exit_portal.mts" }, - after_place = function(pos,def,pr,blockseed) - if minetest.settings:get_bool("only_peaceful_mobs", false) then - return - end - local p1 = vector.offset(pos,-16,-16,-16) - local p2 = vector.offset(pos,16,21,16) + after_place = function(pos,def,pr,p1,p2) + if minetest.settings:get_bool("only_peaceful_mobs", false) then return end minetest.emerge_area(p1,p2,function(blockpos, action, calls_remaining, param) if calls_remaining > 0 then return end minetest.bulk_set_node(minetest.find_nodes_in_area(p1,p2,{"mcl_portals:portal_end"}),{name="air"}) @@ -32,9 +27,9 @@ mcl_structures.register_structure("end_exit_portal",{ if obj then local dragon_entity = obj:get_luaentity() dragon_entity._portal_pos = pos - if blockseed ~= -1 then - dragon_entity._initial = true - end + -- FIXME: if blockseed ~= -1 then + dragon_entity._initial = true + -- end else minetest.log("error", "[mcl_mapgen_core] ERROR! Ender dragon doesn't want to spawn") end @@ -42,17 +37,15 @@ mcl_structures.register_structure("end_exit_portal",{ end) end }) -mcl_structures.register_structure("end_exit_portal_open",{ +vl_structures.register_structure("end_exit_portal_open",{ filenames = { modpath.."/schematics/mcl_structures_end_exit_portal.mts" }, - after_place = function(pos,def,pr) - local p1 = vector.offset(pos,-16,-16,-16) - local p2 = vector.offset(pos,16,16,16) + after_place = function(pos,def,pr,p1,p2) minetest.fix_light(p1,p2) end }) -mcl_structures.register_structure("end_gateway_portal",{ +vl_structures.register_structure("end_gateway_portal",{ filenames = { modpath.."/schematics/mcl_structures_end_gateway_portal.mts" }, @@ -79,7 +72,6 @@ end function make_cage(pos,width) local nodes = {} - local nodes2 = {} local r = math.max(1,math.floor(width/2) - 2) for x=-r,r do for y = 0,width do for z = -r,r do if x == r or x == -r or z==r or z == -r then @@ -97,13 +89,13 @@ end local function get_points_on_circle(pos,r,n) local rt = {} for i=1, n do - table.insert(rt,vector.offset(pos,r * math.cos(((i-1)/n) * (2*math.pi)),0, r* math.sin(((i-1)/n) * (2*math.pi)) )) + table.insert(rt,vector.offset(pos,r * math.cos(((i-1)/n) * (2*math.pi)),0, r * math.sin(((i-1)/n) * (2*math.pi)) )) end return rt end -mcl_structures.register_structure("end_spike",{ - static_pos =get_points_on_circle(vector.offset(mcl_vars.mg_end_exit_portal_pos,0,-20,0),43,10), +vl_structures.register_structure("end_spike",{ + static_pos = get_points_on_circle(vector.offset(mcl_vars.mg_end_exit_portal_pos, 0, -20, 0), 43, 10), place_func = function(pos,def,pr) local d = pr:next(6,12) local h = d * pr:next(4,6) diff --git a/mods/MAPGEN/mcl_structures/fossil.lua b/mods/MAPGEN/mcl_structures/fossil.lua new file mode 100644 index 000000000..6468a5538 --- /dev/null +++ b/mods/MAPGEN/mcl_structures/fossil.lua @@ -0,0 +1,25 @@ +local modname = minetest.get_current_modname() +--local S = minetest.get_translator(modname) +local modpath = minetest.get_modpath(modname) + +vl_structures.register_structure("fossil",{ + place_on = {"group:material_stone","group:sand"}, + flags = "place_center_x, place_center_z", + prepare = { }, + chunk_probability = 15, -- was 25 + y_offset = function(pr) return pr:next(-16,-32) end, + y_max = 15, + y_min = mcl_vars.mg_overworld_min + 35, + biomes = { "Desert" }, + filenames = { + modpath.."/schematics/mcl_structures_fossil_skull_1.mts", -- 4×5×5 + modpath.."/schematics/mcl_structures_fossil_skull_2.mts", -- 5×5×5 + modpath.."/schematics/mcl_structures_fossil_skull_3.mts", -- 5×5×7 + modpath.."/schematics/mcl_structures_fossil_skull_4.mts", -- 7×5×5 + modpath.."/schematics/mcl_structures_fossil_spine_1.mts", -- 3×3×13 + modpath.."/schematics/mcl_structures_fossil_spine_2.mts", -- 5×4×13 + modpath.."/schematics/mcl_structures_fossil_spine_3.mts", -- 7×4×13 + modpath.."/schematics/mcl_structures_fossil_spine_4.mts", -- 8×5×13 + }, +}) + diff --git a/mods/MAPGEN/mcl_structures/foundation.lua b/mods/MAPGEN/mcl_structures/foundation.lua deleted file mode 100644 index f9482d02d..000000000 --- a/mods/MAPGEN/mcl_structures/foundation.lua +++ /dev/null @@ -1,334 +0,0 @@ -local AIR = {name = "air"} -local abs = math.abs -local max = math.max - --- fairly strict: air, ignore, or no_paths marker -local function is_air(node) - return not node or node.name == "air" or node.name == "ignore" or node.name == "mcl_villages:no_paths" -end --- check if a node is walkable (solid), but not tree/leaves -local function is_solid_not_tree(node) - if not node or node.name == "air" or node.name == "ignore" or node.name == "mcl_villages:no_paths" then return false end - local meta = minetest.registered_items[node.name] - local groups = meta and meta.groups - return meta and meta.walkable and not (groups and (groups["deco_block"] or groups["tree"] or groups["leaves"] or groups["plant"])) -end --- check if a node is walkable (solid), but not tree/leaves or buildungs -local function is_solid_not_tree_or_building(node) - if not node or node.name == "air" or node.name == "ignore" or node.name == "mcl_villages:no_paths" then return false end - local meta = minetest.registered_items[node.name] - local groups = meta and meta.groups - return meta and meta.walkable and not (groups and (groups["deco_block"] or groups["tree"] or groups["leaves"] or groups["plant"] or groups["building_block"])) -end --- check if a node is tree -local function is_tree(node) - if not node or node.name == "air" or node.name == "ignore" or node.name == "mcl_villages:no_paths" then return false end - local meta = minetest.registered_items[node.name] - local groups = meta and meta.groups - return groups and (groups["tree"] or groups["leaves"]) -end --- replace a non-solid node, optionally also "additional" -local function make_solid(lvm, cp, with, additional) - local cur = lvm:get_node_at(cp) - if not is_solid_not_tree(cur) or (additional and cur.name == additional.name) then - lvm:set_node_at(cp, with) - end -end -local function excavate(lvm,xi,yi,zi,pr,keep_trees) - local pos, n, c = vector.new(xi,yi,zi), nil, 0 - local node = lvm:get_node_at(pos) - if is_air(node) then return false end -- already empty, nothing to do - if keep_trees and is_tree(node) then return false end - pos.y = pos.y-1 - if not is_air(lvm:get_node_at(pos)) then return false end -- below is solid, do not clear above anymore - -- count empty nodes below otherwise - for x = xi-1,xi+1 do - for z = zi-1,zi+1 do - pos.x, pos.z = x, z - if is_air(lvm:get_node_at(pos)) then c = c + 1 end - end - end - -- try to completely remove trees overhead - -- stop randomly depending on fill, to narrow down the caves - if not keep_trees and not is_tree(node) and (pr:next(0,1e9)/1e9)^2 > c/9.1 then return false end - lvm:set_node_at(vector.new(xi, yi, zi), AIR) - return true -- modified -end -function mcl_structures.clearance(lvm, px, py, pz, sx, sy, sz, corners, surface_mat, pr) - corners = corners or 0 - local wx2, wz2 = max(sx - corners, 1)^-2*2, max(sz - corners, 1)^-2*2 - local cx, cz = px + sx * 0.5 - 0.5, pz + sz * 0.5 - 0.5 - -- excavate the needed volume and some headroom - for xi = px,px+sx-1 do - local dx2 = (cx-xi)^2*wx2 - for zi = pz,pz+sz-1 do - local dz2 = (cz-zi)^2*wz2 - if dx2+dz2 <= 1 then - lvm:set_node_at(vector.new(xi, py, zi), AIR) - local n = lvm:get_node_at(vector.new(xi, py-1, zi)) - if n and n.name ~= surface_mat.name and is_solid_not_tree_or_building(n) then - lvm:set_node_at(vector.new(xi, py-1, zi), surface_mat) - end - -- py+1 to py+4 are filled wider below, this is the top of the building only - for yi = py+5,py+sy do - lvm:set_node_at(vector.new(xi, yi, zi), AIR) - end - end - end - end - -- slightly widen the cave above, to make easier to enter for mobs - for xi = px-1,px+sx do - local dx2 = max(abs(cx-xi)-1,0)^2*wx2 - for zi = pz-1,pz+sz do - local dz2 = max(abs(cz-zi)-1,0)^2*wz2 - if dx2+dz2 <= 1 then - for yi = py+1,py+4 do - lvm:set_node_at(vector.new(xi, yi, zi), AIR) - end - local n = lvm:get_node_at(vector.new(xi, py, zi)) - for yi = py,py-1,-1 do - local n = lvm:get_node_at(vector.new(xi, yi, zi)) - if is_tree(n) then - lvm:set_node_at(vector.new(xi, yi, zi), AIR) - else - if n and n.name ~= surface_mat.name and is_solid_not_tree_or_building(n) then - lvm:set_node_at(vector.new(xi, yi, zi), surface_mat) - end - break - end - end - end - end - end - -- some extra gaps for entry - for xi = px-2,px+sx+1 do - local dx2 = max(abs(cx-xi)-2,0)^2*wx2 - for zi = pz-2,pz+sz+1 do - local dz2 = max(abs(cz-zi)-2,0)^2*wz2 - if dx2+dz2 <= 1 and pr:next(1,4) == 1 then - for yi = py+2,py+4 do - lvm:set_node_at(vector.new(xi, yi, zi), AIR) - end - local n = lvm:get_node_at(vector.new(xi, py+1, zi)) - for yi = py+1,py-1,-1 do - local n = lvm:get_node_at(vector.new(xi, yi, zi)) - if is_tree(n) then - lvm:set_node_at(vector.new(xi, yi, zi), AIR) - else - if n and n.name ~= surface_mat.name and is_solid_not_tree_or_building(n) then - lvm:set_node_at(vector.new(xi, py+1, zi), surface_mat) - end - break - end - end - end - end - end - -- cave some additional area overhead, try to make it interesting though - local min_clear, max_clear = sy+5, sy*2+5 -- FIXME: make parameters - for yi = py+2,py+max_clear do - local dy2 = (py-yi)^2*0.025 - local active = false - for xi = px-2,px+sx+1 do - local dx2 = max(abs(cx-xi)-2,0)^2*wx2 - for zi = pz-2,pz+sz+1 do - local dz2 = max(abs(cz-zi)-2,0)^2*wz2 - local keep_trees = (xi=px+sx) or (zi=pz+sz) -- TODO make configurable? - if dx2+dz2+dy2 <= 1 and excavate(lvm,xi,yi,zi,pr,keep_trees) then active = true end - end - end - if not active and yi > py+min_clear then break end - end -end --- TODO: allow controlling the random depth? --- TODO: add support for dust_mat (snow) -local function grow_foundation(lvm,xi,yi,zi,pr,surface_mat,platform_mat,stone_mat) - local pos, n, c = vector.new(xi,yi,zi), nil, 0 - if is_solid_not_tree(lvm:get_node_at(pos)) then return false end -- already solid, nothing to do - pos.y = pos.y+1 - local cur = lvm:get_node_at(pos) - if not is_solid_not_tree(cur) then return false end -- above is empty, do not fill below - if cur and cur.name and cur.name ~= surface_mat.name then platform_mat = cur end - if pr:next(1,5) == 5 then -- randomly switch to stone sometimes - platform_mat = stone_mat - end - -- count solid nodes above otherwise - for x = xi-1,xi+1 do - for z = zi-1,zi+1 do - pos.x, pos.z = x, z - if is_solid_not_tree(lvm:get_node_at(pos)) then c = c + 1 end - end - end - -- stop randomly depending on fill, to narrow down the foundation - if (pr:next(0,1e9)/1e9)^2 > c/9.1 then return false end - lvm:set_node_at(vector.new(xi, yi, zi), platform_mat) - return true -- modified -end --- generate a foundations around px,py,pz with size sx,sy,sz (sy < 0) --- TODO: add support for dust_mat (snow) --- Rounding: we model an ellipse. At zero rounding, we want the line go through the corner, at sx/2, sz/2. --- For this, we need to make ellipse sized 2a=sqrt(2)*sx, 2b=sqrt(2)*sz, --- Which yields a = sx/sqrt(2), b=sz/sqrt(2) and a^2=sx^2*0.5, b^2=sz^2*0.5 --- To get corners, we decrease a and b by approx. corners each --- The ellipse condition dx^2/a^2+dz^2/b^2 <= 1 then yields dx^2/(sx^2*0.5) + dz^2/(sz^2*0.5) <= 1 --- We use wx2=(sx^2)^-2*2, wz2=(sz^2)^-2*2 and then dx^2*wx2+dz^2*wz2 <= 1 -function mcl_structures.foundation(lvm, px, py, pz, sx, depth, sz, corners, surface_mat, platform_mat, stone_mat, pr) - corners = corners or 0 - local wx2, wz2 = max(sx - corners, 1)^-2*2, max(sz - corners, 1)^-2*2 - local cx, cz = px + sx * 0.5 - 0.5, pz + sz * 0.5 - 0.5 - -- generate a baseplate - for xi = px,px+sx-1 do - local dx2 = (cx-xi)^2*wx2 - for zi = pz,pz+sz-1 do - local dz2 = (cz-zi)^2*wz2 - if dx2+dz2 <= 1 then - lvm:set_node_at(vector.new(xi, py, zi), surface_mat) - make_solid(lvm, vector.new(xi, py-1, zi), platform_mat) - end - end - end - -- slightly widen the baseplate below, to make easier to enter for mobs - if corners and corners > 0 then - for xi = px-1,px+sx do - local dx2 = max(abs(cx-xi)-1,0)^2*wx2 - -- TODO: compute the z value ranges directly? - for zi = pz-1,pz+sz do - local dz2 = max(abs(cz-zi)-1,0)^2*wz2 - if dx2+dz2 <= 1 then - make_solid(lvm, vector.new(xi, py-1, zi), surface_mat) - end - end - end - else - for xi = px+1,px+sx-1-1 do - make_solid(lvm, vector.new(xi, py-1, pz-1), surface_mat, platform_mat) - make_solid(lvm, vector.new(xi, py-1, pz), platform_mat) - make_solid(lvm, vector.new(xi, py-1, pz+sz-1), platform_mat) - make_solid(lvm, vector.new(xi, py-1, pz+sz), surface_mat, platform_mat) - end - for zi = pz+1,pz+sz-1-1 do - make_solid(lvm, vector.new(px-1, py-1, zi), surface_mat, platform_mat) - make_solid(lvm, vector.new(px, py-1, zi), platform_mat) - make_solid(lvm, vector.new(px+sx-1, py-1, zi), platform_mat) - make_solid(lvm, vector.new(px+sx, py-1, zi), surface_mat, platform_mat) - end - -- make some additional steps, along both x sides - for xi = px+1,px+sx-2 do - local cp = vector.new(xi, py-3, pz-1) - if is_solid_not_tree(lvm:get_node_at(cp)) then - cp = vector.new(xi, py-2, pz-1) - make_solid(lvm, cp, surface_mat, platform_mat) - cp.z = pz-2 - make_solid(lvm, cp, surface_mat, platform_mat) - end - local cp = vector.new(xi, py-3, pz+sz) - if is_solid_not_tree(lvm:get_node_at(cp)) then - cp = vector.new(xi, py-2, pz+sz) - make_solid(lvm, cp, surface_mat, platform_mat) - cp.z = pz + sz + 1 - make_solid(lvm, cp, surface_mat, platform_mat) - end - end - -- make some additional steps, along both z sides - for zi = pz+1,pz+sz-2 do - local cp = vector.new(px-1, py-3, zi) - if is_solid_not_tree(lvm:get_node_at(cp)) then - cp = vector.new(px-1, py-2, zi) - make_solid(lvm, cp, surface_mat, platform_mat) - cp.x = px-2 - make_solid(lvm, cp, surface_mat, platform_mat) - end - local cp = vector.new(px+sx, py-3, zi) - if is_solid_not_tree(lvm:get_node_at(cp)) then - cp = vector.new(px+sx, py-2, zi) - make_solid(lvm, cp, surface_mat, platform_mat) - cp.x = px+sx+1 - make_solid(lvm, cp, surface_mat, platform_mat) - end - end - end - -- construct additional baseplate below, also try to make it interesting - for yi = py-2,py-20,-1 do - local dy2 = (py-yi)^2*0.025 - local active = false - for xi = px-1,px+sx do - local dx2 = max(abs(cx-xi)-1,0)^2*wx2 - for zi = pz-1,pz+sz do - local dz2 = max(abs(cz-zi)-1,0)^2*wz2 - if dx2+dy2+dz2 <= 1 then - if grow_foundation(lvm,xi,yi,zi,pr,surface_mat,platform_mat,stone_mat) then active = true end - end - end - end - if not active and yi < py + depth then break end - end -end --- return position and material of surface -function mcl_structures.find_ground(lvm, pos) - if not pos then return nil, nil end - pos = vector.copy(pos) - local cur = lvm:get_node_at(pos) - if not cur or cur.name == "ignore" then - local e1, e2 = lvm:get_emerged_area() - minetest.log("warning","find_ground with invalid position (outside of emerged area?) at "..minetest.pos_to_string(pos)..": "..tostring(cur and cur.name).." area: "..minetest.pos_to_string(e1).." "..minetest.pos_to_string(e2)) - return nil - end - if is_solid_not_tree(cur) then -- find up - local prev = cur - while true do - pos.y = pos.y + 1 - local cur = lvm:get_node_at(pos) - if not cur or cur.name == "ignore" then - minetest.log("action", "No ground, "..tostring(cur and cur.name).." over "..tostring(prev and prev.name).." at "..minetest.pos_to_string(pos)) - return nil - end - if not is_solid_not_tree(cur) then - pos.y = pos.y - 1 - return pos, prev - end - prev = cur - end - else -- find down - while true do - pos.y = pos.y - 1 - local prev = cur - local cur = lvm:get_node_at(pos) - if not cur or cur.name == "ignore" then - minetest.log("action", "No ground, "..tostring(cur and cur.name).." below "..tostring(prev and prev.name).." at "..minetest.pos_to_string(pos)) - return nil - end - if is_solid_not_tree(cur) then - return pos, cur - end - end - end -end --- find suitable height for a structure of this size --- @param lvm VoxelManip: to read data --- @param cpos vector: center --- @param size vector: area size --- @param tolerance number: maximum height difference allowed, default 8 --- @return position, surface material -function mcl_structures.find_level(lvm, cpos, size, tolerance) - local cpos, surface_material = mcl_structures.find_ground(lvm, cpos) - if not cpos then return nil, nil end - local ys = {cpos.y} - local pos = vector.offset(cpos, -math.floor((size.x-1)/2), 0, -math.floor((size.z-1)/2)) -- top left - local pos_c = mcl_structures.find_ground(lvm, pos) - if pos_c then table.insert(ys, pos_c.y) end - local pos_c = mcl_structures.find_ground(lvm, vector.offset(pos, size.x-1, 0, 0)) - if pos_c then table.insert(ys, pos_c.y) end - local pos_c = mcl_structures.find_ground(lvm, vector.offset(pos, 0, 0, size.z-1)) - if pos_c then table.insert(ys, pos_c.y) end - local pos_c = mcl_structures.find_ground(lvm, vector.offset(pos, size.x-1, 0, size.z-1)) - if pos_c then table.insert(ys, pos_c.y) end - table.sort(ys) - -- well supported base, not too uneven? - if #ys <= 4 or math.max(ys[#ys-1]-ys[1], ys[#ys]-ys[2]) > (tolerance or 8) then - minetest.log("action", "[mcl_structures] ground too uneven: "..#ys.." positions, trimmed difference "..(#ys < 2 and "" or math.max(ys[#ys-1]-ys[1], ys[#ys]-ys[2]))) - return nil, nil - end - cpos.y = math.round(0.5*(ys[math.floor(#ys * 0.5)] + ys[math.ceil(#ys * 0.5)])) + 1 -- median, rounded, over surface - return cpos, surface_material -end - diff --git a/mods/MAPGEN/mcl_structures/geode.lua b/mods/MAPGEN/mcl_structures/geode.lua index 56453ee60..2a162c597 100644 --- a/mods/MAPGEN/mcl_structures/geode.lua +++ b/mods/MAPGEN/mcl_structures/geode.lua @@ -75,7 +75,7 @@ local function makegeode(pos,def,pr) return true end -mcl_structures.register_structure("geode",{ +vl_structures.register_structure("geode",{ place_on = {"group:material_stone"}, noise_params = { offset = 0, diff --git a/mods/MAPGEN/mcl_structures/igloo.lua b/mods/MAPGEN/mcl_structures/igloo.lua index b459cd329..06c18414c 100644 --- a/mods/MAPGEN/mcl_structures/igloo.lua +++ b/mods/MAPGEN/mcl_structures/igloo.lua @@ -19,112 +19,90 @@ local function spawn_mobs(p1,p2,vi,zv) end end -local function generate_igloo_basement(pos, orientation, loot, pr) -end - -local function generate_igloo(pos, def, pr) - local path = modpath.."/schematics/mcl_structures_igloo_top.mts" - local rotation = tostring(pr:next(0,3)*90) - -- TODO: ymin, ymax - mcl_structures.place_schematic(pos, -2, nil, nil, path, rotation, nil, true, nil, {padding=0, corners=2}, pr, function(p1, p2) - mcl_structures.construct_nodes(p1, p2, {"mcl_furnaces:furnace","mcl_books:bookshelf"}) - -- Place igloo basement with 50% chance - local r = 1--pr:next(1,2) - if r == 1 then - -- Select basement depth - local dim = mcl_worlds.pos_to_dimension(pos) - local buffer - if dim == "nether" then - buffer = pos.y - (mcl_vars.mg_lava_nether_max + 10) - elseif dim == "end" then - buffer = pos.y - (mcl_vars.mg_end_min + 1) - elseif dim == "overworld" then - buffer = pos.y - (mcl_vars.mg_lava_overworld_max + 10) - else - return true - end - if buffer <= 9 then return true end - local depth = pr:next(9, buffer) - local bpos = vector.new(pos.x, pos.y-depth, pos.z) - -- trapdoor position and orientation - local tpos, dir, tdir - if rotation == "0" then - dir = vector.new(-1, 0, 0) - tdir = vector.new(1, 0, 0) - tpos = vector.new(pos.x+7, pos.y, pos.z+3) - elseif rotation == "90" then - dir = vector.new(0, 0, -1) - tdir = vector.new(0, 0, -1) - tpos = vector.new(pos.x+3, pos.y, pos.z+1) - elseif rotation == "180" then - dir = vector.new(1, 0, 0) - tdir = vector.new(-1, 0, 0) - tpos = vector.new(pos.x+1, pos.y, pos.z+3) - elseif rotation == "270" then - dir = vector.new(0, 0, 1) - tdir = vector.new(0, 0, 1) - tpos = vector.new(pos.x+3, pos.y, pos.z+7) - else - minetest.log("bad rotation: "..tostring(rotation)) - return false - end - local function set_brick(pos) - local c = pr:next(1, 3) -- cracked chance - local m = pr:next(1, 10) -- chance for monster egg - local brick - if m == 1 then - brick = (c == 1 and "mcl_monster_eggs:monster_egg_stonebrickcracked") or "mcl_monster_eggs:monster_egg_stonebrick" - else - brick = (c == 1 and "mcl_core:stonebrickcracked") or "mcl_core:stonebrick" - end - minetest.set_node(pos, {name=brick}) - end - local real_depth = 0 - -- Check how deep we can actually dig - for y=1, depth-5 do - real_depth = real_depth + 1 - local node = minetest.get_node(vector.new(tpos.x, tpos.y-y, tpos.z)) - local def = node and minetest.registered_nodes[node.name] - if not (def and def.walkable and def.liquidtype == "none" and def.is_ground_content) then - bpos.y = tpos.y-y+1 - break - end - end - if real_depth <= 6 then - minetest.log("not deep enough") - return false - end - local path = modpath.."/schematics/mcl_structures_igloo_basement.mts" - mcl_structures.place_schematic(bpos, 0, nil, nil, path, rotation, nil, true, nil, nil, pr, function(p1, p2) - -- Generate ladder to basement - local ladder = {name="mcl_core:ladder", param2=minetest.dir_to_wallmounted(tdir)} - minetest.set_node(tpos, {name="mcl_doors:trapdoor", param2=20+minetest.dir_to_facedir(dir)}) -- TODO: more reliable param2 - for y=1, real_depth do - set_brick(vector.new(tpos.x-1, tpos.y-y, tpos.z )) - set_brick(vector.new(tpos.x+1, tpos.y-y, tpos.z )) - set_brick(vector.new(tpos.x , tpos.y-y, tpos.z-1)) - set_brick(vector.new(tpos.x , tpos.y-y, tpos.z+1)) - minetest.set_node(vector.new(tpos.x, tpos.y-y, tpos.z), ladder) - end - mcl_structures.fill_chests(p1,p2,def.loot,pr) - mcl_structures.construct_nodes(p1,p2,{"mcl_brewing:stand_000","mcl_books:bookshelf"}) - spawn_mobs(p1,p2) - end) +local function igloo_callback(cpos,def,pr,p1,p2,size,rotation) + vl_structures.construct_nodes(p1, p2, {"mcl_furnaces:furnace","mcl_books:bookshelf"}) + -- Place igloo basement with 50% chance + if pr:next(1,2) == 1 then return end + local pos = p1 -- we use top left as reference + -- Select basement depth + local maxdepth = pos.y - (mcl_vars.mg_lava_overworld_max + 10) + if maxdepth <= 9 then return true end + local depth = pr:next(9, maxdepth) + -- trapdoor position and orientation + local tpos, dir, tdir + if rotation == "0" then + dir = vector.new(-1, 0, 0) + tdir = vector.new(1, 0, 0) + tpos = vector.new(pos.x+7, pos.y, pos.z+3) + elseif rotation == "90" then + dir = vector.new(0, 0, -1) + tdir = vector.new(0, 0, -1) + tpos = vector.new(pos.x+3, pos.y, pos.z+1) + elseif rotation == "180" then + dir = vector.new(1, 0, 0) + tdir = vector.new(-1, 0, 0) + tpos = vector.new(pos.x+1, pos.y, pos.z+3) + elseif rotation == "270" then + dir = vector.new(0, 0, 1) + tdir = vector.new(0, 0, 1) + tpos = vector.new(pos.x+3, pos.y, pos.z+7) + else + minetest.log("bad rotation: "..tostring(rotation)) + return false + end + local function set_brick(pos) + local c = pr:next(1, 3) -- cracked chance + local m = pr:next(1, 10) -- chance for monster egg + local brick + if m == 1 then + brick = (c == 1 and "mcl_monster_eggs:monster_egg_stonebrickcracked") or "mcl_monster_eggs:monster_egg_stonebrick" + else + brick = (c == 1 and "mcl_core:stonebrickcracked") or "mcl_core:stonebrick" end + minetest.set_node(pos, {name=brick}) + end + local real_depth = 2 + -- Check how deep we can actually dig + for y=pos.y-real_depth, pos.y-depth, -1 do + real_depth = real_depth + 1 + local node = minetest.get_node(vector.new(tpos.x, y, tpos.z)) + local def = node and minetest.registered_nodes[node.name] + if not (def and def.walkable and def.liquidtype == "none" and def.is_ground_content) then break end + end + local bpos = vector.new(cpos.x, pos.y-real_depth+1, cpos.z) + if real_depth <= 6 then + minetest.log("action", "Ground not deep enough for igloo basement: "..real_depth) + return false + end + local path = modpath.."/schematics/mcl_structures_igloo_basement.mts" + local prepare = { tolerance = -1, foundation = false, clear = false } + vl_structures.place_schematic(bpos, -1, nil, nil, path, rotation, nil, true, nil, prepare, pr, function(p1, p2) + -- Generate ladder to basement + local ladder = {name="mcl_core:ladder", param2=minetest.dir_to_wallmounted(tdir)} + minetest.set_node(tpos, {name="mcl_doors:trapdoor", param2=20+minetest.dir_to_facedir(dir)}) -- TODO: more reliable param2 + for y = tpos.y-1, bpos.y+4, -1 do + set_brick(vector.new(tpos.x-1, y, tpos.z )) + set_brick(vector.new(tpos.x+1, y, tpos.z )) + set_brick(vector.new(tpos.x , y, tpos.z-1)) + set_brick(vector.new(tpos.x , y, tpos.z+1)) + minetest.set_node(vector.new(tpos.x, y, tpos.z), ladder) + end + vl_structures.fill_chests(p1,p2,def.loot,pr) + vl_structures.construct_nodes(p1,p2,{"mcl_brewing:stand_000","mcl_books:bookshelf"}) + spawn_mobs(p1,p2) end) - return true end -mcl_structures.register_structure("igloo",{ +vl_structures.register_structure("igloo",{ + filenames = { modpath.."/schematics/mcl_structures_igloo_top.mts" }, place_on = {"mcl_core:snowblock","mcl_core:snow","group:grass_block_snow"}, - sidelen = 16, chunk_probability = 7, - solid_ground = true, + prepare = { padding = 1, corners = 1, foundation = -6, clear_top=-1 }, y_max = mcl_vars.mg_overworld_max, y_min = 0, y_offset = -2, biomes = { "ColdTaiga", "IcePlainsSpikes", "IcePlains" }, - place_func = generate_igloo, + after_place = igloo_callback, loot = { ["mcl_chests:chest_small"] = {{ stacks_min = 1, diff --git a/mods/MAPGEN/mcl_structures/init.lua b/mods/MAPGEN/mcl_structures/init.lua index 1f43a71bf..c7e305277 100644 --- a/mods/MAPGEN/mcl_structures/init.lua +++ b/mods/MAPGEN/mcl_structures/init.lua @@ -1,62 +1,34 @@ local modname = minetest.get_current_modname() local S = minetest.get_translator(modname) local modpath = minetest.get_modpath(modname) - mcl_structures = {} -dofile(modpath.."/api.lua") -dofile(modpath.."/foundation.lua") -dofile(modpath.."/shipwrecks.lua") +-- some legacy API adapters +mcl_structures.is_disabled = vl_structures.is_disabled +mcl_structures.init_node_construct = vl_structures.init_node_construct +mcl_structures.construct_nodes = vl_structures.construct_nodes +mcl_structures.fill_chests = vl_structures.fill_chests +mcl_structures.spawn_mobs = vl_structures.spawn_mobs + +-- TODO: provide more legacy adapters that translate parameters? + dofile(modpath.."/desert_temple.lua") +dofile(modpath.."/desert_well.lua") +dofile(modpath.."/end_city.lua") +dofile(modpath.."/end_spawn.lua") +dofile(modpath.."/fossil.lua") +dofile(modpath.."/geode.lua") +dofile(modpath.."/igloo.lua") dofile(modpath.."/jungle_temple.lua") dofile(modpath.."/ocean_ruins.lua") -dofile(modpath.."/witch_hut.lua") -dofile(modpath.."/igloo.lua") -dofile(modpath.."/woodland_mansion.lua") -dofile(modpath.."/ruined_portal.lua") -dofile(modpath.."/geode.lua") +dofile(modpath.."/ocean_temple.lua") dofile(modpath.."/pillager_outpost.lua") -dofile(modpath.."/end_spawn.lua") -dofile(modpath.."/end_city.lua") +dofile(modpath.."/ruined_portal.lua") +dofile(modpath.."/shipwrecks.lua") +dofile(modpath.."/witch_hut.lua") +dofile(modpath.."/woodland_mansion.lua") - -mcl_structures.register_structure("desert_well",{ - place_on = {"group:sand"}, - flags = "place_center_x, place_center_z", - not_near = { "desert_temple_new" }, - solid_ground = true, - sidelen = 4, - chunk_probability = 15, - y_max = mcl_vars.mg_overworld_max, - y_min = 1, - y_offset = -2, - biomes = { "Desert" }, - filenames = { modpath.."/schematics/mcl_structures_desert_well.mts" }, -}) - -mcl_structures.register_structure("fossil",{ - place_on = {"group:material_stone","group:sand"}, - flags = "place_center_x, place_center_z", - solid_ground = true, - sidelen = 13, - chunk_probability = 25, - y_offset = function(pr) return ( pr:next(1,16) * -1 ) -16 end, - y_max = 15, - y_min = mcl_vars.mg_overworld_min + 35, - biomes = { "Desert" }, - filenames = { - modpath.."/schematics/mcl_structures_fossil_skull_1.mts", -- 4×5×5 - modpath.."/schematics/mcl_structures_fossil_skull_2.mts", -- 5×5×5 - modpath.."/schematics/mcl_structures_fossil_skull_3.mts", -- 5×5×7 - modpath.."/schematics/mcl_structures_fossil_skull_4.mts", -- 7×5×5 - modpath.."/schematics/mcl_structures_fossil_spine_1.mts", -- 3×3×13 - modpath.."/schematics/mcl_structures_fossil_spine_2.mts", -- 5×4×13 - modpath.."/schematics/mcl_structures_fossil_spine_3.mts", -- 7×4×13 - modpath.."/schematics/mcl_structures_fossil_spine_4.mts", -- 8×5×13 - }, -}) - -mcl_structures.register_structure("boulder",{ +vl_structures.register_structure("boulder",{ filenames = { modpath.."/schematics/mcl_structures_boulder_small.mts", modpath.."/schematics/mcl_structures_boulder_small.mts", @@ -66,70 +38,11 @@ mcl_structures.register_structure("boulder",{ }, },true) --is spawned as a normal decoration. this is just for /spawnstruct -mcl_structures.register_structure("ice_spike_small",{ - filenames = { modpath.."/schematics/mcl_structures_ice_spike_small.mts" }, -},true) --is spawned as a normal decoration. this is just for /spawnstruct -mcl_structures.register_structure("ice_spike_large",{ - sidelen = 6, - filenames = { modpath.."/schematics/mcl_structures_ice_spike_large.mts" }, +vl_structures.register_structure("ice_spike_small",{ + filenames = { modpath.."/schematics/mcl_structures_ice_spike_small.mts" }, },true) --is spawned as a normal decoration. this is just for /spawnstruct --- Debug command -local function dir_to_rotation(dir) - local ax, az = math.abs(dir.x), math.abs(dir.z) - if ax > az then - if dir.x < 0 then - return "270" - end - return "90" - end - if dir.z < 0 then - return "180" - end - return "0" -end +vl_structures.register_structure("ice_spike_large",{ + filenames = { modpath.."/schematics/mcl_structures_ice_spike_large.mts" }, +},true) --is spawned as a normal decoration. this is just for /spawnstruct -minetest.register_chatcommand("spawnstruct", { - params = "dungeon", - description = S("Generate a pre-defined structure near your position."), - privs = {debug = true}, - func = function(name, param) - local player = minetest.get_player_by_name(name) - if not player then return end - local pos = player:get_pos() - if not pos then return end - pos = vector.round(pos) - local dir = minetest.yaw_to_dir(player:get_look_horizontal()) - local rot = dir_to_rotation(dir) - local seed = minetest.hash_node_position(pos) - local pr = PcgRandom(seed) - local errord = false - local message = S("Structure placed.") - if param == "dungeon" and mcl_dungeons and mcl_dungeons.spawn_dungeon then - mcl_dungeons.spawn_dungeon(pos, rot, pr) - elseif param == "" then - message = S("Error: No structure type given. Please use “/spawnstruct ”.") - errord = true - else - for n,d in pairs(mcl_structures.registered_structures) do - if n == param then - mcl_structures.place_structure(pos,d,pr,seed,rot) - return true,message - end - end - message = S("Error: Unknown structure type. Please use “/spawnstruct ”.") - errord = true - end - minetest.chat_send_player(name, message) - if errord then - minetest.chat_send_player(name, S("Use /help spawnstruct to see a list of available types.")) - end - end -}) -minetest.register_on_mods_loaded(function() - local p = "" - for n,_ in pairs(mcl_structures.registered_structures) do - p = p .. " | "..n - end - minetest.registered_chatcommands["spawnstruct"].params = minetest.registered_chatcommands["spawnstruct"].params .. p -end) diff --git a/mods/MAPGEN/mcl_structures/jungle_temple.lua b/mods/MAPGEN/mcl_structures/jungle_temple.lua index 9073d24f4..c80d9401c 100644 --- a/mods/MAPGEN/mcl_structures/jungle_temple.lua +++ b/mods/MAPGEN/mcl_structures/jungle_temple.lua @@ -2,16 +2,14 @@ local modname = minetest.get_current_modname() local S = minetest.get_translator(modname) local modpath = minetest.get_modpath(modname) -mcl_structures.register_structure("jungle_temple",{ +vl_structures.register_structure("jungle_temple",{ place_on = {"group:grass_block","group:dirt","mcl_core:dirt_with_grass"}, flags = "place_center_x, place_center_z", - solid_ground = true, y_offset = function(pr) return pr:next(-3,0) -5 end, chunk_probability = 5, y_max = mcl_vars.mg_overworld_max, y_min = 1, biomes = { "Jungle" }, - sidelen = 18, filenames = { modpath.."/schematics/mcl_structures_jungle_temple.mts", modpath.."/schematics/mcl_structures_jungle_temple_nice.mts", diff --git a/mods/MAPGEN/mcl_structures/mod.conf b/mods/MAPGEN/mcl_structures/mod.conf index 7c632b131..b0c3dce99 100644 --- a/mods/MAPGEN/mcl_structures/mod.conf +++ b/mods/MAPGEN/mcl_structures/mod.conf @@ -1,4 +1,4 @@ name = mcl_structures author = Wuzzy, cora, kno10 description = Structure placement for MCL2 -depends = mcl_init, mcl_util, mcl_loot +depends = mcl_init, mcl_util, mcl_loot, vl_terraforming, vl_structures diff --git a/mods/MAPGEN/mcl_structures/ocean_ruins.lua b/mods/MAPGEN/mcl_structures/ocean_ruins.lua index df94f540d..cd22803df 100644 --- a/mods/MAPGEN/mcl_structures/ocean_ruins.lua +++ b/mods/MAPGEN/mcl_structures/ocean_ruins.lua @@ -75,13 +75,11 @@ local cold = { spawn_by = {"mcl_core:water_source"}, num_spawn_by = 2, flags = "place_center_x, place_center_z, force_placement", - solid_ground = true, y_offset = -1, y_min = mcl_vars.mg_overworld_min, y_max = -2, biomes = cold_oceans, chunk_probability = 10, - sidelen = 20, filenames = { modpath.."/schematics/mcl_structures_ocean_ruins_cold_1.mts", modpath.."/schematics/mcl_structures_ocean_ruins_cold_2.mts", @@ -126,5 +124,5 @@ warm.filenames = { modpath.."/schematics/mcl_structures_ocean_ruins_warm_4.mts", } -mcl_structures.register_structure("cold_ocean_ruins",cold) -mcl_structures.register_structure("warm_ocean_ruins",warm) +vl_structures.register_structure("cold_ocean_ruins",cold) +vl_structures.register_structure("warm_ocean_ruins",warm) diff --git a/mods/MAPGEN/mcl_structures/ocean_temple.lua b/mods/MAPGEN/mcl_structures/ocean_temple.lua new file mode 100644 index 000000000..85ed872b8 --- /dev/null +++ b/mods/MAPGEN/mcl_structures/ocean_temple.lua @@ -0,0 +1,157 @@ +local modname = minetest.get_current_modname() +local modpath = minetest.get_modpath(modname) +--local S = minetest.get_translator(modname) +local water_level = minetest.get_mapgen_setting("water_level") + +local spawnon = { "mcl_stairs:slab_prismarine_dark" } + +local ocean_biomes = { + "RoofedForest_ocean", + "JungleEdgeM_ocean", + "BirchForestM_ocean", + "BirchForest_ocean", + "IcePlains_deep_ocean", + "Jungle_deep_ocean", + "Savanna_ocean", + "MesaPlateauF_ocean", + "ExtremeHillsM_deep_ocean", + "Savanna_deep_ocean", + "SunflowerPlains_ocean", + "Swampland_deep_ocean", + "Swampland_ocean", + "MegaSpruceTaiga_deep_ocean", + "ExtremeHillsM_ocean", + "JungleEdgeM_deep_ocean", + "SunflowerPlains_deep_ocean", + "BirchForest_deep_ocean", + "IcePlainsSpikes_ocean", + "Mesa_ocean", + "StoneBeach_ocean", + "Plains_deep_ocean", + "JungleEdge_deep_ocean", + "SavannaM_deep_ocean", + "Desert_deep_ocean", + "Mesa_deep_ocean", + "ColdTaiga_deep_ocean", + "Plains_ocean", + "MesaPlateauFM_ocean", + "Forest_deep_ocean", + "JungleM_deep_ocean", + "FlowerForest_deep_ocean", + "MushroomIsland_ocean", + "MegaTaiga_ocean", + "StoneBeach_deep_ocean", + "IcePlainsSpikes_deep_ocean", + "ColdTaiga_ocean", + "SavannaM_ocean", + "MesaPlateauF_deep_ocean", + "MesaBryce_deep_ocean", + "ExtremeHills+_deep_ocean", + "ExtremeHills_ocean", + "MushroomIsland_deep_ocean", + "Forest_ocean", + "MegaTaiga_deep_ocean", + "JungleEdge_ocean", + "MesaBryce_ocean", + "MegaSpruceTaiga_ocean", + "ExtremeHills+_ocean", + "Jungle_ocean", + "RoofedForest_deep_ocean", + "IcePlains_ocean", + "FlowerForest_ocean", + "ExtremeHills_deep_ocean", + "MesaPlateauFM_deep_ocean", + "Desert_ocean", + "Taiga_ocean", + "BirchForestM_deep_ocean", + "Taiga_deep_ocean", + "JungleM_ocean" +} + +vl_structures.register_structure("ocean_temple",{ + place_on = {"group:sand","mcl_core:gravel"}, + spawn_by = {"group:water"}, + num_spawn_by = 4, + noise_params = { + offset = 0, + scale = 0.0000122, + spread = {x = 250, y = 250, z = 250}, + seed = 32345, + octaves = 3, + persist = 0.001, + flags = "absvalue", + }, + force_place = true, + biomes = ocean_biomes, + y_max = water_level-4, + y_min = mcl_vars.mg_overworld_min, + filenames = { + modpath .. "/schematics/mcl_structures_ocean_temple.mts", + modpath .. "/schematics/mcl_structures_ocean_temple_2.mts", + }, + prepare = { tolerance = -1, clear = false, foundation = false }, + y_offset = function(pr) return pr:next(-2,0) end, + after_place = function(p,def,pr,p1,p2) + vl_structures.spawn_mobs("mobs_mc:guardian",spawnon,p1,p2,pr,5,true) + vl_structures.spawn_mobs("mobs_mc:guardian_elder",spawnon,p1,p2,pr,1,true) + vl_structures.construct_nodes(p1,p2,{"group:wall"}) + end, + loot = { + ["mcl_chests:chest_small"] = { + { + stacks_min = 3, + stacks_max = 10, + items = { + { itemstring = "mcl_sus_stew:stew", weight = 10, amount_min = 1, amount_max = 1 }, + { itemstring = "mcl_core:paper", weight = 8, amount_min = 1, amount_max = 12 }, + { itemstring = "mcl_fishing:fish_raw", weight = 5, amount_min = 8, amount_max = 21 }, + { itemstring = "mcl_fishing:salmon_raw", weight = 7, amount_min = 4, amount_max = 8 }, + { itemstring = "mcl_tnt:tnt", weight = 1, amount_min = 1, amount_max = 2 }, + } + }, + { + stacks_min = 2, + stacks_max = 6, + items = { + { itemstring = "mcl_core:iron_ingot", weight = 10, amount_min = 1, amount_max = 5 }, + { itemstring = "mcl_core:goldblock", weight = 1, amount_min = 1, amount_max = 2 }, + { itemstring = "mcl_experience:bottle", weight = 5, amount_min = 1, amount_max = 1 }, + { itemstring = "mcl_core:diamond", weight = 5, amount_min = 1, amount_max = 1 }, + { itemstring = "mcl_fishing:fishing_rod", weight = 1, amount_min = 1, amount_max = 1 }, + } + }, + { + stacks_min = 4, + stacks_max = 4, + items = { + --{ itemstring = "FIXME TREASURE MAP", weight = 8, amount_min = 1, amount_max = 5 }, + { itemstring = "mcl_books:book", weight = 1, amount_min = 1, amount_max = 5 }, + { itemstring = "mcl_clock:clock", weight = 1, amount_min = 1, amount_max = 1 }, + { itemstring = "mcl_compass:compass", weight = 1, amount_min = 1, amount_max = 1 }, + { itemstring = "mcl_maps:empty_map", weight = 1, amount_min = 1, amount_max = 1 }, + } + }, + } + } +}) + +vl_structures.register_structure_spawn({ + name = "mobs_mc:guardian", + y_min = mcl_vars.mg_overworld_min, + y_max = mcl_vars.mg_overworld_max, + chance = 10, + interval = 60, + limit = 9, + spawnon = spawnon, +}) + +vl_structures.register_structure_spawn({ + name = "mobs_mc:guardian_elder", + y_min = mcl_vars.mg_overworld_min, + y_max = mcl_vars.mg_overworld_max, + chance = 100, + interval = 60, + limit = 4, + spawnon = spawnon, +}) + diff --git a/mods/MAPGEN/mcl_structures/pillager_outpost.lua b/mods/MAPGEN/mcl_structures/pillager_outpost.lua index d0e6d4c7d..90df3a7f1 100644 --- a/mods/MAPGEN/mcl_structures/pillager_outpost.lua +++ b/mods/MAPGEN/mcl_structures/pillager_outpost.lua @@ -3,12 +3,10 @@ local modpath = minetest.get_modpath(modname) local spawnon = {"mcl_core:stripped_oak","mcl_stairs:slab_birchwood_top"} -mcl_structures.register_structure("pillager_outpost",{ +vl_structures.register_structure("pillager_outpost",{ place_on = {"group:grass_block","group:dirt","mcl_core:dirt_with_grass","group:sand"}, flags = "place_center_x, place_center_z", - solid_ground = true, - prepare = { padding = 2, corners = 4, foundation = 6, clearance = true }, - sidelen = 20, + prepare = { padding = 3, corners = 4, foundation = -6, clear = true }, y_offset = 0, chunk_probability = 15, y_max = mcl_vars.mg_overworld_max, @@ -61,13 +59,13 @@ mcl_structures.register_structure("pillager_outpost",{ }, after_place = function(p,def,pr) local p1, p2 = vector.offset(p,-9,0,-9), vector.offset(p,9,32,9) - mcl_structures.spawn_mobs("mobs_mc:pillager",spawnon,p1,p2,pr,5) - mcl_structures.spawn_mobs("mobs_mc:parrot",{"mesecons_pressureplates:pressure_plate_stone_off"},p1,p2,pr,3) - mcl_structures.spawn_mobs("mobs_mc:iron_golem",{"mesecons_button:button_stone_off"},p1,p2,pr,1) + vl_structures.spawn_mobs("mobs_mc:pillager",spawnon,p1,p2,pr,5) + vl_structures.spawn_mobs("mobs_mc:parrot",{"mesecons_pressureplates:pressure_plate_stone_off"},p1,p2,pr,3) + vl_structures.spawn_mobs("mobs_mc:iron_golem",{"mesecons_button:button_stone_off"},p1,p2,pr,1) end }) -mcl_structures.register_structure_spawn({ +vl_structures.register_structure_spawn({ name = "mobs_mc:pillager", y_min = mcl_vars.mg_overworld_min, y_max = mcl_vars.mg_overworld_max, diff --git a/mods/MAPGEN/mcl_structures/ruined_portal.lua b/mods/MAPGEN/mcl_structures/ruined_portal.lua index d92ae498b..ce60f1516 100644 --- a/mods/MAPGEN/mcl_structures/ruined_portal.lua +++ b/mods/MAPGEN/mcl_structures/ruined_portal.lua @@ -13,12 +13,10 @@ end local def = { place_on = {"group:grass_block","group:dirt","mcl_core:dirt_with_grass","group:grass_block","group:sand","group:grass_block_snow","mcl_core:snow"}, flags = "place_center_x, place_center_z, all_floors", - solid_ground = true, - prepare = { padding = 0, corners = 3, tolerance = 10, foundation = true, clearance = true }, + prepare = { padding = 0, corners = 3, tolerance = 15, foundation = true, clear = true, clear_top = 0 }, chunk_probability = 20, y_max = mcl_vars.mg_overworld_max, y_min = 1, - sidelen = 12, y_offset = -5, filenames = { modpath.."/schematics/mcl_structures_ruined_portal_1.mts", @@ -26,12 +24,10 @@ local def = { modpath.."/schematics/mcl_structures_ruined_portal_3.mts", modpath.."/schematics/mcl_structures_ruined_portal_4.mts", modpath.."/schematics/mcl_structures_ruined_portal_5.mts", + modpath.."/schematics/mcl_structures_ruined_portal_6.mts", modpath.."/schematics/mcl_structures_ruined_portal_99.mts", }, - after_place = function(pos,def,pr) - local hl = def.sidelen / 2 - local p1 = vector.offset(pos,-hl,-hl,-hl) - local p2 = vector.offset(pos,hl,hl,hl) + after_place = function(pos,def,pr,p1,p2) local gold = minetest.find_nodes_in_area(p1,p2,{"mcl_core:goldblock"}) local lava = minetest.find_nodes_in_area(p1,p2,{"mcl_core:lava_source"}) local rack = minetest.find_nodes_in_area(p1,p2,{"mcl_nether:netherrack"}) @@ -101,9 +97,10 @@ local def = { }} } } -mcl_structures.register_structure("ruined_portal_overworld",def) +vl_structures.register_structure("ruined_portal_overworld",def) + local ndef = table.copy(def) ndef.y_min=mcl_vars.mg_lava_nether_max +10 ndef.y_max=mcl_vars.mg_nether_max - 15 ndef.place_on = {"mcl_nether:netherrack","group:soul_block","mcl_blackstone:basalt,mcl_blackstone:blackstone","mcl_crimson:crimson_nylium","mcl_crimson:warped_nylium"}, -mcl_structures.register_structure("ruined_portal_nether",ndef) +vl_structures.register_structure("ruined_portal_nether",ndef) diff --git a/mods/MAPGEN/mcl_structures/schematics/mcl_structures_ruined_portal_6.mts b/mods/MAPGEN/mcl_structures/schematics/mcl_structures_ruined_portal_6.mts new file mode 100644 index 0000000000000000000000000000000000000000..956c7ff07cce8549d013f51e79dcd1394edb67b0 GIT binary patch literal 946 zcmeYb3HD`RVc=#EVi2sy01SK#%!!#r45GQoIq`X^B^jwjRuHNvF*%z-1SFiCUzBQ< zlUSA*Uz}fBl$^>SfTTD#F+DdCq8*_wEhoP`GcTP%3#72P1ZY9A6_|=o$^jZ%T#}!c z8efo~lUbaRni8L1T9R7CphApV25AgcV8fD%GJ&oY21$Zlfh4GeM@KTqlc^~TLU4`g z`8g>`Ir%`t1mR-&NyV8dnTdG};vgZEK#GT0ECyDaky>0*Y?Tb6;)`<=b8;%?yp6bB z)S|${n)&_KzS3X2|5SZEB;*{R^uVe8aF0=Bs|=rKM5_j0WL;Mu2hTFi%V{N{=MJ}L z3KU&RGAO@ob&qwc#)1h#^B);)J-_7Q`Xyh>R!#l3&~8oXjA~=OwwpKfUi9wX82IKz zX@$X>^C{Zt+p0>Wm#v;-bY)Fr)GskbUAafyDelFO*Q8ikY%Ww8muQHj^T(_NBasjW)>X0qlzO%kBRTrG#!XiC)^K4$JEXGI& zyU5t{XML50jz=ExnVN4i#=^6UwBC8+ojN5`#&~kZd~@T>SI($ zp#GZOPVe8YxWMrI*KW(rVsgFP&IcY^x?#qB@dqdVNw#E*BY5S*p zJ&vAOWSMy7^`@BFDTazioWf4A*USh{+0H5U`-RRcM_HY=Bj1}XS7}BnbSqZ+Mx3y? YnXqB=oBb~*tMp&ESjbRs(6?_E0C)VWY5)KL literal 0 HcmV?d00001 diff --git a/mods/MAPGEN/mcl_structures/shipwrecks.lua b/mods/MAPGEN/mcl_structures/shipwrecks.lua index 73e8af8c4..ea88c99c5 100644 --- a/mods/MAPGEN/mcl_structures/shipwrecks.lua +++ b/mods/MAPGEN/mcl_structures/shipwrecks.lua @@ -1,10 +1,7 @@ local modname = minetest.get_current_modname() local modpath = minetest.get_modpath(modname) --local S = minetest.get_translator(modname) - -local seed = minetest.get_mapgen_setting("seed") local water_level = minetest.get_mapgen_setting("water_level") -local pr = PseudoRandom(seed) --schematics by chmodsayshello local schems = { @@ -94,7 +91,7 @@ local beach_biomes = { "Jungle_shore" } -mcl_structures.register_structure("shipwreck",{ +vl_structures.register_structure("shipwreck",{ place_on = {"group:sand","mcl_core:gravel"}, spawn_by = {"group:water"}, num_spawn_by = 4, @@ -107,11 +104,11 @@ mcl_structures.register_structure("shipwreck",{ persist = 0.001, flags = "absvalue", }, - sidelen = 16, flags = "force_placement", biomes = ocean_biomes, y_max = water_level-4, y_min = mcl_vars.mg_overworld_min, + prepare = { tolerance = -1, clear = false, foundation = false }, filenames = schems, y_offset = function(pr) return pr:next(-4,-2) end, loot = { @@ -174,93 +171,3 @@ mcl_structures.register_structure("shipwreck",{ } }) -local spawnon = { "mcl_stairs:slab_prismarine_dark"} - -mcl_structures.register_structure("ocean_temple",{ - place_on = {"group:sand","mcl_core:gravel"}, - spawn_by = {"group:water"}, - num_spawn_by = 4, - noise_params = { - offset = 0, - scale = 0.0000122, - spread = {x = 250, y = 250, z = 250}, - seed = 32345, - octaves = 3, - persist = 0.001, - flags = "absvalue", - }, - sidelen = 32, - flags = "force_placement", - biomes = ocean_biomes, - y_max = water_level-4, - y_min = mcl_vars.mg_overworld_min, - filenames = { - modpath .. "/schematics/mcl_structures_ocean_temple.mts", - modpath .. "/schematics/mcl_structures_ocean_temple_2.mts", - }, - y_offset = function(pr) return pr:next(-2,0) end, - after_place = function(p,def,pr) - local p1 = vector.offset(p,-9,0,-9) - local p2 = vector.offset(p,9,32,9) - mcl_structures.spawn_mobs("mobs_mc:guardian",spawnon,p1,p2,pr,5,true) - mcl_structures.spawn_mobs("mobs_mc:guardian_elder",spawnon,p1,p2,pr,1,true) - mcl_structures.construct_nodes(p1,p2,{"group:wall"}) - end, - loot = { - ["mcl_chests:chest_small"] = { - { - stacks_min = 3, - stacks_max = 10, - items = { - { itemstring = "mcl_sus_stew:stew", weight = 10, amount_min = 1, amount_max = 1 }, - { itemstring = "mcl_core:paper", weight = 8, amount_min = 1, amount_max = 12 }, - { itemstring = "mcl_fishing:fish_raw", weight = 5, amount_min = 8, amount_max = 21 }, - { itemstring = "mcl_fishing:salmon_raw", weight = 7, amount_min = 4, amount_max = 8 }, - { itemstring = "mcl_tnt:tnt", weight = 1, amount_min = 1, amount_max = 2 }, - } - }, - { - stacks_min = 2, - stacks_max = 6, - items = { - { itemstring = "mcl_core:iron_ingot", weight = 10, amount_min = 1, amount_max = 5 }, - { itemstring = "mcl_core:goldblock", weight = 1, amount_min = 1, amount_max = 2 }, - { itemstring = "mcl_experience:bottle", weight = 5, amount_min = 1, amount_max = 1 }, - { itemstring = "mcl_core:diamond", weight = 5, amount_min = 1, amount_max = 1 }, - { itemstring = "mcl_fishing:fishing_rod", weight = 1, amount_min = 1, amount_max = 1 }, - } - }, - { - stacks_min = 4, - stacks_max = 4, - items = { - --{ itemstring = "FIXME TREASURE MAP", weight = 8, amount_min = 1, amount_max = 5 }, - { itemstring = "mcl_books:book", weight = 1, amount_min = 1, amount_max = 5 }, - { itemstring = "mcl_clock:clock", weight = 1, amount_min = 1, amount_max = 1 }, - { itemstring = "mcl_compass:compass", weight = 1, amount_min = 1, amount_max = 1 }, - { itemstring = "mcl_maps:empty_map", weight = 1, amount_min = 1, amount_max = 1 }, - } - }, - } - } -}) - -mcl_structures.register_structure_spawn({ - name = "mobs_mc:guardian", - y_min = mcl_vars.mg_overworld_min, - y_max = mcl_vars.mg_overworld_max, - chance = 10, - interval = 60, - limit = 9, - spawnon = spawnon, -}) - -mcl_structures.register_structure_spawn({ - name = "mobs_mc:guardian_elder", - y_min = mcl_vars.mg_overworld_min, - y_max = mcl_vars.mg_overworld_max, - chance = 100, - interval = 60, - limit = 4, - spawnon = spawnon, -}) diff --git a/mods/MAPGEN/mcl_structures/witch_hut.lua b/mods/MAPGEN/mcl_structures/witch_hut.lua index 7613849ef..d81c2edaf 100644 --- a/mods/MAPGEN/mcl_structures/witch_hut.lua +++ b/mods/MAPGEN/mcl_structures/witch_hut.lua @@ -23,14 +23,16 @@ local function spawn_witch(p1,p2) end end -local function hut_placement_callback(pos,def,pr) - local hl = def.sidelen / 2 - local p1 = vector.offset(pos,-hl,-hl,-hl) - local p2 = vector.offset(pos,hl,hl,hl) - local legs = minetest.find_nodes_in_area(vector.offset(pos,-hl,0,-hl),vector.offset(pos,hl,0,hl), "mcl_core:tree") +local function hut_placement_callback(pos,def,pr,p1,p2) + -- p1.y is the bottom slice only, not a typo, we look for the hut legs + local legs = minetest.find_nodes_in_area(p1,vector.new(p2.x,p1.y,p2.z), "mcl_core:tree") local tree = {} + -- TODO: port leg generation to VoxelManip? for _,leg in pairs(legs) do - while minetest.get_item_group(mcl_vars.get_node(vector.offset(leg,0,-1,0), true, 333333).name, "water") ~= 0 do + while true do + local name = minetest.get_node(vector.offset(leg,0,-1,0)).name + if name == "ignore" then break end + if name ~= "air" and minetest.get_item_group(name, "water") == 0 then break end leg = vector.offset(leg,0,-1,0) table.insert(tree,leg) end @@ -39,14 +41,16 @@ local function hut_placement_callback(pos,def,pr) spawn_witch(p1,p2) end -mcl_structures.register_structure("witch_hut",{ - place_on = {"mcl_core:water_source","mclx_core:river_water_source"}, - flags = "place_center_x, place_center_z, liquid_surface, force_placement", - sidelen = 8, +vl_structures.register_structure("witch_hut",{ + place_on = {"mcl_core:water_source","group:sand","group:grass_block","group:dirt","mclx_core:river_water_source"}, + spawn_by = {"mcl_core:water_source","mclx_core:river_water_source"}, + check_offset = -1, + num_spawn_by = 3, + flags = "place_center_x, place_center_z, all_surfaces", chunk_probability = 8, - prepare = { tolerance=-1, clearance="top", foundation=false }, + prepare = { mode="under_air", tolerance=4, clear_bottom=3, padding=0, corners=1, foundation=false }, y_max = mcl_vars.mg_overworld_max, - y_min = -4, + y_min = -5, y_offset = 0, biomes = { "Swampland", "Swampland_ocean", "Swampland_shore" }, filenames = { modpath.."/schematics/mcl_structures_witch_hut.mts" }, diff --git a/mods/MAPGEN/mcl_structures/woodland_mansion.lua b/mods/MAPGEN/mcl_structures/woodland_mansion.lua index bc994cf4e..f68a399e5 100644 --- a/mods/MAPGEN/mcl_structures/woodland_mansion.lua +++ b/mods/MAPGEN/mcl_structures/woodland_mansion.lua @@ -5,28 +5,24 @@ local peaceful = minetest.settings:get_bool("only_peaceful_mobs", false) local spawnon = {"mcl_deepslate:deepslate","mcl_core:birchwood","mcl_wool:red_carpet","mcl_wool:brown_carpet"} -mcl_structures.register_structure("woodland_cabin",{ +vl_structures.register_structure("woodland_cabin",{ place_on = {"group:grass_block","group:dirt","mcl_core:dirt_with_grass"}, flags = "place_center_x, place_center_z", - solid_ground = true, - prepare = { padding = 2, corners = 5, foundation = true, clearance = true }, + prepare = { padding = 2, corners = 5, foundation = true, clear = true, clear_top = 2 }, force_placement = false, chunk_probability = 20, y_max = mcl_vars.mg_overworld_max, y_min = 1, biomes = { "RoofedForest" }, - sidelen = 32, filenames = { modpath.."/schematics/mcl_structures_woodland_cabin.mts", modpath.."/schematics/mcl_structures_woodland_outpost.mts", }, construct_nodes = {"mcl_barrels:barrel_closed","mcl_books:bookshelf"}, - after_place = function(p,def,pr) - local p1=vector.offset(p,-def.sidelen,-1,-def.sidelen) - local p2=vector.offset(p,def.sidelen,def.sidelen,def.sidelen) - mcl_structures.spawn_mobs("mobs_mc:vindicator",spawnon,p1,p2,pr,5) - mcl_structures.spawn_mobs("mobs_mc:evoker",spawnon,p1,p2,pr,1) - mcl_structures.spawn_mobs("mobs_mc:parrot",{"mcl_heads:wither_skeleton"},p1,p2,pr,1) + after_place = function(p,def,pr,p1,p2) + vl_structures.spawn_mobs("mobs_mc:vindicator",spawnon,p1,p2,pr,5) + vl_structures.spawn_mobs("mobs_mc:evoker",spawnon,p1,p2,pr,1) + vl_structures.spawn_mobs("mobs_mc:parrot",{"mcl_heads:wither_skeleton"},p1,p2,pr,1) end, loot = { ["mcl_chests:chest_small" ] ={{ @@ -69,7 +65,7 @@ mcl_structures.register_structure("woodland_cabin",{ } }) -mcl_structures.register_structure_spawn({ +vl_structures.register_structure_spawn({ name = "mobs_mc:vindicator", y_min = mcl_vars.mg_overworld_min, y_max = mcl_vars.mg_overworld_max, @@ -79,7 +75,7 @@ mcl_structures.register_structure_spawn({ spawnon = spawnon, }) -mcl_structures.register_structure_spawn({ +vl_structures.register_structure_spawn({ name = "mobs_mc:evoker", y_min = mcl_vars.mg_overworld_min, y_max = mcl_vars.mg_overworld_max, diff --git a/mods/MAPGEN/mcl_terrain_features/init.lua b/mods/MAPGEN/mcl_terrain_features/init.lua index 846c802b1..3b5355418 100644 --- a/mods/MAPGEN/mcl_terrain_features/init.lua +++ b/mods/MAPGEN/mcl_terrain_features/init.lua @@ -83,57 +83,76 @@ end local mushrooms = {"mcl_mushrooms:mushroom_brown","mcl_mushrooms:mushroom_red"} -local function get_fallen_tree_schematic(pos,pr) +local function place_tree(pos,def,pr) local tree = minetest.find_node_near(pos,15,{"group:tree"}) if not tree then return end tree = minetest.get_node(tree).name - local maxlen = 8 - local minlen = 2 - local vprob = 120 - local mprob = 160 + local minlen, maxlen = 3, 9 + local vrate, mrate = 120, 160 local len = pr:next(minlen,maxlen) - local schem = { - size = {x = len + 2, y = 2, z = 3}, - data = { - {name = "air", prob=0}, - {name = "air", prob=0}, - } - } - for i = 1,len do - table.insert(schem.data,{name = "mcl_core:vine",param2=4, prob=vprob}) + local dir = pr:next(0,3) + local dx, dy, dz, param2, w1, w2 + if dir == 0 then + dx, dy, dz, param2, w1, w2 = 1, 0, 0, 12, 5, 4 + elseif dir == 1 then + dx, dy, dz, param2, w1, w2 = -1, 0, 0, 12, 4, 5 + elseif dir == 2 then + dx, dy, dz, param2, w1, w2 = 0, 0, 1, 6, 3, 2 + else -- if dir == 3 then + dx, dy, dz, param2, w1, w2 = 0, 0, -1, 6, 2, 3 end - table.insert(schem.data,{name = "air", prob=0}) - table.insert(schem.data,{name = "air", prob=0}) + -- TODO: port this to voxel manipulators + -- ensure we have room for the tree + local minsupport, maxsupport = 99, 1 for i = 1,len do - table.insert(schem.data,{name = "air", prob=0}) + -- check below + local n = minetest.get_node(vector.offset(pos, dx * i, -1, dz * i)).name + local nd = minetest.registered_nodes[n] + if n ~= "air" and nd.groups and nd.groups.solid and i > 2 then + if i < minsupport then minsupport = i end + maxsupport = i + end + -- check space + local n = minetest.get_node(vector.offset(pos, dx * i, 0, dz * i)).name + local nd = minetest.registered_nodes[n] + if n ~= "air" and nd.groups and not nd.groups.plant then + if i < minlen or pr:next(1,maxsupport) == 1 then return end + len = i + break + end end - - table.insert(schem.data,{name = tree, param2 = 0}) - table.insert(schem.data,{name = "air", prob=0}) - for i = 1,len do - table.insert(schem.data,{name = tree, param2 = 12}) + if maxsupport - minsupport < minlen then return end + len = math.min(len, maxsupport - 1) + if len < minlen then return end + -- place the tree + minetest.swap_node(pos, {name = tree, param2 = 0}) + for i = 2,len do + minetest.swap_node(vector.offset(pos, dx * i, 0, dz * i), {name = tree, param2 = param2}) + if pr:next(0,255) < vrate then + local side = vector.offset(pos, dx * i + dz, 0, dz * i + dx) + local n = minetest.get_node(side).name + if n == "air" then + minetest.swap_node(side, {name="mcl_core:vine", param2=w1}) + end + end + if pr:next(0,255) < vrate then + local side = vector.offset(pos, dx * i - dz, 0, dz * i - dx) + local n = minetest.get_node(side).name + if n == "air" then + minetest.swap_node(side, {name="mcl_core:vine", param2=w2}) + end + end + if pr:next(0,255) < mrate then + local top = vector.offset(pos, dx * i, 1, dz * i) + local n = minetest.get_node(top).name + if n == "air" then + minetest.swap_node(top, {name = mushrooms[pr:next(1,#mushrooms)], param2 = 12}) + end + end end - table.insert(schem.data,{name = "air", prob=0}) - table.insert(schem.data,{name = "air", prob=0}) - for i = 1,len do - table.insert(schem.data,{name = mushrooms[pr:next(1,#mushrooms)], param2 = 12, prob=mprob}) - end - - table.insert(schem.data,{name = "air", prob=0}) - table.insert(schem.data,{name = "air", prob=0}) - for i = 1,len do - table.insert(schem.data,{name = "mcl_core:vine",param2=5, prob=vprob}) - end - table.insert(schem.data,{name = "air", prob=0}) - table.insert(schem.data,{name = "air", prob=0}) - for i = 1,len do - table.insert(schem.data,{name = "air", prob=0}) - end - - return schem end -mcl_structures.register_structure("fallen_tree",{ +vl_structures.register_structure("fallen_tree",{ rank = 1100, -- after regular trees place_on = {"group:grass_block"}, terrain_feature = true, @@ -146,24 +165,14 @@ mcl_structures.register_structure("fallen_tree",{ persist = 0.66 }, flags = "place_center_x, place_center_z", - sidelen = 18, + sidelen = 10, solid_ground = true, y_max = mcl_vars.mg_overworld_max, y_min = minetest.get_mapgen_setting("water_level"), - on_place = function(pos,def,pr) - local air_p1 = vector.offset(pos,-def.sidelen/2,1,-def.sidelen/2) - local air_p2 = vector.offset(air_p1,def.sidelen-1,0,def.sidelen-1) - local air = minetest.find_nodes_in_area(air_p1,air_p2,{"air"}) - return #air >= (def.sidelen * def.sidelen) / 2 - end, - place_func = function(pos,def,pr) - local schem=get_fallen_tree_schematic(pos,pr) - if not schem then return end - return minetest.place_schematic(pos,schem,"random") - end + place_func = place_tree }) -mcl_structures.register_structure("lavapool",{ +vl_structures.register_structure("lavapool",{ place_on = {"group:sand", "group:dirt", "group:stone"}, terrain_feature = true, noise_params = { @@ -183,7 +192,7 @@ mcl_structures.register_structure("lavapool",{ end }) -mcl_structures.register_structure("water_lake",{ +vl_structures.register_structure("water_lake",{ place_on = {"group:dirt","group:stone"}, terrain_feature = true, noise_params = { @@ -203,7 +212,7 @@ mcl_structures.register_structure("water_lake",{ end }) -mcl_structures.register_structure("water_lake_mangrove_swamp",{ +vl_structures.register_structure("water_lake_mangrove_swamp",{ place_on = {"mcl_mud:mud"}, biomes = { "MangroveSwamp" }, terrain_feature = true, @@ -224,7 +233,7 @@ mcl_structures.register_structure("water_lake_mangrove_swamp",{ end }) -mcl_structures.register_structure("basalt_column",{ +vl_structures.register_structure("basalt_column",{ place_on = {"mcl_blackstone:blackstone","mcl_blackstone:basalt"}, terrain_feature = true, spawn_by = {"air"}, @@ -268,7 +277,7 @@ mcl_structures.register_structure("basalt_column",{ return true end }) -mcl_structures.register_structure("basalt_pillar",{ +vl_structures.register_structure("basalt_pillar",{ place_on = {"mcl_blackstone:blackstone","mcl_blackstone:basalt"}, terrain_feature = true, noise_params = { @@ -310,7 +319,7 @@ mcl_structures.register_structure("basalt_pillar",{ end }) -mcl_structures.register_structure("lavadelta",{ +vl_structures.register_structure("lavadelta",{ place_on = {"mcl_blackstone:blackstone","mcl_blackstone:basalt"}, spawn_by = {"mcl_blackstone:basalt","mcl_blackstone:blackstone"}, num_spawn_by = 2, diff --git a/mods/MAPGEN/mcl_villages/buildings.lua b/mods/MAPGEN/mcl_villages/buildings.lua index 801484862..5b0d7ef05 100644 --- a/mods/MAPGEN/mcl_villages/buildings.lua +++ b/mods/MAPGEN/mcl_villages/buildings.lua @@ -15,45 +15,22 @@ function mcl_villages.initialize_settlement_info(pr) num_jobs = 0, num_beds = 0, } - - for k, v in pairs(mcl_villages.schematic_houses) do - count_buildings[v["name"]] = 0 - end - for k, v in pairs(mcl_villages.schematic_jobs) do - count_buildings[v["name"]] = 0 - end - return count_buildings end ------------------------------------------------------------------------------- -- evaluate settlement_info and place schematics ------------------------------------------------------------------------------- --- Initialize node -local function construct_node(p1, p2, name) - local r = minetest.registered_nodes[name] - if not r or not r.on_construct then - minetest.log("warning", "[mcl_villages] No on_construct defined for node name " .. name) - end - local nodes = minetest.find_nodes_in_area(p1, p2, name) - for p=1, #nodes do - r.on_construct(nodes[p]) - end - return nodes -end - local function spawn_cats(pos) local sp=minetest.find_nodes_in_area_under_air(vector.offset(pos,-20,-20,-20),vector.offset(pos,20,20,20),{"group:opaque"}) - for i=1,math.random(3) do - local v = minetest.add_entity(vector.offset(sp[math.random(#sp)],0,1,0),"mobs_mc:cat"):get_luaentity() - if v then - v._home = pos - end + for _ = 1, math.random(3) do + local v = minetest.add_entity(vector.offset(sp[math.random(#sp)],0,1,0),"mobs_mc:cat") + if v and v:get_luaentity() then v:get_luaentity()._home = pos end end end local function init_nodes(p1, p2, pr) - mcl_structures.construct_nodes(p1, p2, { + vl_structures.construct_nodes(p1, p2, { "mcl_itemframes:item_frame", "mcl_itemframes:glow_item_frame", "mcl_furnaces:furnace", @@ -69,55 +46,26 @@ local function init_nodes(p1, p2, pr) -- Support mods with custom job sites local job_sites = minetest.find_nodes_in_area(p1, p2, mobs_mc.jobsites) for _, v in pairs(job_sites) do - mcl_structures.init_node_construct(v) + vl_structures.init_node_construct(v) end - -- Do new chest nodes first - local nodes = construct_node(p1, p2, "mcl_chests:chest_small") - if nodes and #nodes > 0 then - for p=1, #nodes do - mcl_villages.fill_chest(nodes[p], pr) - end + local nodes = vl_structures.construct_nodes(p1, p2, {"mcl_chests:chest_small", "mcl_chests:chest" }) or {} + for p=1, #nodes do + mcl_villages.fill_chest(nodes[p], pr) end - - -- Do old chest nodes after - local nodes = construct_node(p1, p2, "mcl_chests:chest") - if nodes and #nodes > 0 then - for p=1, #nodes do - mcl_villages.fill_chest(nodes[p], pr) - end - end -end - --- check ground for a single building, adjust position -local function check_ground(lvm, cpos, size) - local cpos, surface_material = mcl_villages.find_surface(lvm, cpos) - if not cpos then return nil, nil end - local pos = vector.offset(cpos, -math.floor((size.x-1)/2), 0, -math.floor((size.z-1)/2)) - local ys = {pos.y} - local pos_c = mcl_villages.find_surface_down(lvm, vector.offset(pos, 0, size.y, 0)) - if pos_c then table.insert(ys, pos_c.y) end - local pos_c = mcl_villages.find_surface_down(lvm, vector.offset(pos, size.x-1, size.y, 0)) - if pos_c then table.insert(ys, pos_c.y) end - local pos_c = mcl_villages.find_surface_down(lvm, vector.offset(pos, 0, size.y, size.z-1)) - if pos_c then table.insert(ys, pos_c.y) end - local pos_c = mcl_villages.find_surface_down(lvm, vector.offset(pos, size.x-1, size.y, size.z-1)) - if pos_c then table.insert(ys, pos_c.y) end - table.sort(ys) - -- well supported base, not too uneven? - if #ys < 5 or ys[#ys]-ys[1] > 8 then return nil, nil end - cpos.y = math.floor(0.5 * (ys[math.floor(#ys/2)] + ys[math.ceil(#ys/2)]) + 0.5) -- median, rounded - return cpos, surface_material end local function add_building(settlement, building, count_buildings) table.insert(settlement, building) - count_buildings[building["name"]] = count_buildings[building["name"]] + 1 - count_buildings.num_jobs = count_buildings.num_jobs + (building["num_jobs"] or 0) - count_buildings.num_beds = count_buildings.num_beds + (building["num_beds"] or 0) + count_buildings[building.name] = (count_buildings[building.name] or 0) + 1 + count_buildings.num_jobs = count_buildings.num_jobs + (building.num_jobs or 0) + count_buildings.num_beds = count_buildings.num_beds + (building.num_beds or 0) + if building.group then + count_buildings[building.group] = (count_buildings[building.group] or 0) + 1 + end end -local function layout_town(lvm, minp, maxp, pr, input_settlement) +local function layout_town(vm, minp, maxp, pr, input_settlement) local center = vector.new(pr:next(minp.x + 24, maxp.x - 24), maxp.y, pr:next(minp.z + 24, maxp.z - 24)) minetest.log("action", "[mcl_villages] sudo make me a village at: " .. minetest.pos_to_string(minp).." - "..minetest.pos_to_string(maxp)) local possible_rotations = {"0", "90", "180", "270"} @@ -126,8 +74,8 @@ local function layout_town(lvm, minp, maxp, pr, input_settlement) local settlement = {} -- now some buildings around in a circle, radius = size of town center local x, y, z, r, lastr = center.x, center.y, center.z, 0, 99 - local mindist = 4 - if #input_settlement >= 12 then mindist = 3 end + local mindist = 3 + if #input_settlement >= 12 then mindist = 2 end -- draw j circles around center and increase radius by math.random(2,4) for j = 1,20 do local steps = math.min(math.floor(math.pi * 2 * r / 2), 30) -- try up to 30 angles @@ -146,9 +94,9 @@ local function layout_town(lvm, minp, maxp, pr, input_settlement) -- ensure we have 3 space for terraforming, and avoid problems with VoxelManip if tlpos.x - 3 >= minp.x and tlpos.x + size.x + 3 <= maxp.x and tlpos.z + 3 >= minp.z and tlpos.z + size.y + 3 <= maxp.z then - local pos, surface_material = check_ground(lvm, cpos, size) + local pos, surface_material = vl_terraforming.find_level_vm(vm, cpos, size) -- check distance to other buildings. Note that we still want to add baseplates etc. - if pos and mcl_villages.check_distance(settlement, cpos, size.x, size.z, mindist) then + if pos and mcl_villages.surface_mat[surface_material.name] and mcl_villages.check_distance(settlement, cpos, size.x, size.z, mindist) then -- use town bell as new reference point for placement height if #settlement == 0 then center_surface, y = cpos, math.min(maxp.y, pos.y + mcl_villages.max_height_difference * 0.5 + 1) @@ -186,7 +134,7 @@ local function layout_town(lvm, minp, maxp, pr, input_settlement) return settlement end -function mcl_villages.create_site_plan(lvm, minp, maxp, pr) +function mcl_villages.create_site_plan(vm, minp, maxp, pr) local settlement = {} -- initialize all settlement_info table @@ -208,12 +156,12 @@ function mcl_villages.create_site_plan(lvm, minp, maxp, pr) local building_info = mcl_villages.schematic_jobs[rindex] if - (building_info["min_jobs"] == nil or count_buildings.number_of_jobs >= building_info["min_jobs"]) - and (building_info["max_jobs"] == nil or count_buildings.number_of_jobs <= building_info["max_jobs"]) + (building_info.min_jobs == nil or count_buildings.number_of_jobs >= building_info.min_jobs) + and (building_info.max_jobs == nil or count_buildings.number_of_jobs <= building_info.max_jobs) and ( - building_info["num_others"] == nil - or count_buildings[building_info["name"]] == 0 - or building_info["num_others"] * count_buildings[building_info["name"]] < count_buildings.num_jobs + building_info.num_others == nil + or (count_buildings[building_info.group or building_info.name] or 0) == 0 + or building_info.num_others * (count_buildings[building_info.group or building_info.name] or 0) < count_buildings.num_jobs ) then add_building(settlement, building_info, count_buildings) @@ -232,12 +180,12 @@ function mcl_villages.create_site_plan(lvm, minp, maxp, pr) local building_info = mcl_villages.schematic_houses[rindex] if - (building_info["min_jobs"] == nil or count_buildings.number_of_jobs >= building_info["min_jobs"]) - and (building_info["max_jobs"] == nil or count_buildings.number_of_jobs <= building_info["max_jobs"]) + (building_info.min_jobs == nil or count_buildings.number_of_jobs >= building_info.min_jobs) + and (building_info.max_jobs == nil or count_buildings.number_of_jobs <= building_info.max_jobs) and ( - building_info["num_others"] == nil - or count_buildings[building_info["name"]] == 0 - or building_info["num_others"] * count_buildings[building_info["name"]] < count_buildings.num_jobs + building_info.num_others == nil + or (count_buildings[building_info.group or building_info.name] or 0) == 0 + or building_info.num_others * (count_buildings[building_info.group or building_info.name] or 0) < count_buildings.num_jobs ) then add_building(settlement, building_info, count_buildings) @@ -246,7 +194,7 @@ function mcl_villages.create_site_plan(lvm, minp, maxp, pr) -- Based on number of villagers local num_wells = pr:next(1, math.ceil(count_buildings.num_beds / 10)) - for i = 1, num_wells do + for _ = 1, num_wells do local windex = pr:next(1, #mcl_villages.schematic_wells) local cur_schem = table.copy(mcl_villages.schematic_wells[windex]) table.insert(settlement, pr:next(1, #settlement), cur_schem) @@ -261,12 +209,12 @@ function mcl_villages.create_site_plan(lvm, minp, maxp, pr) end table.insert(settlement, 1, bell_info) - return layout_town(lvm, minp, maxp, pr, settlement) + return layout_town(vm, minp, maxp, pr, settlement) end -function mcl_villages.place_schematics(lvm, settlement, blockseed, pr) - -- local lvm = VoxelManip() +function mcl_villages.place_schematics(vm, settlement, blockseed, pr) + -- local vm = VoxelManip() local bell_pos = vector.offset(settlement[1].minp, math.floor(settlement[1].size.x/2), 0, math.floor(settlement[1].size.z/2)) local bell_center_pos local bell_center_node_type @@ -284,12 +232,12 @@ function mcl_villages.place_schematics(lvm, settlement, blockseed, pr) local schematic = loadstring(schem_lua)() -- the foundation and air space for the building was already built before - -- lvm:read_from_map(vector.new(minp.x, minp.y, minp.z), vector.new(maxp.x, maxp.y, maxp.z)) - -- lvm:get_data() + -- vm:read_from_map(vector.new(minp.x, minp.y, minp.z), vector.new(maxp.x, maxp.y, maxp.z)) + -- vm:get_data() -- now added in placement code already, pos has the primary height if (building.yadjust or 0) ~= 0 then minp = vector.offset(minp, 0, building.yadjust, 0) end -- minetest.log("debug", "placing schematics for "..building.name.." at "..minetest.pos_to_string(minp).." on "..surface_material) minetest.place_schematic_on_vmanip( - lvm, + vm, minp, schematic, rotation, @@ -305,27 +253,27 @@ function mcl_villages.place_schematics(lvm, settlement, blockseed, pr) p.x = x for y = minp.y,maxp.y-1 do p.y = y - local n = lvm:get_node_at(p) + local n = vm:get_node_at(p) if n and n.name == "mcl_villages:no_paths" then p.y = y+1 - n = lvm:get_node_at(p) + n = vm:get_node_at(p) if n and n.name == "air" then - lvm:set_node_at(p, {name="mcl_villages:no_paths"}) + vm:set_node_at(p, {name="mcl_villages:no_paths"}) end end end end end - mcl_villages.store_path_ends(lvm, minp, maxp, cpos, blockseed, bell_pos) + mcl_villages.store_path_ends(vm, minp, maxp, cpos, blockseed, bell_pos) if building.name == "belltower" then -- TODO: allow multiple types? bell_center_pos = cpos - local center_node = lvm:get_node_at(cpos) + local center_node = vm:get_node_at(cpos) bell_center_node_type = center_node.name end end - lvm:write_to_map(true) -- for path finder and light + vm:write_to_map(true) -- for path finder and light local biome_data = minetest.get_biome_data(bell_pos) local biome_name = minetest.get_biome_name(biome_data.biome) @@ -344,8 +292,8 @@ function mcl_villages.place_schematics(lvm, settlement, blockseed, pr) end -- read back any changes - local emin, emax = lvm:get_emerged_area() - lvm:read_from_map(emin, emax) + local emin, emax = vm:get_emerged_area() + vm:read_from_map(emin, emax) end function mcl_villages.post_process_village(blockseed) @@ -371,6 +319,7 @@ function mcl_villages.post_process_village(blockseed) else minetest.log("info", "Could not create a golem!") end + spawn_cats(bell) for _, building in pairs(settlement_info) do @@ -392,7 +341,7 @@ function mcl_villages.post_process_village(blockseed) for _, bed_pos in pairs(bld_beds) do local bed_node = minetest.get_node(bed_pos) - local bed_group = core.get_item_group(bed_node.name, "bed") + local bed_group = minetest.get_item_group(bed_node.name, "bed") -- We only spawn at bed bottoms -- 1 is bottom, 2 is top @@ -413,7 +362,7 @@ function mcl_villages.post_process_village(blockseed) local m = minetest.get_meta(bed_pos) m:set_string("bell_pos", minetest.pos_to_string(bell_pos)) if m:get_string("villager") == "" then - local v = minetest.add_entity(bed_pos, "mobs_mc:villager") + local v = minetest.add_entity(vector.offset(bed_pos, 0, 0.06, 0), "mobs_mc:villager") if v then local l = v:get_luaentity() l._bed = bed_pos @@ -440,14 +389,14 @@ function mcl_villages.post_process_village(blockseed) end -- Terraform for an entire village -function mcl_villages.terraform(lvm, settlement, pr) - -- TODO: further optimize by using raw data arrays instead of set_node_at. But OK for a first draft. - -- we make the foundations 1 node wider than requested, to have one node for path laying +function mcl_villages.terraform(vm, settlement, pr) + -- TODO: sort top-down, then bottom-up, or opposite? + -- we make the foundations 2 node wider than necessary, to have one node for path laying for i, building in ipairs(settlement) do if not building.no_clearance then local pos, size = building.pos, building.size pos = vector.offset(pos, -math.floor(size.x/2), 0, -math.floor(size.z/2)) - mcl_structures.clearance(lvm, pos.x-1, pos.y, pos.z-1, size.x+2, size.y, size.z+2, 2, building.surface_mat, pr) + vl_terraforming.clearance_vm(vm, pos.x-1, pos.y, pos.z-1, size.x+2, size.y, size.z+2, 2, building.surface_mat, building.dust_mat, pr) end end for i, building in ipairs(settlement) do @@ -456,10 +405,11 @@ function mcl_villages.terraform(lvm, settlement, pr) local surface_mat = building.surface_mat local platform_mat = building.platform_mat or { name = foundation_materials[surface_mat.name] or "mcl_core:dirt" } local stone_mat = building.stone_mat or { name = "mcl_core:stone" } + local dust_mat = building.dust_mat building.platform_mat = platform_mat -- remember for use in schematic placement building.stone_mat = stone_mat pos = vector.offset(pos, -math.floor((size.x-1)/2), 0, -math.floor((size.z-1)/2)) - mcl_structures.foundation(lvm, pos.x-2, pos.y, pos.z-2, size.x+4, -4, size.z+4, 2, surface_mat, platform_mat, stone_mat, pr) + vl_terraforming.foundation_vm(vm, pos.x-2, pos.y, pos.z-2, size.x+4, -5, size.z+4, 2, surface_mat, platform_mat, stone_mat, dust_mat, pr) end end end diff --git a/mods/MAPGEN/mcl_villages/mod.conf b/mods/MAPGEN/mcl_villages/mod.conf index 72a723428..c840876b9 100644 --- a/mods/MAPGEN/mcl_villages/mod.conf +++ b/mods/MAPGEN/mcl_villages/mod.conf @@ -1,5 +1,5 @@ name = mcl_villages author = Rochambeau, kno10 description = This mod adds settlements on world generation. -depends = mcl_core, mcl_util, mcl_mapgen_core, mcl_structures, mcl_loot, mobs_mc +depends = mcl_core, mcl_util, mcl_mapgen_core, mcl_structures, mcl_loot, mobs_mc, vl_terraforming optional_depends = mcl_farming diff --git a/mods/MAPGEN/mcl_villages/schematics/belltower.mts b/mods/MAPGEN/mcl_villages/schematics/belltower.mts deleted file mode 100644 index 8eb524312cc98c62899ef6d7714398c5ef869d4e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 211 zcmeYb3HD`RVPIuoV_>ZZ0|s^m;oRh$_~iVeRIB2W{JhkpqRixM2Ij=fA_g&#OnG8X zPBD^Hya9sAc0LZz9heZfe$2;lnOEjL^240_&JGrC8}-Wf!f_NS5x zLJ6!8!3au6%Y0V(N)j$-R ziugi`ESn<&g9KS%ql+huOQfkZA+BSJb*K?^Dy4gbF&(TE=pd$=(@O@BD=MbtP%{-7 zpagDMwhTj*Vh##hu!%KBO=xgznl{IVLq*VAOrgwWC&+?9kSDrXjB8B}NJbbzgaCi) zDFmqw5$0UbVurhgNkh1FI7|18u&_>wkR> zQx^m6-mCH3Zy(qh6MYNsFOQ8}+h2&|@I~&?il15Es$T7|hyL_*Uh$VK zAT2d7IkngdOd)A2&(BX`5CkbG$uCOI0IC4f3=$OuiFrUCh>XumEGaEY1e%ePSi&HN zp*X%gF((Jf%#xziR0e*i0ZEBP5Pi}heZ?g}7ZzKAsd$jx@%g1CsYMK;SS1-GK*DhE z#g}K6WW=WzB^DPm=;x*urzYp;6~`A8r4|>L7Nr&d9S3qSlp7D`L+waQV~_+Hm06OS zn^u$v)N2J|#)H|YmggiU0bQA&mkJ4$;@te~)FLYg1u+Ed9CKAP=YJ#e+EUN%4jd<1nR4fKqZG;~-L5rFrQ&sqrO6i3MPt`30$Y zNRgbAn39rOR59mmNT*Pfg2UPY~Ycf{7} z>v?JS_rGSXgxxBvdxY(fQZ<@yh`pFgeo`p)-?mH!Kg zt-_zpHb3v3yZ)4B)8|KS{P&+n>_4R~RC)CJx$pbmtuDMQcK&%AuXI@VliIBeKc;K` zTrshD-eF-bsiLZ2jJ8erwy+YG3}?Q^Z&N>fntRK}pl} dPk*wXUC1}-z34=PXkjOx&2w4xHG`gA0{~T$QF{OY diff --git a/mods/MAPGEN/mcl_villages/schematics/church.mts b/mods/MAPGEN/mcl_villages/schematics/church.mts deleted file mode 100644 index e59f90f1f812d393f49c0ffe6d2e0b8f5fba7ab4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 949 zcmeYb3HD`RVc=!pXW*+x1q_l5yt&Ca@yYo`sa7eOMI{W(iJ3(V(jc+o5+JkK3QWb9 z=jW%y=a-hG7BProm1N+Do0FWMl$4Xozy}vd&q*vUW)MNvmy?(THWSJElA_d924T3S z;*$Kl)TE-!f$Qd7PX$dy1-2B^Zx5JM&%8+X&VPOD%HrFtb6cCkcV5=$5Byy(;7TWqzc))gZJXBf*P_aFyW9?|rLhT5;`V&gHgAdB16UB61Gp(_y z6rF5&?q}?cYj;b;Zxn4wZ(pu{aPPw=srNo{YfgEd{Brfe{jO_LVfXfOz3JDDzsXwp zKl|;W9=!)$OElD~<0n43Sx~<1>aF*O{_hO_8}Q5iqhDwtW7vn4Yu9j>cKlEH^5D?f z&)kg;3IZG~OpPyu`kuGmZhXD+rI*6e1Giim8&g@1p3l#&;#*wdq|9cinWC)Ax;N@* zki-Eug-O93e|Tjyn5A!?+?mwDE|cM<%sb_kp{UiwP0VNa%=vmmYvJFnD<>C8wd~uz UP2tbH^e<7t8GZTxIFjc80G0iX;s5{u diff --git a/mods/MAPGEN/mcl_villages/schematics/farm.mts b/mods/MAPGEN/mcl_villages/schematics/farm.mts deleted file mode 100644 index b45ff1d6f3cf640419ec53309916e48db2d5c7a3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 379 zcmeYb3HD`RVc=xoU|_F@0tOxi-rVGz_~iVeRI8NCq7nw?#LOZjv67diorBYG1P|m^30Nq`1GR0;$j97 zu)&E%xtV$CR>k?5Iq~JGB@Chv!-49{6M?437w4B2C8sh7AuBG(FG(!PhuD?|HK05r zHL(O$S#n}gQGQ9qoVUThz#utYZ);oI*k1Z5-aA@2YAn1Fc z@o8Y_!b2~$m0v&n>Dk{o!{pb-BbF=f{T6cwS~6qyq~#?MX=~+k&h;yo9t@SP`%rMT vY5o@b_&fe@|38`>|0_QGxcNV10ahxH#sLhIlm~?DkZb1gn>CRvxq?)B$kq&UsP;WQj}N# zV#Vhdq~z;GIbH3sYCB+~P zI6ypbRQ)+110Tq>lRip+Hc^@JLnZp{@ zDOQn0WLFu)21Nv<#Rvf@(N0B_ES!*H4J1c<_$1hjtX31b!zU_=F4z{h0F3804^myM zN$Ubh(a(P;1{1iIV&d|$KQEy{*BLK0Miow`ZP~~3X>+QUv>Kt?b?z84yb97Rg$9(NfKA69*KumwU>Cl+B!DY^d z)z9&U*;i+pp>F@m%@1FFZr$g}?`i6O@X6D=ZOKJ*N@jzZTa&iBZ(3jH%E;8igVovZ z&RuUF%sCP;gX8-v)9>SJ1OR5AYrHJ89 zM2N)amzJa!F-YT4!XSYx2@93@lKcWl7$@cDXBS(6$&A#TGzLMCaumly90!gtgw4r` zMP;ce45}d2X*v1jsYM0(CBP5^Gvk5m_@tcD)Ob)JWTsTic^lTv*Q~(fqH!)a?_J`i z^LN*;mnsom!5I5Y`HrfJT<38|U9N))w=JBDJ)NV&nI>pCT|ZGE%X4??^Ajs~@~KRB z+O^X`#N)d736GU0B^DLGKY6?M!Q|HY;?qyR-~BhnUG2K2(RIn6^J6Y0P51s9%D?U VA*koetk8QCk%O)@DNe1~UN^z`&pfha=eNl>B^P zh=al>H7_2-iBF0*WKaYdom`ZdR+5>Q9$%6O40bD2F2rG&x=Vn%l_3sGEdkmO*PfPx z!p9O0W?00GE9ShN(#hAXAmAeIQl0qzaq;ihsv&tCHypfmj5{B>wWUvBEojjy_H@dz zR8L&~^RL{+HCndCJDPfBZ1x)asBxAQb67`gd@W_B6CL8>wxUV$=(PT_Nd*k2-iMqM zx}x}~#A){Oa;@gjgSS?7U6|KuA$xJ&;}&K0+s52ZtJbPca(VhdKu&exuXTG`)Y}CP zR_=;Vx%9QYW%1_ITQQ<5Qk0sCWHVTkBuG9&5I;2! z%!yBmH)N2gC`impEw%!Y@p*|QrA3K3@o71UB@8$;l>jvfg3K$)FG|h;>IBmaV#w|S zxfbdRuqog$C@#s*OHC@uOwLYDEGkP)fjBZJKR?$BL_h)}DK(|o3P{8k0dZ1(NeNH_ z91tiHCHVym66h9!dLB*W6le_tv6?j~|Q%l~x6@P#I`hw)80(aF^W>m};TeH=2 z64QheCTW#UDX(i{uHW4xTeHToLvSH;lk#rinR0$_^j0YbCRg?sq^TeD_!s&AWR+9U z>cnjozS%iHoS#=5Ur>}uBz`M$yVr_T4E@Z2x?D1P?$w(fbd!uRhb|L0%wf3nTp z&)NZ=&y~LBCe$3RdwOPj^=~uV`^R3{8kBzR{`ywx{>tkI>z6l6UtX6Szd~>Sp~Bbu z+4cqgXFpKS+&i27kN%ZfkIsD489!F800D-C|I7Qij&8}ZkHOlK*G&DBaziVumt*73M`>iL*;%ZoHV R=!$bkIKS(Scj6Wj0swN-sXG7w diff --git a/mods/MAPGEN/mcl_villages/schematics/well.mts b/mods/MAPGEN/mcl_villages/schematics/well.mts deleted file mode 100644 index ff8785fdee376e75739005a4b668afb4ea646271..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 444 zcmeYb3HD`RVPIomXW*!Z00uq=-rVGz_~iVeRI8NCq7nw?#LOZFC6HKg36NQA1*U+U z{JhkpqRiy%znX*_Be_~C{o=O-oQq%w$s1j-Y0azF;cjEXmA5QZy62;)#} zhC|$dK?K<;If+T}<@xz345DzO%M(jdi{gv(ON)|I86>bNjxWhCz+t>4gE&YBhT9E| zD&{07I572uG`&bOGBgOj=_97Wa&m*w21Z5e8D|@tpR_pLNi#ZlmSyME-bQYYn;&*{ zOm39s_&D>V53_OL-*e30to2*#*gwu(Dd!&2_;qo^s)j@Xh61NVwxqJ8&`rW2o?Hn@ Ni{?c#%uQYy3jh;rs2BhM diff --git a/mods/MAPGEN/mcl_villages/utils.lua b/mods/MAPGEN/mcl_villages/utils.lua index 4a93cb56a..44e2a0537 100644 --- a/mods/MAPGEN/mcl_villages/utils.lua +++ b/mods/MAPGEN/mcl_villages/utils.lua @@ -1,79 +1,3 @@ -local function is_above_surface(name) - if name == "air" or name == "mcl_bamboo:bamboo" or name == "mcl_core:vine" or name == "mcl_core:snow" then - return true - end - local meta = core.registered_items[name] - local groups = meta and meta.groups - return groups and (groups["deco_block"] or groups["tree"] or groups["leaves"] or groups["plant"]) -end -function mcl_villages.find_surface_down(lvm, pos, surface_node) - local p6 = vector.new(pos) - surface_node = surface_node or lvm:get_node_at(p6) - if not surface_node then return end - local has_air = is_above_surface(surface_node.name) - for y = p6.y - 1, math.max(0, p6.y - 80), -1 do - p6.y = y - local top_node = surface_node - surface_node = lvm:get_node_at(p6) - if not surface_node or surface_node.name == "ignore" then return nil end - if is_above_surface(surface_node.name) then - has_air = true - else - if has_air then - if mcl_villages.surface_mat[surface_node.name] then - --minetest.log("Found "..surface_node.name.." below "..top_node.name) - return p6, surface_node - else - local ndef = minetest.registered_nodes[surface_node.name] - if ndef and ndef.walkable then - --minetest.log("Found non-suitable "..surface_node.name.." below "..top_node.name) - return nil - end - end - end - has_air = false - end - end -end -function mcl_villages.find_surface_up(lvm, pos, surface_node) - local p6 = vector.new(pos) - surface_node = surface_node or lvm:get_node_at(p6) --, true, 1000000) - if not surface_node then return end - for y = p6.y + 1, p6.y + 80 do - p6.y = y - local top_node = lvm:get_node_at(p6) - if not top_node or top_node.name == "ignore" then return nil end - if is_above_surface(top_node.name) then - if mcl_villages.surface_mat[surface_node.name] then - -- minetest.log("Found "..surface_node.name.." below "..top_node.name) - p6.y = p6.y - 1 - return p6, surface_node - else - local ndef = minetest.registered_nodes[surface_node.name] - if ndef and ndef.walkable then - -- minetest.log("Found non-suitable "..surface_node.name.." below "..top_node.name) - return nil - end - end - end - surface_node = top_node - end -end -------------------------------------------------------------------------------- --- function to find surface block y coordinate --- returns surface postion -------------------------------------------------------------------------------- -function mcl_villages.find_surface(lvm, pos) - local p6 = vector.new(pos) - if p6.y < 0 then p6.y = 0 end -- start at water level - local surface_node = lvm:get_node_at(p6) - -- downward, if starting position is empty - if is_above_surface(surface_node.name) then - return mcl_villages.find_surface_down(lvm, p6, surface_node) - else - return mcl_villages.find_surface_up(lvm, p6, surface_node) - end -end -- check the minimum distance of two squares, on axes function mcl_villages.check_distance(settlement, cpos, sizex, sizez, limit) for i, building in ipairs(settlement) do diff --git a/mods/MAPGEN/modpack.conf b/mods/MAPGEN/modpack.conf index eb9c54abd..dc48425a6 100644 --- a/mods/MAPGEN/modpack.conf +++ b/mods/MAPGEN/modpack.conf @@ -1,2 +1,2 @@ name = MAPGEN -description = Meta-modpack containing map generating mods for MineClone 2 +description = Meta-modpack containing map generating mods for VoxeLibre diff --git a/mods/MAPGEN/tsm_railcorridors/init.lua b/mods/MAPGEN/tsm_railcorridors/init.lua index 3e44a82e3..251b93630 100644 --- a/mods/MAPGEN/tsm_railcorridors/init.lua +++ b/mods/MAPGEN/tsm_railcorridors/init.lua @@ -1081,11 +1081,10 @@ local function create_corridor_system(main_cave_coords, pr) return true end -mcl_structures.register_structure("mineshaft",{ +vl_structures.register_structure("mineshaft",{ place_on = {"group:sand","group:grass_block","mcl_core:water_source","group:dirt","mcl_core:dirt_with_grass","mcl_core:gravel","group:material_stone","mcl_core:snow"}, chunk_probability = 4, flags = "place_center_x, place_center_z, force_placement, all_floors", - sidelen = 32, y_max = 40, y_min = mcl_vars.mg_overworld_min, place_func = function(pos,_,pr,blockseed) diff --git a/mods/MAPGEN/vl_structures/API.md b/mods/MAPGEN/vl_structures/API.md new file mode 100644 index 000000000..d59993182 --- /dev/null +++ b/mods/MAPGEN/vl_structures/API.md @@ -0,0 +1,37 @@ +# vl_structures + +Updated API for structure spawning for VoxeLibre and Mineclonia + +## vl_structures.register_structure(name,structure definition,nospawn) +If nospawn is truthy the structure will not be placed by mapgen and the decoration parameters can be omitted. This is intended for secondary structures the placement of which gets triggered by the placement of other structures. It can also be used to register testing structures so they can be used with /spawnstruct. + +### structure definition +{ + fill_ratio = OR noise = {}, + biomes = {}, + y_min =, + y_max =, + place_on = {}, + spawn_by = {}, + num_spawn_by =, + flags = (default: "place_center_x, place_center_z, force_placement") + (same as decoration def) + y_offset =, --can be a number or a function returning a number + filenames = {} OR place_func = function(pos,def,pr) + -- filenames can be a list of any schematics accepted by mcl_structures.place_schematic / minetest.place_schematic + on_place = function(pos,def,pr) end, + -- called before placement. denies placement when returning falsy. + after_place = function(pos,def,pr) + -- executed after successful placement + prepare = table, -- a foundation is automatically built for the structure + loot = , + --a table of loot tables for mcl_loot indexed by node names + -- e.g. { ["mcl_chests:chest_small"] = {loot},... } +} +## vl_structures.registered_structures +Table of the registered structure defintions indexed by name. + +## vl_structures.place_structure(pos, def, pr) +Places a structure using the mapgen placement function + +## vl_structures.place_schematic(pos, schematic, rotation, replacements, force_placement, flags, after_placement_callback, pr, callback_param) diff --git a/mods/MAPGEN/vl_structures/api.lua b/mods/MAPGEN/vl_structures/api.lua new file mode 100644 index 000000000..ad7b67a7b --- /dev/null +++ b/mods/MAPGEN/vl_structures/api.lua @@ -0,0 +1,518 @@ +vl_structures.registered_structures = {} + +local mob_cap_player = tonumber(minetest.settings:get("mcl_mob_cap_player")) or 75 +local mob_cap_animal = tonumber(minetest.settings:get("mcl_mob_cap_animal")) or 10 +local structure_boost = tonumber(minetest.settings:get("vl_structures_boost")) or 1 +local worldseed = minetest.get_mapgen_setting("seed") +local RANDOM_SEED_OFFSET = 959 -- random constant that should be unique across each library +local floor = math.floor +local vector_offset = vector.offset + +-- FIXME: switch to vl_structures_logging? +local logging = true or minetest.settings:get_bool("mcl_logging_structures", true) + +-- FIXME: switch to vl_structures_disabled? +local disabled_structures = minetest.settings:get("mcl_disabled_structures") +disabled_structures = disabled_structures and disabled_structures:split(",") or {} +function mcl_structures.is_disabled(structname) + return table.indexof(disabled_structures,structname) ~= -1 +end + +local mg_name = minetest.get_mapgen_setting("mg_name") + +-- see vl_terraforming for documentation +local DEFAULT_PREPARE = { tolerance = 10, foundation = -3, clear = false, clear_bottom = 0, clear_top = 4, padding = 1, corners = 1 } +local DEFAULT_FLAGS = "place_center_x,place_center_z" + +local function parse_prepare(prepare) + if prepare == nil or prepare == true then return DEFAULT_PREPARE end + if prepare == false then return {} end + if prepare.foundation == true then + prepare = table.copy(prepare) + prepare.foundation = DEFAULT_PREPARE.foundation + end + return prepare +end + +-- check "enabled" tolerances +local function tolerance_enabled(tolerance, mode) + return mode ~= "off" and tolerance and (tolerance == "max" or tolerance == "min" or tolerance >= 0) and true +end + +--- Trim a full path name to its last two parts as short name for logging +local function basename(filename) + local fn = string.split(filename, "/") + return #fn > 1 and (fn[#fn-1].."/"..fn[#fn]) or fn[#fn] +end + +--- Load a schematic file +-- @param filename string: file name +-- @param name string: for logging, optional +-- @return loaded schematic +function vl_structures.load_schematic(filename, name) + -- load, and ensure we have size information + if filename == nil then error("Filename is nil for schematic "..tostring(name)) end + if type(filename) == "string" then minetest.log("action", "Loading "..filename) end + local s = loadstring(minetest.serialize_schematic(filename, "lua", {lua_use_comments = false, lua_num_indent_spaces = 0}) .. " return schematic")() + if not s then + minetest.log("warning", "[vl_structures] failed to load schematic "..basename(filename)) + return nil + elseif not s.size then + minetest.log("warning", "[vl_structures] no size information for schematic "..basename(filename)) + return nil + end + if logging then minetest.log("warning", "[vl_structures] loaded schematic "..basename(filename).." size "..minetest.pos_to_string(s.size)) end + if not s.name then s.name = name or basename(filename) end + return s +end + +-- Expected contents of param: +-- pos vector: position (center.x, base.y, center.z) -- flags NOT supported +-- size vector: structure size after rotation (!) +-- yoffset number: relative to base.y, typically <= 0 +-- y_min number: minimum y range permitted +-- y_max number: maximum y range permitted +-- schematic string or schematic: as in minetest.place_schematic +-- rotation string: as in minetest.place_schematic +-- replacement table: as in minetest.place_schematic +-- force_placement boolean: as in minetest.place_schematic +-- prepare table: instructions for preparation (usually from definition) +-- tolerance number: tolerable ground unevenness, -1 to disable, default 10 +-- foundation boolean or number: level ground underneath structure (true is a minimum depth of -3) +-- clear boolean: clear overhead area +-- clear_min number or string: height from base to start clearing, "top" to start at top +-- clear_max number: height from top to stop primary clearing +-- padding number: additional padding to increase the area, default 1 +-- corners number: corner smoothing of foundation and clear, default 1 +-- pr PcgRandom: random generator +-- name string: for logging +local function emerge_schematic_vm(vm, param) + local pos, size, yoffset, pr = param.pos, param.size, param.yoffset or 0, param.pr + local prepare, surface_mat = parse_prepare(param.prepare), param.surface_mat + -- Step 1: adjust ground to a more level position + if pos and size and prepare and tolerance_enabled(prepare.tolerance, prepare.mode) then + pos, surface_mat = vl_terraforming.find_level_vm(vm, pos, size, prepare.tolerance, prepare.mode) + if not pos then + minetest.log("warning", "[vl_structures] Not spawning "..tostring(param.schematic.name).." at "..minetest.pos_to_string(param.pos).." because ground is too uneven.") + return + end + end + local pmin = vector_offset(pos, -floor((size.x-1)*0.5), yoffset, -floor((size.z-1)*0.5)) + local pmax = vector_offset(pmin, size.x-1, size.y-1, size.z-1) + -- Step 2: prepare ground foundations and clear + if prepare and (prepare.clear or prepare.foundation) then + local prepare_start = os.clock() + -- Get materials from biome: + local b = mg_name ~= "v6" and minetest.registered_biomes[minetest.get_biome_name(minetest.get_biome_data(pos).biome)] + local node_top = b and b.node_top and { name = b.node_top } or surface_mat or { name = "mcl_core:dirt_with_grass" } + local node_filler = { name = b and b.node_filler or "mcl_core:dirt" } + local node_stone = { name = b and b.node_stone or "mcl_core:stone" } + local node_dust = b and b.node_dust and { name = b.node_dust } or nil + if node_top.name == "mcl_core:dirt_with_grass" and b then node_top.param2 = b._mcl_grass_palette_index end + + local corners, padding, depth = prepare.corners or 1, prepare.padding or 1, (type(prepare.foundation) == "number" and prepare.foundation) or -4 + local gp = vector_offset(pmin, -padding, -yoffset, -padding) -- base level + if prepare.clear then + local yoff, ymax = prepare.clear_bottom or 0, size.y + yoffset + (prepare.clear_top or DEFAULT_PREPARE.clear_top) + if prepare.clear_bottom == "top" or prepare.clear_bottom == "above" then yoff = size.y + yoffset end + --minetest.log("action", "[vl_structures] clearing air "..minetest.pos_to_string(gp)..": ".. (size.x + padding * 2)..","..ymax..","..(size.z + padding * 2)) + vl_terraforming.clearance_vm(vm, gp.x, gp.y + yoff, gp.z, + size.x + padding * 2, ymax - yoff, size.z + padding * 2, + corners, node_top, node_dust, pr) + end + if prepare.foundation then + minetest.log("action", "[vl_structures] fill foundation "..minetest.pos_to_string(gp).." with "..tostring(node_top.name).." "..tostring(node_filler.name)) + local depth = (type(prepare.foundation) == "number" and prepare.foundation) or DEFAULT_PREPARE.foundation + vl_terraforming.foundation_vm(vm, gp.x, gp.y - 1, gp.z, + size.x + padding * 2, depth, size.z + padding * 2, + corners, node_top, node_filler, node_stone, node_dust, pr) + end + end + -- note: pos is always the center position + minetest.place_schematic_on_vmanip(vm, vector_offset(pos, 0, (param.yoffset or 0), 0), param.schematic, param.rotation, param.replacements, param.force_placement, "place_center_x,place_center_z") + return pos +end + +-- Additional parameters: +-- emin vector: emerge area minimum +-- emax vector: emerge area maximum +-- after_placement_callback function: callback after placement, (pmin, pmax, size, rotation, pr, param) +-- callback_param table: additional parameters to callback function +local function emerge_schematic(blockpos, action, calls_remaining, param) + if calls_remaining >= 1 then return end + local vm = VoxelManip() + vm:read_from_map(param.emin, param.emax) + local pos = emerge_schematic_vm(vm, param) + if not pos then return end + vm:write_to_map(true) + -- repair walls (TODO: port to vmanip? but no "vm.find_nodes_in_area" yet) + local pmin = vector_offset(pos, -floor((param.size.x-1)*0.5), 0, -floor((param.size.z-1)*0.5)) + local pmax = vector_offset(pmin, param.size.x-1, param.size.y-1, param.size.z-1) + if pmin and pmax and mcl_walls then + for _, n in pairs(minetest.find_nodes_in_area(pmin, pmax, { "group:wall" })) do + mcl_walls.update_wall(n) + end + end + if pmin and pmax and param.after_placement_callback then + param.after_placement_callback(pmin, pmax, param.size, param.rotation, param.pr, param.callback_param) + end +end + +function vl_structures.place_schematic(pos, yoffset, y_min, y_max, schematic, rotation, replacements, force_placement, flags, prepare, pr, after_placement_callback, callback_param) + if schematic and not schematic.size then -- e.g., igloo still passes filenames + schematic = vl_structures.load_schematic(schematic) + end + rotation = vl_structures.parse_rotation(rotation, pr) + prepare = parse_prepare(prepare) + local ppos, pmin, pmax, size = vl_structures.get_extends(pos, schematic.size, yoffset, rotation, flags or DEFAULT_FLAGS) + -- area to emerge. Add some margin to allow for finding better suitable ground etc. + local tolerance = prepare.tolerance or DEFAULT_PREPARE.tolerance -- may be negative to disable foundations + if not type(tolerance) == "number" then tolerance = 8 end -- for emerge only + local emin, emax = vector_offset(pmin, 0, -math.max(tolerance, 0), 0), vector.offset(pmax, 0, math.max(tolerance, 0), 0) + -- if we need to generate a foundation, we need to emerge a larger area: + if prepare.foundation or prepare.clear then -- these functions need some extra margins + local padding = (prepare.padding or 0) + 3 + local depth = prepare.foundation and ((prepare.depth or -4) - 15) or 0 -- minimum depth + local height = prepare.clear and (size.y * 2 + 6) or 0 -- headroom + emin = vector_offset(emin, -padding, depth, -padding) + emax = vector_offset(emax, padding, height, padding) + end + minetest.emerge_area(emin, emax, emerge_schematic, { + emin=emin, emax=emax, name=schematic.name, + pos=ppos, size=size, yoffset=yoffset, y_min=y_min, y_max=y_max, + schematic=schematic, rotation=rotation, replacements=replacements, force_placement=force_placement, + prepare=prepare, pr=pr, + after_placement_callback=after_placement_callback, callback_param=callback_param + }) +end + +local function emerge_complex_schematics(blockpos, action, calls_remaining, param) + if calls_remaining >= 1 then return end + local start = os.clock() + local vm = VoxelManip() + vm:read_from_map(param.emin, param.emax) + local startmain = os.clock() + local pos, size, yoffset, def, pr = param.pos, param.size, param.yoffset or 0, param.def, param.pr + local prepare, surface_mat = parse_prepare(param.prepare or def.prepare), param.surface_mat + + -- pick random daughter schematics + rotations + local daughters = {} + if def.daughters then + for i,d in pairs(def.daughters) do + if not d.schematics or #d.schematics == 0 then + error("Daughter schematics not loaded for structure "..def.name) + end + local ds = d.schematics[#d.schematics > 1 and pr:next(1,#d.schematics) or 1] + local rotation = vl_structures.parse_rotation(d.rotation, pr) + table.insert(daughters, {d, ds, rotation}) + end + end + + -- Step 1: adjust ground to a more level position + if pos and size and prepare and tolerance_enabled(prepare.tolerance, prepare.mode) then + pos, surface_mat = vl_terraforming.find_level_vm(vm, pos, size, prepare.tolerance, prepare.mode) + if not pos then + minetest.log("warning", "[vl_structures] Not spawning "..tostring(def.name or param.schematic.name).." at "..minetest.pos_to_string(param.pos).." because ground is too uneven.") + return + end + -- obey height restrictions, to not violate nether roof + if def.y_max and pos.y - yoffset > def.y_max then pos.y = def.y_max - yoffset end + if def.y_min and pos.y - yoffset < def.y_min then pos.y = def.y_min - yoffset end + end + --if logging and not def.terrain_feature then minetest.log("action", "[vl_structures] "..def.name.." after find_level at "..minetest.pos_to_string(pos).." in "..string.format("%.2fms (main: %.2fms)", (os.clock()-start)*1000, (os.clock()-startmain)*1000)) end + local pmin = vector_offset(pos, -floor((size.x-1)*0.5), yoffset, -floor((size.z-1)*0.5)) + local pmax = vector_offset(pmin, size.x-1, size.y-1, size.z-1) + -- todo: also support checking ground of daughter schematics, but not used by current schematics + -- Step 2: prepare ground foundations and clear + -- todo: allow daugthers to use prepare when parent does not + if prepare and (prepare.clear or prepare.foundation) then + local prepare_start = os.clock() + -- Get materials from biome: + local b = mg_name ~= "v6" and minetest.registered_biomes[minetest.get_biome_name(minetest.get_biome_data(pos).biome)] + local node_top = b and b.node_top and { name = b.node_top } or surface_mat or { name = "mcl_core:dirt_with_grass" } + local node_filler = { name = b and b.node_filler or "mcl_core:dirt" } + local node_stone = { name = b and b.node_stone or "mcl_core:stone" } + local node_dust = b and b.node_dust and { name = b.node_dust } or nil + if node_top.name == "mcl_core:dirt_with_grass" and b then node_top.param2 = b._mcl_grass_palette_index end + + local corners, padding, depth = prepare.corners or 1, prepare.padding or 1, (type(prepare.foundation) == "number" and prepare.foundation) or -4 + local gp = vector_offset(pmin, -padding, -yoffset, -padding) -- base level + if prepare.clear then + local yoff, ymax = prepare.clear_bottom or 0, size.y + yoffset + (prepare.clear_top or DEFAULT_PREPARE.clear_top) + if prepare.clear_bottom == "top" or prepare.clear_bottom == "above" then yoff = size.y + yoffset end + --minetest.log("action", "[vl_structures] clearing air "..minetest.pos_to_string(gp)..": ".. (size.x + padding * 2)..","..ymax..","..(size.z + padding * 2)) + vl_terraforming.clearance_vm(vm, gp.x, gp.y + yoff, gp.z, + size.x + padding * 2, ymax - yoff, size.z + padding * 2, + corners, node_top, node_dust, pr) + -- clear for daughters + for _,tmp in ipairs(daughters) do + local dd, ds, dr = tmp[1], tmp[2], tmp[3] + local ddp = parse_prepare(dd.prepare) + if ddp and ddp.clear then + local dsize = vl_structures.size_rotated(ds.size, dr) -- FIXME: rotation of parent + local corners, padding, yoffset = ddp.corners or 1, ddp.padding or 1, ddp.yoffset or 0 + local yoff, ymax = ddp.clear_bottom or 0, dsize.y + yoffset + (ddp.clear_top or DEFAULT_PREPARE.clear_top) + if ddp.clear_bottom == "top" or ddp.clear_bottom == "above" then yoff = dsize.y + yoffset end + local gp = vector_offset(pos, dd.pos.x - floor((dsize.x-1)*0.5) - padding, + dd.pos.y, + dd.pos.z - floor((dsize.z-1)*0.5) - padding) + local sy = ymax - yoff + --minetest.log("action", "[vl_structures] clearing air "..minetest.pos_to_string(gp)..": ".. (dsize.x + padding * 2)..","..sy..","..(dsize.z + padding * 2)) + if sy > 0 then + vl_terraforming.clearance_vm(vm, gp.x, gp.y + yoff, gp.z, + dsize.x + padding * 2, ymax - yoff, dsize.z + padding * 2, + corners, node_top, node_dust, pr) + end + end + end + end + -- if logging and not def.terrain_feature then minetest.log("action", "[vl_structures] "..def.name.." after clear at "..minetest.pos_to_string(pos).." in "..string.format("%.2fms (main: %.2fms)", (os.clock()-start)*1000, (os.clock()-prepare_start)*1000)) end + if prepare.foundation then + -- minetest.log("action", "[vl_structures] fill foundation "..minetest.pos_to_string(gp).." with "..tostring(node_top.name).." "..tostring(node_filler.name)) + local depth = (type(prepare.foundation) == "number" and prepare.foundation) or DEFAULT_PREPARE.foundation + vl_terraforming.foundation_vm(vm, gp.x, gp.y - 1, gp.z, + size.x + padding * 2, depth, size.z + padding * 2, + corners, node_top, node_filler, node_stone, node_dust, pr) + -- foundation for daughters + for _, tmp in ipairs(daughters) do + local dd, ds, dr = tmp[1], tmp[2], tmp[3] + local ddp = parse_prepare(dd.prepare) + if ddp and ddp.foundation then + local dsize = vl_structures.size_rotated(ds.size, dr) -- FIXME: rotation of parent + local corners, padding, yoffset = ddp.corners or 1, ddp.padding or 1, ddp.yoffset or 0 + local depth = (type(ddp.foundation) == "number" and ddp.foundation) or DEFAULT_PREPARE.foundation + local gp = vector_offset(pos, dd.pos.x - floor((dsize.x-1)*0.5) - padding, + dd.pos.y + (yoffset or 0), + dd.pos.z - floor((dsize.z-1)*0.5) - padding) + vl_terraforming.foundation_vm(vm, gp.x, gp.y - 1, gp.z, + dsize.x + padding * 2, depth, dsize.z + padding * 2, + corners, node_top, node_filler, node_stone, node_dust, pr) + end + end + end + -- if logging and not def.terrain_feature then minetest.log("action", "[vl_structures] "..def.name.." prepared at "..minetest.pos_to_string(pos).." in "..string.format("%.2fms (main: %.2fms)", (os.clock()-start)*1000, (os.clock()-prepare_start)*1000)) end + end + + -- note: pos is always the center position + minetest.place_schematic_on_vmanip(vm, vector_offset(pos, 0, (param.yoffset or 0), 0), param.schematic, param.rotation, param.replacements, param.force_placement, "place_center_x,place_center_z") + + for _,tmp in ipairs(daughters) do + local d, ds, rot = tmp[1], tmp[2], tmp[3] + --local dsize = vl_structures.size_rotated(ds.size, rot) + --local p = vector_offset(pos, d.pos.x - floor((ds.size.x-1)*0.5), d.pos.y + (yoffset or 0), + -- d.pos.z - floor((ds.size.z-1)*0.5)) + local p = vector_offset(pos, d.pos.x, d.pos.y + (yoffset or 0), d.pos.z) + minetest.place_schematic_on_vmanip(vm, p, ds, rot, d.replacements, d.force_placement, "place_center_x,place_center_z") + end + local endmain = os.clock() + vm:write_to_map(true) + -- Note: deliberately pos, p1 and p2 from the parent, as these are calls to the parent. + if def.loot then vl_structures.fill_chests(pmin,pmax,def.loot,pr) end + if def.construct_nodes then vl_structures.construct_nodes(pmin,pmax,def.construct_nodes) end + if def.after_place then def.after_place(pos,def,pr,pmin,pmax,size,param.rotation) end + if logging and not def.terrain_feature then + minetest.log("action", "[vl_structures] "..def.name.." spawned at "..minetest.pos_to_string(pos).." in "..string.format("%.2fms (main: %.2fms)", (os.clock()-start)*1000, (endmain-startmain)*1000)) + end +end + +--- Place a schematic with daughters (nether bulwark, nether outpost with bridges) +local function place_complex_schematics(pos, yoffset, schematic, rotation, def, pr) + if schematic and not schematic.size then -- e.g., igloo still passes filenames + schematic = vl_structures.load_schematic(schematic) + end + rotation = vl_structures.parse_rotation(rotation, pr) + local prepare = parse_prepare(def.prepare) + local ppos, pmin, pmax, size = vl_structures.get_extends(pos, schematic.size, yoffset, rotation, def.flags or DEFAULT_FLAGS) + -- area to emerge. Add some margin to allow for finding better suitable ground etc. + local tolerance = prepare.tolerance or DEFAULT_PREPARE.tolerance -- may be negative to disable foundations + if type(tolerance) ~= "number" then tolerance = 10 end -- for emerge only, min/max/liquid_surface + local emin, emax = vector_offset(pmin, 0, -math.max(tolerance, 0), 0), vector.offset(pmax, 0, math.max(tolerance, 0), 0) + -- if we need to generate a foundation, we need to emerge a larger area: + if prepare.foundation or prepare.clear then -- these functions need some extra margins. Must match mcl_foundations! + local padding = (prepare.padding or 0) + 3 + local depth = prepare.foundation and ((type(prepare.foundation) == "number" and prepare.foundation or DEFAULT_PREPARE.foundation) - 3) or 0 -- minimum depth + local height = prepare.clear and ((prepare.clear_top or DEFAULT_PREPARE.clear_top)*1.5+0.5*(size.y+yoffset)+2) or 0 -- headroom + emin = vector_offset(emin, -padding, depth, -padding) + emax = vector_offset(emax, padding, height, padding) + end + -- finally, add the configured emerge margin for daugther schematics + -- TODO: compute this instead? + if def.emerge_padding then + if #def.emerge_padding ~= 2 then error("Schematic "..def.name.." has an incorrect 'emerge_padding'. Must be two vectors.") end + emin, emax = emin + def.emerge_padding[1], emax + def.emerge_padding[2] + end + -- if logging and not def.terrain_feature then minetest.log("action", "[vl_structures] "..def.name.." needs emerge "..minetest.pos_to_string(emin).."-"..minetest.pos_to_string(emax)) end + minetest.emerge_area(emin, emax, emerge_complex_schematics, { name = def.name, + emin=emin, emax=emax, def=def, schematic=schematic, + pos=ppos, yoffset=yoffset, size=size, rotation=rotation, + pr=pr + }) +end + +-- TODO: remove blockseed? +function vl_structures.place_structure(pos, def, pr, blockseed, rot) + if not def then return end + local log_enabled = logging and not def.terrain_feature + -- load schematics the first time + if def.filenames and not def.schematics then + if #def.filenames == 0 then minetest.log("warning","[vl_structures] schematic "..def.name.." has an empty list of filenames.") end + def.schematics = {} + for _, filename in ipairs(def.filenames) do + local s = vl_structures.load_schematic(filename, def.name) + if s then table.insert(def.schematics, s) end + end + if def.daughters then + for _,d in pairs(def.daughters) do + d.schematics = {} + for _, filename in ipairs(d.filenames) do + local s = vl_structures.load_schematic(filename, d.name) + if s then table.insert(d.schematics, s) end + end + end + end + end + -- Apply vertical offset for schematic + local yoffset = (type(def.y_offset) == "function" and def.y_offset(pr)) or def.y_offset or 0 + if def.schematics and #def.schematics > 0 then + local schematic = def.schematics[pr:next(1,#def.schematics)] + rot = vl_structures.parse_rotation(rot or "random", pr) + place_complex_schematics(pos, yoffset, schematic, rot, def, pr) + if log_enabled then + minetest.log("verbose", "[vl_structures] "..def.name.." to be placed at "..minetest.pos_to_string(pos)) + end + return true + end + -- structure has a custom place function + if not def.place_func then + minetest.log("warning","[vl_structures] no schematics and no place_func for schematic "..def.name) + return false + end + local pp = yoffset ~= 0 and vector_offset(pos, 0, yoffset, 0) or pos + if def.place_func and def.prepare then + minetest.log("warning", "[vl_structures] needed prepare for "..def.name.." placed at "..minetest.pos_to_string(pp).." but do not have size information") + end + if def.place_func and def.place_func(pp,def,pr,blockseed) then + if not def.after_place or (def.after_place and def.after_place(pos,def,pr,pmin,pmax,size,param.rotation)) then + if def.sidelen then + local p1, p2 = vector_offset(pos,-def.sidelen,-def.sidelen,-def.sidelen), vector.offset(pos,def.sidelen,def.sidelen,def.sidelen) + if def.loot then vl_structures.fill_chests(p1,p2,def.loot,pr) end + if def.construct_nodes then vl_structures.construct_nodes(p1,p2,def.construct_nodes) end + end + if log_enabled then + minetest.log("action","[vl_structures] "..def.name.." placed at "..minetest.pos_to_string(pp)) + end + return true + else + minetest.log("warning","[vl_structures] after_place failed for schematic "..def.name) + return false + end + elseif log_enabled then + minetest.log("warning","[vl_structures] place_func failed for schematic "..def.name) + end +end + +--nospawn means it will be placed by another (non-nospawn) structure that contains it's structblock i.e. it will not be placed by mapgen directly +function vl_structures.register_structure(name,def,nospawn) + if vl_structures.is_disabled(name) then return end + def.name = name + vl_structures.registered_structures[name] = def + if def.prepare and def.prepare.clear == nil and (def.prepare.clear_bottom or def.prepare.clear_top) then def.prepare.clear = true end + if not def.noise_params and def.chunk_probability and not def.fill_ratio then + def.fill_ratio = 1.1/80/80 -- 1 per chunk, controlled by chunk probability only + end + if nospawn or def.nospawn then return end -- ice column, boulder + if def.filenames then + for _, filename in ipairs(def.filenames) do + if not mcl_util.file_exists(filename) then + minetest.log("warning","[vl_structures] schematic "..(name or "unknown").." is missing file "..basename(filename)) + return nil + end + end + end + if def.place_on then + minetest.register_on_mods_loaded(function() + def.deco = mcl_mapgen_core.register_decoration({ + name = "vl_structures:deco_"..name, + rank = def.rank or (def.terrain_feature and 900) or 100, -- run before regular decorations + deco_type = "schematic", + schematic = { size = {x = 1, y = 1, z = 1}, data = { { name = "ignore" } } }, + place_on = def.place_on, + spawn_by = def.spawn_by, + num_spawn_by = def.num_spawn_by, + sidelen = 80, -- no def.sidelen subdivisions for now, this field was used differently before + fill_ratio = def.fill_ratio, + noise_params = def.noise_params, + flags = def.flags or "place_center_x, place_center_z", + biomes = def.biomes, + y_max = def.y_max, + y_min = def.y_min + }, function() + def.deco_id = minetest.get_decoration_id("vl_structures:deco_"..name) + minetest.set_gen_notify({decoration=true}, { def.deco_id }) + end) + end) + end +end + +-- To avoid a cyclic dependency, run this when modules have finished loading +minetest.register_on_mods_loaded(function() +mcl_mapgen_core.register_generator("structures", nil, function(minp, maxp, blockseed) + local gennotify = minetest.get_mapgen_object("gennotify") + for _,struct in pairs(vl_structures.registered_structures) do + if struct.deco_id then + for _, pos in pairs(gennotify["decoration#"..struct.deco_id] or {}) do + local pr = PcgRandom(minetest.hash_node_position(pos) + worldseed + RANDOM_SEED_OFFSET) + local realpos = vector_offset(pos, 0, 1, 0) + if struct.chunk_probability == nil or pr:next(0, 1e9)/1e9 * struct.chunk_probability <= structure_boost then + vl_structures.place_structure(realpos, struct, pr, blockseed) + if struct.chunk_probability then break end -- one (attempt) per chunk only + end + end + elseif struct.static_pos then + local pr + for _, pos in pairs(struct.static_pos) do + if vector.in_area(pos, minp, maxp) then + pr = pr or PcgRandom(worldseed + RANDOM_SEED_OFFSET) + vl_structures.place_structure(pos, struct, pr, blockseed) + end + end + end + end + return false, false, false +end, 100, true) +end) + +local structure_spawns = {} +function vl_structures.register_structure_spawn(def) + --name,y_min,y_max,spawnon,biomes,chance,interval,limit + minetest.register_abm({ + label = "Spawn "..def.name, + nodenames = def.spawnon, + min_y = def.y_min or -31000, + max_y = def.y_max or 31000, + interval = def.interval or 60, + chance = def.chance or 5, + action = function(pos, node, active_object_count, active_object_count_wider) + local limit = def.limit or 7 + if active_object_count_wider > limit + mob_cap_animal then return end + if active_object_count_wider > mob_cap_player then return end + local p = vector_offset(pos, 0, 1, 0) + local pname = minetest.get_node(p).name + if def.type_of_spawning == "water" then + if pname ~= "mcl_core:water_source" and pname ~= "mclx_core:river_water_source" then return end + else + if pname ~= "air" then return end + end + if minetest.get_meta(pos):get_string("spawnblock") == "" then return end + if mg_name ~= "v6" and mg_name ~= "singlenode" and def.biomes then + if table.indexof(def.biomes, minetest.get_biome_name(minetest.get_biome_data(p).biome)) == -1 then + return + end + end + local mobdef = minetest.registered_entities[def.name] + if mobdef.can_spawn and not mobdef.can_spawn(p) then return end + minetest.add_entity(p, def.name) + end, + }) +end + diff --git a/mods/MAPGEN/vl_structures/init.lua b/mods/MAPGEN/vl_structures/init.lua new file mode 100644 index 000000000..878cb7994 --- /dev/null +++ b/mods/MAPGEN/vl_structures/init.lua @@ -0,0 +1,49 @@ +local modname = minetest.get_current_modname() +local S = minetest.get_translator(modname) +local modpath = minetest.get_modpath(modname) + +vl_structures = {} + +dofile(modpath.."/util.lua") +dofile(modpath.."/api.lua") + +--- /spawnstruct chat command +minetest.register_chatcommand("spawnstruct", { + params = mcl_dungeons and "dungeon" or "", + description = S("Generate a pre-defined structure near your position."), + privs = {debug = true}, + func = function(name, param) + local player = minetest.get_player_by_name(name) + if not player then return end + local pos = player:get_pos() + if not pos then return end + pos = vector.round(pos) + local dir = minetest.yaw_to_dir(player:get_look_horizontal()) + local rot = math.abs(dir.x) > math.abs(dir.z) and (dir.x < 0 and "270" or "90") or (dir.z < 0 and "180" or "0") + local seed = minetest.hash_node_position(pos) + local pr = PcgRandom(seed) + local errord = false + if param == "dungeon" and mcl_dungeons and mcl_dungeons.spawn_dungeon then + mcl_dungeons.spawn_dungeon(pos, rot, pr) + return true, "Spawning "..param + elseif param == "" then + minetest.chat_send_player(name, S("Error: No structure type given. Please use “/spawnstruct ”.")) + else + for n,d in pairs(vl_structures.registered_structures) do + if n == param then + vl_structures.place_structure(pos, d, pr, seed, rot) + return true, "Spawning "..param + end + end + minetest.chat_send_player(name, S("Error: Unknown structure type. Please use “/spawnstruct ”.")) + end + end +}) +minetest.register_on_mods_loaded(function() + local p = "" + for n,_ in pairs(vl_structures.registered_structures) do + p = p .. " | ".. n + end + minetest.registered_chatcommands["spawnstruct"].params = minetest.registered_chatcommands["spawnstruct"].params .. p +end) + diff --git a/mods/MAPGEN/vl_structures/mod.conf b/mods/MAPGEN/vl_structures/mod.conf new file mode 100644 index 000000000..7d3b8661a --- /dev/null +++ b/mods/MAPGEN/vl_structures/mod.conf @@ -0,0 +1,4 @@ +name = vl_structures +author = kno10 +description = Structures API for VoxeLibre and Mineclonia +depends = mcl_init, mcl_util, mcl_loot, vl_terraforming diff --git a/mods/MAPGEN/vl_structures/util.lua b/mods/MAPGEN/vl_structures/util.lua new file mode 100644 index 000000000..3d7ee74aa --- /dev/null +++ b/mods/MAPGEN/vl_structures/util.lua @@ -0,0 +1,132 @@ +local peaceful = minetest.settings:get_bool("only_peaceful_mobs", false) + +local floor = math.floor +local vector_offset = vector.offset + +local ROTATIONS = { "0", "90", "180", "270" } +--- Parse a rotation value +-- @param rotation string: when "random", a rotation is chosen at random +-- @param[opt] pr PseudoRandom: random generator +-- @return Rotation +function vl_structures.parse_rotation(rotation, pr) + if rotation == "random" and pr then return ROTATIONS[pr:next(1,#ROTATIONS)] end + return rotation +end + +--- Get the size after rotation. +-- @param size vector: Size information +-- @param rotation string or number: only 0, 90, 180, 270 are allowed +-- @return vector: new vector, for safety +function vl_structures.size_rotated(size, rotation) + if rotation == "90" or rotation == "270" or rotation == 90 or rotation == 270 then return vector.new(size.z, size.y, size.x) end + return vector.copy(size) +end + +--- Get top left position after apply centering flags and padding. +-- @param pos vector: Placement position +-- @param[opt] size vector: Size information +-- @param[opt] flags string or table: as in minetest.place_schematic, place_center_x, place_center_y; default none +-- @return vector: new vector, for safety +function vl_structures.top_left_from_flags(pos, size, flags) + local dx, dy, dz = 0, 0, 0 + -- must match src/mapgen/mg_schematic.cpp to be consistent + if type(flags) == "table" then + if flags["place_center_x"] ~= nil then dx = -floor((size.x-1)*0.5) end + if flags["place_center_y"] ~= nil then dy = -floor((size.y-1)*0.5) end + if flags["place_center_z"] ~= nil then dz = -floor((size.z-1)*0.5) end + return vector_offset(pos, dx, dy, dz) + elseif type(flags) == "string" then + if string.find(flags, "place_center_x") then dx = -floor((size.x-1)*0.5) end + if string.find(flags, "place_center_y") then dy = -floor((size.y-1)*0.5) end + if string.find(flags, "place_center_z") then dz = -floor((size.z-1)*0.5) end + return vector_offset(pos, dx, dy, dz) + end + return pos +end + +--- Get the extends of a schematic after rotation and flags +-- @param pos vector: position of base +-- @param size vector: size of structure +-- @param[opt] yoffset number: vertical offset +-- @param[opt] rotation string: rotation value +-- @param[opt] flags string or table: as in minetest.place_schematic, place_center_x, place_center_y; default none +-- @return center on base level, area minimum, area maximum, rotated size (=pmax-pmin+1) +function vl_structures.get_extends(pos, size, yoffset, rotation, flags) + local size = vl_structures.size_rotated(size, rotation) + local pmin = vl_structures.top_left_from_flags(pos, size, flags or DEFAULT_FLAGS) + local cent = vector_offset(pmin, floor((size.x-1)*0.5), 0, floor((size.z-1)*0.5)) -- center + pmin.y = pmin.y + (yoffset or 0) -- to pmin and pmax only + local pmax = vector_offset(pmin, size.x - 1, size.y - 1, size.z - 1) + return cent, pmin, pmax, size +end + +--- Call all on_construct handlers. Also called from mcl_villages for job sites +-- @param pos Node position +function vl_structures.init_node_construct(pos) + local node = minetest.get_node(pos) + local def = node and minetest.registered_nodes[node.name] + if def and def.on_construct then return def.on_construct(pos) end +end + +--- Call on_construct handlers for all nodes of given types +-- @param p1 vector: Lowest coordinates of range +-- @param p2 vector: Highest coordinates of range +-- @param nodes string or table: node name or list of node names +-- @return nodes found +function vl_structures.construct_nodes(p1,p2,nodes) + local nn = minetest.find_nodes_in_area(p1,p2,nodes) + for _,p in pairs(nn) do vl_structures.init_node_construct(p) end + return nn or {} +end + +--- Fill loot chests +-- @param p1 vector: Lowest coordinates of range +-- @param p2 vector: Highest coordinates of range +-- @param loot table: Loot table +-- @param pr PseudoRandom: random generator +function vl_structures.fill_chests(p1,p2,loot,pr) + for it,lt in pairs(loot) do + local nodes = minetest.find_nodes_in_area(p1, p2, it) + for _,p in pairs(nodes) do + local lootitems = mcl_loot.get_multi_loot(lt, pr) + vl_structures.init_node_construct(p) + local meta = minetest.get_meta(p) + local inv = meta:get_inventory() + mcl_loot.fill_inventory(inv, "main", lootitems, pr) + end + end +end + +--- Spawn mobs for a structure +-- @param mob string: mob to spawn +-- @param spawnon string or table: nodes to spawn on +-- @param p1 vector: Lowest coordinates of range +-- @param p2 vector: Highest coordinates of range +-- @param pr PseudoRandom: random generator +-- @param n number: Number of mobs to spawn +-- @param water boolean: Spawn water mobs +function vl_structures.spawn_mobs(mob,spawnon,p1,p2,pr,n,water) + n = n or 1 + local sp = {} + if water then + local nn = minetest.find_nodes_in_area(p1,p2,spawnon) + for k,v in pairs(nn) do + if minetest.get_item_group(minetest.get_node(vector_offset(v,0,1,0)).name,"water") > 0 then + table.insert(sp,v) + end + end + else + sp = minetest.find_nodes_in_area_under_air(p1,p2,spawnon) + end + table.shuffle(sp) + local count = 0 + local mob_def = minetest.registered_entities[mob] + local enabled = (not peaceful) or (mob_def and mob_spawn_class ~= "hostile") + for _, node in pairs(sp) do + if enabled and count < n and minetest.add_entity(vector_offset(node, 0, 1, 0), mob) then + count = count + 1 + end + minetest.get_meta(node):set_string("spawnblock", "yes") -- note: also in peaceful mode! + end +end + diff --git a/mods/MAPGEN/vl_terraforming/API.md b/mods/MAPGEN/vl_terraforming/API.md new file mode 100644 index 000000000..291ae5aa4 --- /dev/null +++ b/mods/MAPGEN/vl_terraforming/API.md @@ -0,0 +1,99 @@ +# vl_terraforming +Terraforming module for VoxeLibre and MineClonia + +This module provides the following key functionalities: + +- given a position, find the ground surface +- given a position and size, find a balanced height (trimmed median height) +- build a baseplate for a building +- clear the area above a building + + +## Rounded corners support + +To get nicer looking baseplates, the code supports rounded corners. + +These are obtained by intersecting the square with an ellipse. +At zero rounding, we want the line go through the corner, at sx/2, sz/2. + +For this, we need to make ellipse sized $2a=\sqrt{2} sx$, $2b=\sqrt{2} sz$, +Which yields $a = sx/\sqrt{2}$, $b=sz/\sqrt{2}$ and $a^2=0.5 sx^2$, $b^2=0.5 sz^2$ +To get corners, we decrease $a$ and $b$ by the corners parameter each +The ellipse condition $dx^2/a^2+dz^2/b^2 \leq 1$ then yields $dx^2/(0.5 sx^2) + dz^2/(0.5 sz^2) \leq 1$ +We use $wx2=2 sx^-2$, $wz2=2 sz^-2$ and then $dx^2 wx2 + dz^2 wz2 \leq 1$. + + +## vl_terraforming.find_ground_vm(vm, pos) + +Find ground starting at the given position. When in a solid area, moves up; otherwise searches downwards. + +This will ignore trees, mushrooms, and similar surface decorations. + + +## vl_terraforming.find_under_air_vm(vm, pos) + +Find ground or liquid surface, starting at the given position. When in a solid or liquid area, moves up; otherwise searches downwards. + +This will ignore trees, mushrooms, and similar surface decorations. + + +## vl_terraforming.find_liquid_surface_vm(vm, pos) + +Find a liquid surface starting at the given position. When in a solid or liquid area, moves up; otherwise searches downwards. + +This will ignore trees, mushrooms, and similar surface decorations. + + + +## vl_terraforming.find_level_vm(vm, cpos, size, tolerance, mode) + +Find "level" ground for a building, centered at the given position, and of the given size. + +For this, five samples are taken: center, top left, top right, bottom left, and bottom right. + +One of these values may be "extreme", and tolerance specifies the maximum height difference of the remaining four values. + +The (rounded) median of these values is used, unless tolerance is set to "min" or "max". + +The "mode" can be set to "solid" (default), "liquid" (liquid surfaces only), "under_air" (both liquid and solid surfaces). + + +## vl_terraforming.foundation_vm(vm, px, py, pz, sx, sy, sz, corners, surface_mat, platform_mat, stone_mat, dust_mat, pr) + +The position (px, py, pz) and the size (sx, sy, sz) give the volume of the main base plate, +where sy < 0, so that you can later place the structure at (px, py, pz). + +The baseplate will be grown by 1 in the level below, to allow mobs to enter, then randomly fade away below. +-sy can be used to control a minimum depth. + +Corners specifies how much to cut the corners, use 0 for a square baseplate. + +The materials specified (as lua nodes, to have param2 support) are used a follows: + +- surface_mat for surface nodes +- platform_mat below surface nodes +- stone_mat randomly used below platform_mat +- dust_mat on top of surface nodes (snow cover, optional) + +pr is a PcgRandom random generator + + +## vl_terraforming.clearance_vm(vm, px, py, pz, sx, sy, sz, corners, surface_mat, dust_mat, pr) + +The position (px, py, pz) and the size (sx, sy, sz) give the volume overhead to clear. + +The area will be grown by 1 above, to allow mobs to enter, then randomly fade away as height increases beyond sy. + +Corners specifies how much to cut the corners, use 0 for a square area. + +The surface_mat will be used to turn nodes into surface nodes when widening the area. + +pr is a PcgRandom random generator + +## TODO + +- [ ] add an API that works on VM buffers +- [ ] add an API version working on the non-VM API +- [ ] benchmark if VM is actually faster than not using VM (5.9 has some optimizations not in VM) +- [ ] improve tree removal + diff --git a/mods/MAPGEN/vl_terraforming/clearance.lua b/mods/MAPGEN/vl_terraforming/clearance.lua new file mode 100644 index 000000000..fed999842 --- /dev/null +++ b/mods/MAPGEN/vl_terraforming/clearance.lua @@ -0,0 +1,174 @@ +local AIR = {name = "air"} +local abs = math.abs +local max = math.max +local floor = math.floor +local vector_new = vector.new +local is_solid_not_tree = vl_terraforming._is_solid_not_tree +local is_tree_not_leaves = vl_terraforming._is_tree_not_leaves + +--- Clear an area for a structure +-- +-- Rounding: we model an ellipse. At zero rounding, we want the line go through the corner, at sx/2, sz/2. +-- For this, we need to make ellipse sized 2a=sqrt(2)*sx, 2b=sqrt(2)*sz, +-- Which yields a = sx/sqrt(2), b=sz/sqrt(2) and a^2=sx^2*0.5, b^2=sz^2*0.5 +-- To get corners, we decrease a and b by approx. corners each +-- The ellipse condition dx^2/a^2+dz^2/b^2 <= 1 then yields dx^2/(sx^2*0.5) + dz^2/(sz^2*0.5) <= 1 +-- We use wx2=sx^-2*2, wz2=sz^-2*2 and then dx^2*wx2+dz^2*wz2 <= 1 +-- +-- @param vm VoxelManip: Lua voxel manipulator +-- @param px number: lowest x +-- @param py number: lowest y +-- @param pz number: lowest z +-- @param sx number: x width +-- @param sy number: y height +-- @param sz number: z depth +-- @param corners number: corner rounding +-- @param surface_mat Node: surface node material +-- @param dust_mat Node: surface dust material +-- @param pr PcgRandom: random generator +function vl_terraforming.clearance_vm(vm, px, py, pz, sx, sy, sz, corners, surface_mat, dust_mat, pr) + if sx <= 0 or sy <= 0 or sz <= 0 then return end + local get_node_at = vm.get_node_at + local set_node_at = vm.set_node_at + corners = corners or 0 + local wx2, wz2 = max(sx - corners, 1)^-2 * 2, max(sz - corners, 1)^-2 * 2 + local cx, cz = px + sx * 0.5 - 0.5, pz + sz * 0.5 - 0.5 + local min_clear, max_clear = py+sy, py+floor(sy*1.5+2) -- todo: make more parameterizable, but adds another parameter + -- excavate the needed volume and some headroom + local vec = vector_new(0, 0, 0) -- single vector, to avoid allocations -- performance! + for xi = px-1,px+sx do + local dx = abs(cx-xi) + local dx2 = max(dx+0.51,0)^2*wx2 + local dx21 = max(dx-0.49,0)^2*wx2 + vec.x = xi + for zi = pz-1,pz+sz do + local dz = abs(cz-zi) + local dz2 = max(dz+0.51,0)^2*wz2 + local dz21 = max(dz-0.49,0)^2*wz2 + vec.z = zi + if xi >= px and xi < px+sx and zi >= pz and zi < pz+sz and dx2+dz2 <= 1 then + vec.y = py + if vm:get_node_at(vec).name ~= "mcl_core:bedrock" then set_node_at(vm, vec, AIR) end + vec.y = py - 1 + local n = get_node_at(vm, vec) + if n and n.name ~= surface_mat.name and is_solid_not_tree(n) then + set_node_at(vm, vec, surface_mat) + end + for yi = py+1,min_clear do -- full height for inner area + vec.y = yi + if vm:get_node_at(vec).name ~= "mcl_core:bedrock" then set_node_at(vm, vec, AIR) end + end + elseif dx21+dz21 <= 1 then + -- widen the cave above by 1, to make easier to enter for mobs + -- todo: make configurable? + vec.y = py + 1 + local name = vm:get_node_at(vec).name + if name ~= "mcl_core:bedrock" then + local mat = AIR + if dust_mat then + vec.y = py + if vm:get_node_at(vec).name == surface_mat.name then mat = dust_mat end + vec.y = py + 1 + end + set_node_at(vm, vec, mat) + end + for yi = py+2,min_clear-1 do + vec.y = yi + if vm:get_node_at(vec).name ~= "mcl_core:bedrock" then set_node_at(vm, vec, AIR) end + if yi > py+4 then + local p = (yi-py) / (max_clear-py) + --minetest.log(tostring(p).."^2 "..tostring(p*p).." rand: "..pr:next(0,1e9)/1e9) + if (pr:next(0,1e9)/1e9) < p then break end + end + end + -- remove some tree parts and fix surfaces down + for yi = py,py-1,-1 do + vec.y = yi + local n = get_node_at(vm, vec) + if is_tree_not_leaves(n) then + set_node_at(vm, vec, surface_mat) + if dust_mat and yi == py then + vec.y = yi + 1 + if vm:get_node_at(vec).name == "air" then set_node_at(vm, vec, dust_mat) end + end + else + if n and n.name ~= surface_mat.name and is_solid_not_tree(n) then + set_node_at(vm, vec, surface_mat) + if dust_mat then + vec.y = yi + 1 + if vm:get_node_at(vec).name == "air" then set_node_at(vm, vec, dust_mat) end + end + end + break + end + end + end + end + end + -- some extra gaps for entry + -- todo: make optional instead of hard-coded 25% + -- todo: only really useful if there is space at px-3,py+3 to px-3,py+5 + --[[ + for xi = px-2,px+sx+1 do + local dx21 = max(abs(cx-xi)-0.49,0)^2*wx2 + local dx22 = max(abs(cx-xi)-1.49,0)^2*wx2 + for zi = pz-2,pz+sz+1 do + local dz21 = max(abs(cz-zi)-0.49,0)^2*wz2 + local dz22 = max(abs(cz-zi)-1.49,0)^2*wz2 + if dx21+dz21 > 1 and dx22+dz22 <= 1 and pr:next(1,4) == 1 then + if py+4 < sy then + for yi = py+2,py+4 do + vec = vector_new(xi, yi, zi) + if vm:get_node_at(vec).name ~= "mcl_core:bedrock" then set_node_at(vm, vec, v) end + end + end + for yi = py+1,py-1,-1 do + local n = get_node_at(vm, vector_new(xi, yi, zi)) + if is_tree_bot_leaves(n) and n.name ~= "mcl_core:bedrock" then + set_node_at(vm, vector_new(xi, yi, zi), AIR) + else + if n and n.name ~= surface_mat.name and is_solid_not_tree(n) then + set_node_at(vm, vector_new(xi, yi, zi), surface_mat) + end + break + end + end + end + end + end + ]]-- + -- cave some additional area overhead, try to make it interesting though + for yi = min_clear+1,max_clear do + local dy2 = max(yi-min_clear-1,0)^2*0.05 + local active = false + for xi = px-2,px+sx+1 do + local dx22 = max(abs(cx-xi)-1.49,0)^2*wx2 + for zi = pz-2,pz+sz+1 do + local dz22 = max(abs(cz-zi)-1.49,0)^2*wz2 + local keep_trees = (xi=px+sx) or (zi=pz+sz) -- TODO make parameter? + if dx22+dy2+dz22 <= 1 then + vec.x, vec.y, vec.z = xi, yi, zi + local name = get_node_at(vm, vec).name + -- don't break bedrock or air + if name == "air" or name == "ignore" or name == "mcl_core:bedrock" or name == "mcl_villages:no_paths" then goto continue end + local meta = minetest.registered_items[name] + local groups = meta and meta.groups + local is_tree = groups.leaves or groups.tree or (groups.compostability or 0 > 50) + if keep_trees and is_tree then goto continue end + vec.y = yi-1 + -- do not clear above solid + local name_below = get_node_at(vm, vec).name + if name_below ~= "air" and name_below ~= "ignore" and name_below ~= "mcl_core:bedrock" then goto continue end + -- try to completely remove trees overhead + -- stop randomly depending on fill, to narrow down the caves + if not keep_trees and not is_tree and (pr:next(0,1e9)/1e9)^0.5 > 1-(dx22+dy2+dz22-0.1) then goto continue end + vec.x, vec.y, vec.z = xi, yi, zi + set_node_at(vm, vec, AIR) + active = true + ::continue:: + end + end + end + if not active then break end + end +end diff --git a/mods/MAPGEN/vl_terraforming/foundation.lua b/mods/MAPGEN/vl_terraforming/foundation.lua new file mode 100644 index 000000000..3769ac147 --- /dev/null +++ b/mods/MAPGEN/vl_terraforming/foundation.lua @@ -0,0 +1,117 @@ +local abs = math.abs +local max = math.max +local vector_new = vector.new + +local is_solid_not_tree = vl_terraforming._is_solid_not_tree +local make_solid_vm = vl_terraforming._make_solid_vm + +--- Grow the foundation downwards +-- @param vm VoxelManip: Lua Voxel Manipulator +-- @param xi number: x coordinate +-- @param yi number: y coordinate +-- @param zi number: z coordinate +-- @param pr PcgRandom: random generator +-- @param surface_mat Node: surface material node +-- @param platform_mat Node: platform material node +-- @param stone_mat Node: stone material node +local function grow_foundation_vm(vm,xi,yi,zi,pr,surface_mat,platform_mat,stone_mat) + local get_node_at = vm.get_node_at + local pos, n, c = vector_new(xi,yi,zi), nil, 0 + if is_solid_not_tree(get_node_at(vm, pos)) then return false end -- already solid, nothing to do + pos.y = pos.y + 1 + local cur = get_node_at(vm, pos) + if not is_solid_not_tree(cur) then return false end -- above is empty, do not fill below + if cur and cur.name and cur.name ~= surface_mat.name then platform_mat = cur end + if pr:next(1,4) == 1 then platform_mat = stone_mat end -- randomly switch to stone sometimes + -- count solid nodes above otherwise + for x = xi-1,xi+1 do + for z = zi-1,zi+1 do + pos.x, pos.z = x, z + if is_solid_not_tree(get_node_at(vm, pos)) then c = c + 1 end + end + end + -- stop randomly depending on fill, to narrow down the foundation + -- TODO: allow controlling the random depth with an additional parameter? + if (pr:next(0,1e9)/1e9)^2 > c/9.1 then return false end + pos.x, pos.y, pos.z = xi, yi, zi + if get_node_at(vm, pos).name == "mcl_core:bedrock" then return false end + vm:set_node_at(pos, platform_mat) + return true +end +--- Generate a foundation from px,py,pz with size sx,sy,sz (sy < 0) plus some margin +-- TODO: add support for dust_mat (snow) +-- +-- Rounding: we model an ellipse. At zero rounding, we want the line go through the corner, at sx/2, sz/2. +-- For this, we need to make ellipse sized 2a=sqrt(2)*sx, 2b=sqrt(2)*sz, +-- Which yields a = sx/sqrt(2), b=sz/sqrt(2) and a^2=sx^2*0.5, b^2=sz^2*0.5 +-- To get corners, we decrease a and b by approx. corners each +-- The ellipse condition dx^2/a^2+dz^2/b^2 <= 1 then yields dx^2/(sx^2*0.5) + dz^2/(sz^2*0.5) <= 1 +-- We use wx2=sx^-2*2, wz2=sz^-2*2 and then dx^2*wx2+dz^2*wz2 <= 1 +-- +-- @param vm VoxelManip: Lua Voxel Manipulator +-- @param px number: lowest x +-- @param py number: lowest y +-- @param pz number: lowest z +-- @param sx number: x width +-- @param sy number: y height +-- @param sz number: z depth +-- @param corners number: Corner rounding +-- @param surface_mat Node: surface material node +-- @param platform_mat Node: platform material node +-- @param stone_mat Node: stone material node +-- @param dust_mat Node: dust material, optional +-- @param pr PcgRandom: random generator +function vl_terraforming.foundation_vm(vm, px, py, pz, sx, sy, sz, corners, surface_mat, platform_mat, stone_mat, dust_mat, pr) + if sx <= 0 or sy >= 0 or sz <= 0 then return end + local get_node_at = vm.get_node_at + local set_node_at = vm.set_node_at + corners = corners or 0 + local wx2, wz2 = max(sx - corners, 1)^-2 * 2, max(sz - corners, 1)^-2 * 2 + local cx, cz = px + sx * 0.5 - 0.5, pz + sz * 0.5 - 0.5 + -- generate a baseplate (2 layers, lower is wider + local pos = vector_new(px, py, pz) + for xi = px-1,px+sx do + local dx2 = max(abs(cx-xi)+0.51,0)^2*wx2 + local dx21 = max(abs(cx-xi)-0.49,0)^2*wx2 + pos.x = xi + for zi = pz-1,pz+sz do + local dz2 = max(abs(cz-zi)+0.51,0)^2*wz2 + local dz21 = max(abs(cz-zi)-0.49,0)^2*wz2 + pos.z = zi + if xi >= px and xi < px+sx and zi >= pz and zi < pz+sz and dx2+dz2 <= 1 then + pos.y = py + if get_node_at(vm, pos).name ~= "mcl_core:bedrock" then + set_node_at(vm, pos, surface_mat) + if dust_mat then + pos.y = py + 1 + if get_node_at(vm, pos).name == "air" then set_node_at(vm, pos, dust_mat) end + end + pos.y = py - 1 + make_solid_vm(vm, pos, platform_mat) + end + elseif dx21+dz21 <= 1 then -- and pr:next(1,4) < 4 then -- TODO: make randomness configurable. + -- slightly widen the baseplate below, to make easier to enter for mobs + pos.y = py - 1 + make_solid_vm(vm, pos, surface_mat) + if dust_mat then + pos.y = py + if get_node_at(vm, pos).name == "air" then set_node_at(vm, pos, dust_mat) end + end + end + end + end + -- construct additional baseplate below, also try to make it interesting + for yi = py-2,py-20,-1 do + local dy2 = max(0,py-2-yi)^2*0.05 + local active = false + for xi = px-1,px+sx do + local dx22 = max(abs(cx-xi)-1.49,0)^2*wx2 + for zi = pz-1,pz+sz do + local dz22 = max(abs(cz-zi)-1.49,0)^2*wz2 + if dx22+dy2+dz22 <= 1 and grow_foundation_vm(vm,xi,yi,zi,pr,surface_mat,platform_mat,stone_mat) then active = true end + end + end + if not active and yi < py + sy then break end + end + -- TODO: add back additional steps for easier entering, optional, and less regular? +end diff --git a/mods/MAPGEN/vl_terraforming/init.lua b/mods/MAPGEN/vl_terraforming/init.lua new file mode 100644 index 000000000..326403c52 --- /dev/null +++ b/mods/MAPGEN/vl_terraforming/init.lua @@ -0,0 +1,8 @@ +local modname = minetest.get_current_modname() +local modpath = minetest.get_modpath(modname) +vl_terraforming = {} + +dofile(modpath.."/util.lua") +dofile(modpath.."/clearance.lua") +dofile(modpath.."/foundation.lua") +dofile(modpath.."/level.lua") diff --git a/mods/MAPGEN/vl_terraforming/level.lua b/mods/MAPGEN/vl_terraforming/level.lua new file mode 100644 index 000000000..388f1b713 --- /dev/null +++ b/mods/MAPGEN/vl_terraforming/level.lua @@ -0,0 +1,205 @@ +local min = math.min +local floor = math.floor +local ceil = math.ceil +local vector_copy = vector.copy +local is_liquid = vl_terraforming._is_liquid +local is_solid_not_tree = vl_terraforming._is_solid_not_tree + +--- Find ground below a given position +-- @param vm VoxelManip: buffer +-- @param pos vector: Start position +-- @return position and material of surface +function vl_terraforming.find_ground_vm(vm, pos) + if not pos then return nil, nil end + pos = vector_copy(pos) + local cur = vm:get_node_at(pos) + if cur.name == "ignore" then + local e1, e2 = vm:get_emerged_area() + minetest.log("warning","find_ground with invalid position (outside of emerged area?) at "..minetest.pos_to_string(pos) + ..": "..tostring(cur and cur.name).." area: "..minetest.pos_to_string(e1).." "..minetest.pos_to_string(e2)) + return nil + end + if is_solid_not_tree(cur) then -- find up + local prev = cur + while true do + pos.y = pos.y + 1 + local cur = vm:get_node_at(pos) + if not cur or cur.name == "ignore" then + -- minetest.log("action", "No ground, "..tostring(cur and cur.name).." over "..tostring(prev and prev.name).." at "..minetest.pos_to_string(pos)) + return nil + end + if not is_solid_not_tree(cur) then + pos.y = pos.y - 1 + return pos, prev + end + prev = cur + end + else -- find down + while true do + pos.y = pos.y - 1 + local prev = cur + local cur = vm:get_node_at(pos) + if not cur or cur.name == "ignore" then + -- minetest.log("action", "No ground, "..tostring(cur and cur.name).." below "..tostring(prev and prev.name).." at "..minetest.pos_to_string(pos)) + return nil + end + if is_liquid(cur) then + return nil + end + if is_solid_not_tree(cur) then + return pos, cur + end + end + end +end +local find_ground_vm = vl_terraforming.find_ground_vm + +--- Find ground or liquid surface for a given position +-- @param vm VoxelManip: buffer +-- @param pos vector: Start position +-- @return position and material of surface +function vl_terraforming.find_under_air_vm(vm, pos) + if not pos then return nil, nil end + pos = vector_copy(pos) + local cur = vm:get_node_at(pos) + if cur.name == "ignore" then + local e1, e2 = vm:get_emerged_area() + minetest.log("warning","find_under_air with invalid position (outside of emerged area?) at "..minetest.pos_to_string(pos) + ..": "..tostring(cur and cur.name).." area: "..minetest.pos_to_string(e1).." "..minetest.pos_to_string(e2)) + return nil + end + if is_solid_not_tree(cur) or is_liquid(cur) then -- find up + local prev = cur + while true do + pos.y = pos.y + 1 + local cur = vm:get_node_at(pos) + if not cur or cur.name == "ignore" then + -- minetest.log("action", "No ground, "..tostring(cur and cur.name).." over "..tostring(prev and prev.name).." at "..minetest.pos_to_string(pos)) + return nil + end + if not is_solid_not_tree(cur) and not is_liquid(cur) then + pos.y = pos.y - 1 + -- minetest.log("action", "Found surface: "..minetest.pos_to_string(pos).." "..tostring(prev and prev.name).." under "..tostring(cur and cur.name)) + return pos, prev + end + prev = cur + end + else -- find down + while true do + pos.y = pos.y - 1 + local prev = cur + local cur = vm:get_node_at(pos) + if not cur or cur.name == "ignore" then + -- minetest.log("action", "No ground, "..tostring(cur and cur.name).." below "..tostring(prev and prev.name).." at "..minetest.pos_to_string(pos)) + return nil + end + if is_solid_not_tree(cur) or is_liquid(cur) then + -- minetest.log("action", "Found surface: "..minetest.pos_to_string(pos).." "..(cur and cur.name).." over "..(prev and prev.name)) + return pos, cur + end + end + end +end +local find_under_air_vm = vl_terraforming.find_under_air_vm + +--- Find liquid surface for a given position +-- @param vm VoxelManip: buffer +-- @param pos vector: Start position +-- @return position and material of surface +function vl_terraforming.find_liquid_surface_vm(vm, pos) + if not pos then return nil, nil end + pos = vector_copy(pos) + local cur = vm:get_node_at(pos) + if cur.name == "ignore" then + local e1, e2 = vm:get_emerged_area() + minetest.log("warning","find_liquid_surface with invalid position (outside of emerged area?) at "..minetest.pos_to_string(pos) + ..": "..tostring(cur and cur.name).." area: "..minetest.pos_to_string(e1).." "..minetest.pos_to_string(e2)) + return nil + end + if is_liquid(cur) then -- find up + local prev = cur + while true do + pos.y = pos.y + 1 + local cur = vm:get_node_at(pos) + if not cur or cur.name == "ignore" then + -- minetest.log("action", "No ground, "..tostring(cur and cur.name).." over "..tostring(prev and prev.name).." at "..minetest.pos_to_string(pos)) + return nil + end + if not is_liquid(cur) then + pos.y = pos.y - 1 + -- minetest.log("action", "Found surface: "..minetest.pos_to_string(pos).." "..tostring(prev and prev.name).." under "..tostring(cur and cur.name)) + return pos, prev + end + prev = cur + end + else -- find down + while true do + pos.y = pos.y - 1 + local prev = cur + local cur = vm:get_node_at(pos) + if not cur or cur.name == "ignore" then + -- minetest.log("action", "No ground, "..tostring(cur and cur.name).." below "..tostring(prev and prev.name).." at "..minetest.pos_to_string(pos)) + return nil + end + if is_solid_not_tree(cur) then + -- minetest.log("action", "No ground, "..tostring(cur and cur.name).." below "..tostring(prev and prev.name).." at "..minetest.pos_to_string(pos)) + return nil + end + if is_liquid(cur) then + -- minetest.log("action", "Found surface: "..minetest.pos_to_string(pos).." "..(cur and cur.name).." over "..(prev and prev.name)) + return pos, cur + end + end + end +end +local find_liquid_surface_vm = vl_terraforming.find_liquid_surface_vm + +--- find suitable height for a structure of this size +-- @param vm VoxelManip: to read data +-- @param cpos vector: center +-- @param size vector: area size +-- @param tolerance number or string: maximum height difference allowed, default 8. +-- @param mode string: "solid" (default), "liquid_surface", "under_air" +-- @return position over surface, surface material (or nil, nil) +function vl_terraforming.find_level_vm(vm, cpos, size, tolerance, mode) + local find_ground = find_ground_vm + if mode == "liquid_surface" or mode == "liquid" then find_ground = find_liquid_surface_vm end + if mode == "under_air" then find_ground = find_under_air_vm end + local pos, surface_material = find_ground(vm, cpos) -- center + if not pos then return nil, nil end + local ys = { pos.y } + pos.y = pos.y + 1 -- above ground + if size.x == 1 and size.z == 1 then return pos end + pos.x, pos.z = pos.x - floor((size.x-1)/2), pos.z - floor((size.z-1)/2) -- top left + local pos_c = find_ground(vm, pos) + if pos_c then table.insert(ys, pos_c.y) end + pos.x = pos.x + size.x - 1 -- top right + local pos_c = find_ground(vm, pos) + if pos_c then table.insert(ys, pos_c.y) end + pos.z = pos.z + size.z - 1 -- bottom right + local pos_c = find_ground(vm, pos) + if pos_c then table.insert(ys, pos_c.y) end + pos.x = pos.x - (size.x - 1) -- bottom left + local pos_c = find_ground(vm, pos) + if pos_c then table.insert(ys, pos_c.y) end + table.sort(ys) + + tolerance = tolerance or 8 + if tolerance == "min" then + cpos.y = ys[1] + 1 + return cpos, surface_material + end + if tolerance == "max" then + cpos.y = ys[#ys] + 1 + return cpos, surface_material + end + -- well supported base, not too uneven? + if #ys < 4 or min(ys[#ys-1]-ys[1], ys[#ys]-ys[2]) > tolerance then + minetest.log("action", "[vl_terraforming] ground too uneven: "..#ys.." positions: "..({dump(ys):gsub("[\n\t ]+", " ")})[1] + .." tolerance "..tostring(#ys > 2 and min(ys[#ys-1]-ys[1], ys[#ys]-ys[2])).." > "..tolerance) + return nil, nil + end + cpos.y = floor(0.5 * (ys[floor(1 + (#ys - 1) * 0.5)] + ys[ceil(1 + (#ys - 1) * 0.5)]) + 0.55) -- median except for largest, rounded, over surface + return cpos, surface_material +end + diff --git a/mods/MAPGEN/vl_terraforming/mod.conf b/mods/MAPGEN/vl_terraforming/mod.conf new file mode 100644 index 000000000..fdec41d98 --- /dev/null +++ b/mods/MAPGEN/vl_terraforming/mod.conf @@ -0,0 +1,3 @@ +name = vl_terraforming +author = kno10 +description = Terraforming API for VoxeLibre and Mineclonia diff --git a/mods/MAPGEN/vl_terraforming/util.lua b/mods/MAPGEN/vl_terraforming/util.lua new file mode 100644 index 000000000..58715e020 --- /dev/null +++ b/mods/MAPGEN/vl_terraforming/util.lua @@ -0,0 +1,63 @@ +--- fairly strict: air, ignore, or no_paths marker +-- @param node string or Node: node or node name +-- @return true for air and ignore nodes +function vl_terraforming._is_air(node) + local name = node.name or node + return name == "air" or name == "ignore" or name == "mcl_villages:no_paths" +end + +--- check if a node is walkable (solid), but not tree/leaves/fungi/bamboo/vines/etc. +-- @param node LUA node or node name +-- @return truthy when solid but not tree/decoration/fungi +function vl_terraforming._is_solid_not_tree(node) + local name = node.name or node + if name == "air" or name == "ignore" or name == "mcl_villages:no_paths" or name == "mcl_core:bedrock" then return false end + if name == "mcl_nether:soul_sand" then return true end -- not "solid". Other exceptions we need? + if name == "mcl_nether:nether_wart_block" then return false end -- crimson forest, treat as tree + -- is deco_block if name == "mcl_crimson:warped_wart_block" then return false end -- warped forest, treat as tree + -- is deco_block if name == "mcl_crimson:shroomlight" then return false end -- crimson forest, treat as tree + -- is deco_block if name == "mcl_core:snow" then return false end + -- is walkable if name == "mcl_core:snowblock" then return true end + local meta = minetest.registered_items[name] + local groups = meta and meta.groups + return meta and meta.walkable and not (groups and (groups.deco_block or groups.tree or groups.leaves or groups.plant)) +end +local is_solid_not_tree = vl_terraforming._is_solid_not_tree + +--- check if a node is tree +-- @param node string or Node: node or node name +-- @return true for tree, leaves +function vl_terraforming._is_tree_not_leaves(node) + local name = node.name or node + if name == "air" or name == "ignore" or name == "mcl_villages:no_paths" then return false end + -- if name == "mcl_nether:nether_wart_block" then return true end -- crimson forest, treat as tree + -- if name == "mcl_crimson:warped_wart_block" then return true end -- warped forest, treat as tree + -- if name == "mcl_crimson:shroomlight" then return true end -- crimson forest, treat as tree + local meta = minetest.registered_items[name] + return meta and meta.groups and meta.groups.tree +end + +--- check if a node is liquid +-- @param node string or Node: node or node name +-- @return true for water, lava +function vl_terraforming._is_liquid(node) + local name = node.name or node + if name == "air" or name == "ignore" or name == "mcl_villages:no_paths" then return false end + local meta = minetest.registered_items[name] + local groups = meta and meta.groups + return groups and groups.liquid +end + +--- replace a non-solid node, optionally also "additional" +-- @param vm voxelmanip +-- @param pos position +-- @param with replacement Lua node (not just name) +-- @param always additional node to awlays replace even when solid +function vl_terraforming._make_solid_vm(vm, pos, with, always) + local cur = vm:get_node_at(pos) + if cur.name == "ignore" or cur.name == "mcl_core:bedrock" then return end + if cur.name == always or not is_solid_not_tree(cur) then + vm:set_node_at(pos, with) + return true + end +end diff --git a/settingtypes.txt b/settingtypes.txt index 10fdb90fc..62d565c4c 100644 --- a/settingtypes.txt +++ b/settingtypes.txt @@ -48,6 +48,9 @@ mcl_disabled_events (Disabled events) string # Control the relative plant growth speed (default: 1) vl_plant_growth (Plant growth factor) float 1.0 0 100 +# Structure frequency multiplier, keep this less than 3 usually +vl_structures_boost (Structure frequency) float 1.0 0.0 10.0 + # Amount of village to generate mcl_villages_village_probability (Probability of villages) int 5 0 100