2014-08-19 18:32:42 +02:00
mg_villages.VILLAGE_CHECK_RADIUS = 2
mg_villages.VILLAGE_CHECK_COUNT = 1
--mg_villages.VILLAGE_CHANCE = 28
--mg_villages.VILLAGE_MIN_SIZE = 20
--mg_villages.VILLAGE_MAX_SIZE = 40
mg_villages.VILLAGE_CHANCE = 28
mg_villages.VILLAGE_MIN_SIZE = 25
mg_villages.VILLAGE_MAX_SIZE = 90 --55
mg_villages.FIRST_ROADSIZE = 3
mg_villages.BIG_ROAD_CHANCE = 0
2014-08-04 22:18:20 +02:00
-- Enable that for really big villages (there are also really slow to generate)
2014-08-19 18:32:42 +02:00
--[[mg_villages.VILLAGE_CHECK_RADIUS = 3
mg_villages.VILLAGE_CHECK_COUNT = 3
mg_villages.VILLAGE_CHANCE = 28
mg_villages.VILLAGE_MIN_SIZE = 100
mg_villages.VILLAGE_MAX_SIZE = 150
mg_villages.FIRST_ROADSIZE = 5
mg_villages.BIG_ROAD_CHANCE = 50 ] ]
2014-08-04 22:18:20 +02:00
2014-08-19 03:45:45 +02:00
2014-08-26 20:41:29 +02:00
-- if set to false, villages will not be integrated into the terrain - which looks very bad
mg_villages.ENABLE_TERRAIN_BLEND = true ;
-- if set to false, holes digged by cavegen and mudflow inside the village will not be repaired; houses will be destroyed
mg_villages.UNDO_CAVEGEN_AND_MUDFLOW = true ;
2014-08-19 03:45:45 +02:00
-- on average, every n.th node may be one of these trees - and it will be a relatively dense packed forrest
mg_villages.sapling_probability = { } ;
mg_villages.sapling_probability [ minetest.get_content_id ( ' default:sapling ' ) ] = 25 ; -- suitable for a relatively dense forrest of normal trees
mg_villages.sapling_probability [ minetest.get_content_id ( ' default:junglesapling ' ) ] = 40 ; -- jungletrees are a bit bigger and need more space
if ( minetest.get_modpath ( ' mg ' ) ) then
mg_villages.sapling_probability [ minetest.get_content_id ( ' mg:savannasapling ' ) ] = 30 ;
mg_villages.sapling_probability [ minetest.get_content_id ( ' mg:pinesapling ' ) ] = 35 ;
end
if ( minetest.get_modpath ( ' moretrees ' ) ) then
mg_villages.sapling_probability [ minetest.get_content_id ( ' moretrees:birch_sapling_ongen ' ) ] = 200 ;
mg_villages.sapling_probability [ minetest.get_content_id ( ' moretrees:spruce_sapling_ongen ' ) ] = 200 ;
mg_villages.sapling_probability [ minetest.get_content_id ( ' moretrees:fir_sapling_ongen ' ) ] = 90 ;
mg_villages.sapling_probability [ minetest.get_content_id ( ' moretrees:jungletree_sapling_ongen ' ) ] = 200 ;
mg_villages.sapling_probability [ minetest.get_content_id ( ' moretrees:beech_sapling_ongen ' ) ] = 30 ;
mg_villages.sapling_probability [ minetest.get_content_id ( ' moretrees:apple_sapling_ongen ' ) ] = 380 ;
mg_villages.sapling_probability [ minetest.get_content_id ( ' moretrees:oak_sapling_ongen ' ) ] = 380 ; -- ca 20x20; height: 10
mg_villages.sapling_probability [ minetest.get_content_id ( ' moretrees:sequoia_sapling_ongen ' ) ] = 90 ; -- ca 10x10
mg_villages.sapling_probability [ minetest.get_content_id ( ' moretrees:palm_sapling_ongen ' ) ] = 90 ;
mg_villages.sapling_probability [ minetest.get_content_id ( ' moretrees:pine_sapling_ongen ' ) ] = 200 ;
mg_villages.sapling_probability [ minetest.get_content_id ( ' moretrees:willow_sapling_ongen ' ) ] = 380 ;
mg_villages.sapling_probability [ minetest.get_content_id ( ' moretrees:rubber_tree_sapling_ongen ' ) ] = 380 ;
end
2014-08-04 22:18:20 +02:00
local function is_village_block ( minp )
local x , z = math.floor ( minp.x / 80 ) , math.floor ( minp.z / 80 )
2014-08-19 18:32:42 +02:00
local vcc = mg_villages.VILLAGE_CHECK_COUNT
2014-08-04 22:18:20 +02:00
return ( x % vcc == 0 ) and ( z % vcc == 0 )
end
mg_villages.villages_at_point = function ( minp , noise1 )
if not is_village_block ( minp ) then return { } end
2014-08-19 18:32:42 +02:00
local vcr , vcc = mg_villages.VILLAGE_CHECK_RADIUS , mg_villages.VILLAGE_CHECK_COUNT
2014-08-04 22:18:20 +02:00
-- Check if there's another village nearby
for xi = - vcr , vcr , vcc do
for zi = - vcr , 0 , vcc do
if xi ~= 0 or zi ~= 0 then
local mp = { x = minp.x + 80 * xi , z = minp.z + 80 * zi }
2014-08-05 01:24:11 +02:00
local pi = PseudoRandom ( mg_villages.get_bseed ( mp ) )
2014-08-04 22:18:20 +02:00
local s = pi : next ( 1 , 400 )
local x = pi : next ( mp.x , mp.x + 79 )
local z = pi : next ( mp.z , mp.z + 79 )
2014-08-19 18:32:42 +02:00
if s <= mg_villages.VILLAGE_CHANCE and noise1 : get2d ( { x = x , y = z } ) >= - 0.3 then return { } end
2014-08-04 22:18:20 +02:00
end
end
end
2014-08-05 01:24:11 +02:00
local pr = PseudoRandom ( mg_villages.get_bseed ( minp ) )
2014-08-19 18:32:42 +02:00
if pr : next ( 1 , 400 ) > mg_villages.VILLAGE_CHANCE then return { } end -- No village here
2014-08-04 22:18:20 +02:00
local x = pr : next ( minp.x , minp.x + 79 )
local z = pr : next ( minp.z , minp.z + 79 )
if noise1 : get2d ( { x = x , y = z } ) < - 0.3 then return { } end -- Deep in the ocean
-- fallback: type "nore" (that is what the mod originally came with)
local village_type = ' nore ' ;
-- if this is the first village for this world, take a medieval one
if ( ( not ( mg_villages.all_villages ) or mg_villages.anz_villages < 1 ) and minetest.get_modpath ( " cottages " ) ) then
village_type = ' medieval ' ;
else
village_type = mg_villages.village_types [ pr : next ( 1 , # mg_villages.village_types ) ] ; -- select a random type
end
if ( not ( mg_villages.village_sizes [ village_type ] ) ) then
2014-08-19 18:32:42 +02:00
mg_villages.village_sizes [ village_type ] = { min = mg_villages.VILLAGE_MIN_SIZE , max = mg_villages.VILLAGE_MAX_SIZE } ;
2014-08-04 22:18:20 +02:00
end
local size = pr : next ( mg_villages.village_sizes [ village_type ] . min , mg_villages.village_sizes [ village_type ] . max )
2014-08-05 19:49:02 +02:00
-- local height = pr:next(5, 20)
local height = pr : next ( 1 , 5 )
2014-08-24 00:59:37 +02:00
-- villages of a size >= 40 are always placed at a height of 1
2014-08-25 01:46:33 +02:00
if ( size >= 40 ) then
2014-08-24 00:59:37 +02:00
height = 1 ;
2014-08-25 01:46:33 +02:00
-- slightly smaller but still relatively large villages have a deterministic height now as well
elseif ( size >= 30 ) then
height = 40 - height ;
elseif ( size >= 25 ) then
height = 35 - height ;
-- even smaller villages need to have a height depending on their sourroundings (at least they're pretty small!)
2014-08-24 00:59:37 +02:00
end
2014-08-04 22:18:20 +02:00
-- print("A village of type \'"..tostring( village_type ).."\' of size "..tostring( size ).." spawned at: x = "..x..", z = "..z)
--print("A village spawned at: x = "..x..", z = "..z)
return { { vx = x , vz = z , vs = size , vh = height , village_type = village_type } }
end
--local function dist_center2(ax, bsizex, az, bsizez)
-- return math.max((ax+bsizex)*(ax+bsizex),ax*ax)+math.max((az+bsizez)*(az+bsizez),az*az)
--end
local function inside_village2 ( bx , sx , bz , sz , village , vnoise )
2014-08-05 01:24:11 +02:00
return mg_villages.inside_village ( bx , bz , village , vnoise ) and mg_villages.inside_village ( bx + sx , bz , village , vnoise ) and mg_villages.inside_village ( bx , bz + sz , village , vnoise ) and mg_villages.inside_village ( bx + sx , bz + sz , village , vnoise )
2014-08-04 22:18:20 +02:00
end
local function choose_building ( l , pr , village_type )
--::choose::
local btype
while true do
local p = pr : next ( 1 , 3000 )
2014-08-18 00:24:15 +02:00
for b , i in ipairs ( mg_villages.BUILDINGS ) do
2014-08-04 22:18:20 +02:00
if i.weight [ village_type ] and i.weight [ village_type ] > 0 and i.max_weight and i.max_weight [ village_type ] and i.max_weight [ village_type ] >= p then
btype = b
break
end
end
-- in case no building was found: take the last one that fits
if ( not ( btype ) ) then
2014-08-18 00:24:15 +02:00
for i =# mg_villages.BUILDINGS , 1 , - 1 do
if ( mg_villages.BUILDINGS [ i ] . weight and mg_villages.BUILDINGS [ i ] . weight [ village_type ] and mg_villages.BUILDINGS [ i ] . weight [ village_type ] > 0 ) then
2014-08-04 22:18:20 +02:00
btype = i ;
i = 1 ;
end
end
end
if ( not ( btype ) ) then
return 1 ;
end
if ( # l < 1
2014-08-18 00:24:15 +02:00
or not ( mg_villages.BUILDINGS [ btype ] . avoid )
or mg_villages.BUILDINGS [ btype ] . avoid == ' '
or not ( mg_villages.BUILDINGS [ l [ # l ] . btype ] . avoid )
or mg_villages.BUILDINGS [ btype ] . avoid ~= mg_villages.BUILDINGS [ l [ # l ] . btype ] . avoid ) then
2014-08-04 22:18:20 +02:00
2014-08-18 00:24:15 +02:00
if mg_villages.BUILDINGS [ btype ] . pervillage ~= nil then
2014-08-04 22:18:20 +02:00
local n = 0
for j = 1 , # l do
2014-08-18 00:24:15 +02:00
if ( l [ j ] . btype == btype or ( mg_villages.BUILDINGS [ btype ] . typ and mg_villages.BUILDINGS [ btype ] . typ == mg_villages.BUILDINGS [ l [ j ] . btype ] . typ ) ) then
2014-08-04 22:18:20 +02:00
n = n + 1
end
end
2014-08-18 00:24:15 +02:00
--if n >= mg_villages.BUILDINGS[btype].pervillage then
2014-08-04 22:18:20 +02:00
-- goto choose
--end
2014-08-18 00:24:15 +02:00
if n < mg_villages.BUILDINGS [ btype ] . pervillage then
2014-08-04 22:18:20 +02:00
return btype
end
else
return btype
end
end
end
--return btype
end
local function choose_building_rot ( l , pr , orient , village_type )
local btype = choose_building ( l , pr , village_type )
local rotation
2014-08-18 00:24:15 +02:00
if mg_villages.BUILDINGS [ btype ] . no_rotate then
2014-08-04 22:18:20 +02:00
rotation = 0
else
2014-08-18 00:24:15 +02:00
if mg_villages.BUILDINGS [ btype ] . orients == nil then
mg_villages.BUILDINGS [ btype ] . orients = { 0 , 1 , 2 , 3 }
2014-08-04 22:18:20 +02:00
end
2014-08-18 00:24:15 +02:00
rotation = ( orient + mg_villages.BUILDINGS [ btype ] . orients [ pr : next ( 1 , # mg_villages.BUILDINGS [ btype ] . orients ) ] ) % 4
2014-08-04 22:18:20 +02:00
end
2014-08-18 00:24:15 +02:00
local bsizex = mg_villages.BUILDINGS [ btype ] . sizex
local bsizez = mg_villages.BUILDINGS [ btype ] . sizez
2014-08-04 22:18:20 +02:00
if rotation % 2 == 1 then
bsizex , bsizez = bsizez , bsizex
end
return btype , rotation , bsizex , bsizez
end
2014-08-17 22:41:22 +02:00
local function placeable ( bx , bz , bsizex , bsizez , l , exclude_roads , orientation )
2014-08-04 22:18:20 +02:00
for _ , a in ipairs ( l ) do
2014-08-11 00:42:12 +02:00
-- with < instead of <=, space_between_buildings can be zero (important for towns where houses are closely packed)
2014-08-17 22:41:22 +02:00
if ( a.btype ~= " road " or not exclude_roads ) and math.abs ( bx + bsizex / 2 - a.x - a.bsizex / 2 ) < ( bsizex + a.bsizex ) / 2 and math.abs ( bz + bsizez / 2 - a.z - a.bsizez / 2 ) < ( bsizez + a.bsizez ) / 2 then
-- dirt roads which go at a 90 degree angel to the current road are not a problem
if ( not ( orientation ) or a.o % 2 == orientation % 2 ) then
return false
end
end
2014-08-04 22:18:20 +02:00
end
return true
end
local function road_in_building ( rx , rz , rdx , rdz , roadsize , l )
if rdx == 0 then
return not placeable ( rx - roadsize + 1 , rz , 2 * roadsize - 2 , 0 , l , true )
else
return not placeable ( rx , rz - roadsize + 1 , 0 , 2 * roadsize - 2 , l , true )
end
end
local function when ( a , b , c )
if a then return b else return c end
end
2014-08-17 22:41:22 +02:00
mg_villages.road_nr = 0 ;
2014-08-11 00:42:12 +02:00
local function generate_road ( village , l , pr , roadsize , rx , rz , rdx , rdz , vnoise , space_between_buildings )
2014-08-04 22:18:20 +02:00
local vx , vz , vh , vs = village.vx , village.vz , village.vh , village.vs
local village_type = village.village_type ;
local calls_to_do = { }
local rxx = rx
local rzz = rz
local mx , m2x , mz , m2z , mmx , mmz
mx , m2x , mz , m2z = rx , rx , rz , rz
local orient1 , orient2
if rdx == 0 then
orient1 = 0
orient2 = 2
else
orient1 = 3
orient2 = 1
end
2014-08-17 22:41:22 +02:00
-- we have one more road
mg_villages.road_nr = mg_villages.road_nr + 1 ;
2014-08-05 01:24:11 +02:00
while mg_villages.inside_village ( rx , rz , village , vnoise ) and not road_in_building ( rx , rz , rdx , rdz , roadsize , l ) do
2014-08-04 22:18:20 +02:00
if roadsize > 1 and pr : next ( 1 , 4 ) == 1 then
--generate_road(vx, vz, vs, vh, l, pr, roadsize-1, rx, rz, math.abs(rdz), math.abs(rdx))
calls_to_do [ # calls_to_do + 1 ] = { rx = rx + ( roadsize - 1 ) * rdx , rz = rz + ( roadsize - 1 ) * rdz , rdx = math.abs ( rdz ) , rdz = math.abs ( rdx ) }
m2x = rx + ( roadsize - 1 ) * rdx
m2z = rz + ( roadsize - 1 ) * rdz
rx = rx + ( 2 * roadsize - 1 ) * rdx
rz = rz + ( 2 * roadsize - 1 ) * rdz
end
--else
--::loop::
local exitloop = false
local bx
local bz
local tries = 0
while true do
2014-08-05 01:24:11 +02:00
if not mg_villages.inside_village ( rx , rz , village , vnoise ) or road_in_building ( rx , rz , rdx , rdz , roadsize , l ) then
2014-08-04 22:18:20 +02:00
exitloop = true
break
end
local village_type_sub = village_type ;
if ( mg_villages.medieval_subtype and village_type_sub == ' medieval ' and math.abs ( village.vx - rx ) > 20 and math.abs ( village.vz - rz ) > 20 ) then
village_type_sub = ' fields ' ;
end
btype , rotation , bsizex , bsizez = choose_building_rot ( l , pr , orient1 , village_type_sub )
bx = rx + math.abs ( rdz ) * ( roadsize + 1 ) - when ( rdx ==- 1 , bsizex - 1 , 0 )
bz = rz + math.abs ( rdx ) * ( roadsize + 1 ) - when ( rdz ==- 1 , bsizez - 1 , 0 )
if placeable ( bx , bz , bsizex , bsizez , l ) and inside_village2 ( bx , bsizex , bz , bsizez , village , vnoise ) then
break
end
if tries > 5 then
rx = rx + rdx
rz = rz + rdz
tries = 0
else
tries = tries + 1
end
--goto loop
end
if exitloop then break end
2014-08-11 00:42:12 +02:00
rx = rx + ( bsizex + space_between_buildings ) * rdx
rz = rz + ( bsizez + space_between_buildings ) * rdz
2014-08-04 22:18:20 +02:00
mx = rx - 2 * rdx
mz = rz - 2 * rdz
2014-08-17 22:41:22 +02:00
l [ # l + 1 ] = { x = bx , y = vh , z = bz , btype = btype , bsizex = bsizex , bsizez = bsizez , brotate = rotation , road_nr = mg_villages.road_nr , side = 1 , o = orient1 }
2014-08-04 22:18:20 +02:00
--end
end
rx = rxx
rz = rzz
2014-08-05 01:24:11 +02:00
while mg_villages.inside_village ( rx , rz , village , vnoise ) and not road_in_building ( rx , rz , rdx , rdz , roadsize , l ) do
2014-08-04 22:18:20 +02:00
if roadsize > 1 and pr : next ( 1 , 4 ) == 1 then
--generate_road(vx, vz, vs, vh, l, pr, roadsize-1, rx, rz, -math.abs(rdz), -math.abs(rdx))
calls_to_do [ # calls_to_do + 1 ] = { rx = rx + ( roadsize - 1 ) * rdx , rz = rz + ( roadsize - 1 ) * rdz , rdx =- math.abs ( rdz ) , rdz =- math.abs ( rdx ) }
m2x = rx + ( roadsize - 1 ) * rdx
m2z = rz + ( roadsize - 1 ) * rdz
rx = rx + ( 2 * roadsize - 1 ) * rdx
rz = rz + ( 2 * roadsize - 1 ) * rdz
end
--else
--::loop::
local exitloop = false
local bx
local bz
local tries = 0
while true do
2014-08-05 01:24:11 +02:00
if not mg_villages.inside_village ( rx , rz , village , vnoise ) or road_in_building ( rx , rz , rdx , rdz , roadsize , l ) then
2014-08-04 22:18:20 +02:00
exitloop = true
break
end
local village_type_sub = village_type ;
if ( mg_villages.medieval_subtype and village_type_sub == ' medieval ' and math.abs ( village.vx - rx ) > ( village.vs / 3 ) and math.abs ( village.vz - rz ) > ( village.vs / 3 ) ) then
village_type_sub = ' fields ' ;
end
btype , rotation , bsizex , bsizez = choose_building_rot ( l , pr , orient2 , village_type_sub )
bx = rx - math.abs ( rdz ) * ( bsizex + roadsize ) - when ( rdx ==- 1 , bsizex - 1 , 0 )
bz = rz - math.abs ( rdx ) * ( bsizez + roadsize ) - when ( rdz ==- 1 , bsizez - 1 , 0 )
if placeable ( bx , bz , bsizex , bsizez , l ) and inside_village2 ( bx , bsizex , bz , bsizez , village , vnoise ) then
break
end
if tries > 5 then
rx = rx + rdx
rz = rz + rdz
tries = 0
else
tries = tries + 1
end
--goto loop
end
if exitloop then break end
2014-08-11 00:42:12 +02:00
rx = rx + ( bsizex + space_between_buildings ) * rdx
rz = rz + ( bsizez + space_between_buildings ) * rdz
2014-08-04 22:18:20 +02:00
m2x = rx - 2 * rdx
m2z = rz - 2 * rdz
2014-08-17 22:41:22 +02:00
l [ # l + 1 ] = { x = bx , y = vh , z = bz , btype = btype , bsizex = bsizex , bsizez = bsizez , brotate = rotation , road_nr = mg_villages.road_nr , side = 2 , o = orient2 }
2014-08-04 22:18:20 +02:00
--end
end
if road_in_building ( rx , rz , rdx , rdz , roadsize , l ) then
mmx = rx - 2 * rdx
mmz = rz - 2 * rdz
end
mx = mmx or rdx * math.max ( rdx * mx , rdx * m2x )
mz = mmz or rdz * math.max ( rdz * mz , rdz * m2z )
if rdx == 0 then
rxmin = rx - roadsize + 1
rxmax = rx + roadsize - 1
rzmin = math.min ( rzz , mz )
rzmax = math.max ( rzz , mz )
2014-08-27 00:44:00 +02:00
-- prolong the main road to the borders of the village
if ( mg_villages.road_nr == 1 ) then
while ( mg_villages.inside_village_area ( rxmin , rzmin , village , vnoise ) ) do
rzmin = rzmin - 1 ;
rzmax = rzmax + 1 ;
end
rzmin = rzmin - 1 ;
rzmax = rzmax + 1 ;
while ( mg_villages.inside_village_area ( rxmax , rzmax , village , vnoise ) ) do
rzmax = rzmax + 1 ;
end
rzmax = rzmax + 1 ;
end
2014-08-04 22:18:20 +02:00
else
rzmin = rz - roadsize + 1
rzmax = rz + roadsize - 1
rxmin = math.min ( rxx , mx )
rxmax = math.max ( rxx , mx )
2014-08-27 00:44:00 +02:00
-- prolong the main road to the borders of the village
if ( mg_villages.road_nr == 1 ) then
while ( mg_villages.inside_village_area ( rxmin , rzmin , village , vnoise ) ) do
rxmin = rxmin - 1 ;
rxmax = rxmax + 1 ;
end
rxmin = rxmin - 1 ;
rxmax = rxmax + 1 ;
while ( mg_villages.inside_village_area ( rxmax , rzmax , village , vnoise ) ) do
rxmax = rxmax + 1 ;
end
rxmax = rxmax + 1 ;
end
2014-08-04 22:18:20 +02:00
end
l [ # l + 1 ] = { x = rxmin , y = vh , z = rzmin , btype = " road " ,
2014-08-17 22:41:22 +02:00
bsizex = rxmax - rxmin + 1 , bsizez = rzmax - rzmin + 1 , brotate = 0 , road_nr = mg_villages.road_nr }
2014-08-04 22:18:20 +02:00
for _ , i in ipairs ( calls_to_do ) do
local new_roadsize = roadsize - 1
2014-08-19 18:32:42 +02:00
if pr : next ( 1 , 100 ) <= mg_villages.BIG_ROAD_CHANCE then
2014-08-04 22:18:20 +02:00
new_roadsize = roadsize
end
--generate_road(vx, vz, vs, vh, l, pr, new_roadsize, i.rx, i.rz, i.rdx, i.rdz, vnoise)
2014-08-11 00:42:12 +02:00
calls [ calls.index ] = { village , l , pr , new_roadsize , i.rx , i.rz , i.rdx , i.rdz , vnoise , space_between_buildings }
2014-08-04 22:18:20 +02:00
calls.index = calls.index + 1
end
end
2014-08-11 00:42:12 +02:00
local function generate_bpos ( village , pr , vnoise , space_between_buildings )
2014-08-04 22:18:20 +02:00
local vx , vz , vh , vs = village.vx , village.vz , village.vh , village.vs
local l = { }
local rx = vx - vs
--[=[local l={}
local total_weight = 0
2014-08-18 00:24:15 +02:00
for _ , i in ipairs ( mg_villages.BUILDINGS ) do
2014-08-04 22:18:20 +02:00
if i.weight == nil then i.weight = 1 end
total_weight = total_weight + i.weight
i.max_weight = total_weight
end
local multiplier = 3000 / total_weight
2014-08-18 00:24:15 +02:00
for _ , i in ipairs ( mg_villages.BUILDINGS ) do
2014-08-04 22:18:20 +02:00
i.max_weight = i.max_weight * multiplier
end
for i = 1 , 2000 do
bx = pr : next ( vx - vs , vx + vs )
bz = pr : next ( vz - vs , vz + vs )
:: choose ::
2014-08-18 00:24:15 +02:00
--[[btype = pr:next(1, #mg_villages.BUILDINGS)
if mg_villages.BUILDINGS [ btype ] . chance ~= nil then
if pr : next ( 1 , mg_villages.BUILDINGS [ btype ] . chance ) ~= 1 then
2014-08-04 22:18:20 +02:00
goto choose
end
end ] ]
p = pr : next ( 1 , 3000 )
2014-08-18 00:24:15 +02:00
for b , i in ipairs ( mg_villages.BUILDINGS ) do
2014-08-04 22:18:20 +02:00
if i.max_weight > p then
btype = b
break
end
end
2014-08-18 00:24:15 +02:00
if mg_villages.BUILDINGS [ btype ] . pervillage ~= nil then
2014-08-04 22:18:20 +02:00
local n = 0
for j = 1 , # l do
if l [ j ] . btype == btype then
n = n + 1
end
end
2014-08-18 00:24:15 +02:00
if n >= mg_villages.BUILDINGS [ btype ] . pervillage then
2014-08-04 22:18:20 +02:00
goto choose
end
end
local rotation
2014-08-18 00:24:15 +02:00
if mg_villages.BUILDINGS [ btype ] . no_rotate then
2014-08-04 22:18:20 +02:00
rotation = 0
else
rotation = pr : next ( 0 , 3 )
end
2014-08-18 00:24:15 +02:00
bsizex = mg_villages.BUILDINGS [ btype ] . sizex
bsizez = mg_villages.BUILDINGS [ btype ] . sizez
2014-08-04 22:18:20 +02:00
if rotation % 2 == 1 then
bsizex , bsizez = bsizez , bsizex
end
if dist_center2 ( bx - vx , bsizex , bz - vz , bsizez ) > vs * vs then goto out end
for _ , a in ipairs ( l ) do
if math.abs ( bx - a.x ) <= ( bsizex + a.bsizex ) / 2 + 2 and math.abs ( bz - a.z ) <= ( bsizez + a.bsizez ) / 2 + 2 then goto out end
end
l [ # l + 1 ] = { x = bx , y = vh , z = bz , btype = btype , bsizex = bsizex , bsizez = bsizez , brotate = rotation }
:: out ::
end
return l ] = ] --
local rz = vz
2014-08-05 01:24:11 +02:00
while mg_villages.inside_village ( rx , rz , village , vnoise ) do
2014-08-04 22:18:20 +02:00
rx = rx - 1
end
rx = rx + 5
calls = { index = 1 }
2014-08-17 22:41:22 +02:00
-- the function below is recursive; we need a way to count roads
mg_villages.road_nr = 0 ;
2014-08-19 18:32:42 +02:00
generate_road ( village , l , pr , mg_villages.FIRST_ROADSIZE , rx , rz , 1 , 0 , vnoise , space_between_buildings )
2014-08-04 22:18:20 +02:00
i = 1
while i < calls.index do
generate_road ( unpack ( calls [ i ] ) )
i = i + 1
end
2014-08-17 22:41:22 +02:00
mg_villages.road_nr = 0 ;
2014-08-04 22:18:20 +02:00
return l
end
2014-08-17 22:41:22 +02:00
-- dirt roads seperate the wheat area around medieval villages into seperate fields and make it look better
mg_villages.generate_dirt_roads = function ( village , vnoise , bpos , secondary_dirt_roads )
local dirt_roads = { } ;
if ( not ( secondary_dirt_roads ) ) then
return dirt_roads ;
end
for _ , pos in ipairs ( bpos ) do
local x = pos.x ;
local z = pos.z ;
local sizex = pos.bsizex ;
local sizez = 2 ;
local orientation = 0 ;
-- prolong the roads; start with a 3x2 piece of road for testing
if ( pos.btype == ' road ' ) then
-- the road streches in x direction
if ( pos.bsizex > pos.bsizez ) then
sizex = 3 ; -- start with a road of length 3
sizez = 2 ;
vx = - 1 ; vz = 0 ; vsx = 1 ; vsz = 0 ;
x = pos.x - sizex ;
z = pos.z + math.floor ( ( pos.bsizez - 2 ) / 2 ) ; -- aim for the middle of the road
orientation = 0 ;
-- if it is not possible to prolong the road at one end, then try the other
if ( not ( placeable ( x , z , sizex , sizez , bpos , false , nil ) ) ) then
x = pos.x + pos.bsizex ;
vx = 0 ;
orientation = 2 ;
end
-- the road stretches in z direction
else
sizex = 2 ;
sizez = 3 ;
vx = 0 ; vz = - 1 ; vsx = 0 ; vsz = 1 ;
x = pos.x + math.floor ( ( pos.bsizex - 2 ) / 2 ) ; -- aim for the middle of the road
z = pos.z - sizez ;
orientation = 1 ;
if ( not ( placeable ( x , z , sizex , sizez , bpos , false , nil ) ) ) then
z = pos.z + pos.bsizez ;
vz = 0 ;
orientation = 3 ;
end
end
else
if ( pos.o == 0 ) then
x = pos.x - pos.side ;
z = pos.z - 2 ;
sizex = pos.bsizex + 1 ;
sizez = 2 ;
vx = 0 ; vz = 0 ; vsx = 1 ; vsz = 0 ;
elseif ( pos.o == 2 ) then
x = pos.x - pos.side + 2 ;
z = pos.z - 2 ;
sizex = pos.bsizex + 1 ;
sizez = 2 ;
vx = - 1 ; vz = 0 ; vsx = 1 ; vsz = 0 ;
elseif ( pos.o == 1 ) then
x = pos.x - 2 ;
z = pos.z - pos.side + 2 ;
sizex = 2 ;
sizez = pos.bsizez + 1 ;
2014-08-19 16:47:07 +02:00
vx = 0 ; vz = - 1 ; vsx = 0 ; vsz = 1 ;
2014-08-17 22:41:22 +02:00
elseif ( pos.o == 3 ) then
x = pos.x - 2 ;
z = pos.z - pos.side ;
sizex = 2 ;
sizez = pos.bsizez + 1 ;
vx = 0 ; vz = 0 ; vsx = 0 ; vsz = 1 ;
end
orientation = pos.o ;
end
-- prolong the dirt road by 1
while ( placeable ( x , z , sizex , sizez , bpos , false , nil )
and placeable ( x , z , sizex , sizez , dirt_roads , false , orientation )
and mg_villages.inside_village_area ( x , z , village , vnoise )
and mg_villages.inside_village_area ( x + sizex , z + sizez , village , vnoise ) ) do
sizex = sizex + vsx ;
sizez = sizez + vsz ;
x = x + vx ;
z = z + vz ;
end
-- the dirt road may exceed the village boundaries slightly, but it may not interfere with other buildings
if ( not ( placeable ( x , z , sizex , sizez , bpos , false , nil ) )
or not ( placeable ( x , z , sizex , sizez , dirt_roads , false , orientation ) ) ) then
sizex = sizex - vsx ;
sizez = sizez - vsz ;
x = x - vx ;
z = z - vz ;
end
if ( placeable ( x , z , sizex , sizez , bpos , false , nil )
and placeable ( x , z , sizex , sizez , dirt_roads , false , orientation ) ) then
dirt_roads [ # dirt_roads + 1 ] = { x = x , y = village.vh , z = z , btype = " dirt_road " , bsizex = sizex , bsizez = sizez , brotate = 0 , o = orientation }
end
end
return dirt_roads ;
end
2014-08-04 22:18:20 +02:00
-- they don't all grow cotton; farming_plus fruits are far more intresting!
-- Note: This function modifies replacements.ids and replacements.table for each building
-- as far as fruits are concerned. It needs to be called before placing a building
-- which contains fruits.
-- The function might as well be a local one.
mg_villages.get_fruit_replacements = function ( replacements , fruit )
if ( not ( fruit ) ) then
return ;
end
for i = 1 , 8 do
local old_name = ' ' ;
local new_name = ' ' ;
-- farming_plus plants sometimes come in 3 or 4 variants, but not in 8 as cotton does
if ( minetest.registered_nodes [ ' farming_plus: ' .. fruit .. ' _ ' .. i ] ) then
old_name = " farming:cotton_ " .. i ;
new_name = ' farming_plus: ' .. fruit .. ' _ ' .. i ;
-- "surplus" cotton variants will be replaced with the full grown fruit
elseif ( minetest.registered_nodes [ ' farming_plus: ' .. fruit ] ) then
old_name = " farming:cotton_ " .. i ;
new_name = ' farming_plus: ' .. fruit ;
-- and plants from farming: are supported as well
elseif ( minetest.registered_nodes [ ' farming: ' .. fruit .. ' _ ' .. i ] ) then
old_name = " farming:cotton_ " .. i ;
new_name = ' farming: ' .. fruit .. ' _ ' .. i ;
elseif ( minetest.registered_nodes [ ' farming: ' .. fruit ] ) then
old_name = " farming:cotton_ " .. i ;
new_name = ' farming: ' .. fruit ;
end
if ( old_name ~= ' ' and new_name ~= ' ' ) then
-- this is mostly used by the voxelmanip based spawning of .we files
replacements.ids [ minetest.get_content_id ( old_name ) ] = minetest.get_content_id ( new_name ) ;
-- this is used by the place_schematic based spawning
for i , v in ipairs ( replacements.table ) do
if ( v and # v and v [ 1 ] == old_name ) then
v [ 2 ] = new_name ;
end
end
end
end
end
-- either uses get_node_or_nil(..) or the data from voxelmanip
-- the function might as well be local (only used by *.mg_drop_moresnow)
mg_villages.get_node_somehow = function ( x , y , z , a , data , param2_data )
if ( a and data and param2_data ) then
return { content = data [ a : index ( x , y , z ) ] , param2 = param2_data [ a : index ( x , y , z ) ] } ;
end
-- no voxelmanip; get the node the normal way
local node = minetest.get_node_or_nil ( { x = x , y = y , z = z } ) ;
if ( not ( node ) ) then
return { content = moresnow.c_ignore , param2 = 0 } ;
end
return { content = minetest.get_content_id ( node.name ) , param2 = node.param2 , name = node.name } ;
end
-- "drop" moresnow snow on diffrent shapes; works for voxelmanip and node-based setting
mg_villages.mg_drop_moresnow = function ( x , z , y_top , y_bottom , a , data , param2_data )
-- this only works if moresnow is installed
if ( not ( moresnow ) or not ( moresnow.suggest_snow_type ) ) then
return ;
end
local y = y_top ;
local node_above = mg_villages.get_node_somehow ( x , y + 1 , z , a , data , param2_data ) ;
local node_below = nil ;
while ( y >= y_bottom ) do
node_below = mg_villages.get_node_somehow ( x , y , z , a , data , param2_data ) ;
if ( node_above.content == moresnow.c_air
and node_below.content
and node_below.content ~= moresnow.c_ignore
and node_below.content ~= moresnow.c_air ) then
-- if the node below drops snow when digged (i.e. is either snow or a moresnow node), we're finished
local get_drop = minetest.get_name_from_content_id ( node_below.content ) ;
if ( get_drop ) then
get_drop = minetest.registered_nodes [ get_drop ] ;
if ( get_drop and get_drop.drop and type ( get_drop.drop ) == ' string ' and get_drop.drop == ' default:snow ' ) then
return ;
end
end
if ( not ( node_below.content )
2014-08-05 19:49:02 +02:00
or node_below.content == mg_villages.road_node
2014-08-04 22:18:20 +02:00
or node_below.content == moresnow.c_snow ) then
return ;
end
local suggested = moresnow.suggest_snow_type ( node_below.content , node_below.param2 ) ;
-- c_snow_top and c_snow_fence can only exist when the node 2 below is a solid one
if ( suggested.new_id == moresnow.c_snow_top
or suggested.new_id == moresnow.c_snow_fence ) then
local node_below2 = mg_villages.get_node_somehow ( x , y - 1 , z , a , data , param2_data ) ;
if ( node_below2.content ~= moresnow.c_ignore
and node_below2.content ~= moresnow.c_air ) then
local suggested2 = moresnow.suggest_snow_type ( node_below2.content , node_below2.param2 ) ;
if ( suggested2.new_id == moresnow.c_snow ) then
return { height = y + 1 , suggested = suggested } ;
end
end
-- it is possible that this is not the right shape; if so, the snow will continue to fall down
elseif ( suggested.new_id ~= moresnow.c_ignore ) then
return { height = y + 1 , suggested = suggested } ;
end
-- TODO return; -- abort; there is no fitting moresnow shape for the node below
end
y = y - 1 ;
node_above = node_below ;
end
end
2014-09-28 03:56:24 +02:00
local function generate_building ( pos , minp , maxp , data , param2_data , a , pr , extranodes , replacements , cid , extra_calls , building_nr_in_bpos )
2014-08-18 00:24:15 +02:00
local binfo = mg_villages.BUILDINGS [ pos.btype ]
2014-08-04 22:18:20 +02:00
local scm
-- schematics of .mts type are not handled here; they need to be placed using place_schematics
if ( binfo.is_mts == 1 ) then
return ;
end
2014-09-22 05:42:42 +02:00
if ( binfo.scm_data_cache ) then
scm = binfo.scm_data_cache ;
2014-08-04 22:18:20 +02:00
else
scm = binfo.scm
end
-- the fruit is set per building, not per village as the other replacements
if ( binfo.farming_plus and binfo.farming_plus == 1 and pos.fruit ) then
mg_villages.get_fruit_replacements ( replacements , pos.fruit ) ;
end
local c_ignore = minetest.get_content_id ( " ignore " )
local c_air = minetest.get_content_id ( " air " )
local c_snow = minetest.get_content_id ( " default:snow " ) ;
local c_dirt = minetest.get_content_id ( " default:dirt " ) ;
local c_dirt_with_grass = minetest.get_content_id ( " default:dirt_with_grass " ) ;
local c_dirt_with_snow = minetest.get_content_id ( " default:dirt_with_snow " ) ;
2014-09-22 05:42:42 +02:00
local scm_x = 0 ;
local scm_z = 0 ;
local step_x = 1 ;
local step_z = 1 ;
local scm_z_start = 0 ;
if ( pos.brotate == 2 ) then
scm_x = pos.bsizex + 1 ;
step_x = - 1 ;
end
if ( pos.brotate == 1 ) then
scm_z = pos.bsizez + 1 ;
step_z = - 1 ;
scm_z_start = scm_z ;
end
2014-08-04 22:18:20 +02:00
for x = 0 , pos.bsizex - 1 do
2014-09-22 05:42:42 +02:00
scm_x = scm_x + step_x ;
scm_z = scm_z_start ;
2014-08-04 22:18:20 +02:00
for z = 0 , pos.bsizez - 1 do
2014-09-22 21:23:38 +02:00
scm_z = scm_z + step_z ;
local xoff = scm_x ;
local zoff = scm_z ;
if ( pos.brotate == 2 ) then
zoff = pos.bsizez - scm_z + 1 ;
elseif ( pos.brotate == 1 ) then
xoff = scm_z ;
zoff = scm_x ;
elseif ( pos.brotate == 3 ) then
xoff = scm_z ;
zoff = pos.bsizex - scm_x + 1 ;
end
2014-09-24 02:02:17 +02:00
local has_snow = false ; -- TODO: make some villages snow covered if moresnow is installed
2014-08-04 22:18:20 +02:00
local ground_type = c_dirt_with_grass ;
for y = 0 , binfo.ysize - 1 do
ax , ay , az = pos.x + x , pos.y + y + binfo.yoff , pos.z + z
if ( ax >= minp.x and ax <= maxp.x ) and ( ay >= minp.y and ay <= maxp.y ) and ( az >= minp.z and az <= maxp.z ) then
2014-09-22 05:42:42 +02:00
local new_content = c_air ;
2014-09-22 21:23:38 +02:00
t = scm [ y + 1 ] [ xoff ] [ zoff ] ;
2014-08-04 22:18:20 +02:00
if ( binfo.yoff + y == 0 ) then
local node_content = data [ a : index ( ax , ay , az ) ] ;
-- no snow on the gravel roads
if ( node_content == c_dirt_with_snow or data [ a : index ( ax , ay + 1 , az ) ] == c_snow ) then
has_snow = true ;
end
ground_type = node_content ;
end
2014-09-22 21:23:38 +02:00
if ( type ( t ) == " table " and t.node ) then
2014-09-22 05:42:42 +02:00
new_content = t.node . content ;
-- replace unkown nodes by name
2014-09-24 17:17:03 +02:00
if ( not ( new_content ) or new_content == c_ignore
2014-09-25 22:13:22 +02:00
and t.node . name and t.node . name ~= ' mg:ignore ' ) then
2014-09-24 17:17:03 +02:00
if ( replacements.table [ t.node . name ] and minetest.registered_nodes [ replacements.table [ t.node . name ] ] ) then
new_content = minetest.get_content_id ( replacements.table [ t.node . name ] ) ;
2014-09-24 23:44:34 +02:00
if ( minetest.registered_nodes [ replacements.table [ t.node . name ] ] . on_construct ) then
if ( not ( extra_calls.on_constr [ new_content ] ) ) then
extra_calls.on_constr [ new_content ] = { { x = ax , y = ay , z = az } } ;
else
table.insert ( extra_calls.on_constr [ new_content ] , { x = ax , y = ay , z = az } ) ;
end
end
2014-09-24 17:17:03 +02:00
-- we tried our best, but the replacement node is not defined
2014-09-25 22:13:22 +02:00
elseif ( t.node . name ~= ' mg:ignore ' ) then
2014-09-24 17:17:03 +02:00
print ( ' [mg_villages] ERROR: Did not find a suitable replacement for ' .. tostring ( t.node . name ) .. ' (suggested but inexistant: ' .. tostring ( replacements.table [ t.node . name ] ) .. ' ). ' ) ;
new_content = cid.c_air ;
end
2014-09-22 05:42:42 +02:00
2014-09-25 22:13:22 +02:00
elseif ( new_content == c_ignore or ( t.node . name and t.node . name == ' mg:ignore ' ) ) then
-- no change; keep the old content
2014-09-22 05:42:42 +02:00
-- do replacements for normal nodes with facedir or wallmounted
elseif ( new_content ~= c_ignore and replacements.ids [ new_content ] ) then
new_content = replacements.ids [ new_content ] ;
end
-- replace all dirt and dirt with grass at that x,z coordinate with the stored ground grass node;
if ( new_content == c_dirt or new_content == c_dirt_with_grass ) then
new_content = ground_type ;
end
2014-08-04 22:18:20 +02:00
-- handle extranodes
2014-09-22 05:42:42 +02:00
if ( t.extranode and t.meta ) then
-- TODO: t.node.* may not contain relevant information here
table.insert ( extranodes , { node = t.node , meta = t.meta , pos = { x = ax , y = ay , z = az } } )
end
data [ a : index ( ax , ay , az ) ] = new_content ;
2014-09-22 21:23:38 +02:00
if ( t.node . param2list ) then
param2_data [ a : index ( ax , ay , az ) ] = t.node . param2list [ pos.brotate + 1 ] ;
elseif ( t.node . param2 ) then
param2_data [ a : index ( ax , ay , az ) ] = t.node . param2 ;
2014-08-04 22:18:20 +02:00
end
2014-09-24 02:02:17 +02:00
-- for this node, we need to call on_construct
if ( t.node . on_constr and t.node . on_constr == true ) then
if ( not ( extra_calls.on_constr [ new_content ] ) ) then
extra_calls.on_constr [ new_content ] = { { x = ax , y = ay , z = az } } ;
else
table.insert ( extra_calls.on_constr [ new_content ] , { x = ax , y = ay , z = az } ) ;
end
end
2014-08-04 22:18:20 +02:00
-- air and gravel
elseif t ~= c_ignore then
2014-09-22 05:42:42 +02:00
new_content = t ;
2014-08-04 22:18:20 +02:00
if ( t and replacements.ids [ t ] ) then
2014-09-22 05:42:42 +02:00
new_content = replacements.ids [ t ] ;
2014-08-04 22:18:20 +02:00
end
if ( t and t == c_dirt or t == c_dirt_with_grass ) then
2014-09-22 05:42:42 +02:00
new_content = ground_type ;
2014-08-04 22:18:20 +02:00
end
2014-09-22 05:42:42 +02:00
data [ a : index ( ax , ay , az ) ] = new_content ;
-- param2 is not set here
2014-08-04 22:18:20 +02:00
end
2014-09-24 02:02:17 +02:00
-- some nodes may require additional actions after placing them
if ( not ( new_content ) or new_content == cid.c_air or new_content == cid.c_ignore ) then
-- do nothing
elseif ( new_content == cid.c_sapling
or new_content == cid.c_jsapling
or new_content == cid.c_savannasapling
or new_content == cid.c_pinesapling ) then
-- store that a tree is to be grown there
table.insert ( extra_calls.trees , { x = ax , y = ay , z = az , typ = new_content } ) ;
elseif ( new_content == cid.c_chest
or new_content == cid.c_chest_locked
or new_content == cid.c_chest_shelf ) then
-- we're dealing with a chest that might need filling
2014-09-28 03:56:24 +02:00
table.insert ( extra_calls.chests , { x = ax , y = ay , z = az , typ = new_content , bpos_i = building_nr_in_bpos } ) ;
2014-09-24 02:02:17 +02:00
elseif ( new_content == cid.c_chest_private
or new_content == cid.c_chest_work
or new_content == cid.c_chest_storage ) then
-- we're dealing with a chest that might need filling
2014-09-28 03:56:24 +02:00
table.insert ( extra_calls.chests , { x = ax , y = ay , z = az , typ = new_content , bpos_i = building_nr_in_bpos } ) ;
2014-09-25 18:59:53 +02:00
-- TODO: perhaps use a locked chest owned by the mob living there?
2014-09-24 02:02:17 +02:00
-- place a normal chest here
data [ a : index ( ax , ay , az ) ] = cid.c_chest ;
elseif ( new_content == cid.c_sign ) then
-- the sign may require some text to be written on it
2014-09-28 03:56:24 +02:00
table.insert ( extra_calls.signs , { x = ax , y = ay , z = az , typ = new_content , bpos_i = building_nr_in_bpos } ) ;
2014-09-24 02:02:17 +02:00
end
2014-08-04 22:18:20 +02:00
end
end
local ax = pos.x + x ;
local az = pos.z + z ;
local y_top = pos.y + binfo.yoff + binfo.ysize ;
if ( y_top + 1 > maxp.y ) then
y_top = maxp.y - 1 ;
end
local y_bottom = pos.y + binfo.yoff ;
if ( y_bottom < minp.y ) then
y_bottom = minp.y ;
end
if ( has_snow and ax >= minp.x and ax <= maxp.x and az >= minp.z and az <= maxp.z ) then
local res = mg_villages.mg_drop_moresnow ( ax , az , y_top , y_bottom , a , data , param2_data ) ;
if ( res ) then
data [ a : index ( ax , res.height , az ) ] = res.suggested . new_id ;
param2_data [ a : index ( ax , res.height , az ) ] = res.suggested . param2 ;
has_snow = false ;
end
end
end
end
end
-- similar to generate_building, except that it uses minetest.place_schematic(..) instead of changing voxelmanip data;
-- this has advantages for nodes that use facedir;
-- the function is called AFTER the mapgen data has been written in init.lua
-- Function is called from init.lua.
mg_villages.place_schematics = function ( bpos , replacements , voxelarea , pr )
local mts_path = mg_villages.modpath .. " /schems/ " ;
for _ , pos in ipairs ( bpos ) do
2014-08-18 00:24:15 +02:00
local binfo = mg_villages.BUILDINGS [ pos.btype ] ;
2014-08-04 22:18:20 +02:00
-- We need to check all 8 corners of the building.
-- This will only work for buildings that are smaller than chunk size (relevant here: about 111 nodes)
-- The function only spawns buildings which are at least partly contained in this chunk/voxelarea.
if ( voxelarea
and ( voxelarea : contains ( pos.x , pos.y - binfo.yoff , pos.z )
or voxelarea : contains ( pos.x + pos.bsizex , pos.y - binfo.yoff , pos.z )
or voxelarea : contains ( pos.x , pos.y - binfo.yoff , pos.z + pos.bsizez )
or voxelarea : contains ( pos.x + pos.bsizex , pos.y - binfo.yoff , pos.z + pos.bsizez )
or voxelarea : contains ( pos.x , pos.y - binfo.yoff + binfo.ysize , pos.z )
or voxelarea : contains ( pos.x + pos.bsizex , pos.y - binfo.yoff + binfo.ysize , pos.z )
or voxelarea : contains ( pos.x , pos.y - binfo.yoff + binfo.ysize , pos.z + pos.bsizez )
or voxelarea : contains ( pos.x + pos.bsizex , pos.y - binfo.yoff + binfo.ysize , pos.z + pos.bsizez ) ) ) then
-- that function places schematics, adds snow where needed
-- and the grass type used directly in the pos/bpos data structure
mg_villages.place_one_schematic ( bpos , replacements , pos , mts_path ) ;
end
end
--print('VILLAGE DATA: '..minetest.serialize( bpos ));
end
-- also adds a snow layer for buildings spawned from .we files
-- function might as well be local
mg_villages.place_one_schematic = function ( bpos , replacements , pos , mts_path )
-- just for the record: count how many times this building has been placed already;
-- multiple placements are commen at chunk boundaries (may be up to 8 placements)
if ( not ( pos.count_placed ) ) then
pos.count_placed = 1 ;
else
pos.count_placed = pos.count_placed + 1 ;
end
2014-08-18 00:24:15 +02:00
local binfo = mg_villages.BUILDINGS [ pos.btype ] ;
2014-08-04 22:18:20 +02:00
local start_pos = { x = ( pos.x ) , y = ( pos.y + binfo.yoff ) , z = ( pos.z ) } ;
local end_pos = { x = ( pos.x + pos.bsizex ) , y = ( pos.y + binfo.yoff + binfo.ysize ) , z = ( pos.z + pos.bsizez ) } ;
-- this function is only responsible for files that are in .mts format
if ( binfo.is_mts == 1 ) then
-- translate rotation
local rotation = 0 ;
if ( pos.brotate == 1 ) then
rotation = 90 ;
elseif ( pos.brotate == 2 ) then
rotation = 180 ;
elseif ( pos.brotate == 3 ) then
rotation = 270 ;
else
rotation = 0 ;
end
if ( binfo.rotated ) then
rotation = ( rotation + binfo.rotated ) % 360 ;
end
-- the fruit is set per building, not per village as the other replacements
if ( binfo.farming_plus and binfo.farming_plus == 1 and pos.fruit ) then
mg_villages.get_fruit_replacements ( replacements , pos.fruit ) ;
end
-- find out which ground types are used and where we need to place snow later on
local ground_types = { } ;
local has_snow = { } ;
for x = start_pos.x , end_pos.x do
for z = start_pos.z , end_pos.z do
-- store which particular grass type (or sand/desert sand or whatever) was there before placing the building
local node = minetest.get_node ( { x = x , y = pos.y , z = z } ) ;
if ( node and node.name and node.name ~= ' ignore ' and node.name ~= ' air '
and node.name ~= ' default:dirt ' and node.name ~= ' default:dirt_with_grass ' ) then
ground_types [ tostring ( x ) .. ' : ' .. tostring ( z ) ] = node.name ;
end
-- find out if there is snow above
node = minetest.get_node ( { x = x , y = ( pos.y + 1 ) , z = z } ) ;
local node2 = minetest.get_node ( { x = x , y = ( start_pos.y - 2 ) , z = z } ) ;
if ( node and node.name and node.name == ' default:snow ' ) then
has_snow [ tostring ( x ) .. ' : ' .. tostring ( z ) ] = true ; -- any value would do here; just has to be defined
-- place snow as a marker one below the building
minetest.swap_node ( { x = x , y = ( start_pos.y - 2 ) , z = z } , { name = ' default:dirt_with_snow ' } ) ;
-- read the marker (the building might have been placed once already)
elseif ( node2 and node2.name and node2.name == ' default:dirt_with_snow ' ) then
has_snow [ tostring ( x ) .. ' : ' .. tostring ( z ) ] = true ;
end
end
end
-- print( 'PLACED BUILDING '..tostring( binfo.scm )..' AT '..minetest.pos_to_string( pos )..'. Max. size: '..tostring( max_xz )..' grows: '..tostring(fruit));
-- force placement (we want entire buildings)
minetest.place_schematic ( start_pos , mts_path .. binfo.scm .. ' .mts ' , tostring ( rotation ) , replacements , true ) ;
-- call on_construct for all the nodes that require it (i.e. furnaces)
for i , v in ipairs ( binfo.on_constr ) do
-- there are only very few nodes which need this special treatment
local nodes = minetest.find_nodes_in_area ( start_pos , end_pos , v ) ;
for _ , p in ipairs ( nodes ) do
minetest.registered_nodes [ v ] . on_construct ( p ) ;
end
end
-- note: after_place_node is not handled here because we do not have a player at hand that could be used for it
-- evry dirt_with_grass node gets replaced with the grass type at that location
-- (as long as it was something else than dirt_with_grass)
local dirt_nodes = minetest.find_nodes_in_area ( start_pos , end_pos , { ' default:dirt_with_grass ' } ) ;
for _ , p in ipairs ( dirt_nodes ) do
local new_type = ground_types [ tostring ( p.x ) .. ' : ' .. tostring ( p.z ) ] ;
if ( new_type ) then
2014-09-21 23:41:57 +02:00
-- minetest.set_node( p, { name = new_type } );
2014-08-04 22:18:20 +02:00
end
end
-- add snow on roofs, slabs, stairs, fences, ...
for x = start_pos.x , end_pos.x do
for z = start_pos.z , end_pos.z do
if ( moresnow and moresnow.suggest_snow_type and has_snow [ tostring ( x ) .. ' : ' .. tostring ( z ) ] ) then
local res = mg_villages.mg_drop_moresnow ( x , z , end_pos.y , start_pos.y , nil , nil , nil ) ;
if ( res ) then
minetest.swap_node ( { x = x , y = res.height , z = z } ,
{ name = minetest.get_name_from_content_id ( res.suggested . new_id ) , param2 = res.suggested . param2 } ) ;
end
end
end
end
end
-- TODO: fill chests etc.
end
local MIN_DIST = 1
local function pos_far_buildings ( x , z , l )
for _ , a in ipairs ( l ) do
if a.x - MIN_DIST <= x and x <= a.x + a.bsizex + MIN_DIST and
a.z - MIN_DIST <= z and z <= a.z + a.bsizez + MIN_DIST then
return false
end
end
return true
end
local function generate_walls ( bpos , data , a , minp , maxp , vh , vx , vz , vs , vnoise )
for x = minp.x , maxp.x do
for z = minp.z , maxp.z do
local xx = ( vnoise : get2d ( { x = x , y = z } ) - 2 ) * 20 + ( 40 / ( vs * vs ) ) * ( ( x - vx ) * ( x - vx ) + ( z - vz ) * ( z - vz ) )
if xx >= 40 and xx <= 44 then
bpos [ # bpos + 1 ] = { x = x , z = z , y = vh , btype = " wall " , bsizex = 1 , bsizez = 1 , brotate = 0 }
end
end
end
end
2014-09-25 18:59:53 +02:00
2014-08-05 19:49:02 +02:00
-- determine which building is to be placed where
-- also choose which blocks to replace with which other blocks (to make villages more intresting)
mg_villages.generate_village = function ( village , vnoise )
2014-08-04 22:18:20 +02:00
local vx , vz , vs , vh = village.vx , village.vz , village.vs , village.vh
local village_type = village.village_type ;
2014-08-05 01:24:11 +02:00
local seed = mg_villages.get_bseed ( { x = vx , z = vz } )
2014-08-04 22:18:20 +02:00
local pr_village = PseudoRandom ( seed )
2014-09-20 17:53:20 +02:00
-- generate a name for the village
village.name = namegen.generate_village_name ( pr_village ) ;
2014-08-04 22:18:20 +02:00
-- only generate a new village if the data is not already stored
-- (the algorithm is fast, but village types and houses which are available may change later on,
-- and that might easily cause chaos if the village is generated again with diffrent input)
2014-08-19 03:45:45 +02:00
if ( village.to_add_data and village.to_add_data . bpos and village.to_add_data . replacements and village.to_add_data . plantlist ) then
2014-08-05 19:49:02 +02:00
--print('VILLAGE GENREATION: USING ALREADY GENERATED VILLAGE: Nr. '..tostring( village.nr ));
return ;
end
2014-08-04 22:18:20 +02:00
2014-08-11 00:42:12 +02:00
-- in the case of medieval villages, we later on want to add wheat fields with dirt roads; 1 wide dirt roads look odd
local space_between_buildings = mg_villages.village_sizes [ village_type ] . space_between_buildings ;
2014-08-05 19:49:02 +02:00
-- actually generate the village structure
2014-08-11 00:42:12 +02:00
local bpos = generate_bpos ( village , pr_village , vnoise , space_between_buildings )
2014-08-05 19:49:02 +02:00
2014-08-17 22:41:22 +02:00
local secondary_dirt_roads = nil ;
-- if there is enough space, add dirt roads between the buildings (those will later be prolonged so that they reach the fields)
if ( space_between_buildings >= 2 and village_type == ' medieval ' ) then
secondary_dirt_roads = " dirt_road " ;
end
local dirt_roads = mg_villages.generate_dirt_roads ( village , vnoise , bpos , secondary_dirt_roads ) ;
2014-08-05 19:49:02 +02:00
-- set fruits for all buildings in the village that need it - regardless weather they will be spawned
-- now or later; after the first call to this function here, the village data will be final
for _ , pos in ipairs ( bpos ) do
2014-08-18 00:24:15 +02:00
local binfo = mg_villages.BUILDINGS [ pos.btype ] ;
2014-08-05 19:49:02 +02:00
if ( binfo.farming_plus and binfo.farming_plus == 1 and mg_villages.fruit_list and not pos.furit ) then
pos.fruit = mg_villages.fruit_list [ pr_village : next ( 1 , # mg_villages.fruit_list ) ] ;
end
2014-08-04 22:18:20 +02:00
end
2014-08-05 19:49:02 +02:00
-- a changing replacement list would also be pretty confusing
local p = PseudoRandom ( seed ) ;
-- if the village is new, replacement_list is nil and a new replacement list will be created
local replacements = mg_villages.get_replacement_table ( village.village_type , p , nil ) ;
2014-08-19 03:45:45 +02:00
-- determine which plants will grow in the area around the village
local plantlist = { } ;
2014-09-25 18:59:53 +02:00
sapling_id = mg_villages.get_content_id_replaced ( ' default:sapling ' , replacements ) ;
2014-08-19 03:45:45 +02:00
-- 1/sapling_p = probability of a sapling beeing placed
local sapling_p = 25 ;
if ( mg_villages.sapling_probability [ sapling_id ] ) then
sapling_p = mg_villages.sapling_probability [ sapling_id ] ;
end
-- medieval villages are sourrounded by wheat fields
if ( village_type == ' medieval ' ) then
2014-09-25 18:59:53 +02:00
local c_wheat = mg_villages.get_content_id_replaced ( ' farming:wheat_8 ' , replacements ) ;
2014-08-19 03:45:45 +02:00
plantlist = {
{ id = sapling_id , p = sapling_p * 10 } , -- trees are rather rare
{ id = c_wheat , p = 1 } } ;
-- lumberjack camps have handy trees nearby
elseif ( village_type == ' lumberjack ' ) then
2014-09-25 18:59:53 +02:00
local c_junglegrass = mg_villages.get_content_id_replaced ( ' default:junglegrass ' , replacements ) ;
2014-08-19 03:45:45 +02:00
plantlist = {
{ id = sapling_id , p = sapling_p } ,
{ id = c_junglegrass , p = 25 } } ;
-- the villages of type taoki grow cotton
elseif ( village_type == ' taoki ' ) then
2014-09-25 18:59:53 +02:00
local c_cotton = mg_villages.get_content_id_replaced ( ' farming:cotton_8 ' , replacements ) ;
2014-08-19 03:45:45 +02:00
plantlist = {
{ id = sapling_id , p = sapling_p * 5 } , -- not too many trees
{ id = c_cotton , p = 1 } } ;
-- default/fallback: grassland
else
2014-09-25 18:59:53 +02:00
local c_grass = mg_villages.get_content_id_replaced ( ' default:grass_5 ' , replacements ) ;
2014-08-19 03:45:45 +02:00
plantlist = {
{ id = sapling_id , p = sapling_p * 10 } , -- only few trees
{ id = c_grass , p = 3 } } ;
end
2014-08-05 19:49:02 +02:00
-- store the generated data in the village table
village.to_add_data = { } ;
village.to_add_data . bpos = bpos ;
village.to_add_data . replacements = replacements.list ;
2014-08-17 22:41:22 +02:00
village.to_add_data . dirt_roads = dirt_roads ;
2014-08-19 03:45:45 +02:00
village.to_add_data . plantlist = plantlist ;
2014-08-05 19:49:02 +02:00
--print('VILLAGE GENREATION: GENERATING NEW VILLAGE Nr. '..tostring( village.nr ));
end
-- actually place the buildings (at least those which came as .we files; .mts files are handled later on)
-- this code is also responsible for tree placement
2014-09-24 02:02:17 +02:00
mg_villages.place_buildings = function ( village , minp , maxp , data , param2_data , a , vnoise , cid )
2014-08-05 19:49:02 +02:00
local vx , vz , vs , vh = village.vx , village.vz , village.vs , village.vh
local village_type = village.village_type ;
local seed = mg_villages.get_bseed ( { x = vx , z = vz } )
local bpos = village.to_add_data . bpos ;
village.to_grow = { } ; -- TODO this is a temporal solution to avoid flying tree trunks
2014-08-04 22:18:20 +02:00
--generate_walls(bpos, data, a, minp, maxp, vh, vx, vz, vs, vnoise)
local pr = PseudoRandom ( seed )
for _ , g in ipairs ( village.to_grow ) do
if pos_far_buildings ( g.x , g.z , bpos ) then
mg.registered_trees [ g.id ] . grow ( data , a , g.x , g.y , g.z , minp , maxp , pr )
end
end
2014-08-05 19:49:02 +02:00
local replacements = mg_villages.get_replacement_table ( village.village_type , p , village.to_add_data . replacements ) ;
2014-08-04 22:18:20 +02:00
2014-09-25 18:59:53 +02:00
cid.c_chest = mg_villages.get_content_id_replaced ( ' default:chest ' , replacements ) ;
cid.c_chest_locked = mg_villages.get_content_id_replaced ( ' default:chest_locked ' , replacements ) ;
cid.c_chest_private = mg_villages.get_content_id_replaced ( ' cottages:chest_private ' , replacements ) ;
cid.c_chest_work = mg_villages.get_content_id_replaced ( ' cottages:chest_work ' , replacements ) ;
cid.c_chest_storage = mg_villages.get_content_id_replaced ( ' cottages:chest_storage ' , replacements ) ;
cid.c_chest_shelf = mg_villages.get_content_id_replaced ( ' cottages:shelf ' , replacements ) ;
cid.c_sign = mg_villages.get_content_id_replaced ( ' default:sign_wall ' , replacements ) ;
print ( ' REPLACEMENTS: ' .. minetest.serialize ( replacements.table ) .. ' CHEST: ' .. tostring ( minetest.get_name_from_content_id ( cid.c_chest ) ) ) ; -- TODO
2014-08-04 22:18:20 +02:00
local extranodes = { }
2014-09-24 02:02:17 +02:00
local extra_calls = { on_constr = { } , trees = { } , chests = { } , signs = { } } ;
2014-09-28 03:56:24 +02:00
for i , pos in ipairs ( bpos ) do
2014-08-04 22:18:20 +02:00
-- replacements are in table format for mapgen-based building spawning
2014-09-28 03:56:24 +02:00
generate_building ( pos , minp , maxp , data , param2_data , a , pr_village , extranodes , replacements , cid , extra_calls , i )
2014-09-24 02:02:17 +02:00
end
2014-08-04 22:18:20 +02:00
-- replacements are in list format for minetest.place_schematic(..) type spawning
2014-08-19 03:45:45 +02:00
return { extranodes = extranodes , bpos = bpos , replacements = replacements.list , dirt_roads = village.to_add_data . dirt_roads ,
2014-09-24 23:44:34 +02:00
plantlist = village.to_add_data . plantlist , extra_calls = extra_calls } ;
2014-08-04 22:18:20 +02:00
end
2014-08-17 22:41:22 +02:00
-- add the dirt roads
mg_villages.place_dirt_roads = function ( village , minp , maxp , data , param2_data , a , vnoise , c_road_node )
local c_air = minetest.get_content_id ( ' air ' ) ;
for _ , pos in ipairs ( village.to_add_data . dirt_roads ) do
local param2 = 0 ;
if ( pos.bsizex > 2 ) then
param2 = 1 ;
end
for x = 0 , pos.bsizex - 1 do
for z = 0 , pos.bsizez - 1 do
local ax = pos.x + x ;
local az = pos.z + z ;
if ( ax >= minp.x and ax <= maxp.x ) and ( ay >= minp.y and ay <= maxp.y ) and ( az >= minp.z and az <= maxp.z ) then
-- roads have a height of 1 block
data [ a : index ( ax , pos.y , az ) ] = c_road_node ;
param2_data [ a : index ( ax , pos.y , az ) ] = param2 ;
-- ...with air above
data [ a : index ( ax , pos.y + 1 , az ) ] = c_air ;
data [ a : index ( ax , pos.y + 2 , az ) ] = c_air ;
end
end
end
end
end
2014-08-19 03:45:45 +02:00