diff --git a/mods/MAPGEN/mcl_villages/api.lua b/mods/MAPGEN/mcl_villages/api.lua index d8f248bf4..0b9bb6fa9 100644 --- a/mods/MAPGEN/mcl_villages/api.lua +++ b/mods/MAPGEN/mcl_villages/api.lua @@ -47,7 +47,7 @@ local function load_schema(name, mts) return { name = name, size = schematic.size, schem_lua = schem_lua } end -local all_optional = { "yadjust", "no_ground_turnip", "no_clearance" } +local all_optional = { "yadjust", "no_ground_turnip", "no_clearance", "rotation_offset" } local function set_all_optional(record, data) for _, field in ipairs(all_optional) do diff --git a/mods/MAPGEN/mcl_villages/buildings.lua b/mods/MAPGEN/mcl_villages/buildings.lua index 5c98e2a6e..db69a8196 100644 --- a/mods/MAPGEN/mcl_villages/buildings.lua +++ b/mods/MAPGEN/mcl_villages/buildings.lua @@ -1,11 +1,15 @@ -local min_jobs = tonumber(minetest.settings:get("mcl_villages_min_jobs")) or 1 -local max_jobs = tonumber(minetest.settings:get("mcl_villages_max_jobs")) or 12 -local placement_priority = minetest.settings:get("mcl_villages_placement_priority") or "random" +local min_jobs = tonumber(minetest.settings:get("vl_villages_min_jobs")) or 2 +local max_jobs = tonumber(minetest.settings:get("vl_villages_max_jobs")) or 14 +local placement_priority = minetest.settings:get("vl_villages_placement_priority") or "houses" -- houses is safer for villagers at night local S = minetest.get_translator(minetest.get_current_modname()) local function add_building(settlement, building, count_buildings) - table.insert(settlement, building) + if placement_priority == "jobs" then + table.insert(settlement, building) + else + table.insert(settlement, 1, building) -- insert "backwards" - todo: add table.reverse + end count_buildings[building.name] = (count_buildings[building.name] or 0) + 1 count_buildings.num_jobs = count_buildings.num_jobs + (building.num_jobs or 0) count_buildings.num_beds = count_buildings.num_beds + (building.num_beds or 0) @@ -35,15 +39,25 @@ local function layout_town(vm, minp, maxp, pr, input_settlement) local building = table.copy(input_settlement[#settlement + 1]) local size = vector.copy(building.size) --local rotation = possible_rotations[pr:next(1, #possible_rotations)] - local rotation = math.floor(math.atan2(center.z-cpos.z, center.x-cpos.x) / math.pi * 2+6.5)%4 - rotation = possible_rotations[1+rotation] + -- instead of random rotations, rotating doors to the center makes the village + -- more defensive and hence safer for the poor villagers, even though less random + -- case distinction is simpler and faster than trigonometry here: + local rotation = building.rotation_offset or 0 + if math.abs(cpos.z-center.z) > math.abs(cpos.x-center.x) then + rotation = rotation + (cpos.z <= center.z and 0 or 2) -- zero indexed for modulo below + else + rotation = rotation + (cpos.x <= center.x and 1 or 3) -- zero indexed for modulo below + end + rotation = possible_rotations[rotation % 4 + 1] + --minetest.log("action", building.name.." at "..minetest.pos_to_string(cpos).." rotation: "..rotation.." to "..minetest.pos_to_string(center).." "..minetest.pos_to_string(center-cpos)) if rotation == "90" or rotation == "270" then size.x, size.z = size.z, size.x end local tlpos = vector.offset(cpos, -math.floor((size.x-1)/2), 0, -math.floor((size.z-1)/2)) -- 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 = vl_terraforming.find_level_vm(vm, cpos, size) + local pos, surface_material = vl_terraforming.find_level_vm(vm, cpos, size, 6) + if pos and pos.y + size.y > maxp.y then pos = nil end -- check distance to other buildings. Note that we still want to add baseplates etc. 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 @@ -149,15 +163,11 @@ function mcl_villages.create_site_plan(vm, minp, maxp, pr) table.insert(settlement, pr:next(1, #settlement), cur_schem) end - if placement_priority == "jobs" then - -- keep ordered as is - elseif placement_priority == "houses" then - table.reverse(settlement) - else + if placement_priority == "randomg" then settlement = mcl_villages.shuffle(settlement, pr) end - table.insert(settlement, 1, bell_info) + return layout_town(vm, minp, maxp, pr, settlement) end @@ -201,19 +211,20 @@ function mcl_villages.place_schematics(vm, settlement, blockseed, pr) local schematic = loadstring(schem_lua)() -- the foundation and air space for the building was already built before - -- minetest.log("debug", "placing schematics for "..building.name.." at "..minetest.pos_to_string(minp).." on "..surface_material) + -- minetest.log("action", "placing schematics for "..building.name.." at "..minetest.pos_to_string(minp).." on "..surface_material.name) minetest.place_schematic_on_vmanip(vm, minp, schematic, rotation, nil, true, { place_center_x = false, place_center_y = false, place_center_z = false }) mcl_villages.store_path_ends(vm, minp, maxp, cpos, blockseed, bell_pos) mcl_villages.increase_no_paths(vm, minp, maxp) -- help the path finder end + local minp, maxp = vm:get_emerged_area() -- safe area for further processing vm:write_to_map(true) -- for path finder and light -- Path planning and placement - mcl_villages.paths(blockseed, minetest.get_biome_name(minetest.get_biome_data(bell_pos).biome)) + mcl_villages.paths(blockseed, minetest.get_biome_name(minetest.get_biome_data(bell_pos).biome), minp, maxp) + mcl_villages.clean_no_paths(minp, maxp) -- Clean up paths and initialize nodes for i, building in ipairs(settlement) do - mcl_villages.clean_no_paths(building.minp, building.maxp) init_nodes(building.minp, building.maxp, pr) end @@ -236,8 +247,9 @@ minetest.register_node("mcl_villages:village_block", { -- e.g. spawning mobs, running minetest.find_path on_timer = function(pos, _) local meta = minetest.get_meta(pos) - minetest.swap_node(pos, { name = meta:get_string("node_type") }) mcl_villages.post_process_village(meta:get_string("blockseed")) + -- not swap_node, to clear metadata afterwards + minetest.set_node(pos, { name = meta:get_string("node_type") }) return false end, }) diff --git a/mods/MAPGEN/mcl_villages/init.lua b/mods/MAPGEN/mcl_villages/init.lua index f91cadc71..ede0b31d5 100644 --- a/mods/MAPGEN/mcl_villages/init.lua +++ b/mods/MAPGEN/mcl_villages/init.lua @@ -118,6 +118,7 @@ mcl_villages.register_well({ name = "well", mts = schem_path .. "new_villages/well.mts", yadjust = -1, + rotation_offset = 3, -- lamp is east }) for i = 1, 6 do @@ -160,6 +161,7 @@ mcl_villages.register_building({ min_jobs = 2, max_jobs = 99, yadjust = 1, + rotation_offset = 1, -- entrance is west }) mcl_villages.register_building({ @@ -175,6 +177,7 @@ mcl_villages.register_building({ mts = schem_path .. "new_villages/blacksmith.mts", num_others = 8, yadjust = 1, + rotation_offset = 1, -- entrance is west }) mcl_villages.register_building({ @@ -182,6 +185,7 @@ mcl_villages.register_building({ mts = schem_path .. "new_villages/butcher.mts", num_others = 8, yadjust = 1, + rotation_offset = 0, -- entrance is north }) mcl_villages.register_building({ @@ -189,6 +193,7 @@ mcl_villages.register_building({ mts = schem_path .. "new_villages/farm.mts", num_others = 3, yadjust = 1, + rotation_offset = 3, -- composters are east }) mcl_villages.register_building({ @@ -196,6 +201,7 @@ mcl_villages.register_building({ mts = schem_path .. "new_villages/fishery.mts", num_others = 8, yadjust = -2, + rotation_offset = 2, -- entrances are east and west }) mcl_villages.register_building({ @@ -205,6 +211,7 @@ mcl_villages.register_building({ num_others = 8, max_jobs = 6, yadjust = 0, + rotation_offset = 1, -- entrance is west }) mcl_villages.register_building({ @@ -214,6 +221,7 @@ mcl_villages.register_building({ num_others = 8, min_jobs = 7, yadjust = 1, + rotation_offset = 3, -- entrance is east }) mcl_villages.register_building({ @@ -233,6 +241,7 @@ mcl_villages.register_building({ min_jobs = 1, max_jobs = 11, yadjust = 0, + rotation_offset = 1, -- entrance is west }) mcl_villages.register_building({ @@ -240,6 +249,7 @@ mcl_villages.register_building({ mts = schem_path .. "new_villages/cartographer.mts", num_others = 15, yadjust = 1, + rotation_offset = 1, -- entrance is west }) mcl_villages.register_building({ @@ -247,6 +257,7 @@ mcl_villages.register_building({ mts = schem_path .. "new_villages/mason.mts", num_others = 8, yadjust = 1, + rotation_offset = 1, -- entrance is west }) mcl_villages.register_building({ @@ -261,6 +272,7 @@ mcl_villages.register_building({ mts = schem_path .. "new_villages/leather_worker.mts", num_others = 8, yadjust = 1, + rotation_offset = 1, -- entrance is west }) mcl_villages.register_building({ @@ -268,6 +280,7 @@ mcl_villages.register_building({ mts = schem_path .. "new_villages/toolsmith.mts", num_others = 8, yadjust = 1, + rotation_offset = 1, -- entrance is west }) mcl_villages.register_building({ @@ -275,6 +288,7 @@ mcl_villages.register_building({ mts = schem_path .. "new_villages/weaponsmith.mts", num_others = 8, yadjust = 1, + rotation_offset = 0, -- entrance is north }) mcl_villages.register_building({ @@ -295,6 +309,7 @@ mcl_villages.register_building({ min_jobs = 8, max_jobs = 99, yadjust = 0, + rotation_offset = 2, -- entrance is west, but tower is south }) mcl_villages.register_building({ @@ -312,6 +327,7 @@ mcl_villages.register_building({ mts = schem_path .. "new_villages/farm_small_1.mts", num_others = 3, yadjust = 1, + rotation_offset = 3, -- composters are south west? }) mcl_villages.register_building({ @@ -319,6 +335,7 @@ mcl_villages.register_building({ mts = schem_path .. "new_villages/farm_small_2.mts", num_others = 3, yadjust = 1, + rotation_offset = 3, -- composters are south west? }) mcl_villages.register_building({ @@ -326,6 +343,7 @@ mcl_villages.register_building({ mts = schem_path .. "new_villages/farm_large_1.mts", num_others = 6, yadjust = 1, + rotation_offset = 3, -- composters are east }) mcl_villages.register_crop({ diff --git a/mods/MAPGEN/mcl_villages/paths.lua b/mods/MAPGEN/mcl_villages/paths.lua index 592c5dfa5..2058f6f64 100644 --- a/mods/MAPGEN/mcl_villages/paths.lua +++ b/mods/MAPGEN/mcl_villages/paths.lua @@ -46,13 +46,8 @@ function mcl_villages.store_path_ends(vm, minp, maxp, pos, blockseed, bell_pos) -- We store by distance because we create paths far away from the bell first local dist = vector.distance(bell_pos, pos) local id = "block_" .. blockseed -- cannot use integers as keys - local tab = path_ends[id] - if not tab then - tab = {} - path_ends[id] = tab - end - if tab[dist] == nil then tab[dist] = {} end - -- TODO: use LVM data instead of nodes? But we only process a subset of the area + -- TODO: benchmark best way + local tab = {} local v = vector.zero() for zi = minp.z, maxp.z do v.z = zi @@ -62,12 +57,14 @@ function mcl_villages.store_path_ends(vm, minp, maxp, pos, blockseed, bell_pos) v.x = xi local n = vm:get_node_at(v) if n and n.name == "mcl_villages:path_endpoint" then - table.insert(tab[dist], minetest.pos_to_string(v)) + table.insert(tab, vector.copy(v)) vm:set_node_at(v, { name = "air" }) end end end end + if not path_ends[id] then path_ends[id] = {} end + table.insert(path_ends[id], {dist, minetest.pos_to_string(pos), tab}) end local function place_lamp(pos, pr) @@ -84,74 +81,103 @@ local function place_lamp(pos, pr) end -- TODO: port this to lvm. -local function smooth_path(path) - -- Smooth out bumps in path or stairs can look naf - for pass = 1, 3 do +local function smooth_path(path, passes, minp, maxp) + -- bridge over water/laver for i = 2, #path - 1 do - local prev_y = path[i - 1].y - local y = path[i].y - local next_y = path[i + 1].y - local bump = minetest.get_node(path[i]).name - - -- TODO: also replace bamboo underneath with dirt here? - if minetest.get_item_group(bump, "water") ~= 0 then - -- ignore in this pass - elseif y >= next_y + 2 and y <= prev_y then - minetest.swap_node(vector.offset(path[i], 0, -1, 0), { name = "air" }) - path[i].y = path[i].y - 1 - elseif y <= next_y - 2 and y >= prev_y then - minetest.swap_node(path[i], { name = "mcl_core:dirt" }) - path[i].y = path[i].y + 1 - elseif y >= prev_y + 2 and y <= next_y then - minetest.swap_node(vector.offset(path[i], 0, -1, 0), { name = "air" }) - path[i].y = path[i].y - 1 - elseif y <= prev_y - 2 and y >= prev_y then - minetest.swap_node(path[i], { name = "mcl_core:dirt" }) - path[i].y = path[i].y + 1 - elseif y < prev_y and y < next_y then - -- Fill in dip to flatten path - minetest.swap_node(path[i], { name = "mcl_core:dirt" }) - path[i].y = path[i].y + 1 - elseif y > prev_y and y > next_y then - -- Remove peak to flatten path - minetest.swap_node(vector.offset(path[i], 0, -1, 0), { name = "air" }) - path[i].y = path[i].y - 1 + while true do + local cur = path[i] + local node = minetest.get_node(cur).name + if node == "air" and vector.in_area(cur, minp, maxp) then + local under = minetest.get_node(vector.offset(path[i], 0, -1, 0)).name + local udef = minetest.registered_nodes[under] + -- do not build paths over leaves + if udef and udef.groups.leaves then + minetest.swap_node(path[i], {name="mcl_villages:no_paths"}) + return -- bad path + end + break + else + local ndef = minetest.registered_nodes[node] + if not ndef then break end -- ignore + if (ndef.groups.water or 0) > 0 or (ndef.groups.lava or 0) > 0 then + cur.y = cur.y + 1 + else + break + end + end end end + -- Smooth out bumps in path to reduce weird stairs + local any_changed = false + for pass = 1, passes do + local changed = false + for i = 2, #path - 1 do + local prev_y = path[i - 1].y + local y = path[i].y + local next_y = path[i + 1].y + local bump = minetest.get_node(path[i]).name + local bdef = minetest.registered_nodes[bump] + + -- TODO: also replace bamboo underneath with dirt here? + if bdef and ((bdef.groups.water or 0) > 0 or (bdef.groups.lava or 0) > 0) then + -- ignore in this pass + elseif (y > next_y + 1 and y <= prev_y) -- large step + or (y > prev_y + 1 and y <= next_y) -- large step + or (y > prev_y and y > next_y) then + -- Remove peaks to flatten path + path[i].y = math.max(prev_y, next_y) + minetest.swap_node(path[i], { name = "air" }) + changed = true + elseif (y < next_y - 1 and y >= prev_y) -- large step + or (y < prev_y - 1 and y >= next_y) -- large step + or (y < prev_y and y < next_y) then + -- Fill in dips to flatten path + path[i].y = math.min(prev_y, next_y) - 1 -- to replace below first + minetest.swap_node(path[i], { name = "mcl_core:dirt" }) -- todo: use sand/sandstone in desert?, use slabs? + path[i].y = path[i].y + 1 -- above dirt + changed = true + end + end + if changed then any_changed = true else break end end + -- by delaying this, we allow making bridges over deep dips: + --[[ + if any_changed then + -- we may not yet have filled a gap + for i = 2, #path - 1 do + local below = vector.offset(path[y], 0, -1, 0) + local bdef = minetest.registered_nodes[minetest.get_node(path[i]).name] + if bdef and not bdef.walkable then + minetest.swap_node(path[i], { name = "mcl_core:dirt" }) -- todo: use sand/sandstone in desert?, use slabs? + end + end + end]] + return path end -- TODO: port this to lvm. local function place_path(path, pr, stair, slab) - -- Smooth out bumps in path or stairs can look naf + -- find water/lava below for i = 2, #path - 1 do local prev_y = path[i - 1].y local y = path[i].y local next_y = path[i + 1].y local bump = minetest.get_node(path[i]).name + local bdef = minetest.registered_nodes[bump] - if minetest.get_item_group(bump, "water") ~= 0 then + if bdef and ((bdef.groups.water or 0) > 0 or (bdef.groups.lava or 0) > 0) then -- Find air local up_pos = vector.copy(path[i]) while true do up_pos.y = up_pos.y + 1 local up_node = minetest.get_node(up_pos).name - if minetest.get_item_group(up_node, "water") == 0 then + local udef = minetest.registered_nodes[up_node] + if udef and (udef.groups.water or 0) == 0 and (udef.groups.lava or 0) == 0 then minetest.swap_node(up_pos, { name = "air" }) path[i] = up_pos break - end + elseif not udef then break end -- ignore node encountered end - elseif y < prev_y and y < next_y then - -- Fill in dip to flatten path - -- TODO: do not break other path/stairs - minetest.swap_node(path[i], { name = "mcl_core:dirt" }) - path[i] = vector.offset(path[i], 0, 1, 0) - elseif y > prev_y and y > next_y then - -- TODO: do not break other path/stairs - -- Remove peak to flatten path - minetest.swap_node(vector.offset(path[i], 0, -1, 0), { name = "air" }) - path[i].y = path[i].y - 1 end end @@ -208,6 +234,8 @@ local function place_path(path, pr, stair, slab) if not done then if groups.water then minetest.add_node(under_pos, { name = slab }) + elseif groups.lava then + minetest.add_node(under_pos, { name = "mcl_stairs:slab_stone" }) elseif groups.sand then minetest.swap_node(under_pos, { name = "mcl_core:sandstonesmooth2" }) elseif groups.soil and not groups.dirtifies_below_solid then @@ -269,7 +297,7 @@ end -- Work out which end points should be connected -- works from the outside of the village in -function mcl_villages.paths(blockseed, biome_name) +function mcl_villages.paths(blockseed, biome_name, minp, maxp) local pr = PcgRandom(blockseed) local pathends = path_ends["block_" .. blockseed] if pathends == nil then @@ -279,62 +307,66 @@ function mcl_villages.paths(blockseed, biome_name) -- Stair and slab style of the village local stair, slab = get_biome_stair_slab(biome_name) - -- Keep track of connections - local connected = {} - -- get a list of reverse sorted keys, which are distances - local dist_keys = {} - for k in pairs(pathends) do table.insert(dist_keys, k) end - table.sort(dist_keys, function(a, b) return a > b end) - --minetest.log("Planning paths with "..#dist_keys.." nodes") - - for i, from in ipairs(dist_keys) do + table.sort(pathends, function(a, b) return a[1] > b[1] end) + --minetest.log("action", "path ends: "..dump(pathends,"")) + -- find ways to connect + local connected, to_place = {}, {} + for _, tmp in ipairs(pathends) do + local from, from_eps = tmp[2], tmp[3] -- ep == end_point - for _, from_ep in ipairs(pathends[from]) do - local from_ep_pos = minetest.string_to_pos(from_ep) - local closest_pos, closest_bld, best = nil, nil, 10000000 - - -- Most buildings only do other buildings that are closer to the bell - -- for the bell do any end points that don't have paths near them - local j = from == 0 and 1 or (i + 1) - for j = j, #dist_keys do - local to = dist_keys[j] - if from ~= to and connected[from .. "-" .. to] == nil and connected[to .. "-" .. from] == nil then - for _, to_ep in ipairs(pathends[to]) do - local to_ep_pos = minetest.string_to_pos(to_ep) + for _, from_ep_pos in ipairs(from_eps) do + -- TODO: add back some logic as before that ensures some longer paths, too? + local cand = {} + for _, tmp in ipairs(pathends) do + local to, to_eps = tmp[2], tmp[3] + if from ~= to and not connected[from .. "-" .. to] and not connected[to .. "-" .. from] then + for _, to_ep_pos in ipairs(to_eps) do local dist = vector.distance(from_ep_pos, to_ep_pos) - if dist < best then - best = dist - closest_pos = to_ep_pos - closest_bld = to - end + table.insert(cand, {dist, from, from_ep_pos, to, to_ep_pos}) end end end - - if closest_pos then - local path = minetest.find_path(from_ep_pos, closest_pos, 64, 2, 2) - if path then smooth_path(path) end - if not path then - path = minetest.find_path(from_ep_pos, closest_pos, 64, 3, 3) - if path then smooth_path(path) end - end - path = minetest.find_path(from_ep_pos, closest_pos, 64, 1, 1) - if path and #path > 0 then - place_path(path, pr, stair, slab) - connected[from .. "-" .. closest_bld] = 1 - else - minetest.log("warning", - string.format( - "[mcl_villages] No good path from %s to %s, distance %d", - minetest.pos_to_string(from_ep_pos), - minetest.pos_to_string(closest_pos), - vector.distance(from_ep_pos, closest_pos) - ) - ) + table.sort(cand, function(a,b) return a[1] < b[1] end) + --minetest.log("action", "candidates: "..dump(cand,"")) + for _, pair in ipairs(cand) do + local dist, from, from_ep_pos, to, to_ep_pos = unpack(pair) + local path = minetest.find_path(from_ep_pos, to_ep_pos, 10, 4, 4) + if path then smooth_path(path, 3, minp, maxp) end + path = minetest.find_path(from_ep_pos, to_ep_pos, 10, 2, 2) + if path then smooth_path(path, 1, minp, maxp) end + path = minetest.find_path(from_ep_pos, to_ep_pos, 12, 1, 1) + if path then + --minetest.log("path "..from.." to "..to.." len "..tostring(#path)) + path = smooth_path(path, 1, minp, maxp) + if path then + connected[from .. "-" .. to] = 1 + table.insert(to_place, pair) + goto continue -- add only one path per building + end end end end + ::continue:: + end + + --minetest.log("action", "to_place: "..dump(to_place,"")) + -- now lay the actual paths + for _, cand in ipairs(to_place) do + local dist, from, from_ep_pos, to, to_ep_pos = unpack(cand) + local path = minetest.find_path(from_ep_pos, to_ep_pos, 12, 1, 1) + if path then + path = place_path(path, pr, stair, slab) + else + minetest.log("warning", + string.format( + "[mcl_villages] No good path from %s to %s, distance %d", + minetest.pos_to_string(from_ep_pos), + minetest.pos_to_string(to_ep_pos), + dist + ) + ) + end end path_ends["block_" .. blockseed] = nil diff --git a/mods/MAPGEN/vl_structures/api.lua b/mods/MAPGEN/vl_structures/api.lua index 4900c0400..bfdbd1a19 100644 --- a/mods/MAPGEN/vl_structures/api.lua +++ b/mods/MAPGEN/vl_structures/api.lua @@ -168,7 +168,7 @@ function vl_structures.register_structure(name,def) for _, pos in ipairs(t) do local pr = PcgRandom(minetest.hash_node_position(pos) + worldseed + RANDOM_SEED_OFFSET) if def.chunk_probability == nil or pr:next(0, 1e9) * 1e-9 * def.chunk_probability <= structure_boost then - vl_structures.place_structure(vector_offset(pos, 0, 1, 0), def, pr, blockseed) + vl_structures.place_structure(pos, def, pr, blockseed) if def.chunk_probability ~= nil then break end -- allow only one per gennotify, e.g., on multiple surfaces end end diff --git a/mods/MAPGEN/vl_structures/emerge.lua b/mods/MAPGEN/vl_structures/emerge.lua index ee7f1b7ce..f315d9b3b 100644 --- a/mods/MAPGEN/vl_structures/emerge.lua +++ b/mods/MAPGEN/vl_structures/emerge.lua @@ -26,6 +26,7 @@ local function emerge_schematics(blockpos, action, calls_remaining, param) 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 + local dust_mat = nil -- Step 0: pick random daughter schematics + rotations local daughters = {} @@ -38,6 +39,10 @@ local function emerge_schematics(blockpos, action, calls_remaining, param) table.insert(daughters, {d, ds, rotation}) end + -- hack to get dust nodes more often, in case the mapgen messed with biomes + local n = vm:get_node_at(vector_offset(param.opos, 0, 1, 0)) + if n.name == "mcl_core:snow" then dust_mat = n end + -- Step 1: adjust ground to a more level position -- todo: also support checking ground of daughter schematics, but not used by current schematics if pos and size and prepare and tolerance_enabled(prepare.tolerance, prepare.mode) then @@ -59,11 +64,13 @@ local function emerge_schematics(blockpos, action, calls_remaining, param) if prepare and (prepare.clear or prepare.foundation) then local prepare_start = os.clock() -- Get materials from biome (TODO: make this a function + table?): - local b = mg_name ~= "v6" and minetest.registered_biomes[minetest.get_biome_name(minetest.get_biome_data(pos).biome)] + -- use original position to not get a different biome than expected for the structure + local b = mg_name ~= "v6" and minetest.registered_biomes[minetest.get_biome_name(minetest.get_biome_data(vector_offset(param.pos,0,1,0)).biome)] + --minetest.log("action", tostring(def.name or param.schematic.name).." in biome: "..dump(b,""):gsub("\n","")) local node_top = b and b.node_top and { name = b.node_top } or surface_mat or vl_structures.DEFAULT_SURFACE local node_filler = b and b.node_filler and { name = b.node_filler } or vl_structures.DEFAULT_FILLER local node_stone = b and b.node_stone and { name = b.node_stone } or vl_structures.DEFAULT_STONE - local node_dust = b and b.node_dust and { name = b.node_dust } or vl_structures.DEFAULT_DUST + local node_dust = b and b.node_dust and { name = b.node_dust } or dust_mat or vl_structures.DEFAULT_DUST if node_top.name == "mcl_core:dirt_with_grass" and b then node_top.param2 = b._mcl_grass_palette_index end -- Step 2a: clear overhead area @@ -101,7 +108,7 @@ local function emerge_schematics(blockpos, action, calls_remaining, param) -- Step 2b: baseplate underneath 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)) + -- minetest.log("action", "[vl_structures] "..tostring(def.name or param.schematic.name).." fill foundation "..minetest.pos_to_string(gp).." with "..tostring(node_top.name).." "..tostring(node_filler.name).." "..tostring(node_dust and node_dust.name)) local depth = (type(prepare.foundation) == "number" and prepare.foundation) or vl_structures.DEFAULT_PREPARE.foundation vl_terraforming.foundation_vm(vm, gp.x, gp.y - 1, gp.z, size.x + padding * 2, depth, size.z + padding * 2, @@ -135,6 +142,7 @@ local function emerge_schematics(blockpos, action, calls_remaining, param) -- todo: allow after_place callbacks for daughter schematics? end local endmain = os.clock() + -- TODO: step 4: sprinkle extra dust on top. vm:write_to_map(true) -- Note: deliberately pos, p1 and p2 from the parent, as these are calls to the parent script if def.loot then vl_structures.fill_chests(pmin,pmax,def.loot,pr) end @@ -173,7 +181,7 @@ vl_structures.place_schematic = function(pos, yoffset, schematic, rotation, def, -- 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_schematics, { name = def.name, emin=emin, emax=emax, def=def, schematic=schematic, - pos=ppos, yoffset=yoffset, size=size, rotation=rotation, + pos=ppos, opos=pos, yoffset=yoffset, size=size, rotation=rotation, pr=pr }) end diff --git a/settingtypes.txt b/settingtypes.txt index 1ecb88c3b..69c0e1729 100644 --- a/settingtypes.txt +++ b/settingtypes.txt @@ -48,8 +48,17 @@ mcl_disabled_events (Disabled events) string # Structure frequency multiplier, keep this less than 3 usually vl_structures_boost (Structure frequency multiplier) float 1.0 0.0 10.0 -# Village frequency multiplier, keep this less than 3 usually -vl_villages_boost (Village frequency multiplier) float 1.0 0.0 10.0 +# Minimum number of job sites for villages to plan (generation may have less) +vl_villages_min_jobs (Small village job sites) int 2 0 20 + +# Maximum number of job sites for villages to plan (generation may have less) +vl_villages_max_jobs (Large villages job sites) int 14 0 20 + +# Placement strategy for buildings: jobs, houses, or random +vl_villages_placement_priority (Village building placement) enum houses houses,jobs,random + +# Village frequency multiplier, keep this less than 3 usually to avoid excessive map emerges +vl_villages_boost (Village frequency multiplier) float 1.0 0.0 5.0 [Players] # If enabled, players respawn at the bed they last lay on instead of normal