1369 lines
60 KiB
Lua
1369 lines
60 KiB
Lua
|
|
|
|
--[[
|
|
build a road (including sideroads) in order to form a village
|
|
Mod for MineTest
|
|
Full Mod can be found here: https://github.com/Sokomine/villages
|
|
|
|
Copyright (C) 2013 Sokomine
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
--]]
|
|
|
|
villages = {}
|
|
|
|
villages.mts_path = minetest.get_modpath("villages").."/buildings/";
|
|
|
|
-- these will be used instead of cotton if farming_plus=1 is set in villages.building_data
|
|
villages.farming_plus_fruits = {'carrot','potatoe','orange','rhubarb','strawberry','tomato','cotton'};
|
|
|
|
|
|
-- some constants
|
|
villages.MIN_BRANCH_DIST = 50; -- roads have to be that far apart
|
|
villages.BRANCH_PROBABILITY = 40; -- how probable is it that we'll branch off after MIN_BRANCH_DIST?
|
|
|
|
-- entries:
|
|
-- burried: amount of blocks in vertical direction that the building is burried/reaches into the ground
|
|
-- rotated: for schems that are already rotated to some degree and/or follow a diffrent orientation standard
|
|
-- farming_plus: if set to 1, all occourances of cotton plants will be replaced with a random plant from farming_plus
|
|
-- avoid: avoid buildings with the same group (that is: avoid having tow of them in a row)
|
|
-- typ: used internally; will later be relevant for spawning of npc
|
|
-- for now, only typ "road" is relevant
|
|
villages.building_data = {
|
|
church_1 = {burried=1, rotated=0, farming_plus=0, avoid='', typ='church'},
|
|
forge_1 = {burried=1, rotated=0, farming_plus=0, avoid='', typ='forge'},
|
|
mill_1 = {burried=1, rotated=0, farming_plus=0, avoid='', typ='mill'},
|
|
hut_1 = {burried=1, rotated=0, farming_plus=0, avoid='', typ='hut'},
|
|
farm_full_1 = {burried=1, rotated=0, farming_plus=0, avoid='', typ='farm_full'},
|
|
farm_full_2 = {burried=1, rotated=0, farming_plus=0, avoid='', typ='farm_full'},
|
|
farm_full_3 = {burried=1, rotated=0, farming_plus=0, avoid='', typ='farm_full'},
|
|
farm_full_4 = {burried=1, rotated=0, farming_plus=0, avoid='', typ='farm_full'},
|
|
farm_full_5 = {burried=1, rotated=0, farming_plus=0, avoid='', typ='farm_full'},
|
|
farm_full_6 = {burried=1, rotated=0, farming_plus=0, avoid='', typ='farm_full'},
|
|
farm_tiny_1 = {burried=1, rotated=0, farming_plus=1, avoid='', typ='farm_tiny'},
|
|
farm_tiny_2 = {burried=1, rotated=0, farming_plus=1, avoid='', typ='farm_tiny'},
|
|
farm_tiny_3 = {burried=1, rotated=0, farming_plus=1, avoid='', typ='farm_tiny'},
|
|
farm_tiny_4 = {burried=1, rotated=0, farming_plus=1, avoid='', typ='farm_tiny'},
|
|
farm_tiny_5 = {burried=1, rotated=0, farming_plus=1, avoid='', typ='farm_tiny'},
|
|
farm_tiny_6 = {burried=1, rotated=0, farming_plus=1, avoid='', typ='farm_tiny'},
|
|
farm_tiny_7 = {burried=1, rotated=0, farming_plus=1, avoid='', typ='farm_tiny'},
|
|
taverne_1 = {burried=1, rotated=0, farming_plus=1, avoid='', typ='tavern'},
|
|
taverne_2 = {burried=1, rotated=0, farming_plus=0, avoid='', typ='tavern'},
|
|
taverne_3 = {burried=1, rotated=0, farming_plus=0, avoid='', typ='tavern'},
|
|
taverne_4 = {burried=1, rotated=0, farming_plus=0, avoid='', typ='tavern'},
|
|
well_1 = {burried=1, rotated=0, farming_plus=0, avoid='well', typ='well'},
|
|
well_2 = {burried=1, rotated=0, farming_plus=0, avoid='well', typ='well'},
|
|
well_3 = {burried=1, rotated=0, farming_plus=0, avoid='well', typ='well'},
|
|
well_4 = {burried=1, rotated=0, farming_plus=0, avoid='well', typ='well'},
|
|
well_5 = {burried=1, rotated=0, farming_plus=0, avoid='well', typ='well'},
|
|
well_6 = {burried=1, rotated=0, farming_plus=0, avoid='well', typ='well'},
|
|
well_7 = {burried=1, rotated=0, farming_plus=0, avoid='well', typ='well'},
|
|
well_8 = {burried=1, rotated=0, farming_plus=0, avoid='well', typ='well'},
|
|
|
|
tree_place_1 = {burried=0, rotated=0, farming_plus=0, avoid='', typ='village_square'},
|
|
tree_place_2 = {burried=0, rotated=0, farming_plus=0, avoid='', typ='village_square'},
|
|
tree_place_3 = {burried=0, rotated=0, farming_plus=0, avoid='', typ='village_square'},
|
|
tree_place_4 = {burried=0, rotated=0, farming_plus=0, avoid='', typ='village_square'},
|
|
tree_place_5 = {burried=0, rotated=0, farming_plus=0, avoid='', typ='village_square'},
|
|
tree_place_6 = {burried=0, rotated=0, farming_plus=0, avoid='', typ='village_square'},
|
|
tree_place_7 = {burried=0, rotated=0, farming_plus=0, avoid='', typ='village_square'},
|
|
tree_place_8 = {burried=0, rotated=0, farming_plus=0, avoid='', typ='village_square'},
|
|
tree_place_9 = {burried=0, rotated=0, farming_plus=0, avoid='', typ='village_square'},
|
|
tree_place_10 = {burried=0, rotated=0, farming_plus=0, avoid='', typ='village_square'},
|
|
|
|
dirtroad_1 = {burried=1, rotated=0, farming_plus=0, avoid='', typ='road'},
|
|
black_road_1 = {burried=1, rotated=0, farming_plus=0, avoid='', typ='road'},
|
|
cobble3_road_1 = {burried=1, rotated=0, farming_plus=0, avoid='', typ='road'},
|
|
gravel_road_1 = {burried=1, rotated=0, farming_plus=0, avoid='', typ='road'},
|
|
grey_road_1 = {burried=1, rotated=0, farming_plus=0, avoid='', typ='road'},
|
|
small_grey_road_1={burried=1, rotated=0, farming_plus=0, avoid='', typ='road'},
|
|
stonebrick_road_1={burried=1, rotated=0, farming_plus=0, avoid='', typ='road'},
|
|
wood2_road_1 = {burried=1, rotated=0, farming_plus=0, avoid='', typ='road'},
|
|
|
|
river_1 = {burried=1, rotated=90, farming_plus=0, avoid='', typ='road'},
|
|
river_2 = {burried=1, rotated=90, farming_plus=0, avoid='', typ='road'},
|
|
river_3 = {burried=1, rotated=90, farming_plus=0, avoid='', typ='road'},
|
|
river_4 = {burried=1, rotated=90, farming_plus=0, avoid='', typ='road'},
|
|
river_5 = {burried=1, rotated=90, farming_plus=0, avoid='', typ='road'},
|
|
river_6 = {burried=1, rotated=90, farming_plus=0, avoid='', typ='road'},
|
|
river_7 = {burried=1, rotated=90, farming_plus=0, avoid='', typ='road'},
|
|
river_8 = {burried=1, rotated=90, farming_plus=0, avoid='', typ='road'},
|
|
river_9 = {burried=1, rotated=90, farming_plus=0, avoid='', typ='road'},
|
|
river_10 = {burried=1, rotated=90, farming_plus=0, avoid='', typ='road'},
|
|
river_11 = {burried=1, rotated=90, farming_plus=0, avoid='', typ='road'},
|
|
|
|
--[[
|
|
-- Houses from Taokis Structure I/O Mod (see https://forum.minetest.net/viewtopic.php?id=5524)
|
|
default_farm_large = {burried=1, rotated=180, farming_plus=0, avoid='', typ='farm_full'},
|
|
default_farm_small = {burried=1, rotated=180, farming_plus=1, avoid='', typ='farm_tiny'},
|
|
default_house_large = {burried=0, rotated=180, farming_plus=0, avoid='', typ='house'},
|
|
default_house_medium = {burried=0, rotated=180, farming_plus=0, avoid='', typ='house'},
|
|
default_house_small = {burried=0, rotated=180, farming_plus=0, avoid='', typ='house'},
|
|
default_tower = {burried=0, rotated=180, farming_plus=0, avoid='', typ='tower'},
|
|
default_fountain_large = {burried=0, rotated=180, farming_plus=0, avoid='well', typ='well'},
|
|
default_fountain_small = {burried=0, rotated=180, farming_plus=0, avoid='well', typ='well'},
|
|
default_pole = {burried=0, rotated=180, farming_plus=0, avoid='', typ='deko'},
|
|
--]]
|
|
}
|
|
|
|
|
|
-- read the data files and fill in information like size and nodes that need on_construct to be called after placing
|
|
villages.init = function()
|
|
-- determine the size of the given houses
|
|
for k,v in pairs( villages.building_data ) do
|
|
-- read the size of the building
|
|
local res = villages.analyze_mts_file( villages.mts_path..k );
|
|
-- store it for later usage
|
|
villages.building_data[ k ].size = {};
|
|
villages.building_data[ k ].size.x = res.size.x;
|
|
villages.building_data[ k ].size.y = res.size.y;
|
|
villages.building_data[ k ].size.z = res.size.z;
|
|
|
|
-- some buildings may be rotated
|
|
if( villages.building_data[ k ].rotated == 90
|
|
or villages.building_data[ k ].rotated == 270 ) then
|
|
|
|
villages.building_data[ k ].size.x = res.size.x;
|
|
villages.building_data[ k ].size.z = res.size.z;
|
|
-- print('Rotated building. Length: '..tostring( villages.building_data[ k ].size.z )..' width: '..tostring( villages.building_data[ k ].size.x ));
|
|
end
|
|
|
|
-- we do need at least the list of nodenames which will need on_constr later on
|
|
villages.building_data[ k ].on_constr = res.on_constr;
|
|
villages.building_data[ k ].nodenames = res.nodenames;
|
|
|
|
-- print it for debugging usage
|
|
--print(tostring(size.x)..' x '..tostring(size.y)..' x '..tostring(size.z)..' -> '..tostring( k ));
|
|
end
|
|
end
|
|
|
|
|
|
|
|
-- at least the cottages may come in a variety of building materials
|
|
-- IMPORTANT: don't add any nodes which have on_construct here UNLESS they where in the original file already
|
|
-- on_construct will only be called for known nodes that need that treatment (see villages.analyze_mts_file and on_constr)
|
|
villages.get_replacement_list = function( housetype )
|
|
|
|
local replacements = {};
|
|
|
|
-- Taokis houses from structure i/o
|
|
if( housetype == 'taoki' ) then
|
|
|
|
table.insert( replacements, {'default:wood', 'default:clay'});
|
|
table.insert( replacements, {'stairs:slab_wood', 'stairs:slab_sandstone'});
|
|
-- I don't like brick roofs that much
|
|
table.insert( replacements, {'default:brick', 'default:stone'});
|
|
table.insert( replacements, {'stairs:slab_brick', 'stairs:slab_stone'});
|
|
|
|
return replacements;
|
|
end
|
|
|
|
-- TODO: are there more possible types?
|
|
-- wells can get the same replacements as the sourrounding village; they'll get a fitting roof that way
|
|
if( housetype ~= 'cottages' and housetype ~= 'well') then
|
|
return {};
|
|
end
|
|
-- glass that served as a marker got copied accidently; there's usually no glass in cottages
|
|
table.insert( replacements, {'default:glass', 'air'});
|
|
-- else some grass would never (re)grow (if it's below a roof)
|
|
table.insert( replacements, {'default:dirt', 'default:dirt_with_grass'});
|
|
|
|
-- TODO: sometimes, half_door/half_door_inverted gets rotated wrong
|
|
-- table.insert( replacements, {'cottages:half_door', 'cottages:half_door_inverted'});
|
|
-- table.insert( replacements, {'cottages:half_door_inverted', 'cottages:half_door'});
|
|
|
|
-- some poor cottage owners cannot afford glass
|
|
if( math.random( 1, 2 ) == 2 ) then
|
|
table.insert( replacements, {'cottages:glass_pane', 'default:fence_wood'});
|
|
end
|
|
|
|
-- 'glass' is admittedly debatable; yet it may represent modernized old houses where only the tree-part was left standing
|
|
-- loam and clay are mentioned multiple times because those are the most likely building materials in reality
|
|
local materials = {'cottages:loam', 'cottages:loam', 'cottages:loam', 'cottages:loam', 'cottages:loam',
|
|
'default:clay', 'default:clay', 'default:clay', 'default:clay', 'default:clay',
|
|
'default:wood','default:junglewood','default:sandstone',
|
|
'default:desert_stone','default:brick','default:cobble','default:stonebrick',
|
|
'default:desert_stonebrick','default:sandstonebrick','default:stone','default:glass'};
|
|
|
|
-- bottom part of the house (usually ground floor from outside)
|
|
local m1 = materials[ math.random( 1, #materials )];
|
|
if( m1 ~= 'default:clay' ) then
|
|
table.insert( replacements, {'default:clay', m1});
|
|
end
|
|
|
|
-- upper part of the house (may be the same as the material for the lower part)
|
|
local m2 = materials[ math.random( 1, #materials )];
|
|
if( m2 ~= 'cottages:loam' ) then
|
|
table.insert( replacements, {'cottages:loam', m2});
|
|
end
|
|
|
|
-- what is sandstone (the floor) may be turned into something else as well
|
|
local mf = materials[ math.random( 1, #materials )];
|
|
-- a glass floor would go too far
|
|
if( mf == 'default:glass' ) then
|
|
mf = 'cottages:loam';
|
|
end
|
|
if( mf ~= 'default:sandstone' ) then
|
|
table.insert( replacements, {'default:sandstone', mf});
|
|
|
|
-- some houses come with slabs of the material; however, slabs are not available in all materials
|
|
local mfs = string.sub( mf, 9 );
|
|
-- loam and clay: use wood for slabs
|
|
if( mfs == ':loam' or mfs == 'clay') then
|
|
mfs = 'wood';
|
|
-- for sandstonebrick, use sandstone
|
|
elseif( mfs == 'sandstonebrick' or mfs == 'desert_stone' or mfs == 'desert_stonebrick') then
|
|
mfs = 'sandstone';
|
|
end
|
|
table.insert( replacements, {'stairs:slab_sandstone', 'stairs:slab_'..mfs});
|
|
end
|
|
|
|
-- replace cobble; for these nodes, a stony material is needed (used in wells as well)
|
|
-- mossycobble is fine here as well
|
|
local cob_materials = { 'default:sandstone', 'default:desert_stone',
|
|
'default:cobble', 'default:cobble',
|
|
'default:stonebrick', 'default:stonebrick', 'default:stonebrick', -- more common than other materials
|
|
'default:mossycobble', 'default:mossycobble','default:mossycobble',
|
|
'default:stone', 'default:stone',
|
|
'default:desert_stonebrick','default:sandstonebrick'};
|
|
local mc = cob_materials[ math.random( 1, #cob_materials )];
|
|
if( mc ~= 'default:cobble' ) then
|
|
table.insert( replacements, {'default:cobble', mc});
|
|
|
|
-- not all of the materials above come with slabs
|
|
local mcs = string.sub( mc, 9 );
|
|
-- loam and clay: use wood for slabs
|
|
if( mcs == 'mossycobble') then
|
|
mcs = 'cobble';
|
|
end
|
|
table.insert( replacements, {'stairs:slab_cobble', 'stairs:slab_'..mcs});
|
|
end
|
|
|
|
|
|
|
|
-- straw is the most likely building material for roofs for historical buildings
|
|
local materials_roof = {'straw', 'straw', 'straw', 'straw', 'straw',
|
|
'wood', 'wood',
|
|
'red',
|
|
'brown',
|
|
'black'};
|
|
local mr = materials_roof[ math.random( 1, #materials_roof )];
|
|
if( mr ~= 'straw' ) then
|
|
-- all three shapes of roof parts have to fit together
|
|
table.insert( replacements, {'cottages:roof_straw', 'cottages:roof_'..mr });
|
|
table.insert( replacements, {'cottages:roof_connector_straw', 'cottages:roof_connector_'..mr });
|
|
table.insert( replacements, {'cottages:roof_flat_straw', 'cottages:roof_flat_'..mr });
|
|
end
|
|
|
|
return replacements;
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- taken from https://github.com/MirceaKitsune/minetest_mods_structures/blob/master/structures_io.lua (Taokis Sructures I/O mod)
|
|
-- gets the size of a structure file
|
|
villages.analyze_mts_file = function( path )
|
|
local size = { x = 0, y = 0, z = 0 }
|
|
|
|
path = path..".mts"
|
|
local file = io.open(path, "r")
|
|
if (file == nil) then return nil end
|
|
|
|
-- thanks to sfan5 for this advanced code that reads the size from schematic files
|
|
local read_s16 = function(fi)
|
|
return string.byte(fi:read(1)) * 256 + string.byte(fi:read(1))
|
|
end
|
|
local function get_schematic_size(f)
|
|
-- make sure those are the first 4 characters, otherwise this might be a corrupt file
|
|
if f:read(4) ~= "MTSM" then return nil end
|
|
-- advance 2 more characters
|
|
f:read(2)
|
|
-- the next characters here are our size, read them
|
|
return read_s16(f), read_s16(f), read_s16(f)
|
|
end
|
|
|
|
size.x, size.y, size.z = get_schematic_size(file)
|
|
|
|
|
|
-- this list is not yet used for anything
|
|
local nodenames = {};
|
|
-- this list is needed for calling on_construct after place_schematic
|
|
local on_constr = {};
|
|
|
|
-- after that: read_s16 (2 bytes) to find out how many diffrent nodenames (node_name_count) are present in the file
|
|
local node_name_count = read_s16( file );
|
|
|
|
for i = 1, node_name_count do
|
|
|
|
-- the length of the next name
|
|
local name_length = read_s16( file );
|
|
-- the text of the next name
|
|
local name_text = file:read( name_length );
|
|
|
|
table.insert( nodenames, name_text );
|
|
-- in order to get this information, the node has to be defined and loaded
|
|
if( minetest.registered_nodes[ name_text ] and minetest.registered_nodes[ name_text ].on_construct) then
|
|
table.insert( on_constr, name_text );
|
|
end
|
|
end
|
|
|
|
file.close(file)
|
|
return { size = { x=size.x, y=size.y, z=size.z}, nodenames = nodenames, on_constr = on_constr };
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- orientation: 0, 90, 180 or 270
|
|
-- housenames: table (list) with the names of houses that can be placed here
|
|
-- if you want houses to be more probable than others, list them more than once here
|
|
-- interspacing_house_road: table for random space between house and road; has to contain at least one entry of the form 0: <value>
|
|
-- example: {95,80,62,10,0} stands for:
|
|
-- take 0 as distance if random( 100 ) > 95
|
|
-- take 1 as distance if random( 100 ) > 80
|
|
-- take 2 as distance if random( 100 ) > 62
|
|
-- ..
|
|
-- take 4 else
|
|
-- interspacing_house_house: see above; this time for space between two subsequent houses
|
|
-- max_length: thoe whole house row cannot exceed this length
|
|
-- max_witdth: the whole house row (hose width + interspacing_house_road) is not allowed to exceed this value
|
|
-- max_houses: allow only that many houses in the row; stop even if max_ength would allow more
|
|
-- inverse_street_dir: usually, the street only extends in positive x or z direction; at the end of the road,
|
|
-- not all available space may be consumed. with inverse_street_dir set to 1, the "hole" is moved to the other end
|
|
-- use_given_order: in case of the central place, the array "housenames" contains the housenames alredy in the desired order
|
|
villages.plan_house_row = function( start_pos, orientation, housenames, interspacing_house_road, interspacing_house_house, max_length, max_width, max_houses, inverse_street_dir, allow_branch, use_given_order, start_with, distance_road_start, distance_house_start, village_square, start_with_length )
|
|
|
|
local current_length = 0;
|
|
local house_row = {};
|
|
local house_pos = { x = start_pos.x, y = start_pos.y, z = start_pos.z };
|
|
|
|
local true_length = 0; -- how long is the road, excluding the last interspacing_house_house?
|
|
|
|
local true_width = 0; -- how wide is the house row?
|
|
|
|
local dist_last_branch = 100000; -- when did we last branch off with a new road? (at the start: never - so the distance is huge)
|
|
|
|
if( not( allow_branch ) or allow_branch == '' ) then
|
|
dist_last_branch = -100000; -- this way, we'll never accumulate enough space to branch off
|
|
end
|
|
|
|
|
|
|
|
-- if the street ought to extend in the other direction, start it at the other end
|
|
if( inverse_street_dir == 1 ) then
|
|
|
|
if( orientation == 0 or orientation == 180 ) then
|
|
start_pos.z = start_pos.z - max_length;
|
|
else
|
|
start_pos.x = start_pos.x - max_length;
|
|
end
|
|
end
|
|
|
|
|
|
-- at the central place, the distance between the houses from village_square.house_rows_square[ 3 ] and the road needs to be increased
|
|
local inc_space_road = 0;
|
|
-- at which position are we in village_square.house_rows_square[3]?
|
|
local next_house_at_place = 0;
|
|
|
|
|
|
|
|
while( current_length < max_length and #house_row < max_houses ) do
|
|
|
|
local selected_house = '';
|
|
local is_branch = 0;
|
|
local add_rotation_if_branch = 0;
|
|
|
|
|
|
-- does the village square end here?
|
|
if( village_square ~= nil
|
|
and (next_house_at_place == #village_square.house_rows_square[3] + 1 )) then
|
|
|
|
inc_space_road = 0;
|
|
|
|
-- do this only at the main road
|
|
if( allow_branch ~= nil and allow_branch ~= "") then
|
|
-- add some free space for village_square.house_rows_square[1]
|
|
true_length = true_length + village_square.width_row[1]; --length_row[2];
|
|
current_length = current_length + village_square.width_row[1]; --length_row[2];
|
|
dist_last_branch = 0;
|
|
end
|
|
|
|
-- avoid doing the same again
|
|
next_house_at_place = next_house_at_place + 1;
|
|
end
|
|
|
|
-- the first houses the row starts with have been determined alredy; the rest is random!
|
|
if( start_with ~= nil and #house_row < #start_with ) then
|
|
|
|
-- Note: Method does not work if inverse_street_dir==1
|
|
selected_house = start_with[ #house_row+1 ];
|
|
|
|
-- we are working on the central place
|
|
elseif( village_square ~= nil and next_house_at_place <= #village_square.house_rows_square[ 3 ]) then
|
|
|
|
-- shall we begin with the place now?
|
|
if( next_house_at_place < 1 ) then
|
|
|
|
-- sideroad
|
|
if( allow_branch == nil or allow_branch == "" ) then
|
|
|
|
next_house_at_place = 1;
|
|
|
|
elseif( dist_last_branch > villages.MIN_BRANCH_DIST
|
|
and math.random( 1, 100 ) < villages.BRANCH_PROBABILITY ) then
|
|
|
|
next_house_at_place = 1;
|
|
|
|
-- add
|
|
true_length = true_length + village_square.width_row[2]; --length_row[1];
|
|
current_length = current_length + village_square.width_row[2]; --length_row[1];
|
|
dist_last_branch = -1 * village_square.square_length; --square_width;
|
|
|
|
|
|
local tree_offset = math.floor( (village_square.square_length - villages.building_data[ village_square.house_rows_square[4][1] ].size.x ) / 2);
|
|
local tree_offset_street = math.floor( (village_square.square_width - villages.building_data[ village_square.house_rows_square[4][1] ].size.z ) / 2)-1;
|
|
|
|
-- make sure there is enough place for the road
|
|
if( villages.building_data[ village_square.house_rows_square[ 3 ][ 1 ] ].typ == 'road' ) then
|
|
tree_offset = tree_offset + villages.building_data[ village_square.house_rows_square[ 3 ][ 1 ] ].size.x;
|
|
else
|
|
tree_offset = tree_offset - villages.building_data[ village_square.house_rows_square[ 3 ][ #village_square.house_rows_square[3] ] ].size.x;
|
|
end
|
|
if( tree_offset < 0 ) then
|
|
tree_offset = 0;
|
|
end
|
|
|
|
-- place the tree
|
|
if( orientation == 90 or orientation == 270 ) then
|
|
|
|
house_pos.x = start_pos.x + current_length + tree_offset;
|
|
house_pos.z = start_pos.z + tree_offset_street;
|
|
else
|
|
house_pos.z = start_pos.z + current_length + tree_offset;
|
|
house_pos.x = start_pos.x + tree_offset_street;
|
|
end
|
|
|
|
house_row[ #house_row + 1 ] = { house = village_square.house_rows_square[4][1],
|
|
dist_next = 0,
|
|
dist_road = 0,
|
|
pos = { x = house_pos.x, y = house_pos.y, z = house_pos.z },
|
|
size = villages.building_data[ village_square.house_rows_square[4][1] ].size,
|
|
rot = 0, -- does not matter in this case
|
|
fruit = nil, -- no fruit (we plant a tree, not a field)
|
|
is_branch = 0,
|
|
village_square = nil };
|
|
-- reset house_pos
|
|
house_pos = { x = start_pos.x, y = start_pos.y, z = start_pos.z };
|
|
end
|
|
end
|
|
|
|
-- add a house that belongs to the plac
|
|
if( next_house_at_place > 0 ) then
|
|
|
|
selected_house = village_square.house_rows_square[ 3 ][ next_house_at_place ];
|
|
next_house_at_place = next_house_at_place + 1;
|
|
|
|
-- how far away do we have to place the houses of village_square.house_rows_square[3] from the main road?
|
|
inc_space_road = village_square.square_length; ---square_length;
|
|
|
|
-- else add a random house
|
|
else
|
|
-- TODO: avoid is not taken into account here
|
|
selected_house = housenames[ math.random( 1, #housenames )];
|
|
end
|
|
|
|
-- which house goes at which position has already been determined
|
|
elseif( use_given_order == 1 ) then
|
|
|
|
-- just get the next from the row
|
|
selected_house = housenames[ #house_row+1 ];
|
|
inc_space_road = 0;
|
|
|
|
-- do we branch off here?
|
|
elseif( dist_last_branch > villages.MIN_BRANCH_DIST
|
|
and math.random( 1, 100 ) < villages.BRANCH_PROBABILITY ) then
|
|
|
|
selected_house = allow_branch; -- select a road
|
|
inc_space_road = 0;
|
|
else
|
|
|
|
-- get a random house
|
|
selected_house = housenames[ math.random( 1, #housenames )];
|
|
inc_space_road = 0;
|
|
|
|
-- avoid to repeat houses/buildings where repeating would be strange (two wells in a row would be pointless)
|
|
while( #house_row > 1
|
|
and villages.building_data[ house_row[ #house_row ].house ].avoid ~= ''
|
|
and villages.building_data[ house_row[ #house_row ].house ].avoid ==
|
|
villages.building_data[ selected_house ].avoid ) do
|
|
|
|
-- get a new house
|
|
selected_house = housenames[ math.random( 1, #housenames )];
|
|
end
|
|
end
|
|
|
|
|
|
-- this can only be a branch if it does not consist entirely of road pieces
|
|
if( villages.building_data[ selected_house ].typ == 'road'
|
|
and villages.building_data[ housenames[ 1 ]].typ ~= 'road') then
|
|
|
|
-- if we have a road that branches off from the main road in the street, that one needs to get rotated seperately
|
|
house_length = villages.building_data[ selected_house ].size.x;
|
|
house_width = villages.building_data[ selected_house ].size.z;
|
|
|
|
add_rotation_if_branch = 90;
|
|
is_branch = 1;
|
|
dist_last_branch = 0;
|
|
else
|
|
-- the length of the house relative to the street is always the same
|
|
house_width = villages.building_data[ selected_house ].size.x;
|
|
-- the width of the house; this is only important so that we do not place it too far away from the road
|
|
house_length = villages.building_data[ selected_house ].size.z;
|
|
|
|
is_branch = 0;
|
|
add_rotation_if_branch = 0;
|
|
end
|
|
|
|
|
|
|
|
-- add the new house (provided there's space for it)
|
|
if( current_length + house_length < max_length ) then
|
|
|
|
|
|
-- get a random space between this and the next house
|
|
local a1 = math.random( 1, 100 );
|
|
local space_inbetween = 10000;
|
|
for i,v in ipairs( interspacing_house_house ) do
|
|
if( a1 > v and space_inbetween == 10000) then
|
|
space_inbetween = (i-1);
|
|
end
|
|
end
|
|
|
|
-- do not exceed the limit with the space between houses if there's no next house coming
|
|
if( current_length + house_length + space_inbetween > max_length ) then
|
|
space_inbetween = 0;
|
|
end
|
|
|
|
|
|
--how far away from the road does the house start?
|
|
local space_road = 10000;
|
|
|
|
|
|
-- increase space at the central village place
|
|
if( inc_space_road ~= 0 and is_branch ~= 1) then
|
|
space_road = -1 + inc_space_road;
|
|
|
|
-- most roads will interconnect nicely that way
|
|
elseif( is_branch==1 ) then
|
|
space_road = -1;
|
|
|
|
-- make room for a village place
|
|
elseif( start_with ~= nil and (#house_row < #start_with or current_length < start_with_length)) then
|
|
space_road = distance_road_start;
|
|
--print( 'distance_road_start: '..tostring( distance_road_start ).." for house "..tostring( selected_house ));
|
|
|
|
else
|
|
-- the house may be some nodes away from the road
|
|
local a2 = math.random( 1, 100 );
|
|
for i,v in ipairs( interspacing_house_road ) do
|
|
if( a1 > v and space_road == 10000) then
|
|
space_road = (i-1);
|
|
end
|
|
end
|
|
|
|
-- make sure we do not get too wide with that
|
|
if( house_width + space_road > max_width ) then
|
|
space_road = 0;
|
|
end
|
|
|
|
-- how wide is the road at its widest point? this will be important for further roads branching off
|
|
if( house_width + space_road > true_width ) then
|
|
true_width = house_width + space_road;
|
|
end
|
|
end
|
|
|
|
-- this helps to calculate the coordinates
|
|
if( orientation == 90 or orientation == 180 ) then
|
|
space_road = space_road * -1;
|
|
end
|
|
|
|
local dir = 1;
|
|
-- if( inverse_street_dir == 1 ) then
|
|
-- dir = -1;
|
|
-- end
|
|
-- house_pos may need to be transformed since place_schematic walks in positive x- and z- direction
|
|
if( orientation == 90 or orientation == 270 ) then
|
|
|
|
house_pos.x = start_pos.x + ( dir * current_length);
|
|
house_pos.z = start_pos.z + space_road;
|
|
else
|
|
house_pos.z = start_pos.z + ( dir * current_length);
|
|
house_pos.x = start_pos.x + space_road;
|
|
end
|
|
|
|
|
|
local rotation = orientation + villages.building_data[ selected_house ].rotated + add_rotation_if_branch;
|
|
while( rotation >= 360 ) do
|
|
rotation = rotation - 360;
|
|
end
|
|
|
|
|
|
if( is_branch ~= 1 ) then
|
|
if( (orientation == 180 )) then
|
|
house_pos.x = house_pos.x - house_width;
|
|
elseif( (orientation == 90 )) then
|
|
house_pos.z = house_pos.z - house_width;
|
|
end
|
|
else
|
|
if( orientation==180 ) then
|
|
house_pos.z = house_pos.z + house_length;
|
|
elseif( orientation==0) then
|
|
house_pos.z = house_pos.z + house_length;
|
|
|
|
end
|
|
end
|
|
|
|
|
|
|
|
|
|
-- determine which fruit is grown (this is later on important for spawning suitable traders for that fruit in the vicinity of the house)
|
|
local fruit = '';
|
|
if( villages.building_data[ selected_house ].farming_plus == 1 ) then
|
|
fruit = villages.farming_plus_fruits[ math.random( 1, #villages.farming_plus_fruits )];
|
|
end
|
|
|
|
-- pass on the copy
|
|
local village_square_copy = nil;
|
|
if( is_branch == 1 and village_square ~= nil and inc_space_road > 0) then
|
|
village_square_copy = village_square;
|
|
end
|
|
|
|
-- some buildings are already rotated
|
|
-- size and pos are redundant; they get saved in order to prevent chaos due to later change of the content of the .mts file through the user
|
|
house_row[ #house_row + 1 ] = { house = selected_house, dist_next = space_inbetween, dist_road = space_road,
|
|
pos = { x = house_pos.x, y = house_pos.y, z = house_pos.z },
|
|
size = villages.building_data[ selected_house ].size,
|
|
rot = rotation,
|
|
fruit = fruit,
|
|
is_branch = is_branch,
|
|
village_square = village_square_copy };
|
|
|
|
-- the row got longer
|
|
true_length = current_length + house_length;
|
|
current_length = current_length + house_length + space_inbetween;
|
|
dist_last_branch = dist_last_branch + house_length + space_inbetween;
|
|
|
|
-- no more space, so make sure the main loop does not run again
|
|
else
|
|
max_houses = 0;
|
|
end
|
|
end
|
|
|
|
|
|
-- let the road extend into the other direction
|
|
if( inverse_street_dir == 1 ) then
|
|
|
|
|
|
if( orientation == 0 or orientation == 180 ) then
|
|
start_pos.z = start_pos.z + max_length;
|
|
else
|
|
start_pos.x = start_pos.x + max_length;
|
|
end
|
|
|
|
|
|
-- how much space is left empty at the end of the road?
|
|
local leftover = max_length - true_length;
|
|
|
|
-- which value gets modified depends on the orientation
|
|
local move_x = 0;
|
|
local move_z = 0;
|
|
if( orientation == 90 or orientation == 270 ) then
|
|
move_x = leftover;
|
|
else
|
|
move_z = leftover;
|
|
end
|
|
|
|
-- apply the movement
|
|
for k,v in pairs( house_row ) do
|
|
house_row[ k ].pos.x = house_row[ k ].pos.x + move_x;
|
|
house_row[ k ].pos.z = house_row[ k ].pos.z + move_z;
|
|
end
|
|
end
|
|
|
|
|
|
if( #house_row == 0 ) then
|
|
print( ' ERROR: NO HOUSE FOR ROAD. Length: '..tostring( max_length )); -- TODO
|
|
return { {road_length=0, road_width=0} };
|
|
end
|
|
-- store the road length
|
|
house_row[ 1 ].road_length = true_length;
|
|
house_row[ 1 ].road_width = true_width;
|
|
return house_row;
|
|
end
|
|
|
|
|
|
|
|
-- the function works only if each road is at least large enough for one house
|
|
-- max_houses is a limit for *each* side of the house rows - not for both combined
|
|
villages.plan_road_with_house_rows = function( start_pos_orig, orientation, housenames,
|
|
interspacing_house_road, interspacing_house_house,
|
|
max_length, max_width, max_houses, roadnames, recursion_depth, buildings_offset, village_square )
|
|
|
|
-- north/south or east/west are generated the same - inverse_street_dir makes the distinction
|
|
local inverse_street_dir = 0;
|
|
if( orientation == 180 or orientation == 90 ) then
|
|
inverse_street_dir = 1;
|
|
buildings_offset = -1 * buildings_offset;
|
|
else
|
|
inverse_street_dir = 0;
|
|
end
|
|
|
|
local start_pos = { {}, {}};
|
|
-- start_pos needs to be adjusted - depending on how wide the road is
|
|
start_pos[ 1 ] = { x = start_pos_orig.x, y = start_pos_orig.y, z = start_pos_orig.z };
|
|
start_pos[ 2 ] = { x = start_pos_orig.x, y = start_pos_orig.y, z = start_pos_orig.z };
|
|
|
|
-- find out how large the road pieces are (they're required to all have the same size!)
|
|
local road_size = { x = villages.building_data[ roadnames[ 1 ]].size.x,
|
|
y = villages.building_data[ roadnames[ 1 ]].size.y,
|
|
z = villages.building_data[ roadnames[ 1 ]].size.z };
|
|
|
|
|
|
-- adjust start_pos according to road_size
|
|
if( orientation == 0 or orientation == 180 ) then
|
|
|
|
start_pos[1].x = start_pos[1].x + road_size.x;
|
|
start_pos[2].x = start_pos[1].x - road_size.x;
|
|
|
|
start_pos[1].z = start_pos[1].z + buildings_offset;
|
|
start_pos[2].z = start_pos[2].z + buildings_offset;
|
|
else
|
|
start_pos[1].z = start_pos[1].z - road_size.x;
|
|
start_pos[2].z = start_pos[1].z + road_size.x;
|
|
|
|
start_pos[1].x = start_pos[1].x + buildings_offset;
|
|
start_pos[2].x = start_pos[2].x + buildings_offset;
|
|
end
|
|
|
|
-- the houses on both sides of the road face each other - thus they have opposite orientations
|
|
local orientations = { orientation, orientation };
|
|
if( orientation == 0 or orientation == 180 ) then
|
|
orientations[1] = 0;
|
|
orientations[2] = 180;
|
|
else
|
|
orientations[1] = 90;
|
|
orientations[2] = 270;
|
|
end
|
|
|
|
-- the whole construct (two house rows + road in the middle) shall not exceed max_width
|
|
local max_row_width = math.floor( max_width - road_size.x ) / 2;
|
|
|
|
-- do not create branches if they are not allowed anyway
|
|
local allow_branch = '';
|
|
if( recursion_depth < 1 ) then
|
|
allow_branch = roadnames[1];
|
|
end
|
|
|
|
local house_rows = { {}, {} };
|
|
|
|
-- determine the data for the central village square - of which we need only one
|
|
local village_squares = {nil,nil};
|
|
if( recursion_depth < 1 ) then
|
|
-- village_squares[ math.random( 1,2 )] = villages.plan_village_square( 'cottages', allow_branch );
|
|
|
|
-- all house rows are constructed in positive direction - thus we can add the square only reiiably at one side
|
|
local place_square_at_side = 1;
|
|
if( orientation == 90 or orientation == 270 ) then
|
|
place_square_at_side = 2;
|
|
end
|
|
village_squares[ place_square_at_side ] = villages.plan_village_square( 'cottages', allow_branch );
|
|
|
|
print('VILLAGE SQUARE TYP: '..minetest.serialize( village_squares ));
|
|
end
|
|
|
|
for i = 1,2 do
|
|
|
|
if( village_square ~= nil ) then
|
|
|
|
local new_index = {1,2};
|
|
local start_with = village_square.house_rows_square[ i ];
|
|
if( orientation==90 or orientation==270 ) then
|
|
new_index = {2,1};
|
|
start_with = village_square.house_rows_square[ new_index[i] ];
|
|
end
|
|
--local distance_road_start = village_square.square_length + 1; --length_row[3] + 1; --village_square.square_width;
|
|
--local distance_road_start = village_square.length_row[3] + 1; --village_square.square_width;
|
|
local distance_road_start = village_square.square_width + 1; --village_square.square_width;
|
|
local start_with_length = distance_road_start + village_square.width_row[ 3 ]+1;
|
|
|
|
if( ((orientation==90 or orientation==270) and
|
|
( (i==2 and villages.building_data[ village_square.house_rows_square[ 3 ][ #village_square.house_rows_square[ 3 ] ]].typ == 'road')
|
|
or(i==1 and villages.building_data[ village_square.house_rows_square[ 3 ][ 1 ] ].typ == 'road')))
|
|
|
|
or ((orientation==0 or orientation==180) and
|
|
( (i==1 and villages.building_data[ village_square.house_rows_square[ 3 ][ #village_square.house_rows_square[ 3 ] ]].typ == 'road')
|
|
or(i==2 and villages.building_data[ village_square.house_rows_square[ 3 ][ 1 ] ].typ == 'road')))) then
|
|
distance_road_start = 0;
|
|
else
|
|
start_with[ #start_with+1 ] = housenames[ math.random(1,#housenames )];
|
|
end
|
|
house_rows[ i ] = villages.plan_house_row( start_pos[ i ], orientations[ i ],
|
|
housenames, {101,0}, {101,0},
|
|
math.floor( max_length * 3.0/4.0), max_row_width, max_houses, inverse_street_dir, allow_branch, 0,
|
|
start_with, distance_road_start, 0, village_squares[i], start_with_length );
|
|
else
|
|
house_rows[ i ] = villages.plan_house_row( start_pos[ i ], orientations[ i ],
|
|
housenames, interspacing_house_road, interspacing_house_house,
|
|
max_length - math.abs(buildings_offset), max_row_width, max_houses, inverse_street_dir, allow_branch, 0, nil,0,0, village_squares[i],0);
|
|
end
|
|
|
|
-- handle any new branches that came up
|
|
if( recursion_depth < 1) then
|
|
for j,v in ipairs( house_rows[ i ] ) do
|
|
if( v.is_branch == 1 ) then
|
|
|
|
local branch_buildings_offset = house_rows[ i ][ 1 ].road_width + 2;
|
|
|
|
-- the new road shall be no longer than 2/3 that of the old one
|
|
local branch_max_length = math.random( 1, math.floor( max_length * 2.0/3.0));
|
|
-- ...but at least some nodes long (else there won't be enough space for at least one house
|
|
if( branch_max_length < 30 ) then
|
|
branch_max_length = 30 + math.abs( branch_buildings_offset );
|
|
end
|
|
|
|
local branch_orientation = orientations[i] + 90;
|
|
if( branch_orientation >= 360 ) then
|
|
branch_orientation = 0;
|
|
end
|
|
|
|
-- these need rotation in the opposite direction
|
|
if( branch_orientation == 90 ) then
|
|
branch_orientation = 270;
|
|
elseif( branch_orientation == 270 ) then
|
|
branch_orientation = 90;
|
|
end
|
|
|
|
local new_road_pos = {x=v.pos.x, y=v.pos.y, z=v.pos.z};
|
|
|
|
if( v.village_square ~= nil) then
|
|
-- TODO
|
|
branch_buildings_offset = 2;
|
|
end
|
|
-- plan the road that branches off to the side
|
|
local branch_road = villages.plan_road_with_house_rows( new_road_pos,
|
|
branch_orientation,
|
|
housenames, interspacing_house_road, interspacing_house_house,
|
|
branch_max_length,
|
|
max_width, max_houses, roadnames, (recursion_depth+1), branch_buildings_offset, v.village_square )
|
|
-- save it
|
|
house_rows[ i ][ j ].branch = branch_road;
|
|
end
|
|
end -- of for
|
|
end -- of if( recursion_depth)
|
|
end
|
|
|
|
-- the road can be built the same way - by adding diffrent road parts; interspacing is always 0
|
|
-- no branches allowed here (these are handled on the sides)
|
|
local road_row = villages.plan_house_row( start_pos_orig, orientations[ 1 ],
|
|
roadnames, {0}, {0},
|
|
-- TODO: add length to the road to compensate for branch_buildings_offset?
|
|
max_length, road_size.x+1, 100000, inverse_street_dir, nil, 0, nil, 0,0, nil, 0 );
|
|
|
|
-- how long is the road? determine the maximum
|
|
local length = math.max( house_rows[ 1 ][ 1 ].road_length, house_rows[ 2 ][ 1 ].road_length );
|
|
length = math.max( road_row[ 1 ].road_length, length );
|
|
|
|
return { house_rows = { house_rows[ 1 ], house_rows[ 2 ], {}, {} },
|
|
road_row = road_row,
|
|
length = length,
|
|
orientations = { orientation1, orientation2, nil, nil }
|
|
};
|
|
end
|
|
|
|
|
|
|
|
-- place one schematic; if replacements is given, write a temporary file for the new schematic
|
|
villages.build_house = function( pos, housename, orientation, replacements, new_fruit )
|
|
|
|
if( not( housename )) then
|
|
print( 'ERROR! Missiong housename in villages.build_house.');
|
|
return;
|
|
end
|
|
|
|
local new_filename = villages.mts_path..housename..'.mts';
|
|
|
|
-- churches always come with glass - even if the other buildings can't afoord it
|
|
if( villages.building_data[ housename ].typ == 'church' ) then
|
|
for i,v in ipairs( replacements ) do
|
|
if( v ~= nil and v[1]=='cottages:glass_pane' and v[2]=='default:fence_wood' ) then
|
|
replacements[ i ] = nil;
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
-- TODO: the half_door does not rotate as desired yet
|
|
--[[
|
|
if( orientation==0 or orientation==180) then
|
|
table.insert( replacements, {'cottages:half_door', 'cottages:half_door_inverted'});
|
|
table.insert( replacements, {'cottages:half_door_inverted', 'cottages:half_door'});
|
|
else
|
|
table.insert( replacements, {'cottages:half_door', 'cottages:half_door'});
|
|
table.insert( replacements, {'cottages:half_door_inverted', 'cottages:half_door_inverted'});
|
|
end
|
|
--]]
|
|
|
|
-- houses with farming_plus set to 1 grow farming_plus stuff instead of the cotton they come with
|
|
if( villages.building_data[ housename ].farming_plus == 1 ) then
|
|
if( replacements == nil ) then
|
|
replacements = {};
|
|
end
|
|
|
|
if( not( new_fruit ) or new_fruit == '' ) then
|
|
new_fruit = 'cotton'; -- fallback
|
|
end
|
|
-- cotton may come in up to 8 variants (not that they look much diffrent...)
|
|
for i=1,8 do
|
|
|
|
local new_fruit_name = new_fruit;
|
|
-- farming_plus plants sometimes come in 3 or 4 variants, but not in 8 as cotton does
|
|
if( minetest.registered_nodes[ 'farming_plus:'..new_fruit_name..'_'..i ]) then
|
|
new_fruit_name = new_fruit_name..'_'..i;
|
|
table.insert( replacements, {"farming:cotton_"..i, 'farming_plus:'..new_fruit_name });
|
|
|
|
-- "surplus" cotton variants will be replaced with the full grown fruit
|
|
elseif( minetest.registered_nodes[ 'farming_plus:'..new_fruit_name ]) then
|
|
new_fruit_name = new_fruit_name;
|
|
table.insert( replacements, {"farming:cotton_"..i, 'farming_plus:'..new_fruit_name });
|
|
|
|
-- and plants from farming: are supported as well
|
|
elseif( minetest.registered_nodes[ 'farming:'..new_fruit_name..'_'..i ]) then
|
|
new_fruit_name = new_fruit_name..'_'..i;
|
|
table.insert( replacements, {"farming:cotton_"..i, 'farming:'..new_fruit_name });
|
|
|
|
end
|
|
end
|
|
end
|
|
|
|
local help_size = { x = villages.building_data[ housename ].size.x,
|
|
y = villages.building_data[ housename ].size.y,
|
|
z = villages.building_data[ housename ].size.z };
|
|
|
|
if( orientation==90 or orientation==270 ) then
|
|
help_size.x = help_size.z;
|
|
help_size.z = villages.building_data[ housename ].size.x;
|
|
end
|
|
|
|
local start_pos = {x=pos.x, y=(pos.y - villages.building_data[ housename ].burried), z=pos.z};
|
|
local end_pos = {x=(start_pos.x + help_size.x),
|
|
y=(start_pos.y + help_size.y),
|
|
z=(start_pos.z + help_size.z) };
|
|
|
|
-- actually build the house
|
|
minetest.place_schematic( start_pos, new_filename, orientation, replacements );
|
|
|
|
|
|
local building_size = villages.building_data[ housename ].size;
|
|
|
|
for i, v in ipairs( villages.building_data[ housename ].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);
|
|
|
|
-- at the start of this mod, we've already checked that the node and an on_construct function for it exist
|
|
for _, p in ipairs( nodes ) do
|
|
minetest.registered_nodes[ v ].on_construct( p );
|
|
end
|
|
|
|
end
|
|
|
|
-- write something on signs
|
|
local signs = minetest.find_nodes_in_area( start_pos, end_pos, 'default:sign_wall');
|
|
for _, p in ipairs( signs ) do
|
|
-- TODO: write the name of the inhabitants on the signs
|
|
|
|
local meta = minetest.get_meta( p );
|
|
local descr = 'This is '..tostring( housename )..'.';
|
|
if( new_fruit and new_fruit ~= '' ) then
|
|
|
|
local pluralname = new_fruit;
|
|
if( new_fruit == 'rhubarb' or new_fruit=='cotton' ) then
|
|
pluralname = new_fruit;
|
|
elseif( new_fruit == 'tomato' ) then
|
|
pluralname = 'tomatoes';
|
|
elseif( new_fruit == 'strawberry' ) then
|
|
pluralname = 'strawberries';
|
|
else
|
|
pluralname = new_fruit..'s';
|
|
end
|
|
descr = descr..' We grow '..tostring( pluralname )..'!';
|
|
|
|
elseif( villages.building_data[ housename ].typ == 'farm_full' ) then
|
|
descr = descr..' We grow wheat.';
|
|
end
|
|
meta:set_string( 'text', descr );
|
|
meta:set_string( 'infotext', descr );
|
|
end
|
|
|
|
|
|
-- fill chests with content
|
|
for i,v in ipairs( {'cottages:chest_private','cottages:chest_storage', 'cottages:chest_work' } ) do
|
|
local chests = minetest.find_nodes_in_area( start_pos, end_pos, v );
|
|
for _, p in ipairs( chests ) do
|
|
fill_chest.fill_chest_random( p );
|
|
end
|
|
end
|
|
|
|
--print( tostring( pos.x )..':'..tostring( pos.y )..':'..tostring( pos.z )..' '..tostring( orientation )..' -> '..tostring( new_filename ));
|
|
end
|
|
|
|
|
|
|
|
-- actually build the road in the world
|
|
villages.build_road = function( road, replacements )
|
|
|
|
-- build the road as such
|
|
for i,v in ipairs( road.road_row ) do
|
|
villages.build_house( v.pos, v.house, v.rot, replacements, v.fruit );
|
|
end
|
|
|
|
-- for all houee rows
|
|
for j,w in ipairs( road.house_rows ) do
|
|
|
|
-- build house row nr. j
|
|
for i,v in ipairs( road.house_rows[ j ] ) do
|
|
|
|
if( v.is_branch ~= 1 or not( v.branch)) then
|
|
villages.build_house( v.pos, v.house, v.rot, replacements, v.fruit );
|
|
|
|
-- build branches recursively
|
|
else
|
|
villages.build_road( v.branch, replacements );
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
|
|
villages.plan_village_square = function( typ, road )
|
|
|
|
-- all this needs to go into the center
|
|
local churches = {};
|
|
local inns = {};
|
|
local forges = {}; -- the forge is debatable; at least we put a well next to it!
|
|
local wells = {};
|
|
local trees = {};
|
|
local houses = {};
|
|
|
|
-- which buildings of each type are available?
|
|
if( typ == 'cottages' ) then
|
|
-- the churches are all the same - just rotated a bit
|
|
churches = {'church_1'}; --,'church_2','church_3','church_4'};
|
|
inns = {'taverne_1','taverne_2'}; -- taverne_3 and taverne_4 are too small for the central village place
|
|
forges = {'forge_1'};
|
|
wells = {'well_1','well_2','well_3','well_4','well_5','well_6','well_7','well_8',};
|
|
trees = {'tree_place_1','tree_place_2','tree_place_3','tree_place_4','tree_place_5','tree_place_6','tree_place_7','tree_place_8','tree_place_9','tree_place_10'};
|
|
end
|
|
|
|
-- select one of each building types
|
|
local church = churches[ math.random( 1, #churches ) ];
|
|
local inn = inns[ math.random( 1, #inns ) ];
|
|
local forge = forges[ math.random( 1, #forges ) ];
|
|
local well = wells[ math.random( 1, #wells ) ];
|
|
local tree = trees[ math.random( 1, #trees ) ];
|
|
|
|
|
|
-- arrange the numbers 1-4 in a random pattern
|
|
local help = {1,2,3}; -- possible positions in the list...
|
|
local sort = {};
|
|
|
|
for i = 1, 2 do
|
|
local nr = math.random( 1, #help );
|
|
sort[ #sort + 1 ] = help[ nr ];
|
|
table.remove( help, nr );
|
|
end
|
|
-- get the last number
|
|
sort[ #sort + 1 ] = help[ 1 ];
|
|
|
|
|
|
-- the four house rows around the central village place; the 5th represents the place center
|
|
local rows = { {}, {}, {}, {}, {}};
|
|
rows[ sort[ 1 ]] = { church };
|
|
rows[ sort[ 2 ]] = { inn };
|
|
-- the well can be left or right of the forge
|
|
if( math.random(1,2)==1 ) then
|
|
rows[ sort[ 3 ]] = { forge, well };
|
|
else
|
|
rows[ sort[ 3 ]] = { well, forge };
|
|
end
|
|
-- the tree is always in the middle
|
|
rows[ 4 ] = { tree };
|
|
|
|
|
|
-- how much space is needed (regarding street length) for each of the rows?
|
|
local row_min_length = { 0, 0, 0, 0 };
|
|
row_min_length[ sort[ 1 ]] = villages.building_data[ church ].size.z + 2;
|
|
row_min_length[ sort[ 2 ]] = villages.building_data[ inn ].size.z;
|
|
row_min_length[ sort[ 3 ]] = villages.building_data[ forge ].size.z + 1 -- space inbetween
|
|
+ villages.building_data[ well ].size.z;
|
|
|
|
-- +1 for some distance to the road
|
|
local row_min_width = { 0, 0, 0, 0 };
|
|
row_min_width[ sort[ 1 ]] = villages.building_data[ church ].size.x+1;
|
|
row_min_width[ sort[ 2 ]] = villages.building_data[ inn ].size.x+1;
|
|
|
|
-- either the forge or the well defines how wide this is
|
|
if( villages.building_data[ forge ].size.x >
|
|
villages.building_data[ well ].size.x ) then
|
|
|
|
row_min_width[sort[ 3]] = villages.building_data[ forge ].size.x+1;
|
|
else
|
|
row_min_width[sort[ 3]] = villages.building_data[ well ].size.x+1;
|
|
end
|
|
|
|
-- there has to be at least enough space for the tree
|
|
local min_length = villages.building_data[ tree ].size.x + 4;
|
|
local min_width = villages.building_data[ tree ].size.z + 4;
|
|
-- make it quadratic so that rotation is no problem
|
|
if( min_length < min_width ) then
|
|
min_length = min_width;
|
|
else
|
|
min_width = min_length;
|
|
end
|
|
|
|
-- row 1 and 2 are opposite of each other
|
|
if( row_min_length[ 1 ] > min_length ) then
|
|
min_length = row_min_length[ 1 ];
|
|
end
|
|
if( row_min_length[ 2 ] > min_length ) then
|
|
min_length = row_min_length[ 2 ];
|
|
end
|
|
|
|
|
|
-- row 3 and 4 are opposite each other and at a right angel compared towards row1 and row2
|
|
if( row_min_length[ 3 ] > min_width ) then
|
|
min_width = row_min_length[ 3 ];
|
|
end
|
|
local min_total_width = min_width + row_min_width[ 1 ] + row_min_width[ 2 ];
|
|
|
|
-- we need to add a road that branches off - either at the start or at the end of the 3rd row
|
|
if( math.random(1,2) == 1 ) then
|
|
table.insert( rows[3], 1, road ); -- insert at beginning
|
|
else
|
|
table.insert( rows[3], road ); -- append
|
|
end
|
|
-- TODO: this may affect min_width/min_length
|
|
|
|
return { -- the rows of houses at the square; row 3 is opposite of the main road
|
|
house_rows_square = rows,
|
|
-- how far away do we have to place the houses of house_rows_square[3] from the main road?
|
|
square_length = min_length+1,
|
|
-- in case rows[3] is shorter than the place needed for the tree and some space around:
|
|
square_width = min_width,
|
|
-- how much space do we have do reserve for rows[1]?
|
|
width_row = row_min_width,
|
|
length_row = row_min_length,
|
|
-- how much space do we have to leave between road and the house rows of the road that branches off?
|
|
dist_road_branch = min_length + row_min_width[ 3 ] +2,
|
|
};
|
|
end
|
|
|
|
|
|
-- tiny farms are much more common than large ones; in this case: 8 times more common
|
|
villages.houses_cottages = {'farm_tiny_1','farm_tiny_2','farm_tiny_3','farm_tiny_4','farm_tiny_5','farm_tiny_6','farm_tiny_7',
|
|
'farm_tiny_1','farm_tiny_2','farm_tiny_3','farm_tiny_4','farm_tiny_5','farm_tiny_6','farm_tiny_7',
|
|
'farm_tiny_1','farm_tiny_2','farm_tiny_3','farm_tiny_4','farm_tiny_5','farm_tiny_6','farm_tiny_7',
|
|
'farm_tiny_1','farm_tiny_2','farm_tiny_3','farm_tiny_4','farm_tiny_5','farm_tiny_6','farm_tiny_7',
|
|
'farm_tiny_1','farm_tiny_2','farm_tiny_3','farm_tiny_4','farm_tiny_5','farm_tiny_6','farm_tiny_7',
|
|
'farm_tiny_1','farm_tiny_2','farm_tiny_3','farm_tiny_4','farm_tiny_5','farm_tiny_6','farm_tiny_7',
|
|
'farm_tiny_1','farm_tiny_2','farm_tiny_3','farm_tiny_4','farm_tiny_5','farm_tiny_6','farm_tiny_7',
|
|
'farm_tiny_1','farm_tiny_2','farm_tiny_3','farm_tiny_4','farm_tiny_5','farm_tiny_6','farm_tiny_7',
|
|
'farm_tiny_1','farm_tiny_2','farm_tiny_3','farm_tiny_4','farm_tiny_5','farm_tiny_6','farm_tiny_7',
|
|
'farm_tiny_1','farm_tiny_2','farm_tiny_3','farm_tiny_4','farm_tiny_5','farm_tiny_6','farm_tiny_7',
|
|
'farm_tiny_1','farm_tiny_2','farm_tiny_3','farm_tiny_4','farm_tiny_5','farm_tiny_6','farm_tiny_7',
|
|
'farm_tiny_1','farm_tiny_2','farm_tiny_3','farm_tiny_4','farm_tiny_5','farm_tiny_6','farm_tiny_7',
|
|
'hut_1', 'hut_1', 'hut_1',
|
|
'farm_full_1','farm_full_2','farm_full_3','farm_full_4','farm_full_5','farm_full_6',
|
|
'farm_full_1','farm_full_2','farm_full_3','farm_full_4','farm_full_5','farm_full_6',
|
|
'farm_full_1','farm_full_2','farm_full_3','farm_full_4','farm_full_5','farm_full_6',
|
|
'well_1', 'well_2', 'well_3', 'well_4', 'well_5', 'well_6', 'well_7', 'well_8',
|
|
'taverne_3','taverne_4', -- small taverns
|
|
};
|
|
|
|
villages.houses_taoki = {
|
|
'default_farm_large',
|
|
'default_farm_small',
|
|
'default_fountain_large',
|
|
'default_house_large',
|
|
'default_house_medium',
|
|
'default_house_small',
|
|
'default_tower',
|
|
'default_fountain_small',
|
|
'default_pole' };
|
|
|
|
|
|
|
|
|
|
|
|
minetest.after( 0, villages.init);
|
|
|
|
-- contains all it takes to fill chests with random items possibly found in homes
|
|
dofile(minetest.get_modpath("villages").."/fill_chest.lua");
|
|
|
|
|
|
minetest.register_chatcommand("road", {
|
|
params = "<orientation>",
|
|
description = "Builds a road with the given orientation (0,90,180 or 270).",
|
|
privs = {},
|
|
func = function(name, param)
|
|
|
|
if( param ~= "0" and param ~= "90" and param ~= "180" and param ~= "270" ) then
|
|
minetest.chat_send_player(name, "This value of orientation is not supported.");
|
|
return;
|
|
end
|
|
|
|
--local square = villages.plan_village_square( 'cottages', 'dirtroad_1');
|
|
--minetest.chat_send_player( name, minetest.serialize( square ));
|
|
--if( 1==1 ) then return; end
|
|
|
|
|
|
local player = minetest.env:get_player_by_name(name);
|
|
local pos = player:getpos();
|
|
|
|
minetest.chat_send_player(name, "Building road with orientation "..param.." at your position: "..minetest.serialize( pos )..".");
|
|
param = tonumber( param );
|
|
|
|
|
|
local distances_road = {101,90,70,20,10,0};
|
|
-- local distances_road = {101,101,101,101,101,101,90,80,70,60,50,40,30,20,10,0}; -- very wide-spaced
|
|
local distances_houses = {101,60,20,0}; -- never without any distance (because many cottages have fences)
|
|
-- TODO: for testing
|
|
distances_road = {101,0};
|
|
distances_houses = {101,0};
|
|
|
|
-- usually a dirt road; roughly each 3th street gets build alongside a river
|
|
local street_nodes = {'river_1', 'dirtroad_1', 'black_road_1', 'cobble3_road_1', 'dirtroad_1', 'gravel_road_1',
|
|
'grey_road_1', 'small_grey_road_1', 'stonebrick_road_1', 'wood2_road_1'};
|
|
local selected_road = math.random( 1, #street_nodes );
|
|
if( selected_road ==1 ) then
|
|
street_nodes = {'river_1','river_2','river_3','river_4','river_5','river_6','river_7','river_8','river_9','river_10','river_11'};
|
|
else
|
|
street_nodes = { street_nodes[ selected_road ] };
|
|
end
|
|
|
|
local replacements = {};
|
|
local houses_road = villages.houses_cottages;
|
|
if( math.random( 1,4 )==-1) then -- TODO: disabled for now
|
|
houses_road = villages.houses_taoki;
|
|
replacements = villages.get_replacement_list( 'taoki' );
|
|
else
|
|
replacements = villages.get_replacement_list( 'cottages' );
|
|
end
|
|
|
|
-- TODO
|
|
--local tmpres = villages.plan_village_square( 'cottages', 'dirtroad_1' );
|
|
--print( minetest.serialize( tmpres ));
|
|
---- villages.build_road( tmpres, replacements );
|
|
--if (1==1) then return; end
|
|
|
|
-- plan the road
|
|
local my_road = villages.plan_road_with_house_rows( {x=pos.x,y=(pos.y+0.5),z=pos.z}, param, houses_road, distances_road, distances_houses,130,90,30,street_nodes,0,0,nil);
|
|
|
|
|
|
|
|
-- actually build it (place the houses etc.)
|
|
villages.build_road( my_road, replacements );
|
|
|
|
minetest.chat_send_player(name, "ROAD building finished successfully.");
|
|
end
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- does what the name says - creates a flat terrain for a village
|
|
villages.make_flat = function( originpos, dim )
|
|
|
|
-- read voxel manipulator from map
|
|
local vm = minetest.get_voxel_manip();
|
|
local ep1, ep2 = vm:read_from_map({x=originpos.x,y=originpos.y,z=originpos.z}, {x=(originpos.x+dim.x),y=(originpos.y+dim.y),z=(originpos.z+dim.z)});
|
|
local data = vm:get_data();
|
|
print( 'Will work from '..minetest.serialize( originpos )..' onward '..minetest.serialize( dim )..' nodes.');
|
|
|
|
local a = VoxelArea:new{
|
|
MinEdge={x=ep1.x, y=ep1.y, z=ep1.z},
|
|
MaxEdge={x=ep2.x, y=ep2.y, z=ep2.z},
|
|
}
|
|
|
|
for y = 0, dim.y-1 do
|
|
|
|
local layer_node_id = 126; -- air
|
|
if( y > 9 ) then
|
|
layer_node_id = minetest.get_content_id( 'default:glass'); --126; -- air
|
|
elseif( y==9 ) then
|
|
layer_node_id = minetest.get_content_id( 'default:dirt_with_grass' );
|
|
elseif( y==8 ) then
|
|
layer_node_id = minetest.get_content_id( 'default:dirt' );
|
|
elseif( y==7 ) then
|
|
layer_node_id = minetest.get_content_id( 'default:wood' ); -- TODO: water_source
|
|
elseif( y==6 or y==5 or y==4) then
|
|
layer_node_id = minetest.get_content_id( 'cottages:loam' );
|
|
else
|
|
layer_node_id = minetest.get_content_id( 'default:stone' );
|
|
end
|
|
print( 'y='..tostring(y)..' Setting layer to '..tostring(layer_node_id));
|
|
|
|
for z = 0, dim.z-1 do
|
|
for x = 0, dim.x-1 do
|
|
|
|
local i = a:index(originpos.x+x, originpos.y+y, originpos.z+z);
|
|
data[ i ] = layer_node_id;
|
|
end
|
|
end
|
|
end
|
|
|
|
-- write the map data back
|
|
vm:set_data(data);
|
|
vm:write_to_map();
|
|
vm:update_map();
|
|
|
|
end
|
|
|
|
|
|
minetest.register_chatcommand("flat", {
|
|
params = "",
|
|
description = "Make the area flat.",
|
|
privs = {},
|
|
func = function(name, param)
|
|
|
|
|
|
local player = minetest.env:get_player_by_name(name);
|
|
local pos = player:getpos();
|
|
|
|
minetest.chat_send_player(name, "Flattening area at your position: "..minetest.serialize( pos )..".");
|
|
|
|
-- a depth of 10 is more than sufficient; the place above needs to be set to air
|
|
villages.make_flat( {x=pos.x, y=(pos.y-0.5), z=pos.z}, {x=8,y=40,z=6} );
|
|
end
|
|
});
|