Big villages overhaul

This commit is contained in:
kno10 2024-07-19 14:56:06 +02:00
parent ad6a3792f3
commit 2ec440132a
7 changed files with 737 additions and 479 deletions

@ -2,7 +2,7 @@
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- build schematic, replace material, rotation -- build schematic, replace material, rotation
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
function settlements.build_schematic(vm, data, va, pos, building, replace_wall, name) function mcl_villages.build_schematic(vm, data, va, pos, building, replace_wall, name)
-- get building node material for better integration to surrounding -- get building node material for better integration to surrounding
local platform_material = mcl_vars.get_node(pos) local platform_material = mcl_vars.get_node(pos)
if not platform_material or (platform_material.name == "air" or platform_material.name == "ignore") then if not platform_material or (platform_material.name == "air" or platform_material.name == "ignore") then
@ -36,7 +36,7 @@ function settlements.build_schematic(vm, data, va, pos, building, replace_wall,
local height = schematic["size"]["y"] local height = schematic["size"]["y"]
local possible_rotations = {"0", "90", "180", "270"} local possible_rotations = {"0", "90", "180", "270"}
local rotation = possible_rotations[ math.random( #possible_rotations ) ] local rotation = possible_rotations[ math.random( #possible_rotations ) ]
settlements.foundation( mcl_villages.foundation(
pos, pos,
width, width,
depth, depth,
@ -57,109 +57,102 @@ end]]
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- initialize settlement_info -- initialize settlement_info
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
function settlements.initialize_settlement_info(pr) function mcl_villages.initialize_settlement_info(pr)
local count_buildings = {} local count_buildings = {}
-- count_buildings table reset -- count_buildings table reset
for k,v in pairs(settlements.schematic_table) do for k,v in pairs(mcl_villages.schematic_table) do
count_buildings[v["name"]] = 0 count_buildings[v["name"]] = 0
end end
-- randomize number of buildings -- randomize number of buildings
local number_of_buildings = pr:next(10, 25) local number_of_buildings = pr:next(10, 25)
local number_built = 1 local number_built = 0
settlements.debug("Village ".. number_of_buildings) mcl_villages.debug("Village ".. number_of_buildings)
return count_buildings, number_of_buildings, number_built return count_buildings, number_of_buildings, number_built
end end
-------------------------------------------------------------------------------
-- check ground for a single building
-------------------------------------------------------------------------------
local function try_place_building(pos_surface, building_all_info, rotation, settlement_info, pr)
local fwidth, fdepth = building_all_info["hwidth"], building_all_info["hdepth"]
if rotation == "90" or rotation == "270" then fwidth, fdepth = fdepth, fwidth end
local fheight = building_all_info["hheight"]
-- use building centers for better placement
pos_surface.x = pos_surface.x - math.ceil(fwidth / 2)
pos_surface.z = pos_surface.z - math.ceil(fdepth / 2)
-- to find the y position, also check the corners
local ys = {pos_surface.y}
local pos_c
pos_c = mcl_villages.find_surface_down(vector.new(pos_surface.x-1, pos_surface.y+fheight, pos_surface.z-1))
if pos_c then table.insert(ys, pos_c.y) end
pos_c = mcl_villages.find_surface_down(vector.new(pos_surface.x+fwidth+2, pos_surface.y+fheight, pos_surface.z-1))
if pos_c then table.insert(ys, pos_c.y) end
pos_c = mcl_villages.find_surface_down(vector.new(pos_surface.x-1, pos_surface.y+fheight, pos_surface.z+fdepth+2))
if pos_c then table.insert(ys, pos_c.y) end
pos_c = mcl_villages.find_surface_down(vector.new(pos_surface.x+fwidth+2, pos_surface.y+fheight, pos_surface.z+fdepth+2))
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] > fheight + 3 then return nil end
pos_surface.y = ys[math.ceil(#ys/2)]
-- check distance to other buildings
if mcl_villages.check_distance(settlement_info, pos_surface, building_all_info["hsize"]) then
return pos_surface
end
return nil
end
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- fill settlement_info -- fill settlement_info
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
function settlements.create_site_plan(maxp, minp, pr) function mcl_villages.create_site_plan(minp, maxp, pr)
local settlement_info = {} local center = vector.new(math.floor((minp.x+maxp.x)/2),maxp.y,math.floor((minp.z+maxp.z)/2))
local building_all_info minetest.log("action", "sudo make me a village at: " .. minetest.pos_to_string(center))
local possible_rotations = {"0", "90", "180", "270"} local possible_rotations = {"0", "90", "180", "270"}
local center_surface
-- find center of chunk local count_buildings, number_of_buildings, number_built = mcl_villages.initialize_settlement_info(pr)
local center = { local settlement_info = {}
x=math.floor((minp.x+maxp.x)/2),
y=maxp.y,
z=math.floor((minp.z+maxp.z)/2)
}
-- find center_surface of chunk
local center_surface , surface_material = settlements.find_surface(center, true)
local chunks = {}
chunks[mcl_vars.get_chunk_number(center)] = true
-- go build settlement around center
if not center_surface then
minetest.log("action", "Cannot build village at: " .. minetest.pos_to_string(center))
return false
else
minetest.log("action", "Village built.")
--minetest.log("action", "Build village at: " .. minetest.pos_to_string(center) .. " with surface material: " .. surface_material)
end
-- initialize all settlement_info table
local count_buildings, number_of_buildings, number_built = settlements.initialize_settlement_info(pr)
-- first building is townhall in the center
building_all_info = settlements.schematic_table[1]
local rotation = possible_rotations[ pr:next(1, #possible_rotations ) ]
-- add to settlement info table
local index = 1
settlement_info[index] = {
pos = center_surface,
name = building_all_info["name"],
hsize = building_all_info["hsize"],
rotat = rotation,
surface_mat = surface_material
}
--increase index for following buildings
index = index + 1
-- now some buildings around in a circle, radius = size of town center -- now some buildings around in a circle, radius = size of town center
local x, z, r = center_surface.x, center_surface.z, building_all_info["hsize"] local x, y, z, r = center.x, maxp.y, center.z, 0
-- draw j circles around center and increase radius by math.random(2,5) -- draw j circles around center and increase radius by math.random(2,5)
for j = 1,20 do for j = 1,10 do
-- set position on imaginary circle for angle = 0, math.pi*2, 0.262 do -- 24 attempts on a circle
for j = 0, 360, 15 do local pos1 = vector.new(math.floor(x + r * math.cos(angle) + 0.5), y, math.floor(z - r * math.sin(angle) + 0.5))
local angle = j * math.pi / 180 local pos_surface, surface_material = mcl_villages.find_surface(pos1, false)
local ptx, ptz = x + r * math.cos( angle ), z + r * math.sin( angle ) if pos_surface then
ptx = settlements.round(ptx, 0) local randomized_schematic_table = mcl_villages.shuffle(mcl_villages.schematic_table, pr)
ptz = settlements.round(ptz, 0) if #settlement_info == 0 then randomized_schematic_table = { mcl_villages.schematic_table[1] } end -- place town bell first
local pos1 = { x=ptx, y=center_surface.y+50, z=ptz}
local chunk_number = mcl_vars.get_chunk_number(pos1)
local pos_surface, surface_material
if chunks[chunk_number] then
pos_surface, surface_material = settlements.find_surface(pos1)
else
chunks[chunk_number] = true
pos_surface, surface_material = settlements.find_surface(pos1, true)
end
if not pos_surface then break end
local randomized_schematic_table = shuffle(settlements.schematic_table, pr)
-- pick schematic -- pick schematic
local size = #randomized_schematic_table local size = #randomized_schematic_table
for i = size, 1, -1 do for i = 1, #randomized_schematic_table do
local building_all_info = randomized_schematic_table[i]
-- already enough buildings of that type? -- already enough buildings of that type?
if count_buildings[randomized_schematic_table[i]["name"]] < randomized_schematic_table[i]["max_num"]*number_of_buildings then if count_buildings[building_all_info["name"]] < building_all_info["max_num"]*number_of_buildings then
building_all_info = randomized_schematic_table[i] local rotation = possible_rotations[pr:next(1, #possible_rotations)]
-- check distance to other buildings local pos = try_place_building(pos_surface, building_all_info, rotation, settlement_info, pr)
local distance_to_other_buildings_ok = settlements.check_distance(settlement_info, pos_surface, building_all_info["hsize"]) if pos then
if distance_to_other_buildings_ok then if #settlement_info == 0 then -- town bell
-- count built houses center_surface, y = pos, pos.y + max_height_difference
end
-- limit height differences to town center
if math.abs(pos.y - center_surface.y) > max_height_difference * 0.7 then
break -- other buildings likely will not fit either
end
count_buildings[building_all_info["name"]] = count_buildings[building_all_info["name"]] +1 count_buildings[building_all_info["name"]] = count_buildings[building_all_info["name"]] +1
rotation = possible_rotations[ pr:next(1, #possible_rotations ) ]
number_built = number_built + 1 number_built = number_built + 1
settlement_info[index] = {
pos = pos_surface, pos.y = pos.y + (building_all_info["yadjust"] or 0)
table.insert(settlement_info, {
pos = pos,
name = building_all_info["name"], name = building_all_info["name"],
hsize = building_all_info["hsize"], hsize = building_all_info["hsize"],
rotat = rotation, rotat = rotation,
surface_mat = surface_material surface_mat = surface_material
} })
index = index + 1 -- minetest.log("action", "Placing "..building_all_info["name"].." at "..minetest.pos_to_string(pos))
break break
end end
end end
@ -168,12 +161,21 @@ function settlements.create_site_plan(maxp, minp, pr)
break break
end end
end end
if r == 0 then break end -- no angles in the very center
end
if number_built >= number_of_buildings then if number_built >= number_of_buildings then
break break
end end
r = r + pr:next(2,5) r = r + pr:next(2,5)
if r > 35 then break end -- avoid touching neighboring blocks
end end
settlements.debug("really ".. number_built) mcl_villages.debug("really ".. number_built)
if number_built <= 8 then
minetest.log("action", "Bad village location, could only place "..number_built.." buildings.")
return
end
minetest.log("action", "Village completed at " .. minetest.pos_to_string(center))
minetest.log("Village completed at " .. minetest.pos_to_string(center)) -- for debugging only
return settlement_info return settlement_info
end end
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
@ -201,6 +203,7 @@ local function spawn_iron_golem(pos)
--minetest.log("action", "Attempt to spawn iron golem.") --minetest.log("action", "Attempt to spawn iron golem.")
local p = minetest.find_node_near(pos,50,"mcl_core:grass_path") local p = minetest.find_node_near(pos,50,"mcl_core:grass_path")
if p then if p then
p.y = p.y + 1
local l=minetest.add_entity(p,"mobs_mc:iron_golem"):get_luaentity() local l=minetest.add_entity(p,"mobs_mc:iron_golem"):get_luaentity()
if l then if l then
l._home = p l._home = p
@ -245,100 +248,63 @@ local function init_nodes(p1, p2, size, rotation, pr)
if nodes and #nodes > 0 then if nodes and #nodes > 0 then
for p=1, #nodes do for p=1, #nodes do
local pos = nodes[p] local pos = nodes[p]
settlements.fill_chest(pos, pr) mcl_villages.fill_chest(pos, pr)
end end
end end
end end
function settlements.place_schematics(settlement_info, pr) function mcl_villages.place_schematics(settlement_info, pr)
local building_all_info local building_all_info
local lvm = VoxelManip()
for i, built_house in ipairs(settlement_info) do for i, built_house in ipairs(settlement_info) do
local is_last = i == #settlement_info local is_last = i == #settlement_info
for j, schem in ipairs(settlements.schematic_table) do for j, schem in ipairs(mcl_villages.schematic_table) do
if settlement_info[i]["name"] == schem["name"] then if settlement_info[i]["name"] == schem["name"] then
building_all_info = schem building_all_info = schem
break break
end end
end end
local pos = settlement_info[i]["pos"] local pos = settlement_info[i]["pos"]
local rotation = settlement_info[i]["rotat"] local rotation = settlement_info[i]["rotat"]
-- get building node material for better integration to surrounding -- get building node material for better integration to surrounding
local platform_material = settlement_info[i]["surface_mat"] local surface_material = settlement_info[i]["surface_mat"] or "mcl_core:stone"
--platform_material_name = minetest.get_name_from_content_id(platform_material) local platform_material = surface_material
-- pick random material local schem_lua = building_all_info["schem_lua"]
--local material = wallmaterial[pr:next(1,#wallmaterial)] if not schem_lua then
-- schem_lua = minetest.serialize_schematic(building_all_info["mts"], "lua", { lua_use_comments = false, lua_num_indent_spaces = 0 }) .. " return schematic"
local building = building_all_info["mts"] building_all_info["schem_lua"] = schem_lua
local replace_wall = building_all_info["rplc"]
-- schematic conversion to lua
local schem_lua = minetest.serialize_schematic(building,
"lua",
{lua_use_comments = false, lua_num_indent_spaces = 0}).." return schematic"
schem_lua = schem_lua:gsub("mcl_core:stonebrickcarved", "mcl_villages:stonebrickcarved")
-- replace material
if replace_wall then
--Note, block substitution isn't matching node names exactly; so nodes that are to be substituted that have the same prefixes cause bugs.
-- Example: Attempting to swap out 'mcl_core:stonebrick'; which has multiple, additional sub-variants: (carved, cracked, mossy). Will currently cause issues, so leaving disabled.
if platform_material == "mcl_core:snow" or platform_material == "mcl_core:dirt_with_grass_snow" or platform_material == "mcl_core:podzol" then
schem_lua = schem_lua:gsub("mcl_core:tree", "mcl_core:sprucetree")
schem_lua = schem_lua:gsub("mcl_core:wood", "mcl_core:sprucewood")
--schem_lua = schem_lua:gsub("mcl_fences:fence", "mcl_fences:spruce_fence")
--schem_lua = schem_lua:gsub("mcl_stairs:slab_wood_top", "mcl_stairs:slab_sprucewood_top")
--schem_lua = schem_lua:gsub("mcl_stairs:stair_wood", "mcl_stairs:stair_sprucewood")
--schem_lua = schem_lua:gsub("mesecons_pressureplates:pressure_plate_wood_off", "mesecons_pressureplates:pressure_plate_sprucewood_off")
elseif platform_material == "mcl_core:sand" or platform_material == "mcl_core:redsand" then
schem_lua = schem_lua:gsub("mcl_core:tree", "mcl_core:sandstonecarved")
schem_lua = schem_lua:gsub("mcl_core:cobble", "mcl_core:sandstone")
schem_lua = schem_lua:gsub("mcl_core:wood", "mcl_core:sandstonesmooth")
--schem_lua = schem_lua:gsub("mcl_fences:fence", "mcl_fences:birch_fence")
--schem_lua = schem_lua:gsub("mcl_stairs:slab_wood_top", "mcl_stairs:slab_birchwood_top")
--schem_lua = schem_lua:gsub("mcl_stairs:stair_wood", "mcl_stairs:stair_birchwood")
--schem_lua = schem_lua:gsub("mesecons_pressureplates:pressure_plate_wood_off", "mesecons_pressureplates:pressure_plate_birchwood_off")
--schem_lua = schem_lua:gsub("mcl_stairs:stair_stonebrick", "mcl_stairs:stair_redsandstone")
--schem_lua = schem_lua:gsub("mcl_core:stonebrick", "mcl_core:redsandstonesmooth")
schem_lua = schem_lua:gsub("mcl_core:brick_block", "mcl_core:redsandstone")
end end
end schem_lua = schem_lua:gsub('"mcl_core:dirt"', '"'..platform_material..'"')
schem_lua = schem_lua:gsub("mcl_core:dirt_with_grass", platform_material) schem_lua = schem_lua:gsub('"mcl_core:dirt_with_grass"', '"'..surface_material..'"')
local schematic = loadstring(mcl_villages.substitute_materials(pos, schem_lua, pr))()
--[[ Disable special junglewood for now.
-- special material for spawning npcs
schem_lua = schem_lua:gsub("mcl_core:junglewood", "settlements:junglewood")
--]]
schem_lua = schem_lua:gsub("mcl_stairs:stair_wood_outer", "mcl_stairs:slab_wood")
schem_lua = schem_lua:gsub("mcl_stairs:stair_stone_rough_outer", "air")
-- format schematic string
local schematic = loadstring(schem_lua)()
local is_belltower = building_all_info["name"] == "belltower" local is_belltower = building_all_info["name"] == "belltower"
-- build foundation for the building an make room above -- already built the foundation for the building and made room above
local sx, sy, sz = schematic.size.x, schematic.size.y, schematic.size.z
mcl_structures.place_schematic( local p2 = vector.new(pos.x+sx-1,pos.y+sy-1,pos.z+sz-1)
lvm:read_from_map(pos, p2)
minetest.place_schematic_on_vmanip(
lvm,
pos, pos,
schematic, schematic,
rotation, rotation,
nil, nil,
true, true,
nil, { place_center_x = false, place_center_y = false, place_center_z = false }
function(p1, p2, size, rotation, pr)
if is_belltower then
spawn_iron_golem(p1)
else
init_nodes(p1, p2, size, rotation, pr)
spawn_villagers(p1,p2)
fix_village_water(p1,p2)
end
end,
pr
) )
lvm:write_to_map(true) -- FIXME: postpone
if rotation == "90" or rotation == "270" then sx, sz = sz, sx end
init_nodes(pos, p2, schematic.size, rotation, pr)
if is_belltower then
spawn_iron_golem(pos)
else
spawn_villagers(pos,p2)
fix_village_water(pos,p2)
end
end end
end end

@ -1,5 +1,5 @@
-- switch for debugging -- switch for debugging
function settlements.debug(message) function mcl_villages.debug(message)
-- minetest.chat_send_all(message) -- minetest.chat_send_all(message)
-- minetest.log("warning", "[mcl_villages] "..message) -- minetest.log("warning", "[mcl_villages] "..message)
minetest.log("verbose", "[mcl_villages] "..message) minetest.log("verbose", "[mcl_villages] "..message)
@ -20,44 +20,51 @@ local wallmaterial = {
"mcl_core:sandstonesmooth2" "mcl_core:sandstonesmooth2"
} }
--]] --]]
settlements.surface_mat = {}
-------------------------------------------------------------------------------
-- Set array to list
-- https://stackoverflow.com/questions/656199/search-for-an-item-in-a-lua-list
-------------------------------------------------------------------------------
function settlements.grundstellungen()
settlements.surface_mat = settlements.Set {
"mcl_core:dirt_with_grass",
--"mcl_core:dry_dirt_with_grass",
"mcl_core:dirt_with_grass_snow",
--"mcl_core:dirt_with_dry_grass",
"mcl_core:podzol",
"mcl_core:sand",
"mcl_core:redsand",
--"mcl_core:silver_sand",
"mcl_core:snow"
}
end
-- --
-- possible surfaces where buildings can be built -- possible surfaces where buildings can be built
-- --
mcl_villages.surface_mat = {}
mcl_villages.surface_mat["mcl_core:andesite"] = true
mcl_villages.surface_mat["mcl_core:diorite"] = true
mcl_villages.surface_mat["mcl_core:dirt"] = true
mcl_villages.surface_mat["mcl_core:dirt_with_grass"] = true
--mcl_villages.surface_mat["mcl_core:dirt_with_dry_grass"] = true
mcl_villages.surface_mat["mcl_core:dirt_with_grass_snow"] = true
--mcl_villages.surface_mat["mcl_core:dry_dirt_with_grass"] = true
mcl_villages.surface_mat["mcl_core:grass_path"] = true
mcl_villages.surface_mat["mcl_core:granite"] = true
mcl_villages.surface_mat["mcl_core:podzol"] = true
mcl_villages.surface_mat["mcl_core:redsand"] = true
mcl_villages.surface_mat["mcl_core:sand"] = true
mcl_villages.surface_mat["mcl_core:sandstone"] = true
mcl_villages.surface_mat["mcl_core:sandstonesmooth"] = true
mcl_villages.surface_mat["mcl_core:sandstonesmooth2"] = true
--mcl_villages.surface_mat["mcl_core:silver_sand"] = true
mcl_villages.surface_mat["mcl_core:snow"] = true
mcl_villages.surface_mat["mcl_core:stone"] = true
mcl_villages.surface_mat["mcl_core:stone_with_coal"] = true
mcl_villages.surface_mat["mcl_core:stone_with_iron"] = true
mcl_villages.surface_mat["mcl_colorblocks:hardened_clay"] = true
mcl_villages.surface_mat["mcl_colorblocks:hardened_clay_orange"] = true
mcl_villages.surface_mat["mcl_colorblocks:hardened_clay_red"] = true
mcl_villages.surface_mat["mcl_colorblocks:hardened_clay_white"] = true
-- --
-- path to schematics -- path to schematics
-- --
schem_path = settlements.modpath.."/schematics/" schem_path = mcl_villages.modpath.."/schematics/"
-- --
-- list of schematics -- list of schematics
-- --
local basic_pseudobiome_villages = minetest.settings:get_bool("basic_pseudobiome_villages", true) local basic_pseudobiome_villages = minetest.settings:get_bool("basic_pseudobiome_villages", true)
settlements.schematic_table = { mcl_villages.schematic_table = {
{name = "belltower", mts = schem_path.."belltower.mts", hwidth = 5, hdepth = 5, hheight = 9, hsize = 14, max_num = 0 , rplc = basic_pseudobiome_villages }, {name = "belltower", mts = schem_path.."belltower.mts", hwidth = 5, hdepth = 5, hheight = 9, hsize = 14, max_num = 0.0001 , rplc = basic_pseudobiome_villages, yadjust = 1 },
{name = "large_house", mts = schem_path.."large_house.mts", hwidth = 12, hdepth = 12, hheight = 9, hsize = 14, max_num = 0.08 , rplc = basic_pseudobiome_villages }, {name = "large_house", mts = schem_path.."large_house.mts", hwidth = 12, hdepth = 12, hheight = 9, hsize = 14, max_num = 0.08 , rplc = basic_pseudobiome_villages },
{name = "blacksmith", mts = schem_path.."blacksmith.mts", hwidth = 8, hdepth = 11, hheight = 13, hsize = 13, max_num = 0.055 , rplc = basic_pseudobiome_villages }, {name = "blacksmith", mts = schem_path.."blacksmith.mts", hwidth = 8, hdepth = 11, hheight = 13, hsize = 13, max_num = 0.055 , rplc = basic_pseudobiome_villages },
{name = "butcher", mts = schem_path.."butcher.mts", hwidth = 12, hdepth = 8, hheight = 10, hsize = 14, max_num = 0.03 , rplc = basic_pseudobiome_villages }, {name = "butcher", mts = schem_path.."butcher.mts", hwidth = 12, hdepth = 8, hheight = 10, hsize = 14, max_num = 0.03 , rplc = basic_pseudobiome_villages },
{name = "church", mts = schem_path.."church.mts", hwidth = 13, hdepth = 13, hheight = 14, hsize = 15, max_num = 0.04 , rplc = basic_pseudobiome_villages }, {name = "church", mts = schem_path.."church.mts", hwidth = 13, hdepth = 13, hheight = 14, hsize = 15, max_num = 0.04 , rplc = basic_pseudobiome_villages },
{name = "farm", mts = schem_path.."farm.mts", hwidth = 9, hdepth = 7, hheight = 13, hsize = 13, max_num = 0.1 , rplc = basic_pseudobiome_villages }, {name = "farm", mts = schem_path.."farm.mts", hwidth = 9, hdepth = 7, hheight = 13, hsize = 13, max_num = 0.1 , rplc = basic_pseudobiome_villages, yadjust = 1 },
{name = "lamp", mts = schem_path.."lamp.mts", hwidth = 3, hdepth = 4, hheight = 13, hsize = 10, max_num = 0.1 , rplc = false }, {name = "lamp", mts = schem_path.."lamp.mts", hwidth = 3, hdepth = 4, hheight = 13, hsize = 10, max_num = 0.1 , rplc = false },
{name = "library", mts = schem_path.."library.mts", hwidth = 12, hdepth = 12, hheight = 8, hsize = 13, max_num = 0.04 , rplc = basic_pseudobiome_villages }, {name = "library", mts = schem_path.."library.mts", hwidth = 12, hdepth = 12, hheight = 8, hsize = 13, max_num = 0.04 , rplc = basic_pseudobiome_villages },
{name = "medium_house", mts = schem_path.."medium_house.mts", hwidth = 9, hdepth = 12, hheight = 8, hsize = 14, max_num = 0.08 , rplc = basic_pseudobiome_villages }, {name = "medium_house", mts = schem_path.."medium_house.mts", hwidth = 9, hdepth = 12, hheight = 8, hsize = 14, max_num = 0.08 , rplc = basic_pseudobiome_villages },
@ -67,7 +74,7 @@ settlements.schematic_table = {
} }
-- --
-- maximum allowed difference in height for building a sttlement -- maximum allowed difference in height for building a settlement
-- --
max_height_difference = 56 max_height_difference = 56
-- --
@ -75,3 +82,185 @@ max_height_difference = 56
-- --
half_map_chunk_size = 40 half_map_chunk_size = 40
--quarter_map_chunk_size = 20 --quarter_map_chunk_size = 20
--
-- Biome based block substitutions
--
-- TODO maybe this should be in the biomes?
mcl_villages.biome_map = {
BambooJungle = "bamboo",
BambooJungleEdge = "bamboo",
BambooJungleEdgeM = "bamboo",
BambooJungleM = "bamboo",
Jungle = "jungle",
JungleEdge = "jungle",
JungleEdgeM = "jungle",
JungleM = "jungle",
Desert = "desert",
Savanna = "acacia",
SavannaM = "acacia",
Mesa = "hardened_clay",
MesaBryce = "hardened_clay ",
MesaPlateauF = "hardened_clay",
MesaPlateauFM = "hardened_clay",
MangroveSwamp = "mangrove",
RoofedForest = "dark_oak",
BirchForest = "birch",
BirchForestM = "birch",
ColdTaiga = "spruce",
ExtremeHills = "spruce",
ExtremeHillsM = "spruce",
IcePlains = "spruce",
IcePlainsSpikes = "spruce",
MegaSpruceTaiga = "spruce",
MegaTaiga = "spruce",
Taiga = "spruce",
["ExtremeHills+"] = "spruce",
CherryGrove = "cherry",
-- no change
--FlowerForest = "oak",
--Forest = "oak",
--MushroomIsland = "",
--Plains = "oak",
--StoneBeach = "",
--SunflowerPlains = "oak",
--Swampland = "oak",
}
mcl_villages.material_substitions = {
desert = {
{ '"mcl_stairs:slab_oak([^"]*)"', '"mcl_stairs:slab_sandstonesmooth%1"' },
{
'"mesecons_pressureplates:pressure_plate_oak_([^"]+)"',
'"mesecons_pressureplates:pressure_plate_birchwood_%1"',
},
{ '"mcl_doors:trapdoor_oak([^"]*)"', '"mcl_doors:birch_trapdoor%1"' },
{ '"mcl_doors:door_oak([^"]*)"', '"mcl_doors:birch_door%1"' },
{ "mcl_core:cobble", "mcl_core:sandstone" },
{ '"mcl_stairs:stair_cobble([^"]*)"', '"mcl_stairs:stair_sandstone%1"' },
{ '"mcl_walls:cobble([^"]*)"', '"mcl_walls:sandstone%1"' },
{ '"mcl_stairs:slab_cobble([^"]*)"', '"mcl_stairs:slab_sandstone%1"' },
{ '"mcl_core:stonebrick"', '"mcl_core:redsandstone"' },
{ '"mcl_core:stonebrick_([^"]+)"', '"mcl_core:redsandstone_%1"' },
{ '"mcl_walls:stonebrick([^"]*)"', '"mcl_walls:redsandstone%1"' },
{ '"mcl_stairs:stair_stonebrick"', '"mcl_stairs:stair_redsandstone"' },
{ '"mcl_stairs:stair_stonebrick_([^"]+)"', '"mcl_stairs:stair_redsandstone_%1"' },
{ '"mcl_stairs:slab_brick_block([^"]*)"', '"mcl_core:redsandstonesmooth2%1"' },
{ '"mcl_core:brick_block"', '"mcl_core:redsandstonesmooth2"' },
{ "mcl_trees:tree_oak", "mcl_core:redsandstonecarved" },
{ "mcl_trees:wood_oak", "mcl_core:redsandstonesmooth" },
{ '"mcl_fences:oak_fence([^"]*)"', '"mcl_fences:birch_fence%1"' },
{ '"mcl_stairs:stair_oak_bark([^"]*)"', '"mcl_stairs:stair_sandstonesmooth2%1"' },
{ '"mcl_stairs:stair_oak([^"]*)"', '"mcl_stairs:stair_sandstonesmooth%1"' },
},
spruce = {
{ '"mcl_stairs:slab_oak([^"]*)"', '"mcl_stairs:slab_sprucewood%1"' },
{
'"mesecons_pressureplates:pressure_plate_oak_([^"]+)"',
'"mesecons_pressureplates:pressure_plate_sprucewood_%1"',
},
{ '"mcl_doors:trapdoor_oak([^"]*)"', '"mcl_doors:spruce_trapdoor%1"' },
{ '"mcl_doors:door_oak([^"]*)"', '"mcl_doors:spruce_door%1"' },
{ "mcl_trees:tree_oak", "mcl_trees:tree_spruce" },
{ "mcl_trees:wood_oak", "mcl_trees:wood_spruce" },
{ '"mcl_fences:oak_fence([^"]*)"', '"mcl_fences:spruce_fence%1"' },
{ '"mcl_stairs:stair_oak([^"]*)"', '"mcl_stairs:stair_spruce%1"' },
},
birch = {
{ '"mcl_stairs:slab_oak([^"]*)"', '"mcl_stairs:slab_birchwood%1"' },
{
'"mesecons_pressureplates:pressure_plate_oak_([^"]+)"',
'"mesecons_pressureplates:pressure_plate_birchwood_%1"',
},
{ '"mcl_doors:trapdoor_oak([^"]*)"', '"mcl_doors:birch_trapdoor%1"' },
{ '"mcl_doors:door_oak([^"]*)"', '"mcl_doors:birch_door%1"' },
{ "mcl_trees:tree_oak", "mcl_trees:tree_birch" },
{ "mcl_trees:wood_oak", "mcl_trees:wood_birch" },
{ '"mcl_fences:oak_fence([^"]*)"', '"mcl_fences:birch_fence%1"' },
{ '"mcl_stairs:stair_oak([^"]*)"', '"mcl_stairs:stair_birch%1"' },
},
acacia = {
{ '"mcl_stairs:slab_oak([^"]*)"', '"mcl_stairs:slab_acaciawood%1"' },
{
'"mesecons_pressureplates:pressure_plate_oak_([^"]+)"',
'"mesecons_pressureplates:pressure_plate_acaciawood_%1"',
},
{ '"mcl_doors:trapdoor_oak([^"]*)"', '"mcl_doors:acacia_trapdoor%1"' },
{ '"mcl_doors:door_oak([^"]*)"', '"mcl_doors:acacia_door%1"' },
{ "mcl_trees:tree_oak", "mcl_trees:tree_acacia" },
{ "mcl_trees:wood_oak", "mcl_trees:wood_acacia" },
{ '"mcl_fences:oak_fence([^"]*)"', '"mcl_fences:acacia_fence%1"' },
{ '"mcl_stairs:stair_oak([^"]*)"', '"mcl_stairs:stair_acacia%1"' },
},
dark_oak = {
{ '"mcl_stairs:slab_oak([^"]*)"', '"mcl_stairs:slab_darkwood%1"' },
{
'"mesecons_pressureplates:pressure_plate_oak_([^"]+)"',
'"mesecons_pressureplates:pressure_plate_darkwood_%1"',
},
{ '"mcl_doors:trapdoor_oak([^"]*)"', '"mcl_doors:dark_oak_trapdoor%1"' },
{ '"mcl_doors:door_oak([^"]*)"', '"mcl_doors:dark_oak_door%1"' },
{ "mcl_trees:tree_oak", "mcl_trees:tree_dark_oak" },
{ "mcl_trees:wood_oak", "mcl_trees:wood_dark_oak" },
{ '"mcl_fences:oak_fence([^"]*)"', '"mcl_fences:dark_oak_fence%1"' },
{ '"mcl_stairs:stair_oak([^"]*)"', '"mcl_stairs:stair_dark_oak%1"' },
},
jungle = {
{ '"mcl_stairs:slab_oak([^"]*)"', '"mcl_stairs:slab_junglewood%1"' },
{
'"mesecons_pressureplates:pressure_plate_oak_([^"]+)"',
'"mesecons_pressureplates:pressure_plate_junglewood_%1"',
},
{ '"mcl_doors:trapdoor_oak([^"]*)"', '"mcl_doors:jungle_trapdoor%1"' },
{ '"mcl_doors:door_oak([^"]*)"', '"mcl_doors:jungle_door%1"' },
{ "mcl_trees:tree_oak", "mcl_trees:tree_jungle" },
{ "mcl_trees:wood_oak", "mcl_trees:wood_jungle" },
{ '"mcl_fences:oak_fence([^"]*)"', '"mcl_fences:jungle_fence%1"' },
{ '"mcl_stairs:stair_oak([^"]*)"', '"mcl_stairs:stair_jungle%1"' },
},
bamboo = {
{ '"mcl_stairs:slab_oak([^"]*)"', '"mcl_stairs:slab_bamboo_block%1"' },
{
'"mesecons_pressureplates:pressure_plate_oak_([^"]+)"',
'"mesecons_pressureplates:pressure_plate_bamboo_%1"',
},
{ '"mcl_doors:trapdoor_oak([^"]*)"', '"mcl_doors:trapdoor_bamboo%1"' },
{ '"mcl_doors:door_oak([^"]*)"', '"mcl_doors:door_bamboo%1"' },
{ "mcl_core:cobble", "mcl_core:andesite" },
{ '"mcl_stairs:stair_cobble([^"]*)"', '"mcl_stairs:stair_andesite%1"' },
{ '"mcl_walls:cobble([^"]*)"', '"mcl_walls:andesite%1"' },
{ '"mcl_stairs:slab_cobble([^"]*)"', '"mcl_stairs:slab_andesite%1"' },
{ "mcl_trees:tree_oak", "mcl_trees:tree_bamboo" },
{ "mcl_trees:wood_oak", "mcl_trees:wood_bamboo" },
{ '"mcl_fences:oak_fence([^"]*)"', '"mcl_fences:bamboo_fence%1"' },
{ '"mcl_stairs:stair_oak([^"]*)"', '"mcl_stairs:stair_bamboo%1"' },
},
cherry = {
{ '"mcl_stairs:slab_oak([^"]*)"', '"mcl_stairs:slab_cherry_blossom%1"' },
{
'"mesecons_pressureplates:pressure_plate_oak_([^"]+)"',
'"mesecons_pressureplates:pressure_plate_cherry_blossom_%1"',
},
{ '"mcl_doors:trapdoor_oak([^"]*)"', '"mcl_doors:trapdoor_cherry_blossom%1"' },
{ '"mcl_doors:door_oak([^"]*)"', '"mcl_doors:door_cherry_blossom%1"' },
{ "mcl_trees:tree_oak", "mcl_trees:tree_cherry_blossom" },
{ "mcl_trees:wood_oak", "mcl_trees:wood_cherry_blossom" },
{ '"mcl_fences:oak_fence([^"]*)"', '"mcl_fences:cherry_blossom_fence%1"' },
{ '"mcl_stairs:stair_oak([^"]*)"', '"mcl_stairs:stair_cherry_blossom%1"' },
},
}

@ -1,48 +1,71 @@
local function mcl_log (message)
mcl_util.mcl_log (message, "[Village - Foundation]")
end
local foundation_materials = {} local foundation_materials = {}
foundation_materials["mcl_core:sand"] = "mcl_core:sandstone" foundation_materials["mcl_core:sand"] = "mcl_core:sandstone"
--"mcl_core:sandstonecarved" --"mcl_core:sandstonecarved"
------------------------------------------------------------------------------- local function is_air(node)
-- function to fill empty space below baseplate when building on a hill return not node or node.name == "air" or node.name == "ignore"
-------------------------------------------------------------------------------
function settlements.ground(pos, pr, platform_material) -- role model: Wendelsteinkircherl, Brannenburg
local p2 = vector.new(pos)
local cnt = 0
local mat = "mcl_core:dirt"
if not platform_material then
mat = "mcl_core:dirt"
else
mat = platform_material
end end
local function is_solid(node)
p2.y = p2.y-1 if not node or node.name == "air" or node.name == "ignore" then return false end
while true do --if string.find(node.name,"leaf") then return false end
cnt = cnt+1 --if string.find(node.name,"tree") then return false end
if cnt > 20 then break end local ndef = minetest.registered_nodes[node.name]
if cnt>pr:next(2,4) then return ndef and ndef.walkable
if not platform_material then end
mat = "mcl_core:stone" local function excavate(lvm,xi,yi,zi,pr)
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
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
end end
minetest.swap_node(p2, {name=mat}) -- try to completely remove trees overhead
p2.y = p2.y-1 if not string.find(node.name, "leaf") and not string.find(node.name, "tree") then
-- stop randomly depending on fill, to narrow down the caves
if pr:next(0,905) > c * 100 then return false end
end end
lvm:set_node_at(vector.new(xi, yi, zi),{name="air"})
return true -- modified
end
local function grow_foundation(lvm,xi,yi,zi,pr,surface_mat,platform_mat)
local pos, n, c = vector.new(xi,yi,zi), nil, 0
if is_solid(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(cur) then return false end -- above is empty, do not fill below
if cur and cur.name and cur.name ~= surface_mat then platform_mat = cur.name end
if pr:next(1,5) == 5 then -- randomly switch to stone sometimes
platform_mat = "mcl_core:stone"
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(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,905) > c * 100 then return false end
lvm:set_node_at(vector.new(xi, yi, zi),{name=platform_mat})
return true -- modified
end end
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- function clear space above baseplate -- function clear space above baseplate
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
function settlements.terraform(settlement_info, pr) function mcl_villages.terraform(settlement_info, pr)
local fheight, fwidth, fdepth, schematic_data local fheight, fwidth, fdepth, schematic_data
--local lvm, emin, emax = minetest.get_mapgen_object("voxelmanip")
local lvm = VoxelManip()
for i, built_house in ipairs(settlement_info) do for i, built_house in ipairs(settlement_info) do
-- pick right schematic_info to current built_house -- pick right schematic_info to current built_house
for j, schem in ipairs(settlements.schematic_table) do for j, schem in ipairs(mcl_villages.schematic_table) do
if settlement_info[i]["name"] == schem["name"] then if settlement_info[i]["name"] == schem["name"] then
schematic_data = schem schematic_data = schem
break break
@ -50,41 +73,187 @@ function settlements.terraform(settlement_info, pr)
end end
local pos = settlement_info[i]["pos"] local pos = settlement_info[i]["pos"]
if settlement_info[i]["rotat"] == "0" or settlement_info[i]["rotat"] == "180" then if settlement_info[i]["rotat"] == "0" or settlement_info[i]["rotat"] == "180" then
fwidth = schematic_data["hwidth"] fwidth, fdepth = schematic_data["hwidth"], schematic_data["hdepth"]
fdepth = schematic_data["hdepth"]
else else
fwidth = schematic_data["hdepth"] fwidth, fdepth = schematic_data["hdepth"], schematic_data["hwidth"]
fdepth = schematic_data["hwidth"]
end end
--fheight = schematic_data["hheight"] * 3 -- remove trees and leaves above
fheight = schematic_data["hheight"] -- remove trees and leaves above fheight = schematic_data["hheight"] -- remove trees and leaves above
-- use biome-specific materials
local surface_mat = settlement_info[i]["surface_mat"] local surface_mat = settlement_info[i]["surface_mat"]
mcl_log("Surface material: " .. tostring(surface_mat)) mcl_villages.debug("Surface material: " .. tostring(surface_mat))
local platform_mat = foundation_materials[surface_mat] local platform_mat = foundation_materials[surface_mat] or "mcl_core:dirt"
mcl_log("Foundation material: " .. tostring(platform_mat)) mcl_villages.debug("Foundation material: " .. tostring(platform_mat))
-- lvm:read_from_map(vector.new(pos.x-2, pos.y-20, pos.z-2), vector.new(pos.x+fwidth+2, pos.y+fheight+20, pos.z+fdepth+2))
-- now that every info is available -> create platform and clear space above -- TODO: further optimize by using raw data arrays instead of set_node_at. But OK for a first draft.
-- lvm:get_data()
for xi = 0,fwidth-1 do -- excavate the needed volume, some headroom, and add a baseplate
for zi = 0,fdepth-1 do local p2 = vector.new(pos)
for yi = 0,fheight *3 do for xi = pos.x,pos.x+fwidth-1 do
if yi == 0 then for zi = pos.z,pos.z+fdepth-1 do
local p = {x=pos.x+xi, y=pos.y, z=pos.z+zi} lvm:set_node_at(vector.new(xi, pos.y+1, zi),{name="air"})
-- Pass in biome info and make foundations of same material (seed: apple for desert) -- pos.y+2 to pos.y+5 are filled larger below!
settlements.ground(p, pr, platform_mat) for yi = pos.y+6,pos.y+fheight do
else lvm:set_node_at(vector.new(xi, yi, zi),{name="air"})
-- write ground end
-- local p = {x=pos.x+xi, y=pos.y+yi, z=pos.z+zi} local cp = vector.new(xi, pos.y, zi)
-- local node = mcl_vars.get_node(p) local cur = lvm:get_node_at(cp)
-- if node and node.name ~= "air" then if not is_solid(cur) or cur.name == platform_mat then
-- minetest.swap_node(p,{name="air"}) lvm:set_node_at(cp, {name=surface_mat})
-- end end
minetest.swap_node({x=pos.x+xi, y=pos.y+yi, z=pos.z+zi},{name="air"}) local cp = vector.new(xi, pos.y - 1, zi)
local cur = lvm:get_node_at(cp)
if not is_solid(cur) then
lvm:set_node_at(cp, {name=platform_mat})
end
end
end
-- slightly widen the cave, to make easier to enter for mobs
for xi = pos.x-1,pos.x+fwidth do
for zi = pos.z-1,pos.z+fdepth do
for yi = pos.y+2,pos.y+5 do
lvm:set_node_at(vector.new(xi, yi, zi),{name="air"})
end
end
end
-- some extra gaps
for xi = pos.x-2,pos.x+fwidth+1 do
for zi = pos.z-2,pos.z+fdepth+1 do
if pr:next(1,4) == 1 then
for yi = pos.y+3,pos.y+5 do
lvm:set_node_at(vector.new(xi, yi, zi),{name="air"})
end end
end end
end end
end end
-- slightly widen the baseplate, to make easier to enter for mobs
for xi = pos.x,pos.x+fwidth-1 do
local cp = vector.new(xi, pos.y-1, pos.z)
local cur = lvm:get_node_at(cp)
if not is_solid(cur) then
lvm:set_node_at(cp, {name=platform_mat})
end
local cp = vector.new(xi, pos.y-1, pos.z-1)
local cur = lvm:get_node_at(cp)
if not is_solid(cur) or cur.name == platform_mat then
lvm:set_node_at(cp, {name=surface_mat})
end
local cp = vector.new(xi, pos.y-1, pos.z+fdepth-1)
local cur = lvm:get_node_at(cp)
if not is_solid(cur) then
lvm:set_node_at(cp, {name=platform_mat})
end
local cp = vector.new(xi, pos.y-1, pos.z+fdepth)
local cur = lvm:get_node_at(cp)
if not is_solid(cur) or cur.name == platform_mat then
lvm:set_node_at(cp, {name=surface_mat})
end
end
for zi = pos.z,pos.z+fdepth-1 do
local cp = vector.new(pos.x, pos.y-1, zi)
local cur = lvm:get_node_at(cp)
if not is_solid(cur) then
lvm:set_node_at(cp, {name=platform_mat})
end
local cp = vector.new(pos.x-1, pos.y-1, zi)
local cur = lvm:get_node_at(cp)
if not is_solid(cur) or cur.name == platform_mat then
lvm:set_node_at(cp, {name=surface_mat})
end
local cp = vector.new(pos.x+fwidth-1, pos.y-1, zi)
local cur = lvm:get_node_at(cp)
if not is_solid(cur) then
lvm:set_node_at(cp, {name=platform_mat})
end
local cp = vector.new(pos.x+fwidth, pos.y-1, zi)
local cur = lvm:get_node_at(cp)
if not is_solid(cur) or cur.name == platform_mat then
lvm:set_node_at(cp, {name=surface_mat})
end
end
-- make some additional steps, along both x sides
for xi = pos.x,pos.x+fwidth-1 do
local cp = vector.new(xi, pos.y-3, pos.z-1)
if is_solid(lvm:get_node_at(cp)) then
cp = vector.new(xi, pos.y-2, pos.z-1)
local cur = lvm:get_node_at(cp)
if not is_solid(cur) or cur.name == platform_mat then
lvm:set_node_at(cp, {name=surface_mat})
end
cp.z = pos.z-2
cur = lvm:get_node_at(cp)
if not is_solid(cur) or cur.name == platform_mat then
lvm:set_node_at(cp, {name=surface_mat})
end
end
local cp = vector.new(xi, pos.y-3, pos.z+fdepth)
if is_solid(lvm:get_node_at(cp)) then
cp = vector.new(xi, pos.y-2, pos.z+fdepth)
local cur = lvm:get_node_at(cp)
if not is_solid(cur) or cur.name == platform_mat then
lvm:set_node_at(cp, {name=surface_mat})
end
cp.z = pos.z + fdepth + 1
cur = lvm:get_node_at(cp)
if not is_solid(cur) or cur.name == platform_mat then
lvm:set_node_at(cp, {name=surface_mat})
end
end
end
-- make some additional steps, along both z sides
for zi = pos.z,pos.z+fdepth-1 do
local cp = vector.new(pos.x-1, pos.y-3, zi)
if is_solid(lvm:get_node_at(cp)) then
cp = vector.new(pos.x-1, pos.y-2, zi)
local cur = lvm:get_node_at(cp)
if not is_solid(cur) or cur.name == platform_mat then
lvm:set_node_at(cp, {name=surface_mat})
end
cp.x = pos.x-2
cur = lvm:get_node_at(cp)
if not is_solid(cur) or cur.name == platform_mat then
lvm:set_node_at(cp, {name=surface_mat})
end
end
local cp = vector.new(pos.x+fwidth, pos.y-3, zi)
if is_solid(lvm:get_node_at(cp)) then
cp = vector.new(pos.x+fwidth, pos.y-2, zi)
local cur = lvm:get_node_at(cp)
if not is_solid(cur) or cur.name == platform_mat then
lvm:set_node_at(cp, {name=surface_mat})
end
cp.x = pos.x+fwidth+1
cur = lvm:get_node_at(cp)
if not is_solid(cur) or cur.name == platform_mat then
lvm:set_node_at(cp, {name=surface_mat})
end
end
end
-- cave some additional area overhead, try to make it interesting though
for yi = pos.y+3,pos.y+fheight*3 do
local active = false
for xi = pos.x-2,pos.x+fwidth+1 do
for zi = pos.z-2,pos.z+fdepth+1 do
if excavate(lvm,xi,yi,zi,pr) then
active = true
end
end
end
if not active and yi > pos.y+fheight+5 then break end
end
-- construct additional baseplate below, also try to make it interesting
for yi = pos.y-2,pos.y-20,-1 do
local active = false
for xi = pos.x-1,pos.x+fwidth do
for zi = pos.z-1,pos.z+fdepth do
if grow_foundation(lvm,xi,yi,zi,pr,surface_mat,platform_mat) then
active = true
end
end
end
if not active and yi < pos.y-5 then break end
end
lvm:write_to_map(false)
end end
end end

@ -1,20 +1,16 @@
settlements = {} mcl_villages = {}
settlements.modpath = minetest.get_modpath(minetest.get_current_modname()) mcl_villages.modpath = minetest.get_modpath(minetest.get_current_modname())
dofile(settlements.modpath.."/const.lua") local village_chance = tonumber(minetest.settings:get("mcl_villages_village_chance")) or 5
dofile(settlements.modpath.."/utils.lua")
dofile(settlements.modpath.."/foundation.lua") dofile(mcl_villages.modpath.."/const.lua")
dofile(settlements.modpath.."/buildings.lua") dofile(mcl_villages.modpath.."/utils.lua")
dofile(settlements.modpath.."/paths.lua") dofile(mcl_villages.modpath.."/foundation.lua")
--dofile(settlements.modpath.."/convert_lua_mts.lua") dofile(mcl_villages.modpath.."/buildings.lua")
-- dofile(mcl_villages.modpath.."/paths.lua")
-- load settlements on server
--
settlements.grundstellungen()
local S = minetest.get_translator(minetest.get_current_modname()) local S = minetest.get_translator(minetest.get_current_modname())
local villagegen={}
-- --
-- register block for npc spawn -- register block for npc spawn
-- --
@ -32,42 +28,20 @@ minetest.register_node("mcl_villages:stonebrickcarved", {
minetest.register_node("mcl_villages:structblock", {drawtype="airlike",groups = {not_in_creative_inventory=1},}) minetest.register_node("mcl_villages:structblock", {drawtype="airlike",groups = {not_in_creative_inventory=1},})
--[[ Enable for testing, but use MineClone2's own spawn code if/when merging.
--
-- register inhabitants
--
if minetest.get_modpath("mobs_mc") then
mcl_mobs:register_spawn("mobs_mc:villager", --name
{"mcl_core:stonebrickcarved"}, --nodes
15, --max_light
0, --min_light
20, --chance
7, --active_object_count
31000, --max_height
nil) --day_toggle
end
--]]
-- --
-- on map generation, try to build a settlement -- on map generation, try to build a settlement
-- --
local function build_a_settlement(minp, maxp, blockseed) local function build_a_settlement(minp, maxp, blockseed)
local pr = PseudoRandom(blockseed) if mcl_villages.village_exists(blockseed) then return end
-- fill settlement_info with buildings and their data local pr = PseudoRandom(blockseed)
local settlement_info = settlements.create_site_plan(maxp, minp, pr) local settlement_info = mcl_villages.create_site_plan(minp, maxp, pr)
if not settlement_info then return end if not settlement_info then return end
-- evaluate settlement_info and prepair terrain mcl_villages.terraform(settlement_info, pr)
settlements.terraform(settlement_info, pr) mcl_villages.place_schematics(settlement_info, pr)
mcl_villages.paths(settlement_info)
-- evaluate settlement_info and build paths between buildings mcl_villages.add_village(blockseed, settlement_info)
settlements.paths(settlement_info)
-- evaluate settlement_info and place schematics
settlements.place_schematics(settlement_info, pr)
end end
local function ecb_village(blockpos, action, calls_remaining, param) local function ecb_village(blockpos, action, calls_remaining, param)
@ -76,33 +50,24 @@ local function ecb_village(blockpos, action, calls_remaining, param)
build_a_settlement(minp, maxp, blockseed) build_a_settlement(minp, maxp, blockseed)
end end
local villagegen={}
-- Disable natural generation in singlenode. -- Disable natural generation in singlenode.
local mg_name = minetest.get_mapgen_setting("mg_name") local mg_name = minetest.get_mapgen_setting("mg_name")
if mg_name ~= "singlenode" then if mg_name ~= "singlenode" then
mcl_mapgen_core.register_generator("villages", nil, function(minp, maxp, blockseed) mcl_mapgen_core.register_generator("villages", nil, function(minp, maxp, blockseed)
if maxp.y < 0 then return end if maxp.y < 0 or village_chance == 0 then return end
local pr = PseudoRandom(blockseed)
-- randomly try to build settlements if pr:next(0, 100) > village_chance then return end
if blockseed % 77 ~= 17 then return end
--minetest.log("Rng good. Generate attempt")
-- needed for manual and automated settlement building
-- don't build settlements on (too) uneven terrain
local n=minetest.get_node_or_nil(minp) local n=minetest.get_node_or_nil(minp)
if n and n.name == "mcl_villages:structblock" then return end --if n and n.name == "mcl_villages:structblock" then return end
--minetest.log("No existing village attempt here") if n and n.name ~= "air" then return end
if villagegen[minetest.pos_to_string(minp)] ~= nil then return end
--minetest.log("Not in village gen. Put down placeholder: " .. minetest.pos_to_string(minp) .. " || " .. minetest.pos_to_string(maxp))
minetest.set_node(minp,{name="mcl_villages:structblock"}) minetest.set_node(minp,{name="mcl_villages:structblock"})
local height_difference = settlements.evaluate_heightmap() --[[minetest.emerge_area(
if not height_difference or height_difference > max_height_difference then minp, maxp, --vector.offset(minp, -16, -16, -16), vector.offset(maxp, 16, 16, 16),
minetest.log("action", "Do not spawn village here as heightmap not good") ecb_village,
return { minp = vector.copy(minp), maxp = vector.copy(maxp), blockseed = blockseed }
end )]]--
--minetest.log("Build me a village: " .. minetest.pos_to_string(minp) .. " || " .. minetest.pos_to_string(maxp))
villagegen[minetest.pos_to_string(minp)]={minp=vector.new(minp), maxp=vector.new(maxp), blockseed=blockseed} villagegen[minetest.pos_to_string(minp)]={minp=vector.new(minp), maxp=vector.new(maxp), blockseed=blockseed}
end) end)
end end
@ -125,7 +90,7 @@ if minetest.is_creative_enabled("") then
minetest.register_craftitem("mcl_villages:tool", { minetest.register_craftitem("mcl_villages:tool", {
description = S("mcl_villages build tool"), description = S("mcl_villages build tool"),
inventory_image = "default_tool_woodshovel.png", inventory_image = "default_tool_woodshovel.png",
-- build ssettlement -- build settlement
on_place = function(itemstack, placer, pointed_thing) on_place = function(itemstack, placer, pointed_thing)
if not pointed_thing.under then return end if not pointed_thing.under then return end
if not minetest.check_player_privs(placer, "server") then if not minetest.check_player_privs(placer, "server") then

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

@ -1,7 +1,7 @@
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- generate paths between buildings -- generate paths between buildings
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
function settlements.paths(settlement_info) function mcl_villages.paths(settlement_info)
local starting_point local starting_point
local end_point local end_point
local distance local distance
@ -67,7 +67,7 @@ function settlements.paths(settlement_info)
distance = dist_east_p_to_end distance = dist_east_p_to_end
end end
-- find surface of new starting point -- find surface of new starting point
local surface_point, surface_mat = settlements.find_surface(starting_point) local surface_point, surface_mat = mcl_villages.find_surface(starting_point)
-- replace surface node with mcl_core:grass_path -- replace surface node with mcl_core:grass_path
if surface_point if surface_point
then then

@ -1,106 +1,105 @@
local get_node = mcl_vars.get_node
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- function to copy tables -- function to copy tables
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
function settlements.shallowCopy(original) function mcl_villages.shallowCopy(original)
local copy = {} local copy = {}
for key, value in pairs(original) do for key, value in pairs(original) do
copy[key] = value copy[key] = value
end end
return copy return copy
end end
--
--
--
function settlements.round(num, numDecimalPlaces)
local mult = 10^(numDecimalPlaces or 0)
return math.floor(num * mult + 0.5) / mult
end
local function is_above_surface(name)
return name == "air" or
string.find(name,"grass") or
string.find(name,"tree") or
string.find(name,"leaves") or
string.find(name,"snow") or
string.find(name,"fern") or
string.find(name,"flower") or
string.find(name,"bush")
end
local get_node = mcl_vars.get_node
function mcl_villages.find_surface_down(pos, surface_node)
local p6 = vector.new(pos)
surface_node = surface_node or get_node(p6) --, true, 1000000)
if not surface_node then return end
for y = p6.y - 1, math.max(0,p6.y - 120), -1 do
p6.y = y
local top_node = surface_node
surface_node = get_node(p6)
if not surface_node then return nil end
if is_above_surface(top_node.name) then
if mcl_villages.surface_mat[surface_node.name] then
-- minetest.log("verbose", "Found "..surface_node.name.." below "..top_node.name)
return p6, surface_node.name
end
else
local ndef = minetest.registered_nodes[surface_node.name]
if ndef and ndef.walkable then
return nil
end
end
end
end
function mcl_villages.find_surface_up(pos, surface_node)
local p6 = vector.new(pos)
surface_node = surface_node or get_node(p6) --, true, 1000000)
if not surface_node then return end
for y = p6.y + 1, p6.y + 50 do
p6.y = y
local top_node = get_node(p6)
if not top_node then return nil end
if is_above_surface(top_node.name) then
if mcl_villages.surface_mat[surface_node.name] then
-- minetest.log("verbose","Found "..surface_node.name.." below "..top_node.name)
p6.y = p6.y - 1
return p6, surface_node.name
end
else
local ndef = minetest.registered_nodes[surface_node.name]
if ndef and ndef.walkable then
return nil
end
end
surface_node = top_node
end
end
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- function to find surface block y coordinate -- function to find surface block y coordinate
-- returns surface postion -- returns surface postion
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
function settlements.find_surface(pos, wait) function mcl_villages.find_surface(pos, wait)
local p6 = vector.new(pos) local p6 = vector.new(pos)
local cnt = 0 if p6.y < 0 then p6.y = 0 end -- start at water level
local itter = 1 -- count up or down
local cnt_max = 200
-- check, in which direction to look for surface
local surface_node local surface_node
if wait then if wait then
surface_node = get_node(p6, true, 10000000) surface_node = get_node(p6, true, 10000000)
else else
surface_node = get_node(p6) surface_node = get_node(p6)
end end
if surface_node.name=="air" or surface_node.name=="ignore" then -- downward, if starting position is empty
itter = -1 if is_above_surface(surface_node.name) then
end return mcl_villages.find_surface_down(p6, surface_node)
-- go through nodes an find surface
while cnt < cnt_max do
-- Check Surface_node and Node above
if surface_node and settlements.surface_mat[surface_node.name] then
local surface_node_plus_1 = get_node({ x=p6.x, y=p6.y+1, z=p6.z})
if surface_node_plus_1 then
local surface_node_minus_1 = get_node({ x=p6.x, y=p6.y-1, z=p6.z})
local is_leaf_below = minetest.get_item_group(surface_node_minus_1, "leaves") ~= 0 or
string.find(surface_node_minus_1.name,"leaves")
if not is_leaf_below and ((string.find(surface_node_plus_1.name,"air") or
string.find(surface_node_plus_1.name,"fern") or
string.find(surface_node_plus_1.name,"flower") or
string.find(surface_node_plus_1.name,"bush") or
string.find(surface_node_plus_1.name,"tree") or
string.find(surface_node_plus_1.name,"grass")) or
string.find(surface_node_plus_1.name,"snow"))
then
settlements.debug("find_surface success: " ..surface_node.name.. " " .. surface_node_plus_1.name)
settlements.debug("node below: " .. tostring(surface_node_minus_1.name))
settlements.debug("node below leaves group: " .. tostring(minetest.get_item_group(surface_node_minus_1, "leaves")))
return p6, surface_node.name
else else
settlements.debug("find_surface2: wrong surface+1") return mcl_villages.find_surface_up(p6, surface_node)
end end
else
settlements.debug("find_surface8: missing node or plus_1")
end
else
settlements.debug("find_surface3: wrong surface "..surface_node.name.." at pos "..minetest.pos_to_string(p6))
end
p6.y = p6.y + itter
if p6.y < 0 then
settlements.debug("find_surface4: y<0")
return nil
end
cnt = cnt+1
surface_node = get_node(p6)
end
settlements.debug("find_surface5: cnt_max overflow")
return nil
end end
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- check distance for new building -- check distance for new building
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
function settlements.check_distance(settlement_info, building_pos, building_size) function mcl_villages.check_distance(settlement_info, building_pos, building_size)
local distance
for i, built_house in ipairs(settlement_info) do for i, built_house in ipairs(settlement_info) do
distance = math.sqrt( local dx, dz = building_pos.x - built_house["pos"].x, building_pos.z - built_house["pos"].z
((building_pos.x - built_house["pos"].x)*(building_pos.x - built_house["pos"].x))+ local dsq = dx*dx+dz*dz
((building_pos.z - built_house["pos"].z)*(building_pos.z - built_house["pos"].z))) if dsq < building_size^2 or dsq < built_house["hsize"]^2 then return false end
if distance < building_size or distance < built_house["hsize"] then
return false
end
end end
return true return true
end end
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- fill chests -- fill chests
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
function settlements.fill_chest(pos, pr) function mcl_villages.fill_chest(pos, pr)
-- initialize chest (mts chests don't have meta) -- initialize chest (mts chests don't have meta)
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
if meta:get_string("infotext") ~= "Chest" then if meta:get_string("infotext") ~= "Chest" then
@ -148,17 +147,15 @@ end
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- initialize furnace -- initialize furnace
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
function settlements.initialize_furnace(pos) function mcl_villages.initialize_furnace(pos)
-- find chests within radius -- find chests within radius
local furnacepos = minetest.find_node_near(pos, local furnacepos = minetest.find_node_near(pos,
7, --radius 7, --radius
{"mcl_furnaces:furnace"}) {"mcl_furnaces:furnace"})
-- initialize furnacepos (mts furnacepos don't have meta) -- initialize furnacepos (mts furnacepos don't have meta)
if furnacepos if furnacepos then
then
local meta = minetest.get_meta(furnacepos) local meta = minetest.get_meta(furnacepos)
if meta:get_string("infotext") ~= "furnace" if meta:get_string("infotext") ~= "furnace" then
then
minetest.registered_nodes["mcl_furnaces:furnace"].on_construct(furnacepos) minetest.registered_nodes["mcl_furnaces:furnace"].on_construct(furnacepos)
end end
end end
@ -166,17 +163,15 @@ end
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- initialize anvil -- initialize anvil
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
function settlements.initialize_anvil(pos) function mcl_villages.initialize_anvil(pos)
-- find chests within radius -- find chests within radius
local anvilpos = minetest.find_node_near(pos, local anvilpos = minetest.find_node_near(pos,
7, --radius 7, --radius
{"mcl_anvils:anvil"}) {"mcl_anvils:anvil"})
-- initialize anvilpos (mts anvilpos don't have meta) -- initialize anvilpos (mts anvilpos don't have meta)
if anvilpos if anvilpos then
then
local meta = minetest.get_meta(anvilpos) local meta = minetest.get_meta(anvilpos)
if meta:get_string("infotext") ~= "anvil" if meta:get_string("infotext") ~= "anvil" then
then
minetest.registered_nodes["mcl_anvils:anvil"].on_construct(anvilpos) minetest.registered_nodes["mcl_anvils:anvil"].on_construct(anvilpos)
end end
end end
@ -184,87 +179,61 @@ end
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- randomize table -- randomize table
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
function shuffle(tbl, pr) function mcl_villages.shuffle(tbl, pr)
local table = settlements.shallowCopy(tbl) local copy = {}
local size = #table for key, value in ipairs(tbl) do
for i = size, 1, -1 do table.insert(copy, pr:next(1, #copy + 1), value)
local rand = pr:next(1, size)
table[i], table[rand] = table[rand], table[i]
end end
return table return copy
end
-------------------------------------------------------------------------------
-- evaluate heightmap
-------------------------------------------------------------------------------
function settlements.evaluate_heightmap()
local heightmap = minetest.get_mapgen_object("heightmap")
if not heightmap then
minetest.log("action", "No heightmap. That should not happen")
return max_height_difference + 1
end end
--minetest.log("action", "heightmap size: " .. tostring(#heightmap)) -- Load a schema and replace nodes in it based on biome
function mcl_villages.substitute_materials(pos, schem_lua, pr)
local modified_schem_lua = schem_lua
local biome_data = minetest.get_biome_data(pos)
local biome_name = minetest.get_biome_name(biome_data.biome)
-- max height and min height, initialize with impossible values for easier first time setting if mcl_villages.biome_map[biome_name] and mcl_villages.material_substitions[mcl_villages.biome_map[biome_name]] then
local max_y = -50000 for _, sub in pairs(mcl_villages.material_substitions[mcl_villages.biome_map[biome_name]]) do
local min_y = 50000 modified_schem_lua = modified_schem_lua:gsub(sub[1], sub[2])
-- only evaluate the center square of heightmap 40 x 40 end
local square_start = 1621
local square_end = 1661
for j = 1 , 40, 1 do
if square_start >= #heightmap then
--minetest.log("action", "Heightmap size reached. Go no further outside")
break
end end
for i = square_start, square_end, 1 do
--minetest.log("action", "current hm index: " .. tostring(i) .. "current hm entry: " .. tostring(heightmap[i]))
if i >= #heightmap then return modified_schem_lua
--minetest.log("action", "Heightmap size reached. Go no further")
break
end end
local current_hm_entry = heightmap[i]
if current_hm_entry then
-- skip buggy heightmaps, return high value. Converted mcl5 maps can be -31007
if current_hm_entry == -31000 or heightmap[i] == 31000 then
--minetest.log("action", "incorrect heighmap values. abandon")
return max_height_difference + 1
end
if current_hm_entry < min_y then
min_y = current_hm_entry
end
if current_hm_entry > max_y then
max_y = current_hm_entry
end
else
--minetest.log("action", "Failed to get hm index: " .. tostring(i) .. "and ... " .. tostring(#heightmap))
end
end
-- set next line
square_start = square_start + 80
square_end = square_end + 80
end
-- return the difference between highest and lowest pos in chunk
local height_diff = max_y - min_y
--minetest.log("action", "height_diff = " .. tostring(height_diff)) local villages = {}
local mod_storage = minetest.get_mod_storage()
-- filter buggy heightmaps local function lazy_load_village(name)
if height_diff <= 1 then if not villages[name] then
return max_height_difference + 1 local data = mod_storage:get("mcl_villages." .. name)
if data then
villages[name] = minetest.deserialize(data)
end end
--minetest.log("action", "return heigh diff = " .. tostring(height_diff))
-- debug info
settlements.debug("heightdiff ".. height_diff)
return height_diff
end end
------------------------------------------------------------------------------- end
-- Set array to list
-- https://stackoverflow.com/questions/656199/search-for-an-item-in-a-lua-list function mcl_villages.get_village(name)
------------------------------------------------------------------------------- lazy_load_village(name)
function settlements.Set (list) if villages[name] then
local set = {} return table.copy(villages[name])
for _, l in ipairs(list) do set[l] = true end end
return set end
function mcl_villages.village_exists(name)
lazy_load_village(name)
return villages[name] ~= nil
end
function mcl_villages.add_village(name, data)
lazy_load_village(name)
if villages[name] then
minetest.log("info","Village already exists: " .. name )
return false
end
local new_village = {name = name, data = data}
mod_storage:set_string("mcl_villages." .. name, minetest.serialize(new_village))
return true
end end