Big villages overhaul

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

View File

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

View File

@ -1,5 +1,5 @@
-- switch for debugging
function settlements.debug(message)
function mcl_villages.debug(message)
-- minetest.chat_send_all(message)
-- minetest.log("warning", "[mcl_villages] "..message)
minetest.log("verbose", "[mcl_villages] "..message)
@ -20,44 +20,51 @@ local wallmaterial = {
"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
--
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
--
schem_path = settlements.modpath.."/schematics/"
schem_path = mcl_villages.modpath.."/schematics/"
--
-- list of schematics
--
local basic_pseudobiome_villages = minetest.settings:get_bool("basic_pseudobiome_villages", true)
settlements.schematic_table = {
{name = "belltower", mts = schem_path.."belltower.mts", hwidth = 5, hdepth = 5, hheight = 9, hsize = 14, max_num = 0 , rplc = basic_pseudobiome_villages },
mcl_villages.schematic_table = {
{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 = "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 = "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 = "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 },
@ -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
--
@ -75,3 +82,185 @@ max_height_difference = 56
--
half_map_chunk_size = 40
--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"' },
},
}

View File

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

View File

@ -1,20 +1,16 @@
settlements = {}
settlements.modpath = minetest.get_modpath(minetest.get_current_modname())
mcl_villages = {}
mcl_villages.modpath = minetest.get_modpath(minetest.get_current_modname())
dofile(settlements.modpath.."/const.lua")
dofile(settlements.modpath.."/utils.lua")
dofile(settlements.modpath.."/foundation.lua")
dofile(settlements.modpath.."/buildings.lua")
dofile(settlements.modpath.."/paths.lua")
--dofile(settlements.modpath.."/convert_lua_mts.lua")
--
-- load settlements on server
--
settlements.grundstellungen()
local village_chance = tonumber(minetest.settings:get("mcl_villages_village_chance")) or 5
dofile(mcl_villages.modpath.."/const.lua")
dofile(mcl_villages.modpath.."/utils.lua")
dofile(mcl_villages.modpath.."/foundation.lua")
dofile(mcl_villages.modpath.."/buildings.lua")
dofile(mcl_villages.modpath.."/paths.lua")
local S = minetest.get_translator(minetest.get_current_modname())
local villagegen={}
--
-- 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},})
--[[ Enable for testing, but use MineClone2's own spawn code if/when merging.
--
-- register inhabitants
--
if minetest.get_modpath("mobs_mc") then
mcl_mobs:register_spawn("mobs_mc:villager", --name
{"mcl_core:stonebrickcarved"}, --nodes
15, --max_light
0, --min_light
20, --chance
7, --active_object_count
31000, --max_height
nil) --day_toggle
end
--]]
--
-- on map generation, try to build a settlement
--
local function build_a_settlement(minp, maxp, blockseed)
local pr = PseudoRandom(blockseed)
if mcl_villages.village_exists(blockseed) then return end
-- fill settlement_info with buildings and their data
local settlement_info = settlements.create_site_plan(maxp, minp, pr)
local pr = PseudoRandom(blockseed)
local settlement_info = mcl_villages.create_site_plan(minp, maxp, pr)
if not settlement_info then return end
-- evaluate settlement_info and prepair terrain
settlements.terraform(settlement_info, pr)
-- evaluate settlement_info and build paths between buildings
settlements.paths(settlement_info)
-- evaluate settlement_info and place schematics
settlements.place_schematics(settlement_info, pr)
mcl_villages.terraform(settlement_info, pr)
mcl_villages.place_schematics(settlement_info, pr)
mcl_villages.paths(settlement_info)
mcl_villages.add_village(blockseed, settlement_info)
end
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)
end
local villagegen={}
-- Disable natural generation in singlenode.
local mg_name = minetest.get_mapgen_setting("mg_name")
if mg_name ~= "singlenode" then
mcl_mapgen_core.register_generator("villages", nil, function(minp, maxp, blockseed)
if maxp.y < 0 then return end
-- randomly try to build settlements
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
if maxp.y < 0 or village_chance == 0 then return end
local pr = PseudoRandom(blockseed)
if pr:next(0, 100) > village_chance then return end
local n=minetest.get_node_or_nil(minp)
if n and n.name == "mcl_villages:structblock" then return end
--minetest.log("No existing village attempt here")
if villagegen[minetest.pos_to_string(minp)] ~= nil then return end
--minetest.log("Not in village gen. Put down placeholder: " .. minetest.pos_to_string(minp) .. " || " .. minetest.pos_to_string(maxp))
--if n and n.name == "mcl_villages:structblock" then return end
if n and n.name ~= "air" then return end
minetest.set_node(minp,{name="mcl_villages:structblock"})
local height_difference = settlements.evaluate_heightmap()
if not height_difference or height_difference > max_height_difference then
minetest.log("action", "Do not spawn village here as heightmap not good")
return
end
--minetest.log("Build me a village: " .. minetest.pos_to_string(minp) .. " || " .. minetest.pos_to_string(maxp))
--[[minetest.emerge_area(
minp, maxp, --vector.offset(minp, -16, -16, -16), vector.offset(maxp, 16, 16, 16),
ecb_village,
{ minp = vector.copy(minp), maxp = vector.copy(maxp), blockseed = blockseed }
)]]--
villagegen[minetest.pos_to_string(minp)]={minp=vector.new(minp), maxp=vector.new(maxp), blockseed=blockseed}
end)
end
@ -125,15 +90,15 @@ if minetest.is_creative_enabled("") then
minetest.register_craftitem("mcl_villages:tool", {
description = S("mcl_villages build tool"),
inventory_image = "default_tool_woodshovel.png",
-- build ssettlement
-- build settlement
on_place = function(itemstack, placer, pointed_thing)
if not pointed_thing.under then return end
if not minetest.check_player_privs(placer, "server") then
minetest.chat_send_player(placer:get_player_name(), S("Placement denied. You need the “server” privilege to place villages."))
return
end
local minp = vector.subtract( pointed_thing.under, half_map_chunk_size)
local maxp = vector.add( pointed_thing.under, half_map_chunk_size)
local minp = vector.subtract(pointed_thing.under, half_map_chunk_size)
local maxp = vector.add(pointed_thing.under, half_map_chunk_size)
build_a_settlement(minp, maxp, math.random(0,32767))
end
})

View File

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

View File

@ -1,7 +1,7 @@
-------------------------------------------------------------------------------
-- generate paths between buildings
-------------------------------------------------------------------------------
function settlements.paths(settlement_info)
function mcl_villages.paths(settlement_info)
local starting_point
local end_point
local distance
@ -67,7 +67,7 @@ function settlements.paths(settlement_info)
distance = dist_east_p_to_end
end
-- 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
if surface_point
then

View File

@ -1,106 +1,105 @@
local get_node = mcl_vars.get_node
-------------------------------------------------------------------------------
-- function to copy tables
-------------------------------------------------------------------------------
function settlements.shallowCopy(original)
function mcl_villages.shallowCopy(original)
local copy = {}
for key, value in pairs(original) do
copy[key] = value
end
return copy
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
-- returns surface postion
-------------------------------------------------------------------------------
function settlements.find_surface(pos, wait)
function mcl_villages.find_surface(pos, wait)
local p6 = vector.new(pos)
local cnt = 0
local itter = 1 -- count up or down
local cnt_max = 200
-- check, in which direction to look for surface
if p6.y < 0 then p6.y = 0 end -- start at water level
local surface_node
if wait then
surface_node = get_node(p6, true, 10000000)
else
surface_node = get_node(p6)
end
if surface_node.name=="air" or surface_node.name=="ignore" then
itter = -1
-- downward, if starting position is empty
if is_above_surface(surface_node.name) then
return mcl_villages.find_surface_down(p6, surface_node)
else
return mcl_villages.find_surface_up(p6, surface_node)
end
-- 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
settlements.debug("find_surface2: wrong surface+1")
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
-------------------------------------------------------------------------------
-- check distance for new building
-------------------------------------------------------------------------------
function settlements.check_distance(settlement_info, building_pos, building_size)
local distance
function mcl_villages.check_distance(settlement_info, building_pos, building_size)
for i, built_house in ipairs(settlement_info) do
distance = math.sqrt(
((building_pos.x - built_house["pos"].x)*(building_pos.x - built_house["pos"].x))+
((building_pos.z - built_house["pos"].z)*(building_pos.z - built_house["pos"].z)))
if distance < building_size or distance < built_house["hsize"] then
return false
end
local dx, dz = building_pos.x - built_house["pos"].x, building_pos.z - built_house["pos"].z
local dsq = dx*dx+dz*dz
if dsq < building_size^2 or dsq < built_house["hsize"]^2 then return false end
end
return true
end
-------------------------------------------------------------------------------
-- fill chests
-------------------------------------------------------------------------------
function settlements.fill_chest(pos, pr)
function mcl_villages.fill_chest(pos, pr)
-- initialize chest (mts chests don't have meta)
local meta = minetest.get_meta(pos)
if meta:get_string("infotext") ~= "Chest" then
@ -148,123 +147,93 @@ end
-------------------------------------------------------------------------------
-- initialize furnace
-------------------------------------------------------------------------------
function settlements.initialize_furnace(pos)
-- find chests within radius
local furnacepos = minetest.find_node_near(pos,
7, --radius
{"mcl_furnaces:furnace"})
-- initialize furnacepos (mts furnacepos don't have meta)
if furnacepos
then
local meta = minetest.get_meta(furnacepos)
if meta:get_string("infotext") ~= "furnace"
then
minetest.registered_nodes["mcl_furnaces:furnace"].on_construct(furnacepos)
end
end
function mcl_villages.initialize_furnace(pos)
-- find chests within radius
local furnacepos = minetest.find_node_near(pos,
7, --radius
{"mcl_furnaces:furnace"})
-- initialize furnacepos (mts furnacepos don't have meta)
if furnacepos then
local meta = minetest.get_meta(furnacepos)
if meta:get_string("infotext") ~= "furnace" then
minetest.registered_nodes["mcl_furnaces:furnace"].on_construct(furnacepos)
end
end
end
-------------------------------------------------------------------------------
-- initialize anvil
-------------------------------------------------------------------------------
function settlements.initialize_anvil(pos)
-- find chests within radius
local anvilpos = minetest.find_node_near(pos,
7, --radius
{"mcl_anvils:anvil"})
-- initialize anvilpos (mts anvilpos don't have meta)
if anvilpos
then
local meta = minetest.get_meta(anvilpos)
if meta:get_string("infotext") ~= "anvil"
then
minetest.registered_nodes["mcl_anvils:anvil"].on_construct(anvilpos)
end
end
function mcl_villages.initialize_anvil(pos)
-- find chests within radius
local anvilpos = minetest.find_node_near(pos,
7, --radius
{"mcl_anvils:anvil"})
-- initialize anvilpos (mts anvilpos don't have meta)
if anvilpos then
local meta = minetest.get_meta(anvilpos)
if meta:get_string("infotext") ~= "anvil" then
minetest.registered_nodes["mcl_anvils:anvil"].on_construct(anvilpos)
end
end
end
-------------------------------------------------------------------------------
-- randomize table
-------------------------------------------------------------------------------
function shuffle(tbl, pr)
local table = settlements.shallowCopy(tbl)
local size = #table
for i = size, 1, -1 do
local rand = pr:next(1, size)
table[i], table[rand] = table[rand], table[i]
function mcl_villages.shuffle(tbl, pr)
local copy = {}
for key, value in ipairs(tbl) do
table.insert(copy, pr:next(1, #copy + 1), value)
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
-- 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)
--minetest.log("action", "heightmap size: " .. tostring(#heightmap))
-- max height and min height, initialize with impossible values for easier first time setting
local max_y = -50000
local min_y = 50000
-- only evaluate the center square of heightmap 40 x 40
local square_start = 1621
local square_end = 1661
for j = 1 , 40, 1 do
if square_start >= #heightmap then
--minetest.log("action", "Heightmap size reached. Go no further outside")
break
if mcl_villages.biome_map[biome_name] and mcl_villages.material_substitions[mcl_villages.biome_map[biome_name]] then
for _, sub in pairs(mcl_villages.material_substitions[mcl_villages.biome_map[biome_name]]) do
modified_schem_lua = modified_schem_lua:gsub(sub[1], sub[2])
end
for i = square_start, square_end, 1 do
--minetest.log("action", "current hm index: " .. tostring(i) .. "current hm entry: " .. tostring(heightmap[i]))
end
if i >= #heightmap then
--minetest.log("action", "Heightmap size reached. Go no further")
break
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
return modified_schem_lua
end
local villages = {}
local mod_storage = minetest.get_mod_storage()
local function lazy_load_village(name)
if not villages[name] then
local data = mod_storage:get("mcl_villages." .. name)
if data then
villages[name] = minetest.deserialize(data)
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
end
--minetest.log("action", "height_diff = " .. tostring(height_diff))
-- filter buggy heightmaps
if height_diff <= 1 then
return max_height_difference + 1
function mcl_villages.get_village(name)
lazy_load_village(name)
if villages[name] then
return table.copy(villages[name])
end
--minetest.log("action", "return heigh diff = " .. tostring(height_diff))
-- debug info
settlements.debug("heightdiff ".. height_diff)
return height_diff
end
-------------------------------------------------------------------------------
-- Set array to list
-- https://stackoverflow.com/questions/656199/search-for-an-item-in-a-lua-list
-------------------------------------------------------------------------------
function settlements.Set (list)
local set = {}
for _, l in ipairs(list) do set[l] = true end
return set
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