ESM UPDATE 2.0

major overhaul
This commit is contained in:
maikerumine 2016-10-22 14:16:12 -04:00
parent ed9821f1b5
commit aed732d3eb
522 changed files with 24150 additions and 558 deletions

View File

@ -1,10 +0,0 @@
[mod] Hazmat Suit [hazmat_suit]
===================================
Adds hazmat suit to 3d_armor. It protects rather well from fire (if enabled in configuration) and radiation, and it has built-in oxygen supply.
Requires technic mod.
Depends: 3d_armor, technic
Source code by numZero
Textures by HybridDog and numZero

View File

@ -1,2 +0,0 @@
3d_armor
es

View File

@ -1 +0,0 @@
Adds hazmat suit (protects from water, fire and radiation) to 3d_armor.

View File

@ -1,126 +0,0 @@
local part_count = 4
local level = 35
local heal = 20
local use = 1000
local fire = 4
local water = 1
local radiation = 50
if minetest.get_modpath("shields") then
level = level / 0.9
end
if part_count == #armor.elements then
level = level / 1.1
end
level = math.floor(level / part_count)
heal = math.floor(heal / part_count)
fire = math.floor(fire / part_count)
radiation = math.floor(radiation / part_count)
minetest.register_craftitem("hazmat_suit:helmet_hazmat", {
description = "Hazmat Helmet",
inventory_image = "hazmat_suit_inv_helmet_hazmat.png",
stack_max = 1,
})
minetest.register_craftitem("hazmat_suit:chestplate_hazmat", {
description = "Hazmat Chestplate",
inventory_image = "hazmat_suit_inv_chestplate_hazmat.png",
stack_max = 1,
})
minetest.register_craftitem("hazmat_suit:sleeve_hazmat", {
description = "Hazmat Sleeve",
inventory_image = "hazmat_suit_inv_sleeve_hazmat.png",
stack_max = 1,
})
minetest.register_craftitem("hazmat_suit:leggings_hazmat", {
description = "Hazmat Leggins",
inventory_image = "hazmat_suit_inv_leggings_hazmat.png",
stack_max = 1,
})
minetest.register_craftitem("hazmat_suit:boots_hazmat", {
description = "Hazmat Boots",
inventory_image = "hazmat_suit_inv_boots_hazmat.png",
stack_max = 1,
})
minetest.register_tool("hazmat_suit:suit_hazmat", {
description = "Hazmat Suit",
inventory_image = "hazmat_suit_inv_suit_hazmat.png",
groups = {
armor_head = level,
armor_torso = level,
armor_legs = level,
armor_feet = level,
armor_heal = heal,
armor_use = use,
armor_fire = fire,
armor_water = water,
armor_radiation = radiation,
},
wear = 0,
})
minetest.register_craft({
output = "hazmat_suit:helmet_hazmat",
recipe = {
{"", "es:infinium_ingot", ""},
{"es:infinium_ingot", "default:glass", "es:infinium_ingot"},
{"es:rubber", "es:rubber", "es:rubber"},
},
})
minetest.register_craft({
output = "hazmat_suit:chestplate_hazmat",
recipe = {
{"es:purpellium_ingot", "dye:yellow", "es:purpellium_ingot"},
{"es:infinium_ingot", "es:purpellium_ingot", "es:infinium_ingot"},
{"es:purpellium_ingot", "es:infinium_ingot", "es:purpellium_ingot"},
},
})
minetest.register_craft({
output = "hazmat_suit:sleeve_hazmat",
recipe = {
{"es:rubber", "dye:yellow"},
{"", "es:infinium_ingot"},
{"", "es:rubber"},
},
})
minetest.register_craft({
output = "hazmat_suit:leggings_hazmat",
recipe = {
{"es:rubber", "es:purpellium_ingot", "es:rubber"},
{"es:infinium_ingot", "es:rubber", "es:infinium_ingot"},
{"es:purpellium_ingot", "", "es:purpellium_ingot"},
},
})
minetest.register_craft({
output = "hazmat_suit:boots_hazmat",
recipe = {
{"", "", ""},
{"es:rubber", "", "es:rubber"},
{"es:infinium_ingot", "", "es:infinium_ingot"},
},
})
minetest.register_craft({
output = "hazmat_suit:suit_hazmat",
type = "shapeless",
recipe = {
"hazmat_suit:helmet_hazmat",
"hazmat_suit:chestplate_hazmat",
"hazmat_suit:leggings_hazmat",
"hazmat_suit:boots_hazmat",
"hazmat_suit:sleeve_hazmat",
"hazmat_suit:sleeve_hazmat",
},
})

Binary file not shown.

Before

Width:  |  Height:  |  Size: 198 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 160 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 248 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 189 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 189 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 302 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 792 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1005 B

View File

@ -0,0 +1,10 @@
This mod is not finished yet.
Create a file named
list_of_schematics.txt
in the folder containing this mod (not the world!).
Each line of the file ought to contain the name and full path to a schematic.
Those will be offered to you in the build chest's menu under "main".
Type "/giveme handle_schematics:build" to get a build chest.

View File

@ -0,0 +1,215 @@
-- This code is used to read Minecraft schematic files.
--
-- The .schematic file format is described here:
-- http://minecraft.gamepedia.com/Schematic_file_format?cookieSetup=true
-- It is based on the NBT format, which is described here:
-- http://minecraft.gamepedia.com/NBT_Format?cookieSetup=true
-- position in the decompressed string data_stream
local curr_index = 1;
-- helper arry so that the values do not have to be calculated anew each time
local pot256 = { 1 };
for i=1,8 do
pot256[ #pot256+1 ] = pot256[ #pot256 ] * 256;
end
-- read length bytes from data_stream and turn it into an integer value
local read_signed = function( data_stream, length)
local res = 0;
for i=length,1,-1 do
res = res + (string.byte( data_stream, curr_index )* pot256[ i ]);
-- move one further
curr_index = curr_index+1;
end
return res;
end
-- this table will collect the few tags we're actually intrested in
local mc_schematic_data = {};
-- this will be a recursive function
local read_tag;
-- needs to be defined now because it will contain a recursive function
local read_one_tag;
-- read payload of one tag (= a data element in a NBT data structure)
read_one_tag = function( data_stream, tag, title_tag )
if( tag<= 0 or not(data_stream)) then
return;
elseif( tag==1 ) then -- TAG_BYTE: 1 byte
return read_signed( data_stream, 1 );
elseif( tag==2 ) then -- TAG_SHORT: 2 bytes
return read_signed( data_stream, 2 );
elseif( tag==3 ) then -- TAG_INT: 4 bytes
return read_signed( data_stream, 4 );
elseif( tag==4 ) then -- TAG_LONG: 8 bytes
return read_signed( data_stream, 8 );
elseif( tag==5 ) then -- TAG_FLOAT: 4 bytes
return read_signed( data_stream, 4 ); -- the float values are unused here
elseif( tag==6 ) then -- TAG_DOUBLE: 8 bytes
return read_signed( data_stream, 8 ); -- the float values are unused here
elseif( tag==7 ) then -- TAG_Byte_Array
local size = read_signed( data_stream, 4 ); -- TAG_INT
local res = {};
for i=1,size do
-- a Byte_Array does not contain any sub-tags
res[i] = read_one_tag( data_stream, 1, nil ); -- read TAG_BYTE
end
return res;
elseif( tag==8 ) then -- TAG_String
local size = read_signed( data_stream, 2);
local res = string.sub( data_stream, curr_index, curr_index+size-1 );
-- move on in the data stream
curr_index = curr_index + size;
return res;
elseif( tag==9 ) then -- TAG_List
-- these exact values are not particulary intresting
local tagtyp = read_signed( data_stream, 1 ); -- TAG_BYTE
local size = read_signed( data_stream, 4 ); -- TAG_INT
local res = {};
for i=1,size do
-- we need to pass title_tag on to the "child"
res[i] = read_one_tag( data_stream, tagtyp, title_tag );
end
return res;
elseif( tag==10 ) then -- TAG_Compound
return read_tag( data_stream, title_tag );
elseif( tag==11 ) then -- TAG_Int_Array
local size = read_signed( data_stream, 4 ); -- TAG_INT
local res = {};
for i=1,size do
-- a Int_Array does not contain any sub-tags
res[i] = read_one_tag( data_stream, 3, nil ); -- TAG_INT
end
return res;
end
end
-- read tag type, tag name and call read_one_tag to get the payload;
read_tag = function( data_stream, title_tag )
local schematic_data = {};
while( data_stream ) do
local tag = string.byte( data_stream, curr_index);
-- move on in the data stream
curr_index = curr_index + 1;
if( not( tag ) or tag <= 0 ) then
return;
end
local tag_name_length = string.byte( data_stream, curr_index ) * 256 + string.byte(data_stream, curr_index + 1);
-- move 2 further
curr_index = curr_index + 2;
local tag_name = string.sub( data_stream, curr_index, curr_index+tag_name_length-1 );
-- move on...
curr_index = curr_index + tag_name_length;
--print('[analyze_mc_schematic_file] Found: Tag '..tostring( tag )..' <'..tostring( tag_name )..'>');
local res = read_one_tag( data_stream, tag, tag_name );
-- Entities and TileEntities are ignored
if( title_tag == 'Schematic'
and( tag_name == 'Width'
or tag_name == 'Height'
or tag_name == 'Length'
or tag_name == 'Materials' -- "Classic" or "Alpha" (=Survival)
or tag_name == 'Blocks'
or tag_name == 'Data'
)) then
mc_schematic_data[ tag_name ] = res;
end
end
return;
end
handle_schematics.analyze_mc_schematic_file = function( path )
-- these files are usually compressed; there is no point to start if the
-- decompress function is missing
if( minetest.decompress == nil) then
return nil;
end
local file, err = save_restore.file_access(path..'.schematic', "rb")
if (file == nil) then
-- print('[analyze_mc_schematic_file] ERROR: NO such file: '..tostring( path..'.schematic'));
return nil
end
local compressed_data = file:read( "*all" );
--local data_string = minetest.decompress(compressed_data, "deflate" );
local data_string = compressed_data; -- TODO
print('FILE SIZE: '..tostring( string.len( data_string ))); -- TODO
file.close(file)
-- we use this (to this file) global variable to store gathered information;
-- doing so inside the recursive functions proved problematic
mc_schematic_data = {};
-- this index will iterate through the schematic data
curr_index = 1;
-- actually analyze the data
read_tag( data_string, nil );
if( not( mc_schematic_data.Width )
or not( mc_schematic_data.Height )
or not( mc_schematic_data.Length )
or not( mc_schematic_data.Blocks )
or not( mc_schematic_data.Data )) then
print('[analyze_mc_schematic_file] ERROR: Failed to analyze '..tostring( path..'.schematic'));
return nil;
end
local translation_function = handle_schematics.findMC2MTConversion;
if( minetest.get_modpath('mccompat')) then
translation_function = mccompat.findMC2MTConversion;
end
local max_msg = 40; -- just for error handling
local size = {x=mc_schematic_data.Width, y=mc_schematic_data.Height, z=mc_schematic_data.Length};
local scm = {};
local nodenames = {};
local nodenames_id = {};
for y=1,size.y do
scm[y] = {};
for x=1,size.x do
scm[y][x] = {};
for z =1,size.z do
local new_node = translation_function(
-- (Y×length + Z)×width + X.
mc_schematic_data.Blocks[ ((y-1)*size.z + (z-1) )*size.x + (size.x-x) +1],
mc_schematic_data.Data[ ((y-1)*size.z + (z-1) )*size.x + (size.x-x) +1] );
-- some MC nodes store the information about a node in TWO block and data fields (doors, large flowers, ...)
if( new_node[3] and new_node[3]~=0 ) then
new_node = translation_function(
-- (Y×length + Z)×width + X.
mc_schematic_data.Blocks[ ((y-1)*size.z + (z-1) )*size.x + (size.x-x) +1],
mc_schematic_data.Data[ ((y-1)*size.z + (z-1) )*size.x + (size.x-x) +1],
mc_schematic_data.Blocks[ ((y-1+new_node[3])*size.z + (z-1) )*size.x + (size.x-x) +1],
mc_schematic_data.Data[ ((y-1+new_node[3])*size.z + (z-1) )*size.x + (size.x-x) +1] );
end
if( not( nodenames_id[ new_node[1]] )) then
nodenames_id[ new_node[1] ] = #nodenames + 1;
nodenames[ nodenames_id[ new_node[1] ]] = new_node[1];
end
-- print a few warning messages in case something goes wrong - but do not exaggerate
if( not( new_node[2] and max_msg>0)) then
-- print('[handle_schematics:schematic] MISSING param2: '..minetest.serialize( new_node ));
new_node[2]=0;
max_msg=max_msg-1;
end
-- save some space by not saving air
if( new_node[1] ~= 'air' ) then
scm[y][x][z] = { nodenames_id[ new_node[1]], new_node[2]};
end
end
end
end
return { size = { x=size.x, y=size.y, z=size.z}, nodenames = nodenames, on_constr = {}, after_place_node = {}, rotated=90, burried=0, scm_data_cache = scm, metadata = {}};
end

View File

@ -0,0 +1,279 @@
--[[ taken from src/mg_schematic.cpp:
Minetest Schematic File Format
All values are stored in big-endian byte order.
[u32] signature: 'MTSM'
[u16] version: 3
[u16] size X
[u16] size Y
[u16] size Z
For each Y:
[u8] slice probability value
[Name-ID table] Name ID Mapping Table
[u16] name-id count
For each name-id mapping:
[u16] name length
[u8[] ] name
ZLib deflated {
For each node in schematic: (for z, y, x)
[u16] content
For each node in schematic:
[u8] probability of occurance (param1)
For each node in schematic:
[u8] param2
}
Version changes:
1 - Initial version
2 - Fixed messy never/always place; 0 probability is now never, 0xFF is always
3 - Added y-slice probabilities; this allows for variable height structures
--]]
--handle_schematics = {}
-- 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
-- nodenames: contains all the node names that are used in the schematic
-- on_constr: lists all the node names for which on_construct has to be called after placement of the schematic
handle_schematics.analyze_mts_file = function( path )
local size = { x = 0, y = 0, z = 0, version = 0 }
local version = 0;
local file, err = save_restore.file_access(path..'.mts', "rb")
if (file == nil) then
return nil
end
--print('[handle_schematics] Analyzing .mts file '..tostring( path..'.mts' ));
--if( not( string.byte )) then
-- print( '[handle_schematics] Error: string.byte undefined.');
-- 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
local version = read_s16(f); --f:read(2)
-- the next characters here are our size, read them
return read_s16(f), read_s16(f), read_s16(f), version
end
size.x, size.y, size.z, size.version = get_schematic_size(file)
-- read the slice probability for each y value that was introduced in version 3
if( size.version >= 3 ) then
-- the probability is not very intresting for buildings so we just skip it
file:read( size.y );
end
-- this list is not yet used for anything
local nodenames = {};
-- this list is needed for calling on_construct after place_schematic
local on_constr = {};
-- nodes that require after_place_node to be called
local after_place_node = {};
-- 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
-- some nodes need after_place_node to be called for initialization
if( minetest.registered_nodes[ name_text ] and minetest.registered_nodes[ name_text ].after_place_node) then
table.insert( after_place_node, name_text );
end
end
local rotated = 0;
local burried = 0;
local parts = path:split('_');
if( parts and #parts > 2 ) then
if( parts[#parts]=="0" or parts[#parts]=="90" or parts[#parts]=="180" or parts[#parts]=="270" ) then
rotated = tonumber( parts[#parts] );
burried = tonumber( parts[ #parts-1 ] );
if( not( burried ) or burried>20 or burried<0) then
burried = 0;
end
end
end
-- decompression was recently added; if it is not yet present, we need to use normal place_schematic
if( minetest.decompress == nil) then
file.close(file);
return nil; -- normal place_schematic is no longer supported as minetest.decompress is now part of the release version of minetest
-- return { size = { x=size.x, y=size.y, z=size.z}, nodenames = nodenames, on_constr = on_constr, after_place_node = after_place_node, rotated=rotated, burried=burried, scm_data_cache = nil };
end
local compressed_data = file:read( "*all" );
local data_string = minetest.decompress(compressed_data, "deflate" );
file.close(file)
local ids = {};
local needs_on_constr = {};
local is_air = 0;
-- translate nodenames to ids
for i,v in ipairs( nodenames ) do
ids[ i ] = minetest.get_content_id( v );
needs_on_constr[ i ] = false;
if( minetest.registered_nodes[ v ] and minetest.registered_nodes[ v ].on_construct ) then
needs_on_constr[ i ] = true;
end
if( v == 'air' ) then
is_air = i;
end
end
local p2offset = (size.x*size.y*size.z)*3;
local i = 1;
local scm = {};
for z = 1, size.z do
for y = 1, size.y do
for x = 1, size.x do
if( not( scm[y] )) then
scm[y] = {};
end
if( not( scm[y][x] )) then
scm[y][x] = {};
end
local id = string.byte( data_string, i ) * 256 + string.byte( data_string, i+1 );
i = i + 2;
local p2 = string.byte( data_string, p2offset + math.floor(i/2));
id = id+1;
if( id ~= is_air ) then
scm[y][x][z] = {id, p2};
end
end
end
end
return { size = { x=size.x, y=size.y, z=size.z}, nodenames = nodenames, on_constr = on_constr, after_place_node = after_place_node, rotated=rotated, burried=burried, scm_data_cache = scm };
end
handle_schematics.store_mts_file = function( path, data )
data.nodenames[ #data.nodenames+1 ] = 'air';
local file, err = save_restore.file_access(path..'.mts', "wb")
if (file == nil) then
return nil
end
local write_s16 = function( fi, a )
fi:write( string.char( math.floor( a/256) ));
fi:write( string.char( a%256 ));
end
data.size.version = 3; -- we only support version 3 of the .mts file format
file:write( "MTSM" );
write_s16( file, data.size.version );
write_s16( file, data.size.x );
write_s16( file, data.size.y );
write_s16( file, data.size.z );
-- set the slice probability for each y value that was introduced in version 3
if( data.size.version >= 3 ) then
-- the probability is not very intresting for buildings so we just skip it
for i=1,data.size.y do
file:write( string.char(255) );
end
end
-- set how many diffrent nodenames (node_name_count) are present in the file
write_s16( file, #data.nodenames );
for i = 1, #data.nodenames do
-- the length of the next name
write_s16( file, string.len( data.nodenames[ i ] ));
file:write( data.nodenames[ i ] );
end
-- this string will later be compressed
local node_data = "";
-- actual node data
for z = 1, data.size.z do
for y = 1, data.size.y do
for x = 1, data.size.x do
local a = data.scm_data_cache[y][x][z];
if( a and type( a ) == 'table') then
node_data = node_data..string.char( math.floor( a[1]/256) )..string.char( a[1]%256-1);
else
node_data = node_data..string.char( 0 )..string.char( #data.nodenames-1 );
end
end
end
end
-- probability of occurance
for z = 1, data.size.z do
for y = 1, data.size.y do
for x = 1, data.size.x do
node_data = node_data..string.char( 255 );
end
end
end
-- param2
for z = 1, data.size.z do
for y = 1, data.size.y do
for x = 1, data.size.x do
local a = data.scm_data_cache[y][x][z];
if( a and type( a) == 'table' ) then
node_data = node_data..string.char( a[2] );
else
node_data = node_data..string.char( 0 );
end
end
end
end
local compressed_data = minetest.compress( node_data, "deflate" );
file:write( compressed_data );
file.close(file);
print('SAVING '..path..'.mts (converted from .we).');
end
-- read .mts and .we files
handle_schematics.analyze_file = function( file_name, origin_offset, store_as_mts )
local res = handle_schematics.analyze_mts_file( file_name );
-- alternatively, read the mts file
if( not( res )) then
res = handle_schematics.analyze_we_file( file_name, origin_offset );
if( not( res )) then
res = handle_schematics.analyze_mc_schematic_file( file_name );
end
-- print error message only if all import methods failed
if( not( res )) then
print('[handle_schematics] ERROR: Failed to import file \"'..tostring( file_name )..'\"[.mts|.we|.wem|.schematic]');
-- convert to .mts for later usage
elseif( store_as_mts ) then
handle_schematics.store_mts_file( store_as_mts, res );
end
end
return res;
end

View File

@ -0,0 +1,100 @@
handle_schematics.analyze_we_file = function(scm, we_origin)
local c_ignore = minetest.get_content_id("ignore")
-- this table will contain the nodes read
local nodes = {}
-- check if it is a worldedit file
-- (no idea why reading that is done in such a complicated way; a simple deserialize and iteration over all nodes ought to do as well)
local f, err = save_restore.file_access( scm..".we", "r")
if not f then
f, err = save_restore.file_access( scm..".wem", "r")
if not f then
-- error("Could not open schematic '" .. scm .. ".we': " .. err)
return nil;
end
end
local value = f:read("*a")
f:close()
local nodes = worldedit_file.load_schematic(value, we_origin)
-- create a list of nodenames
local nodenames = {};
local nodenames_id = {};
for i,ent in ipairs( nodes ) do
if( ent and ent.name and not( nodenames_id[ ent.name ])) then
nodenames_id[ ent.name ] = #nodenames + 1;
nodenames[ nodenames_id[ ent.name ] ] = ent.name;
end
end
scm = {}
local maxx, maxy, maxz = -1, -1, -1
local all_meta = {};
for i = 1, #nodes do
local ent = nodes[i]
ent.x = ent.x + 1
ent.y = ent.y + 1
ent.z = ent.z + 1
if ent.x > maxx then
maxx = ent.x
end
if ent.y > maxy then
maxy = ent.y
end
if ent.z > maxz then
maxz = ent.z
end
if scm[ent.y] == nil then
scm[ent.y] = {}
end
if scm[ent.y][ent.x] == nil then
scm[ent.y][ent.x] = {}
end
if ent.param2 == nil then
ent.param2 = 0
end
-- metadata is only of intrest if it is not empty
if( ent.meta and (ent.meta.fields or ent.meta.inventory)) then
local has_meta = false;
for _,v in pairs( ent.meta.fields ) do
has_meta = true;
end
for _,v in pairs(ent.meta.inventory) do
has_meta = true;
end
if( has_meta == true ) then
all_meta[ #all_meta+1 ] = {
x=ent.x,
y=ent.y,
z=ent.z,
fields = ent.meta.fields,
inventory = ent.meta.inventory};
end
end
scm[ent.y][ent.x][ent.z] = { nodenames_id[ ent.name ], ent.param2 };
end
for y = 1, maxy do
if scm[y] == nil then
scm[y] = {}
end
for x = 1, maxx do
if scm[y][x] == nil then
scm[y][x] = {}
end
end
end
local size = {};
size.y = math.max(maxy,0);
size.x = math.max(maxx,0);
size.z = math.max(maxz,0);
return { size = { x=size.x, y=size.y, z=size.z}, nodenames = nodenames, on_constr = {}, after_place_node = {}, rotated=0, burried=0, scm_data_cache = scm, metadata = all_meta };
end

View File

@ -0,0 +1,876 @@
-----------------------------------------------------------------------------------------------------------------
-- interface for manual placement of houses
-----------------------------------------------------------------------------------------------------------------
-- functions specific to the build_chest are now stored in this table
build_chest = {};
-- scaffolding that will be placed instead of other nodes in order to show
-- how large the building will be
build_chest.SUPPORT = 'build_chest:support';
-- contains information about all the buildings
build_chest.building = {};
-- returns the id under which the building is stored
build_chest.add_building = function( file_name, data )
if( not( file_name ) or not( data )) then
return;
end
build_chest.building[ file_name ] = data;
end
-- that many options can be shown simultaneously on one menu page
build_chest.MAX_OPTIONS = 24; -- 3 columns with 8 entries each
build_chest.menu = {};
build_chest.menu.main = {};
-- create a tree structure for the menu
build_chest.add_entry = function( path )
if( not( path ) or #path<1 ) then
return;
end
local sub_menu = build_chest.menu;
for i,v in ipairs( path ) do
if( not( sub_menu[ v ] )) then
sub_menu[ v ] = {};
end
sub_menu = sub_menu[ v ];
end
end
-- add a menu entry that will always be available
build_chest.add_entry( {'save a building'} );
-- needed for saving buildings
build_chest.end_pos_list = {};
--dofile( minetest.get_modpath( minetest.get_current_modname()).."/build_chest_handle_replacements.lua");
--dofile( minetest.get_modpath( minetest.get_current_modname()).."/build_chest_preview_image.lua");
--dofile( minetest.get_modpath( minetest.get_current_modname()).."/build_chest_add_schems.lua");
build_chest.read_building = function( building_name )
-- read data
local res = handle_schematics.analyze_file( building_name, nil, nil );
if( not( res )) then
return;
end
build_chest.building[ building_name ].size = res.size;
build_chest.building[ building_name ].nodenames = res.nodenames;
build_chest.building[ building_name ].rotated = res.rotated;
build_chest.building[ building_name ].burried = res.burried;
build_chest.building[ building_name ].metadata = res.metadata;
-- scm_data_cache is not stored as that would take up too much storage space
--build_chest.building[ building_name ].scm_data_cache = res.scm_data_cache;
-- create a statistic about how often each node occours
build_chest.building[ building_name ].statistic = handle_schematics.count_nodes( res );
build_chest.building[ building_name ].preview = build_chest.preview_image_create_views( res,
build_chest.building[ building_name ].orients );
return res;
end
build_chest.get_start_pos = function( pos )
-- rotate the building so that it faces the player
local node = minetest.get_node( pos );
local meta = minetest.get_meta( pos );
local building_name = meta:get_string( 'building_name' );
if( not( building_name )) then
return "No building_name provided.";
end
if( not( build_chest.building[ building_name ] )) then
return "No data found for this building.";
end
if( not( build_chest.building[ building_name ].size )) then
if( not( build_chest.read_building( building_name ))) then
return "Unable to read data file of this building.";
end
end
local selected_building = build_chest.building[ building_name ];
local mirror = 0; -- place_schematic does not support mirroring
local start_pos = {x=pos.x, y=pos.y, z=pos.z};
-- yoff(set) from mg_villages (manually given)
if( selected_building.yoff ) then
start_pos.y = start_pos.y + selected_building.yoff -1;
end
-- make sure the building always extends forward and to the right of the player
local param2_rotated = handle_schematics.translate_param2_to_rotation( node.param2, mirror, start_pos,
selected_building.size, selected_building.rotated, selected_building.burried, selected_building.orients,
selected_building.yoff );
-- save the data for later removal/improvement of the building in the chest
meta:set_string( 'start_pos', minetest.serialize( param2_rotated.start_pos ));
meta:set_string( 'end_pos', minetest.serialize( param2_rotated.end_pos ));
meta:set_string( 'rotate', tostring(param2_rotated.rotate ));
meta:set_int( 'mirror', mirror );
-- no replacements yet
meta:set_string( 'replacements', minetest.serialize( {} ));
return start_pos;
end
build_chest.show_size_data = function( building_name )
if( not( building_name )
or building_name == ''
or not( build_chest.building[ building_name ] )
or not( build_chest.building[ building_name ].size )) then
return "";
end
local size = build_chest.building[ building_name ].size;
-- show which building has been selected
return "label[0.3,9.5;Selected building:]"..
"label[2.3,9.5;"..minetest.formspec_escape(building_name).."]"..
-- size of the building
"label[0.3,9.8;Size ( wide x length x height ):]"..
"label[4.3,9.8;"..tostring( size.x )..' x '..tostring( size.z )..' x '..tostring( size.y ).."]";
end
-- helper function for update_formspec that handles saving of a building
handle_schematics.update_formspec_save_building = function( formspec, meta, player, fields, pos )
local saved_as_filename = meta:get_string('saved_as_filename');
if( saved_as_filename and saved_as_filename ~= "" ) then
local p1str = meta:get_string('p1');
local p2str = meta:get_string('p2');
return formspec..
"label[2.0,3;This area has been saved to the file]"..
"label[2.5,3.3;"..minetest.formspec_escape( saved_as_filename ).."]"..
"label[2.0,3.6;The area extends from]"..
"label[2.5,3.9;"..minetest.formspec_escape( p1str ).."]"..
"label[2.0,4.2;to the point]"..
"label[2.5,4.5;"..minetest.formspec_escape( p2str ).."]"..
"button[5,8.0;3,0.5;back;Back]";
end
local end_pos_mark = build_chest.end_pos_list[ player:get_player_name() ];
if( end_pos_mark
and end_pos_mark.x==pos.x
and end_pos_mark.y==pos.y
and end_pos_mark.z==pos.z ) then
return formspec..
"label[2,3.0;This chest marks the end position of your building. Please put another]"..
"label[2,3.3;build chest in front of your building and save it with that chest.]"..
"button[5,8.0;3,0.5;back;Back]";
end
if( end_pos_mark and end_pos_mark.start_pos ) then
if( end_pos_mark.start_pos.x == pos.x
and end_pos_mark.start_pos.y == pos.y
and end_pos_mark.start_pos.z == pos.z ) then
local p2 = {x=end_pos_mark.x, y=end_pos_mark.y, z=end_pos_mark.z};
local p1 = {x=end_pos_mark.start_pos.x, y=end_pos_mark.start_pos.y, z=end_pos_mark.start_pos.z};
local height = math.abs( p1.y - p2.y )+1;
local width = 0;
local length = 0;
if( end_pos_mark.param2==0 or end_pos_mark.param2==2 ) then
-- adjust p1 and p2 so that only the area we really care about is marked
if( p1.z > p2.z ) then
p1.z = p1.z-1;
p2.z = p2.z+1;
else
p1.z = p1.z+1;
p2.z = p2.z-1;
end
width = math.abs( p1.x - p2.x )+1;
length = math.abs( p1.z - p2.z )+1;
else
if( p1.x > p2.x ) then
p1.x = p1.x-1;
p2.x = p2.x+1;
else
p1.x = p1.x+1;
p2.x = p2.x-1;
end
length = math.abs( p1.x - p2.x )+1;
width = math.abs( p1.z - p2.z )+1;
end
return formspec..
-- p1 and p2 are passed on as inputs in order to avoid any unwanted future interferences
-- with any other build chests
"field[40,40;0.1,0.1;save_as_p1;;"..minetest.pos_to_string(p1).."]"..
"field[40,40;0.1,0.1;save_as_p2;;"..minetest.pos_to_string(p2).."]"..
"label[2,2.4;How high is your building? This does *not* include the height offset below. The]"..
"label[2,2.7;default value is calculated from the height difference between start and end position.]"..
"label[2,3.15;Total height of your building:]"..
"field[6,3.5;1,0.5;save_as_height;;"..tostring(height).."]"..
-- note: in mg_villages, yoff has to be 0 in order to include the ground floor as well;
-- "1" means the building without floor; here, "1" means a floating building
"label[2,3.8;The hight offset sets how deep your building will be burried in the ground. Examples:]"..
"label[2.5,4.1;A value of -4 will include a cellar which extends 4 nodes below this build chest.]"..
"label[2.5,4.4;A value of -1 will include the floor below the chest, but no cellar.]"..
"label[2.5,4.7;A positive value will make your building float in the air.]"..
"label[2,5.15;Add height offset:]"..
"field[6,5.5;1,0.5;save_as_yoff;;0]"..
"label[2,5.8;Without the changes entered in the input form above, your building will extend from]"..
"label[2.5,6.1;"..minetest.formspec_escape(
minetest.pos_to_string( p1 ).." to "..
minetest.pos_to_string( p2 ).." and span a volume of "..
-- x and z are swpapped here if rotated by 90 or 270 degree
tostring(width )..' (width) x '..
tostring(length)..' (depth) x '..
tostring(height)..' (height)').."]"..
"label[2,6.7;Please enter a descriptive filename. Allowed charcters: "..
minetest.formspec_escape("a-z, A-Z, 0-9, -, _, .").."]"..
"label[2,7.15;Save schematic as:]"..
"field[6,7.5;4,0.5;save_as_filename;;]"..
"button[2,8.0;3,0.5;abort_set_start_pos;Abort]"..
"button[6,8.0;3,0.5;save_as;Save building now]";
else
return formspec..
"label[3,3;You have selected another build chest as start position.]"..
"button[5,8.0;3,0.5;back;Back]"..
"button[5,5.0;3,0.5;abort_set_start_pos;Reset start position]";
end
end
if( fields.error_msg ) then
return formspec..
"label[4,4.5;Error while trying to set the start position:]"..
"textarea[4,5;6,2;error_msg;;"..
minetest.formspec_escape( fields.error_msg ).."]"..
"button[5,8.0;3,0.5;back;Back]";
end
return formspec..
"label[2.5,2.2;First, let us assume that you are facing the front of this build chest.]"..
"label[2,3.1;Are you looking at the BACKSIDE of your building, and does said backside stretch]"..
"label[2,3.4;to the right and in front of you? Then click on the button below:]"..
"button[4,4;5,0.5;set_end_pos;Set this position as new end position]"..
"label[2,5.2;Have you set the end position with another build chest using the method above]"..
"label[2,5.5;in the meantime? And are you now looking at the FRONT of your building, which]"..
"label[2,5.8;streches in front of you and to the right? Then click on Proceed:]"..
"button[5,6.4;3,0.5;set_start_pos;Proceed with saving]"..
"label[4,7.4;If this confuses you, you can also abort the process.]"..
"button[5,8.0;3,0.5;back;Abort]";
end
build_chest.update_formspec = function( pos, page, player, fields )
-- information about the village the build chest may belong to and about the owner
local meta = minetest.get_meta( pos );
local village_name = meta:get_string( 'village' );
local village_pos = minetest.deserialize( meta:get_string( 'village_pos' ));
local owner_name = meta:get_string( 'owner' );
local building_name = meta:get_string('building_name' );
-- distance from village center
local distance = math.floor( math.sqrt( (village_pos.x - pos.x ) * (village_pos.x - pos.x )
+ (village_pos.y - pos.y ) * (village_pos.x - pos.y )
+ (village_pos.z - pos.z ) * (village_pos.x - pos.z ) ));
-- the statistic is needed for all the replacements later on as it also contains the list of nodenames
if( building_name and building_name~=""and not( build_chest.building[ building_name ].size )) then
build_chest.read_building( building_name );
end
if( page == 'please_remove' ) then
if( build_chest.stages_formspec_page_please_remove ) then
return build_chest.stages_formspec_page_please_remove( building_name, owner_name, village_name, village_pos, distance );
end
elseif( page == 'finished' ) then
if( build_chest.stages_formspec_page_finished ) then
return build_chest.stages_formspec_page_finished( building_name, owner_name, village_name, village_pos, distance );
end
elseif( page ~= 'main' ) then
-- if in doubt, return the old formspec
return meta:get_string('formspec');
end
-- create the header
local formspec = "size[13,10]"..
"label[3.3,0.0;Building box]"..
"label[0.3,0.4;Located at:]" .."label[3.3,0.4;"..(minetest.pos_to_string( pos ) or '?')..", which is "..tostring( distance ).." m away]"
.."label[7.3,0.4;from the village center]"..
"label[0.3,0.8;Part of village:]" .."label[3.3,0.8;"..(village_name or "?").."]"
.."label[7.3,0.8;located at "..(minetest.pos_to_string( village_pos ) or '?').."]"..
"label[0.3,1.2;Owned by:]" .."label[3.3,1.2;"..(owner_name or "?").."]"..
"label[3.3,1.6;Click on a menu entry to select it:]"..
build_chest.show_size_data( building_name );
local current_path = minetest.deserialize( meta:get_string( 'current_path' ) or 'return {}' );
if( #current_path > 0 ) then
formspec = formspec.."button[9.9,0.4;2,0.5;back;Back]";
end
-- offer a menu to set the positions for saving a building
if( #current_path > 0 and current_path[1]=='save a building' ) then
return handle_schematics.update_formspec_save_building( formspec, meta, player, fields, pos);
end
-- the building has been placed; offer to restore a backup
local backup_file = meta:get_string('backup');
if( backup_file and backup_file ~= "" ) then
return formspec.."button[3,3;3,0.5;restore_backup;Restore original landscape]";
end
-- offer diffrent replacement groups
if( fields.set_wood and fields.set_wood ~= "" ) then
return formspec..
"label[1,2.2;Select replacement for "..tostring( fields.set_wood )..".]"..
"label[1,2.5;Trees, saplings and other blocks will be replaced accordingly as well.]"..
-- invisible field that encodes the value given here
"field[-20,-20;0.1,0.1;set_wood;;"..minetest.formspec_escape( fields.set_wood ).."]"..
build_chest.replacements_get_group_list_formspec( pos, 'wood', 'wood_selection' );
end
if( fields.set_farming and fields.set_farming ~= "" ) then
return formspec..
"label[1,2.5;Select the fruit the farm is going to grow:]"..
-- invisible field that encodes the value given here
"field[-20,-20;0.1,0.1;set_farming;;"..minetest.formspec_escape( fields.set_farming ).."]"..
build_chest.replacements_get_group_list_formspec( pos, 'farming', 'farming_selection' );
end
if( fields.set_roof and fields.set_roof ~= "" ) then
return formspec..
"label[1,2.5;Select a roof type for the house:]"..
-- invisible field that encodes the value given here
"field[-20,-20;0.1,0.1;set_roof;;"..minetest.formspec_escape( fields.set_roof ).."]"..
build_chest.replacements_get_group_list_formspec( pos, 'roof', 'roof_selection' );
end
if( fields.preview and building_name ) then
return formspec..build_chest.preview_image_formspec( building_name,
minetest.deserialize( meta:get_string( 'replacements' )), fields.preview);
end
-- show list of all node names used
local start_pos = meta:get_string('start_pos');
if( building_name and building_name ~= '' and start_pos and start_pos ~= '' and meta:get_string('replacements')) then
return formspec..build_chest.replacements_get_list_formspec( pos );
end
-- find out where we currently are in the menu tree
local menu = build_chest.menu;
for i,v in ipairs( current_path ) do
if( menu and menu[ v ] ) then
menu = menu[ v ];
end
end
-- all submenu points at this menu position are options that need to be shown
local options = {};
for k,v in pairs( menu ) do
table.insert( options, k );
end
-- handle if there are multiple files under the same menu point
if( #options == 0 and build_chest.building[ current_path[#current_path]] ) then
options = {current_path[#current_path]};
end
-- we have found an end-node - a particular building
if( #options == 1 and options[1] and build_chest.building[ options[1]] ) then
-- a building has been selected
meta:set_string( 'building_name', options[1] );
local start_pos = build_chest.get_start_pos( pos );
if( type(start_pos)=='table' and start_pos and start_pos.x and build_chest.building[ options[1]].size) then
-- size information has just been read; we can now display it
formspec = formspec..build_chest.show_size_data( building_name );
-- do replacements for realtest where necessary (this needs to be done only once)
local replacements = {};
replacements_group['realtest'].replace( replacements );
meta:set_string( 'replacements', minetest.serialize( replacements ));
return formspec..build_chest.replacements_get_list_formspec( pos );
elseif( type(start_pos)=='string' ) then
return formspec.."label[3,3;Error reading building data:]"..
"label[3.5,3.5;"..start_pos.."]";
else
return formspec.."label[3,3;Error reading building data.]";
end
end
table.sort( options );
local page_nr = meta:get_int( 'page_nr' );
-- if the options do not fit on a single page, split them up
if( #options > build_chest.MAX_OPTIONS ) then
if( not( page_nr )) then
page_nr = 0;
end
local new_options = {};
local new_index = build_chest.MAX_OPTIONS*page_nr;
for i=1,build_chest.MAX_OPTIONS do
if( options[ new_index+i ] ) then
new_options[ i ] = options[ new_index+i ];
end
end
-- we need to add prev/next buttons to the formspec
formspec = formspec.."label[7.5,1.5;"..minetest.formspec_escape(
"Showing "..tostring( new_index+1 )..
'-'..tostring( math.min( new_index+build_chest.MAX_OPTIONS, #options))..
'/'..tostring( #options )).."]";
if( page_nr > 0 ) then
formspec = formspec.."button[9.5,1.5;1,0.5;prev;prev]";
end
if( build_chest.MAX_OPTIONS*(page_nr+1) < #options ) then
formspec = formspec.."button[11,1.5;1,0.5;next;next]";
end
options = new_options;
end
-- found an end node of the menu graph
-- elseif( build_chest.stages_formspec_page_first_stage ) then
-- return build_chest.stages_formspec_page_first_stage( v.menu_path[( #current_path )], player, pos, meta, );
-- end
-- show the menu with the next options
local i = 0;
local x = 0;
local y = 0;
if( #options < 9 ) then
x = x + 4;
end
-- order alphabeticly
table.sort( options, function(a,b) return a < b end );
for index,k in ipairs( options ) do
i = i+1;
-- new column
if( y==8 ) then
x = x+4;
y = 0;
end
formspec = formspec .."button["..(x)..","..(y+2.5)..";4,0.5;selection;"..k.."]"
y = y+1;
--x = x+4;
end
return formspec;
end
build_chest.on_receive_fields = function(pos, formname, fields, player)
local meta = minetest.get_meta(pos);
local owner = meta:get_string('owner');
local pname = player:get_player_name();
-- make sure not everyone can mess up the build chest
if( owner and owner ~= '' and owner ~= pname
and minetest.is_protected( pos, pname )) then
minetest.chat_send_player( pname,
"Sorry. This build chest belongs to "..tostring( owner ).." and only "..
"accepts input from its owner or other players who can build here.");
return;
end
local building_name = meta:get_string('building_name' );
-- the statistic is needed for all the replacements later on as it also contains the list of nodenames
if( building_name and building_name~=""and not( build_chest.building[ building_name ].size )) then
build_chest.read_building( building_name );
end
-- general menu handling
-- back button selected
if( fields.back ) then
local current_path = minetest.deserialize( meta:get_string( 'current_path' ) or 'return {}' );
table.remove( current_path ); -- revert latest selection
meta:set_string( 'current_path', minetest.serialize( current_path ));
meta:set_string( 'building_name', '');
meta:set_int( 'replace_row', 0 );
meta:set_int( 'page_nr', 0 );
meta:set_string( 'saved_as_filename', nil);
-- menu entry selected
elseif( fields.selection ) then
local current_path = minetest.deserialize( meta:get_string( 'current_path' ) or 'return {}' );
table.insert( current_path, fields.selection );
meta:set_string( 'current_path', minetest.serialize( current_path ));
-- if there are more menu items than can be shown on one page: show previous page
elseif( fields.prev ) then
local page_nr = meta:get_int( 'page_nr' );
if( not( page_nr )) then
page_nr = 0;
end
page_nr = math.max( page_nr - 1 );
meta:set_int( 'page_nr', page_nr );
-- if there are more menu items than can be shown on one page: show next page
elseif( fields.next ) then
local page_nr = meta:get_int( 'page_nr' );
if( not( page_nr )) then
page_nr = 0;
end
meta:set_int( 'page_nr', page_nr+1 );
-- specific to the build chest
-- the player has choosen a material from the list; ask for a replacement
elseif( fields.build_chest_replacements ) then
local event = minetest.explode_table_event( fields.build_chest_replacements );
local building_name = meta:get_string('building_name');
if( event and event.row and event.row > 0
and building_name
and build_chest.building[ building_name ] ) then
meta:set_int('replace_row', event.row );
end
-- the player has asked for a particular replacement
elseif( fields.store_replacement
and fields.replace_row_with and fields.replace_row_with ~= ""
and fields.replace_row_material and fields.replace_row_material ~= "") then
build_chest.replacements_apply( pos, meta, fields.replace_row_material, fields.replace_row_with );
elseif( fields.replace_rest_with_air ) then
build_chest.replacements_replace_rest_with_air( pos, meta );
elseif( fields.wood_selection ) then
build_chest.replacements_apply_for_group( pos, meta, 'wood', fields.wood_selection, fields.set_wood );
fields.set_wood = nil;
elseif( fields.farming_selection ) then
build_chest.replacements_apply_for_group( pos, meta, 'farming', fields.farming_selection, fields.set_farming );
fields.set_farming = nil;
elseif( fields.roof_selection ) then
build_chest.replacements_apply_for_group( pos, meta, 'roof', fields.roof_selection, fields.set_roof );
fields.set_roof = nil;
elseif( fields.proceed_with_scaffolding ) then
local building_name = meta:get_string('building_name');
local start_pos = minetest.deserialize( meta:get_string('start_pos'));
local end_pos = minetest.deserialize( meta:get_string('end_pos'));
local filename = meta:get_string('backup' );
if( not( filename ) or filename == "" ) then
local base_filename = 'backup_'..
meta:get_string('owner')..'_'..
tostring( start_pos.x )..':'..tostring( start_pos.y )..':'..tostring( start_pos.z )..'_'..
'0_0';
-- store a backup of the original landscape
-- <worldname>/backup_PLAYERNAME_x_y_z_burried_rotation.mts
handle_schematics.create_schematic_with_meta( start_pos, end_pos, base_filename );
meta:set_string('backup', base_filename );
-- clear metadata so that the new building can be placed
handle_schematics.clear_meta( start_pos, end_pos );
minetest.chat_send_player( pname, 'CREATING backup schematic for this place in \"schems/'..base_filename..'.mts\".');
end
-- TODO: use scaffolding here (exchange some replacements)
local replacement_list = minetest.deserialize( meta:get_string( 'replacements' ));
local rotate = meta:get_string('rotate');
local mirror = meta:get_string('mirror');
local axis = build_chest.building[ building_name ].axis;
local no_plotmarker = 1;
-- actually place the building
--minetest.place_schematic( start_pos, building_name..'.mts', rotate, replacement_list, true );
mirror = nil;
fields.error_msg = handle_schematics.place_building_from_file( start_pos, end_pos, building_name, replacement_list, rotate, axis, mirror, no_plotmarker );
if( fields.error_msg ) then
fields.error_msg = 'Error: '..tostring( fields.error_msg );
end
-- restore the original landscape
elseif( fields.restore_backup ) then
local start_pos = minetest.deserialize( meta:get_string('start_pos'));
local end_pos = minetest.deserialize( meta:get_string('end_pos'));
local backup_file = meta:get_string( 'backup' );
if( start_pos and end_pos and start_pos.x and end_pos.x and backup_file and backup_file ~= "" ) then
if( save_restore.file_exists( 'schems/'..backup_file..'.mts' )) then
filename = minetest.get_worldpath()..'/schems/'..backup_file..'.mts';
minetest.place_schematic( start_pos, filename, "0", {}, true );
-- no rotation needed - the metadata can be applied as-is (with the offset applied)
handle_schematics.restore_meta( backup_file, nil, start_pos, end_pos, 0, nil);
meta:set_string('backup', nil );
end
end
-- store a new end position
elseif( fields.set_end_pos ) then
local node = minetest.get_node( pos );
if( node and node.param2 ) then
build_chest.end_pos_list[ pname ] = {x=pos.x, y=pos.y, z=pos.z, param2=node.param2 };
end
elseif( fields.set_start_pos ) then
local error_msg = "";
local end_pos = build_chest.end_pos_list[ pname ];
if( not( end_pos )) then
error_msg = "Please mark the end position of your building first!";
else
local node = minetest.get_node( pos );
if( not( node ) or not( node.param2 )) then
error_msg = "A strange error happened.";
elseif( (node.param2 == 0 and end_pos.param2 ~= 2)
or (node.param2 == 1 and end_pos.param2 ~= 3)
or (node.param2 == 2 and end_pos.param2 ~= 0)
or (node.param2 == 3 and end_pos.param2 ~= 1)) then
error_msg = "One build chest needs to point to the front of your building, and "..
"the other one to the backside. This does not seem to be the case.";
elseif( (node.param2 == 2 and ( pos.x < end_pos.x or pos.z < end_pos.z )) -- x and z need to get larger
or (node.param2 == 3 and ( pos.x < end_pos.x or pos.z > end_pos.z )) -- x gets larger, z gets smaller
or (node.param2 == 0 and ( pos.x > end_pos.x or pos.z > end_pos.z )) -- x and z need to get smaller
or (node.param2 == 1 and ( pos.x > end_pos.x or pos.z < end_pos.z )) -- x gets smaller, z gets larger
) then
error_msg = "The end position does not fit to the orientation of this build chest.";
-- the chest takes up one node as well
elseif( math.abs(pos.x-end_pos.x)<1) then
error_msg = "Start- and end position share the same x value.";
elseif( math.abs(pos.z-end_pos.z)<1) then
error_msg = "Start- and end position share the same z value.";
-- all ok; we may proceed
else
error_msg = "";
build_chest.end_pos_list[ pname ].start_pos = {x=pos.x, y=pos.y, z=pos.z, param2=node.param2 };
end
fields.error_msg = error_msg;
end
-- in case the player selected the wrong chest for the save dialog
elseif( fields.abort_set_start_pos ) then
local end_pos = build_chest.end_pos_list[ pname ];
if( end_pos ) then
build_chest.end_pos_list[ pname ].start_pos = nil;
end
elseif( fields.save_as ) then
if( fields.save_as_p1 and fields.save_as_p2 and fields.save_as_filename ) then
-- restore p1 and p2, the positions of the area that is to be saved
local p1 = minetest.string_to_pos( fields.save_as_p1 );
local p2 = minetest.string_to_pos( fields.save_as_p2 );
-- take height changes into account
if( fields.save_as_height ) then
local new_height = tonumber( fields.save_as_height );
-- the new height is measured from the start position as well
if( new_height and new_height ~= (math.abs(p1.y-p2.y)+1)) then
p2.y = p1.y+new_height;
end
end
local burried = 0;
if( fields.save_as_yoff ) then
burried = tonumber( fields.save_as_yoff );
if( not( burried )) then
burried = 0;
end
-- the yoffset is applied to the start position
p1.y = p1.y + burried;
-- TODO: real negative values are not supported by analyze_mts_file
if( burried ~= 0 ) then
burried = -1*burried;
end
end
-- create an automatic filename if none is provided
local filename = fields.save_as_filename;
-- TODO: check the input if it contains only allowed chars (a-z, A-Z, 0-9, -, _, .)
if( not( filename )) then
filename = pname..'_'..tostring( p1 )..'_'..tostring(p2);
end
-- param2 needs to be translated init initial rotation as well
local node = minetest.get_node( pos );
if( node.param2 == 0 ) then
filename = filename..'_'..burried..'_90';
elseif( node.param2 == 3 ) then
filename = filename..'_'..burried..'_180';
elseif( node.param2 == 1 ) then
filename = filename..'_'..burried..'_0';
elseif( node.param2 == 2 ) then
filename = filename..'_'..burried..'_270';
end
-- TODO: forbid overwriting existing files?
local worldpath = minetest.get_worldpath();
local filename_complete = worldpath..'/schems/'..filename..'.mts';
handle_schematics.create_schematic_with_meta( p1, p2, filename );
-- store that we have saved this area
meta:set_string('saved_as_filename', filename);
meta:set_string('p1', minetest.pos_to_string( p1 ));
meta:set_string('p2', minetest.pos_to_string( p2 ));
-- forget the end position
build_chest.end_pos_list[ pname ] = nil;
-- add this chest to the menu
local worldnameparts = string.split( worldpath, '/worlds/' );
if( not( worldnameparts ) or #worldnameparts < 1 ) then
worldnameparts = {'unkown world'};
end
build_chest.add_entry( {'main','worlds', worldnameparts[ #worldnameparts], 'schems', filename, worldpath..'/schems/'..filename});
build_chest.add_building( worldpath..'/schems/'..filename, {scm=filename, typ='nn'});
minetest.chat_send_player( pname,
'Created schematic \''..tostring( filename )..'\'. Saved area from '..
minetest.pos_to_string( p1 )..' to '..
minetest.pos_to_string( p2 ));
end
end
-- the final build stage may offer further replacements
if( build_chest.stages_on_receive_fields ) then
build_chest.stages_on_receive_fields(pos, formname, fields, player, meta);
end
local formspec = build_chest.update_formspec( pos, 'main', player, fields );
-- add the position information so that we can show the formspec directly and still find out
-- which build chest was responsible
formspec = formspec.."field[20,20;0.1,0.1;pos2str;Pos;"..minetest.pos_to_string( pos ).."]";
-- save the formspec data to the chest
meta:set_string( 'formspec', formspec );
-- show the formspec directly to the player to make it react more smoothly
minetest.show_formspec( pname, "handle_schematics:build", formspec );
end
minetest.register_node("handle_schematics:build", { --TODO
description = "Building-Spawner",
tiles = {"default_chest_side.png", "default_chest_top.png", "default_chest_side.png",
"default_chest_side.png", "default_chest_side.png", "default_chest_front.png"},
-- drawtype = 'signlike',
-- paramtype = "light",
-- paramtype2 = "wallmounted",
-- sunlight_propagates = true,
-- walkable = false,
-- selection_box = {
-- type = "wallmounted",
-- },
paramtype2 = "facedir",
groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2},
legacy_facedir_simple = true,
after_place_node = function(pos, placer, itemstack)
-- TODO: check if placement is allowed
local meta = minetest.get_meta( pos );
meta:set_string( 'current_path', minetest.serialize( {} ));
meta:set_string( 'village', 'BEISPIELSTADT' ); --TODO
meta:set_string( 'village_pos', minetest.serialize( {x=1,y=2,z=3} )); -- TODO
meta:set_string( 'owner', placer:get_player_name());
meta:set_string('formspec', build_chest.update_formspec( pos, 'main', placer, {} ));
end,
on_receive_fields = function( pos, formname, fields, player )
return build_chest.on_receive_fields(pos, formname, fields, player);
end,
-- taken from towntest
allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
if from_list=="needed" or to_list=="needed" then return 0 end
return count
end,
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
if listname=="needed" then return 0 end
return stack:get_count()
end,
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
if listname=="needed" then return 0 end
-- if listname=="lumberjack" then return 0 end
return stack:get_count()
end,
can_dig = function(pos,player)
local meta = minetest.get_meta( pos );
local inv = meta:get_inventory();
local owner_name = meta:get_string( 'owner' );
local building_name = meta:get_string( 'building_name' );
local name = player:get_player_name();
if( not( meta ) or not( owner_name )) then
return true;
end
if( owner_name ~= name ) then
minetest.chat_send_player(name, "This building chest belongs to "..tostring( owner_name )..". You can't take it.");
return false;
end
if( building_name ~= nil and building_name ~= "" ) then
minetest.chat_send_player(name, "This building chest has been assigned to a building project. You can't take it away now.");
return false;
end
return true;
end,
-- have all materials been supplied and the remaining parts removed?
on_metadata_inventory_take = function(pos, listname, index, stack, player)
local meta = minetest.get_meta( pos );
local inv = meta:get_inventory();
local stage = meta:get_int( 'building_stage' );
if( inv:is_empty( 'needed' ) and inv:is_empty( 'main' )) then
if( stage==nil or stage < 6 ) then
build_chest.update_needed_list( pos, stage+1 ); -- request the material for the very first building step
else
-- TODO: show this update directly to the player via minetest.show_formspec( pname, formname, formspec );
meta:set_string( 'formspec', build_chest.update_formspec( pos, 'finished', player, {} ));
end
end
end,
on_metadata_inventory_put = function(pos, listname, index, stack, player)
return build_chest.on_metadata_inventory_put( pos, listname, index, stack, player );
end,
})
-- a player clicked on something in a formspec he was shown
handle_schematics.form_input_handler = function( player, formname, fields)
if(formname == "handle_schematics:build" and fields and fields.pos2str) then
local pos = minetest.string_to_pos( fields.pos2str );
build_chest.on_receive_fields(pos, formname, fields, player);
end
end
-- make sure we receive player input; needed for showing formspecs directly (which is in turn faster than just updating the node)
minetest.register_on_player_receive_fields( handle_schematics.form_input_handler );

View File

@ -0,0 +1,164 @@
local build_chest_add_files_to_menu_from_directory = function( schem, path, entry_name, backup_name, menu_path_list)
-- we need the filename without extension (*.mts, *.we, *.wem)
local schemname = schem;
local i = string.find( schem, '.mts', -4 );
if( i ) then
schemname = string.sub( schem, 1, i-1 );
else
i = string.find( schem, '.we', -3 );
if( i ) then
schemname = string.sub( schem, 1, i-1 );
else
i = string.find( schem, '.wem', -4 );
if( i ) then
schemname = string.sub( schem, 1, i-1 );
else
return;
end
end
end
-- only add known file types
if( not( schemname )) then
return;
end
i = string.find( schemname, 'backup_' );
menu_path_list[ #menu_path_list+1 ] = schemname;
menu_path_list[ #menu_path_list+1 ] = path..schemname;
-- probably one of those automatic backups of the landscape
if( i and i==1 and backup_name) then
menu_path_list[1] = backup_name;
-- normal entry
else
menu_path_list[1] = entry_name;
end
build_chest.add_entry( menu_path_list);
build_chest.add_building( path..schemname, {scm=schemname, typ='nn'});
end
-- search for mods and modpacks containing schematics in any form
local build_chest_check_all_directories_mods_and_modpacks = function( path, menu_title, gamename )
local d2 = minetest.get_dir_list( path..'/mods', true );
for _,modname in ipairs( d2 ) do
local d3 = minetest.get_dir_list( path..'/mods/'..modname, true );
for _,subdir in ipairs( d3 ) do
if( subdir ~= 'textures' and subdir ~= 'sounds' and subdir ~= 'models' and subdir ~= '.git' and subdir ~= 'locale') then
local d4 = minetest.get_dir_list( path..'/mods/'..modname..'/'..subdir, false );
for _,filename in ipairs( d4 ) do
build_chest_add_files_to_menu_from_directory(
filename,
path..'/mods/'..modname..'/'..subdir..'/',
menu_title,
nil,
{'OVERWRITE THIS', gamename, modname} );
end
-- it might be a modpack
d4 = minetest.get_dir_list( path..'/mods/'..modname..'/'..subdir, true );
for _,subsubdir in ipairs( d4 ) do
if( subsubdir ~= 'textures' and subsubdir ~= 'sounds' and subsubdir ~= 'models' and subsubdir ~= '.git' and subsubdir ~= 'locale') then
local d5 = minetest.get_dir_list( path..'/mods/'..modname..'/'..subdir..'/'..subsubdir, false );
for _,filename in ipairs( d5 ) do
build_chest_add_files_to_menu_from_directory(
filename,
path..'/mods/'..modname..'/'..subdir..'/'..subsubdir..'/',
menu_title,
nil,
-- folders from modpacks get marked with a *..*
{'OVERWRITE THIS', gamename, '*'..subdir..'*'} );
end
end
end
end
end
end
end
local build_chest_check_all_directories = function()
-- find the name of the directory directly above the current worldpath
local worldpath = minetest.get_worldpath();
local p = 1;
local last_found = 1;
while( last_found ) do
p = last_found;
last_found = string.find( worldpath, '/', last_found+1 );
end
-- abort on Windows
if( p == 1 ) then
return;
end
worldpath = string.sub( worldpath, 1, p );
--[[
local p = 1;
while( not( string.find( worldpath, '/', -1*p ))) do
p = p+1;
end
local found = 1;
for p=string.len( worldpath ),1,-1 do
if( p>found
and (string.byte( worldpath, p )=='/'
or string.byte( worldpath, p )=='\\')) then
found = p;
end
end
--]]
worldpath = string.sub( worldpath, 1, string.len( worldpath )-p );
-- locate .mts, .wem and .we files in the worlds/WORLDNAME/schems/* folders
local d1 = minetest.get_dir_list( worldpath, true );
for _,worldname in ipairs( d1 ) do
-- get list of subdirectories
local d2 = minetest.get_dir_list( worldpath..'/'..worldname, true );
for _,subdir in ipairs( d2 ) do
if( subdir=='schems' ) then
local d3 = minetest.get_dir_list( worldpath..'/'..worldname..'/schems', false );
for _,filename in ipairs( d3 ) do
build_chest_add_files_to_menu_from_directory(
filename,
worldpath..'/'..worldname..'/schems/',
'import from world',
'landscape backups',
{'OVERWRITE THIS', worldname });
end
end
end
end
local main_path = string.sub( worldpath, 1, string.len(worldpath)-string.len('/worlds'));
-- search in MODS/* subfolder
build_chest_check_all_directories_mods_and_modpacks( main_path, 'import from mod', 'mods' );
-- search in all GAMES/* folders for mods containing schematics
local game_path = main_path..'/games';
d1 = minetest.get_dir_list( game_path, true );
for _,gamename in ipairs( d1 ) do
build_chest_check_all_directories_mods_and_modpacks( game_path..'/'..gamename, 'import from game', gamename );
end
end
-- TODO: hopfefully, security will get more relaxed regarding reading directories in the future
-- if security is enabled, our options to get schematics are a bit limited
if( minetest.setting_getbool( 'secure.enable_security' )) then
local worldpath = minetest.get_worldpath();
local d3 = minetest.get_dir_list( worldpath..'/schems', false );
if( d3 ) then
for _,filename in ipairs( d3 ) do
build_chest_add_files_to_menu_from_directory(
filename,
worldpath..'/schems/',
'import from world',
'landscape backups',
{'OVERWRITE THIS', '-current world-' });
end
end
else
-- check worlds, mods and games folders for schematics and add them to the menu
build_chest_check_all_directories()
end

View File

@ -0,0 +1,58 @@
build_chest.add_files_to_menu = function( path, add_path )
local file,err = io.open( path, "rb")
if (file == nil) then
return;
end
local text = file:read("*a");
file:close();
for schem_file_name in string.gmatch(text, "([^\r\n]*)[\r\n]*") do
if( schem_file_name and schem_file_name ~= "" ) then
local help = string.split( schem_file_name, '/', true, -1, false);
local i = #help;
local found = 1;
-- search from the end of the file name for the first occourance of "mods" or "worlds"
-- as that will be the path where we will put it into the menu
while (i>1 and found==1) do
if( help[i]=='mods' or help[i]=='worlds' ) then
found = i;
end
i = i-1;
end
local name = help[#help];
local length1 = string.len( name );
local length2 = string.len( schem_file_name );
-- remove the file name extension
if( string.sub( name, -4 )=='.mts' ) then
name = string.sub( name, 1, length1-4 );
schem_file_name = string.sub( schem_file_name, 1, length2-4 );
elseif( string.sub( name, -3 )=='.we' ) then
name = string.sub( name, 1, length1-3 );
schem_file_name = string.sub( schem_file_name, 1, length2-3 );
elseif( string.sub( name, -10 )==".schematic" ) then
name = string.sub( name, 1, length1-10);
schem_file_name = string.sub( schem_file_name, 1, length2-10);
end
help[#help] = name;
-- build the new menu path
local menu_path = {'main'};
for j=(i+1),#help do
table.insert( menu_path, help[j] );
end
schem_file_name = add_path..schem_file_name;
table.insert( menu_path, schem_file_name );
build_chest.add_entry( menu_path );
build_chest.add_building( schem_file_name, {scm=help[#help], typ='nn'});
end
end
end
build_chest.add_files_to_menu( minetest.get_modpath( minetest.get_current_modname()).."/list_of_schematics.txt", "");

View File

@ -0,0 +1,256 @@
-------------------------------------------------------------
--- contains the handling of replacements for the build chest
-------------------------------------------------------------
-- internal function
build_chest.replacements_get_extra_buttons = function( group, name, types_found_list, button_name, extra_buttons )
-- find out if there are any nodes that may need a group replacement
local found_type = "";
for k,w in ipairs( replacements_group[ group ].all ) do
-- we have found the full block of that group type
if( name == w ) then
found_type = w;
-- no primary node found; there may still be subordinate types
else
for nr,t in ipairs( replacements_group[ group ].data[ w ] ) do
if( name==t and not( types_found_list[ w ])) then
found_type = w;
end
end
end
end
if( found_type ~= "" and not( types_found_list[ found_type ])) then
extra_buttons.offset = extra_buttons.offset + 1;
extra_buttons.text = extra_buttons.text.."button[9.9,"..
tostring( (extra_buttons.offset*0.9)+2.8 )..";3.0,0.5;"..
tostring( button_name )..";"..
minetest.formspec_escape( found_type ).."]";
-- remember that we found and offered this type already; avoid duplicates
types_found_list[ found_type ] = 1;
end
return extra_buttons;
end
build_chest.replacements_get_list_formspec = function( pos, selected_row )
if( not( pos )) then
return "";
end
local meta = minetest.env:get_meta( pos );
local replacements = minetest.deserialize( meta:get_string( 'replacements' ));
local building_name = meta:get_string( 'building_name' );
if( not( building_name ) or not( build_chest.building[ building_name ])) then
return "";
end
local replace_row = meta:get_int('replace_row');
local formspec = "tableoptions[" ..
"color=#ff8000;" ..
"background=#0368;" ..
"border=true;" ..
--"highlight=#00008040;" ..
"highlight=#aaaaaaaa;" ..
"highlight_text=#7fffff]" ..
"tablecolumns[" ..
"color;" ..
"text,width=1,align=right;" ..
"color;" ..
"text,width=5;" ..
"color;" ..
"text,width=1;" ..
"color;" ..
"text,width=5]" ..
-- "tabheader["..
-- "1,1;columns;amount,original material,,target material;1;true;true]"..
"table["..
"0.5,2.7;9.4,6.8;build_chest_replacements;";
local j=1;
local may_proceed = true;
local replace_row_material = nil;
local replace_row_with = "";
-- make sure the statistic has been created
if( not( build_chest.building[ building_name ].statistic )) then
if( not( build_chest.read_building( building_name ))) then
return "label[2,2;Error: Unable to read building file.]";
end
end
-- used for setting wood type or plant(farming) type etc.
local extra_buttons = { text = "", offset = 0};
-- there may be wood types that only occour as stairs and/or slabs etc., without full blocks
local types_found_list_wood = {};
local types_found_list_farming = {};
local types_found_list_roof = {};
local not_the_first_entry = false;
for i,v in ipairs( build_chest.building[ building_name ].statistic ) do
local name = build_chest.building[ building_name ].nodenames[ v[1]];
-- nodes that are to be ignored do not need to be replaced
if( name ~= 'air' and name ~= 'ignore' and name ~= 'mg:ignore' and v[2] and v[2]>0) then
local anz = v[2];
-- find out if this node name gets replaced
local repl = name;
for j,r in ipairs( replacements ) do
if( r and r[1]==name ) then
repl = r[2];
end
end
-- avoid empty lines at the end
if( not_the_first_entry ) then
formspec = formspec..',';
end
formspec = formspec..'#fff,'..tostring( anz )..',';
if( name == repl and repl and minetest.registered_nodes[ repl ]) then
formspec = formspec.."#0ff,,#fff,,";
else
if( name and minetest.registered_nodes[ name ] ) then
formspec = formspec.."#0f0,"; -- green
else
formspec = formspec.."#ff0,"; -- yellow
end
formspec = formspec..name..',#fff,'..minetest.formspec_escape('-->')..',';
end
if( repl and (minetest.registered_nodes[ repl ] or repl=='air') ) then
formspec = formspec.."#0f0,"..repl; -- green
else
formspec = formspec.."#ff0,?"; -- yellow
may_proceed = false; -- we need a replacement for this material
end
if( j == replace_row ) then
replace_row_material = name;
if( repl ~= name ) then
replace_row_with = repl;
end
end
extra_buttons = build_chest.replacements_get_extra_buttons( 'wood', name, types_found_list_wood, 'set_wood', extra_buttons );
extra_buttons = build_chest.replacements_get_extra_buttons( 'farming', name, types_found_list_farming, 'set_farming', extra_buttons );
extra_buttons = build_chest.replacements_get_extra_buttons( 'roof', name, types_found_list_farming, 'set_roof', extra_buttons );
j=j+1;
not_the_first_entry = true;
end
end
formspec = formspec.."]";
-- add the proceed-button as soon as all unkown materials have been replaced
if( may_proceed ) then
formspec = formspec.."button[9.9,9.0;2.0,0.5;proceed_with_scaffolding;Proceed]";
else
formspec = formspec.."button[9.9,9.0;3.2,0.5;replace_rest_with_air;Suggest air for unknown]";
end
formspec = formspec.."button[9.9,1.0;2.0,0.5;preview;Preview]";
if( extra_buttons.text and extra_buttons.text ~= "" ) then
formspec = formspec..extra_buttons.text..
"label[9.9,2.8;Replace by type:]";
end
if( replace_row_material ) then
formspec = formspec..
"label[0.5,2.1;Replace "..
minetest.formspec_escape( replace_row_material ).."]"..
"label[6.5,2.1;with:]"..
"field[7.5,2.4;4,0.5;replace_row_with;;"..
minetest.formspec_escape( replace_row_with ).."]"..
"field[-10,-10;0.1,0.1;replace_row_material;;"..
minetest.formspec_escape( replace_row_material ).."]"..
"button[11.1,2.1;1,0.5;store_replacement;Store]";
end
return formspec;
end
-- set replacements for all unknown nodes to air so that the building can be spawned
build_chest.replacements_replace_rest_with_air = function( pos, meta )
local building_name = meta:get_string( 'building_name' );
if( not( building_name ) or not( build_chest.building[ building_name ])) then
return;
end
local replacements_orig = minetest.deserialize( meta:get_string( 'replacements' ));
for i,v in ipairs( build_chest.building[ building_name ].statistic ) do
local name = build_chest.building[ building_name ].nodenames[ v[1]];
-- nodes that are to be ignored do not need to be replaced
if( name ~= 'air' and name ~= 'ignore' and name ~= 'mg:ignore' and v[2] and v[2]>0) then
-- find out if this node name gets replaced
local repl = name;
for j,r in ipairs( replacements_orig ) do
if( r and r[1]==name ) then
repl = r[2];
-- set replacements for inexisting nodes to air
if( not( minetest.registered_nodes[ repl ] )) then
r[2] = 'air';
end
end
end
-- replace nodes that do not exist with air
if( not( repl ) or not( minetest.registered_nodes[ repl ])) then
table.insert( replacements_orig, { name, 'air' });
end
end
end
-- store the new set of replacements
meta:set_string( 'replacements', minetest.serialize( replacements_orig ));
end
build_chest.replacements_apply = function( pos, meta, old_material, new_material )
-- a new value has been entered - we do not need to remember the row any longer
meta:set_int('replace_row', 0 );
local found = false;
-- only accept replacements which can actually be placed
if( new_material=='air' or minetest.registered_nodes[ new_material ] ) then
local replacements_orig = minetest.deserialize( meta:get_string( 'replacements' ));
for i,v in ipairs(replacements_orig) do
if( v and v[1]==old_material ) then
v[2] = new_material;
found = true;
end
end
if( not( found )) then
table.insert( replacements_orig, { old_material, new_material });
end
-- store the new set of replacements
meta:set_string( 'replacements', minetest.serialize( replacements_orig ));
end
end
build_chest.replacements_get_group_list_formspec = function( pos, group, button_name )
local formspec = "";
for i,v in ipairs( replacements_group[ group ].found ) do
formspec = formspec.."item_image_button["..tostring(((i-1)%8)+1)..","..
tostring(3+math.floor((i-1)/8))..";1,1;"..
tostring( v )..";"..tostring( button_name )..";"..tostring(i).."]";
end
return formspec;
end
build_chest.replacements_apply_for_group = function( pos, meta, group, selected, old_material )
local nr = tonumber( selected );
if( not(nr) or nr <= 0 or nr > #replacements_group[ group ].found ) then
return;
end
local new_material = replacements_group[ group ].found[ nr ];
if( old_material and old_material == new_material ) then
return;
end
local replacements = minetest.deserialize( meta:get_string( 'replacements' ));
if( not( replacements )) then
replacements = {};
end
replacements_group[ group ].replace_material( replacements, old_material, new_material );
-- store the new set of replacements
meta:set_string( 'replacements', minetest.serialize( replacements ));
end

View File

@ -0,0 +1,248 @@
build_chest.preview_image_draw_tile = function( content_id, image, x, z, dx, dz, tile_nr )
if( not( image )) then
local node_name = minetest.get_name_from_content_id( content_id );
if( not( node_name )) then
return '';
end
local node_def = minetest.registered_nodes[ node_name ];
if( not( node_def )) then
return '';
end
local tiles = node_def.tiles;
if( not( tiles )) then
tiles = node_def.tile_images;
end
local tile = nil;
if( tiles ~= nil ) then
if( not(tile_nr) or tile_nr > #tiles or tile_nr < 1 ) then
tile_nr = 1;
end
tile = tiles[tile_nr];
end
if type(tile)=="table" then
tile=tile["name"]
end
image = tile;
if( not( image )) then
image = "unknown_object.png";
end
end
return "image["..tostring(x)..",".. tostring(z) ..";"..dx..','..dz..";" .. image .."]";
end
-- creates a 2d preview image (or rather, the data structure for it) of the building
-- internal function
build_chest.preview_image_create_one_view = function( data, side )
local params = {1, data.size.x, 1, 1, data.size.z, 1, 0, 0};
if( side==1 ) then
params = {1, data.size.x, 1, 1, data.size.z, 1, 0, 0};
elseif( side==2 ) then
params = {1, data.size.z, 1, 1, data.size.x, 1, 1, 1};
elseif( side==3 ) then
params = {1, data.size.x, 1, data.size.z, 0, -1, 0, 1};
elseif( side==4 ) then
params = {1, data.size.z, 1, data.size.x, 0, -1, 1, 0};
end
-- do not create preview images for buildings that are too big
if( params[2] * params[4] > 2500 ) then
return nil;
end
local preview = {};
for y = 1, data.size.y do
preview[ y ] = {};
for x = params[1], params[2], params[3] do
local found = nil;
local z = params[4];
local target_x = x;
if( params[8]==1 ) then
target_x = math.max( params[1],params[2] )- x;
end
while( not( found ) and z~= params[5]) do
local node = -1;
if( params[7]==0 ) then
node = data.scm_data_cache[y][x][z];
else
node = data.scm_data_cache[y][z][x];
end
if( node and node[1]
and data.nodenames[ node[1] ]
and data.nodenames[ node[1] ] ~= 'air'
and data.nodenames[ node[1] ] ~= 'ignore'
and data.nodenames[ node[1] ] ~= 'mg:ignore'
and data.nodenames[ node[1] ] ~= 'default:torch' ) then
-- a preview node is only set if there's no air there
preview[y][target_x] = node[1];
found = 1;
end
z = z+params[6];
end
if( not( found )) then
preview[y][target_x] = -1;
end
end
end
return preview;
end
-- internal function
build_chest.preview_image_create_view_from_top = function( data )
-- no view from top if the image is too big
if( data.size.z * data.size.y > 2500 ) then
return nil;
end
local preview = {};
for z = 1, data.size.z do
preview[ z ] = {};
for x = 1, data.size.x do
local found = nil;
local y = data.size.y;
while( not( found ) and y > 1) do
local node = data.scm_data_cache[y][x][z];
if( node and node[1]
and data.nodenames[ node[1] ]
and data.nodenames[ node[1] ] ~= 'air'
and data.nodenames[ node[1] ] ~= 'ignore'
and data.nodenames[ node[1] ] ~= 'mg:ignore'
and data.nodenames[ node[1] ] ~= 'default:torch' ) then
-- a preview node is only set if there's no air there
preview[z][x] = node[1];
found = 1;
end
y = y-1;
end
if( not( found )) then
preview[z][x] = -1;
end
end
end
return preview;
end
-- function called by the build chest to display one view
build_chest.preview_image_formspec = function( building_name, replacements, side_name )
if( not( building_name )
or not( build_chest.building[ building_name ] )
or not( build_chest.building[ building_name ].preview )) then
return "";
end
local side_names = {"front","right","back","left","top"};
local side = 1;
for i,v in ipairs( side_names ) do
if( side_name and side_name==v ) then
side = i;
end
end
local formspec = "";
for i=1,5 do
if( i ~= side ) then
formspec = formspec.."button["..tostring(3.3+1.2*(i-1))..
",2.2;1,0.5;preview;"..side_names[i].."]";
else
formspec = formspec.."label["..tostring(3.3+1.2*(i-1))..",2.2;"..side_names[i].."]";
end
end
local data = build_chest.building[ building_name ];
-- the draw_tile function is based on content_id
local content_ids = {};
for i,v in ipairs( data.nodenames ) do
local found = false;
for j,w in ipairs( replacements ) do
if( w and w[1] and w[1]==v) then
found = true;
if( minetest.registered_nodes[ w[2]] ) then
content_ids[ i ] = minetest.get_content_id( w[2] );
end
end
end
if( not( found )) then
if( minetest.registered_nodes[ v ]) then
content_ids[ i ] = minetest.get_content_id( v );
elseif( v ~= 'air' ) then
content_ids[ i ] = -1;
end
end
end
local scale = 0.5;
local tile_nr = 3; -- view from the side
if( side ~= 5 ) then
local scale_y = 6.0/data.size.y;
local scale_z = 10.0/data.size.z;
if( scale_y > scale_z) then
scale = scale_z;
else
scale = scale_y;
end
else
local scale_x = 10.0/data.size.x; -- only relevant for view from top
local scale_z = 6.0/data.size.z;
if( scale_x > scale_z) then
scale = scale_z;
else
scale = scale_x;
end
tile_nr = 1; -- view from top
end
if( not( side )) then
side = 1;
end
local preview = data.preview[ side ];
if( not( preview )) then
formspec = formspec.."label[3,3;Sorry, this schematic is too big for a preview image.]";
return formspec;
end
for y,y_values in ipairs( preview ) do
for l,v in ipairs( y_values ) do
-- air, ignore and mg:ignore are not stored
if( v and content_ids[ v ]==-1 ) then
formspec = formspec..build_chest.preview_image_draw_tile( nil, "unknown_node.png", (l*scale), 9-(y*scale), scale*1.3, scale*1.2, tile_nr);
elseif( v and v>0 and content_ids[v]) then
formspec = formspec..build_chest.preview_image_draw_tile( content_ids[ v ], nil, (l*scale), 9-(y*scale), scale*1.3, scale*1.2, tile_nr);
end
end
end
return formspec;
end
-- create all five preview images
build_chest.preview_image_create_views = function( res, orients )
-- create a 2d overview image (or rather, the data structure for it)
local preview = {
build_chest.preview_image_create_one_view( res, 2 ),
build_chest.preview_image_create_one_view( res, 1 ),
build_chest.preview_image_create_one_view( res, 4 ),
build_chest.preview_image_create_one_view( res, 3 )};
-- the building might be stored in rotated form
if( orients and #orients and orients[1] ) then
if( orients[1]==1 ) then
preview = {preview[2],preview[3],preview[4],preview[1]};
elseif( orients[1]==2 ) then
preview = {preview[3],preview[4],preview[1],preview[2]};
elseif( orients[1]==3 ) then
preview = {preview[4],preview[1],preview[2],preview[3]};
end
end
-- ...and add a preview image from top
preview[5] = build_chest.preview_image_create_view_from_top( res );
return preview;
end
-- this function makes sure that the building will always extend to the right and in front of the build chest

View File

@ -0,0 +1,23 @@
default?
doors?
farming?
wool?
stairs?
cottages?
moretrees?
trees?
forest?
dryplants?
cavestuff?
snow?
moresnow?
darkage?
ethereal?
deco?
metals?
grounds?
moreblocks?
bell?
mobf_trader?
docfarming?
mccompat?

View File

@ -0,0 +1,162 @@
handle_schematics.sort_pos_get_size = function( p1, p2 )
local res = {x=p1.x, y=p1.y, z=p1.z,
sizex = math.abs( p1.x - p2.x )+1,
sizey = math.abs( p1.y - p2.y )+1,
sizez = math.abs( p1.z - p2.z )+1};
if( p2.x < p1.x ) then
res.x = p2.x;
end
if( p2.y < p1.y ) then
res.y = p2.y;
end
if( p2.z < p1.z ) then
res.z = p2.z;
end
return res;
end
local handle_schematics_get_meta_table = function( pos, all_meta, start_pos )
local m = minetest.get_meta( pos ):to_table();
local empty_meta = true;
-- the inventory part contains functions and cannot be fed to minetest.serialize directly
local invlist = {};
local count_inv = 0;
local inv_is_empty = true;
for name, list in pairs( m.inventory ) do
invlist[ name ] = {};
count_inv = count_inv + 1;
for i, stack in ipairs(list) do
if( not( stack:is_empty())) then
invlist[ name ][ i ] = stack:to_string();
empty_meta = false;
inv_is_empty = false;
end
end
end
-- the fields part at least is unproblematic
local count_fields = 0;
if( empty_meta and m.fields ) then
for k,v in pairs( m.fields ) do
empty_meta = false;
count_fields = count_fields + 1;
end
end
-- ignore default:sign_wall without text on it
if( count_inv==0
and count_fields<=3 and m.fields.formspec and m.fields.infotext
and m.fields.formspec == "field[text;;${text}]"
and m.fields.infotext == "\"\"") then
-- also consider signs empty if their text has been set once and deleted afterwards
if( not( m.fields.text ) or m.fields.text == "" ) then
print('SKIPPING empty sign AT '..minetest.pos_to_string( pos)..' while saving metadata.');
empty_meta = true;
end
elseif( count_inv > 0 and inv_is_empty
and count_fields>0 and m.fields.formspec ) then
local n = minetest.get_node( pos );
if( n and n.name
and (n.name=='default:chest' or n.name=='default:chest_locked' or n.name=='default:bookshelf'
or n.name=='default:furnace' or n.name=='default:furnace_active'
or n.name=='cottages:shelf' or n.name=='cottages:anvil' or n.name=='cottages:threshing_floor' )) then
print('SKIPPING empty '..tostring(n.name)..' AT '..minetest.pos_to_string( pos )..' while saving metadata.');
empty_meta = true;
end
end
-- only save if there is something to be saved
if( not( empty_meta )) then
-- positions are stored as relative positions
all_meta[ #all_meta+1 ] = {
x=pos.x-start_pos.x,
y=pos.y-start_pos.y,
z=pos.z-start_pos.z,
fields = m.fields,
inventory = invlist};
end
end
-- reads metadata values from start_pos to end_pos and stores them in a file
handle_schematics.save_meta = function( start_pos, end_pos, filename )
local all_meta = {};
local p = handle_schematics.sort_pos_get_size( start_pos, end_pos );
if( minetest.find_nodes_with_meta ) then
for _,pos in ipairs( minetest.find_nodes_with_meta( start_pos, end_pos )) do
handle_schematics_get_meta_table( pos, all_meta, p );
end
else
for x=p.x, p.x+p.sizex do
for y=p.y, p.y+p.sizey do
for z=p.z, p.z+p.sizez do
handle_schematics_get_meta_table( {x=x-p.x, y=y-p.y, z=z-p.z}, all_meta, p );
end
end
end
end
if( #all_meta > 0 ) then
save_restore.save_data( 'schems/'..filename..'.meta', all_meta );
end
end
-- all metadata values will be deleted when this function is called,
-- making the area ready for new voxelmanip/schematic data
handle_schematics.clear_meta = function( start_pos, end_pos )
local empty_meta = { inventory = {}, fields = {} };
if( minetest.find_nodes_with_meta ) then
for _,pos in ipairs( minetest.find_nodes_with_meta( start_pos, end_pos )) do
local meta = minetest.get_meta( pos );
meta:from_table( empty_meta );
end
end
end
-- restore metadata from file
-- TODO: use relative instead of absolute positions (already done for .we files)
-- TODO: handle mirror
handle_schematics.restore_meta = function( filename, all_meta, start_pos, end_pos, rotate, mirror )
if( not( all_meta ) and filename ) then
all_meta = save_restore.restore_data( 'schems/'..filename..'.meta' );
end
for _,pos in ipairs( all_meta ) do
local p = {};
if( rotate == 0 ) then
p = {x=start_pos.x+pos.x-1, y=start_pos.y+pos.y-1, z=start_pos.z+pos.z-1};
elseif( rotate == 1 ) then
p = {x=start_pos.x+pos.z-1, y=start_pos.y+pos.y-1, z=end_pos.z -pos.x+1};
elseif( rotate == 2 ) then
p = {x=end_pos.x -pos.x+1, y=start_pos.y+pos.y-1, z=end_pos.z -pos.z+1};
elseif( rotate == 3 ) then
p = {x=end_pos.x -pos.z+1, y=start_pos.y+pos.y-1, z=start_pos.z+pos.x-1};
end
local meta = minetest.get_meta( p );
meta:from_table( {inventory = pos.inventory, fields = pos.fields });
end
end
-- return true on success; will overwrite existing files
handle_schematics.create_schematic_with_meta = function( p1, p2, base_filename )
-- create directory for the schematics (same path as WorldEdit uses)
save_restore.create_directory( '/schems' );
local complete_filename = minetest.get_worldpath()..'/schems/'..base_filename..'.mts';
-- actually create the schematic
minetest.create_schematic( p1, p2, nil, complete_filename, nil);
-- save metadata; the file will only be created if there is any metadata that is to be saved
handle_schematics.save_meta( p1, p2, base_filename );
return save_restore.file_exists( complete_filename );
end

View File

@ -0,0 +1,107 @@
-- helper function; sorts by the second element of the table
local function handle_schematics_comp(a,b)
if (a[2] > b[2]) then
return true;
end
end
-- create a statistic about how frequent each node name occoured
handle_schematics.count_nodes = function( data )
local statistic = {};
-- make sure all node names are counted (air may sometimes be included without occouring)
for id=1, #data.nodenames do
statistic[ id ] = { id, 0};
end
for z = 1, data.size.z do
for y = 1, data.size.y do
for x = 1, data.size.x do
local a = data.scm_data_cache[y][x][z];
if( a ) then
local id = 0;
if( type( a )=='table' ) then
id = a[1];
else
id = a;
end
if( statistic[ id ] and statistic[ id ][ 2 ] ) then
statistic[ id ] = { id, statistic[ id ][ 2 ]+1 };
end
end
end
end
end
table.sort( statistic, handle_schematics_comp );
return statistic;
end
-- this function makes sure that the building will always extend to the right and in front of the build chest
handle_schematics.translate_param2_to_rotation = function( param2, mirror, start_pos, orig_max, rotated, burried, orients, yoff )
-- mg_villages stores available rotations of buildings in orients={0,1,2,3] format
if( orients and #orients and orients[1]~=0) then
-- reset rotated - else we'd apply it twice
rotated = 0;
if( orients[1]==1 ) then
rotated = rotated + 90;
elseif( orients[1]==2 ) then
rotated = rotated + 180;
elseif( orients[1]==3 ) then
rotated = rotated + 270;
end
if( rotated >= 360 ) then
rotated = rotated % 360;
end
end
local max = {x=orig_max.x, y=orig_max.y, z=orig_max.z};
-- if the schematic has been saved in a rotated way, swapping x and z may be necessary
if( rotated==90 or rotated==270) then
max.x = orig_max.z;
max.z = orig_max.x;
end
-- the building may have a cellar or something alike
if( burried and burried ~= 0 and yoff == nil ) then
start_pos.y = start_pos.y - burried;
end
-- make sure the building always extends forward and to the right of the player
local rotate = 0;
if( param2 == 0 ) then rotate = 270; if( mirror==1 ) then start_pos.x = start_pos.x - max.x + max.z; end -- z gets larger
elseif( param2 == 1 ) then rotate = 0; start_pos.z = start_pos.z - max.z; -- x gets larger
elseif( param2 == 2 ) then rotate = 90; start_pos.z = start_pos.z - max.x;
if( mirror==0 ) then start_pos.x = start_pos.x - max.z; -- z gets smaller
else start_pos.x = start_pos.x - max.x; end
elseif( param2 == 3 ) then rotate = 180; start_pos.x = start_pos.x - max.x; -- x gets smaller
end
if( param2 == 1 or param2 == 0) then
start_pos.z = start_pos.z + 1;
elseif( param2 == 1 or param2 == 2 ) then
start_pos.x = start_pos.x + 1;
end
if( param2 == 1 ) then
start_pos.x = start_pos.x + 1;
end
rotate = rotate + rotated;
-- make sure the rotation does not reach or exceed 360 degree
if( rotate >= 360 ) then
rotate = rotate - 360;
end
-- rotate dimensions when needed
if( param2==0 or param2==2) then
local tmp = max.x;
max.x = max.z;
max.z = tmp;
end
return { rotate=rotate, start_pos = {x=start_pos.x, y=start_pos.y, z=start_pos.z},
end_pos = {x=(start_pos.x+max.x-1), y=(start_pos.y+max.y-1), z=(start_pos.z+max.z-1) },
max = {x=max.x, y=max.y, z=max.z}};
end

View File

@ -0,0 +1,61 @@
handle_schematics = {}
handle_schematics.modpath = minetest.get_modpath( "handle_schematics");
-- adds worldedit_file.* namespace
-- deserialize worldedit savefiles
dofile(handle_schematics.modpath.."/worldedit_file.lua")
-- uses handle_schematics.* namespace
-- reads and analyzes .mts files (minetest schematics)
dofile(handle_schematics.modpath.."/analyze_mts_file.lua")
-- reads and analyzes worldedit files
dofile(handle_schematics.modpath.."/analyze_we_file.lua")
-- reads and analyzes Minecraft schematic files
dofile(handle_schematics.modpath.."/translate_nodenames_for_mc_schematic.lua")
dofile(handle_schematics.modpath.."/analyze_mc_schematic_file.lua")
-- handles rotation and mirroring
dofile(handle_schematics.modpath.."/rotate.lua")
-- count nodes, take param2 into account for rotation etc.
dofile(handle_schematics.modpath.."/handle_schematics_misc.lua")
-- store and restore metadata
dofile(handle_schematics.modpath.."/save_restore.lua");
dofile(handle_schematics.modpath.."/handle_schematics_meta.lua");
-- uses replacements_group.* namespace
-- these functions are responsible for the optional dependencies; they check
-- which nodes are available and may be offered as possible replacements
replacements_group = {};
-- the replacement groups do add some non-ground nodes; needed by mg_villages
replacements_group.node_is_ground = {}
dofile(handle_schematics.modpath.."/replacements_wood.lua")
dofile(handle_schematics.modpath.."/replacements_realtest.lua")
dofile(handle_schematics.modpath.."/replacements_farming.lua")
dofile(handle_schematics.modpath.."/replacements_roof.lua")
-- transforms the replacement list into a table;
-- also creates a replacement if needed and replaces default:torch
dofile(handle_schematics.modpath.."/replacements_get_table.lua")
-- uses build_chest.* namespace
-- a chest for spawning buildings manually
dofile(handle_schematics.modpath.."/build_chest.lua")
-- makes the replacements from replacements_group.* available to the build chest
dofile(handle_schematics.modpath.."/build_chest_handle_replacements.lua");
-- creates 2d previews of the schematic from left/right/back/front/top
dofile(handle_schematics.modpath.."/build_chest_preview_image.lua");
-- reads a file and adds the files listed there as menu entries
dofile(handle_schematics.modpath.."/build_chest_add_schems_from_file.lua");
-- locate schematics through directories
dofile(handle_schematics.modpath.."/build_chest_add_schems_by_directory.lua");
-- the main functionality of the mod;
-- provides the function handle_schematics.place_building_from_file
-- (and also place_buildings for mg_villages)
dofile(handle_schematics.modpath.."/place_buildings.lua")
-- dofile(handle_schematics.modpath.."/fill_chest.lua")
dofile(handle_schematics.modpath.."/nodes.lua")

View File

@ -0,0 +1,24 @@
---------------------------------------------------------------------------------------
-- helper node that is used during construction of a house; scaffolding
---------------------------------------------------------------------------------------
minetest.register_node("handle_schematics:support", {
description = "support structure for buildings",
tiles = {"handle_schematics_support.png"},
groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2},
walkable = false,
climbable = true,
paramtype = "light",
drawtype = "plantlike",
})
minetest.register_craft({
output = "handle_schematics:support",
recipe = {
{"default:stick", "", "default:stick", }
}
})

View File

@ -0,0 +1,835 @@
-- TODO: this function also occours in replacements.lua
handle_schematics.get_content_id_replaced = function( node_name, replacements )
if( not( node_name ) or not( replacements ) or not(replacements.table )) then
return minetest.get_content_id( 'ignore' );
end
if( replacements.table[ node_name ]) then
return minetest.get_content_id( replacements.table[ node_name ] );
else
return minetest.get_content_id( node_name );
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)
handle_schematics.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
handle_schematics.mg_drop_moresnow = function( x, z, y_top, y_bottom, a, data, param2_data)
-- this only works if moresnow is installed
if( not( handle_schematics.moresnow_installed )) then
return;
end
local y = y_top;
local node_above = handle_schematics.get_node_somehow( x, y+1, z, a, data, param2_data );
local node_below = nil;
while( y >= y_bottom ) do
node_below = handle_schematics.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
-- turn water into ice, but don't drop snow on it
if( node_below.content == minetest.get_content_id("default:water_source")
or node_below.content == minetest.get_content_id("default:river_water_source")) then
return { height = y, suggested = {new_id = minetest.get_content_id('default:ice'), param2 = 0 }};
end
-- 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)
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 = handle_schematics.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
-- helper function for generate_building
-- places a marker that allows players to buy plots with houses on them (in order to modify the buildings)
local function generate_building_plotmarker( pos, minp, maxp, data, param2_data, a, cid, building_nr_in_bpos, village_id, filename)
-- position the plot marker so that players can later buy this plot + building in order to modify it
-- pos.o contains the original orientation (determined by the road and the side the building is
local p = {x=pos.x, y=pos.y+1, z=pos.z};
if( pos.o == 0 ) then
p.x = p.x - 1;
p.z = p.z + pos.bsizez - 1;
elseif( pos.o == 2 ) then
p.x = p.x + pos.bsizex;
elseif( pos.o == 1 ) then
p.z = p.z + pos.bsizez;
p.x = p.x + pos.bsizex - 1;
elseif( pos.o == 3 ) then
p.z = p.z - 1;
end
-- actually position the marker
if( p.x >= minp.x and p.x <= maxp.x and p.z >= minp.z and p.z <= maxp.z and p.y >= minp.y and p.y <= maxp.y) then
if( handle_schematics.moresnow_installed
and data[ a:index(p.x, p.y, p.z)] == cid.c_snow
and p.y<maxp.y
and moresnow and moresnow.c_snow_top and cid.c_snow_top ~= cid.c_ignore) then
data[ a:index(p.x, p.y+1, p.z)] = moresnow.c_snow_top;
end
data[ a:index(p.x, p.y, p.z)] = cid.c_plotmarker;
param2_data[a:index(p.x, p.y, p.z)] = pos.brotate;
-- store the necessary information in the marker so that it knows for which building it is responsible
local meta = minetest.get_meta( p );
meta:set_string('village_id', village_id );
meta:set_int( 'plot_nr', building_nr_in_bpos );
meta:set_string('infotext', 'Plot No. '..tostring( building_nr_in_bpos ).. ' with '..tostring( filename ));
end
end
-- we do have a list of all nodenames the building contains (the .mts file provided it);
-- we can thus apply all replacements to these nodenames;
-- this also checks param2 and sets some other variables to indicate that it's i.e. a tree or a chest
-- (which both need special handling later on)
local function generate_building_translate_nodenames( nodenames, replacements, cid, binfo_scm, mirror_x, mirror_z )
if( not( nodenames )) then
return;
end
local i;
local v;
local new_nodes = {};
for i,node_name in ipairs( nodenames ) do
new_nodes[ i ] = {}; -- array for collecting information about the new content id for nodes with number "i" in their .mts savefile
-- some nodes may be called differently when mirrored; needed for doors
local new_node_name = node_name;
if( new_node_name and ( mirror_x or mirror_z ) and handle_schematics.mirrored_node[ new_node_name ] ) then
new_node_name = handle_schematics.mirrored_node[ node_name ];
new_nodes[ i ].is_mirrored = 1; -- currently unused
end
-- apply the replacements
if( new_node_name and replacements.table[ new_node_name ] ) then
new_node_name = replacements.table[ new_node_name ];
new_nodes[ i ].is_replaced = 1; -- currently unused
end
-- those chests do not exist as regular nodes; they're just placeholders
if( node_name == 'cottages:chest_private'
or node_name == 'cottages:chest_work'
or node_name == 'cottages:chest_storage' ) then
new_nodes[ i ].is_replaced = 1; -- currently unused
new_nodes[ i ].special_chest = node_name;
-- TODO: perhaps use a locked chest owned by the mob living there?
-- place a normal chest here
new_nodes[ i ].new_content = cid.c_chest;
new_nodes[ i ].special_chest = node_name;
new_node_name = 'default:chest';
elseif(new_node_name == 'cottages:chest_private'
or new_node_name == 'cottages:chest_work'
or new_node_name == 'cottages:chest_storage' ) then
new_nodes[ i ].is_replaced = 1; -- currently unused
new_nodes[ i ].special_chest = new_node_name;
-- TODO: perhaps use a locked chest owned by the mob living there?
-- place a normal chest here
new_nodes[ i ].new_content = cid.c_chest;
end
-- only existing nodes can be placed
if( new_node_name and minetest.registered_nodes[ new_node_name ]) then
local regnode = minetest.registered_nodes[ new_node_name ];
new_nodes[ i ].new_node_name = new_node_name;
new_nodes[ i ].new_content = minetest.get_content_id( new_node_name );
if( regnode.on_construct ) then
new_nodes[ i ].on_construct = 1;
end
local new_content = new_nodes[ i ].new_content;
if( new_content == cid.c_dirt or new_content == cid.c_dirt_with_grass ) then
new_nodes[ i ].is_grass = 1;
elseif( new_content == cid.c_sapling
or new_content == cid.c_jsapling
or new_content == cid.c_psapling
or new_content == cid.c_savannasapling
or new_content == cid.c_pinesapling ) then
-- store that a tree is to be grown there
new_nodes[ i ].is_tree = 1;
elseif( new_content == cid.c_chest
or new_content == cid.c_chest_locked
or new_content == cid.c_chest_shelf
or new_content == cid.c_chest_ash
or new_content == cid.c_chest_aspen
or new_content == cid.c_chest_birch
or new_content == cid.c_chest_maple
or new_content == cid.c_chest_chestnut
or new_content == cid.c_chest_pine
or new_content == cid.c_chest_spruce) then
-- we're dealing with a chest that might need filling
new_nodes[ i ].is_chestlike = 1;
elseif( new_content == cid.c_sign ) then
-- the sign may require some text to be written on it
new_nodes[ i ].is_sign = 1;
end
-- handle_schematics.get_param2_rotated( 'facedir', param2 ) needs to be called for nodes
-- which use either facedir or wallmounted;
-- realtest rotates some nodes diffrently and does not come with default:ladder
if( node_name == 'default:ladder' and not( minetest.registered_nodes[ node_name ])) then
new_nodes[ i ].change_param2 = {}; --{ 2->1, 5->2, 3->3, 4->0 }
new_nodes[ i ].change_param2[2] = 1;
new_nodes[ i ].change_param2[5] = 2;
new_nodes[ i ].change_param2[3] = 3;
new_nodes[ i ].change_param2[4] = 0;
new_nodes[ i ].paramtype2 = 'facedir';
-- ..except if they are stairs or ladders
elseif( string.sub( node_name, 1, 7 ) == 'stairs:' or string.sub( node_name, 1, 6 ) == 'doors:') then
new_nodes[ i ].paramtype2 = 'facedir';
-- normal nodes
elseif( regnode and regnode.paramtype2 and (regnode.paramtype2=='facedir' or regnode.paramtype2=='wallmounted')) then
new_nodes[ i ].paramtype2 = regnode.paramtype2;
end
-- we tried our best, but the replacement node is not defined
elseif( new_node_name ~= 'mg:ignore' ) then
local msg = 'ERROR: Did not find a suitable replacement for '..tostring( node_name )..' (suggested but inexistant: '..
tostring( new_node_name )..'). Building: '..tostring( binfo_scm )..'.';
if( mg_villages and mg_villages.print ) then
mg_villages.print( mg_villages.DEBUG_LEVEL_WARNING, msg );
else
print( msg );
end
msg = nil;
new_nodes[ i ].ignore = 1; -- keep the old content
else -- handle mg:ignore
new_nodes[ i ].ignore = 1;
end
end
return new_nodes;
end
local function generate_building(pos, minp, maxp, data, param2_data, a, extranodes, replacements, cid, extra_calls, building_nr_in_bpos, village_id, binfo_extra, road_node, keep_ground)
local binfo = binfo_extra;
if( not( binfo ) and mg_villages) then
binfo = mg_villages.BUILDINGS[pos.btype]
end
local scm
-- the building got removed from mg_villages.BUILDINGS in the meantime
if( not( binfo )) then
return;
end
-- schematics of .mts type are not handled here; they need to be placed using place_schematics
if( binfo.is_mts and binfo.is_mts == 1 ) then
return;
end
-- roads are very simple structures that are not stored as schematics
if( pos.btype == 'road' ) then
handle_schematics.place_road( minp, maxp, data, param2_data, a, road_node, pos, cid.c_air );
return;
end
if( not( pos.no_plotmarker )) then
generate_building_plotmarker( pos, minp, maxp, data, param2_data, a, cid, building_nr_in_bpos, village_id, binfo.scm );
end
-- skip building if it is not located at least partly in the area that is currently beeing generated
if( pos.x > maxp.x or pos.x + pos.bsizex < minp.x
or pos.z > maxp.z or pos.z + pos.bsizez < minp.z ) then
return;
end
if( pos.btype and
(( binfo.sizex ~= pos.bsizex and binfo.sizex ~= pos.bsizez )
or ( binfo.sizez ~= pos.bsizex and binfo.sizez ~= pos.bsizez )
or not( binfo.scm_data_cache ))) then
if( mg_villages and mg_villages.print ) then
mg_villages.print( mg_villages.DEBUG_LEVEL_WARNING,
'ERROR: This village was created using diffrent buildings than those known know. Cannot place unknown building.');
else
print( 'ERROR: Size information about this building differs. Cannot place building.');
end
return;
end
if( binfo.scm_data_cache )then
scm = binfo.scm_data_cache;
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 and mg_villages) 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" );
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
local mirror_x = false;
local mirror_z = false;
if( pos.mirror ) then
if( binfo.axis and binfo.axis == 1 ) then
mirror_x = true;
mirror_z = false;
else
mirror_x = false;
mirror_z = true;
end
end
-- translate all nodenames and apply the replacements
local new_nodes = generate_building_translate_nodenames( binfo.nodenames, replacements, cid, binfo.scm, mirror_x, mirror_z );
for x = 0, pos.bsizex-1 do
scm_x = scm_x + step_x;
scm_z = scm_z_start;
for z = 0, pos.bsizez-1 do
scm_z = scm_z + step_z;
local xoff = scm_x;
local zoff = scm_z;
if( pos.brotate == 2 ) then
if( mirror_x ) then
xoff = pos.bsizex - scm_x + 1;
end
if( mirror_z ) then
zoff = scm_z;
else
zoff = pos.bsizez - scm_z + 1;
end
elseif( pos.brotate == 1 ) then
if( mirror_x ) then
xoff = pos.bsizez - scm_z + 1;
else
xoff = scm_z;
end
if( mirror_z ) then
zoff = pos.bsizex - scm_x + 1;
else
zoff = scm_x;
end
elseif( pos.brotate == 3 ) then
if( mirror_x ) then
xoff = pos.bsizez - scm_z + 1;
else
xoff = scm_z;
end
if( mirror_z ) then
zoff = scm_x;
else
zoff = pos.bsizex - scm_x + 1;
end
elseif( pos.brotate == 0 ) then
if( mirror_x ) then
xoff = pos.bsizex - scm_x + 1;
end
if( mirror_z ) then
zoff = pos.bsizez - scm_z + 1;
end
end
local has_snow = false;
local ground_type = c_dirt_with_grass;
for y = 0, binfo.ysize-1 do
local ax = pos.x+x;
local ay = pos.y+y+binfo.yoff;
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
local new_content = c_air;
local t = scm[y+1][xoff][zoff];
local node_content = data[a:index(ax, ay, az)];
if( binfo.yoff+y == 0 ) then
-- 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
if( not( t )) then
if( node_content ~= cid.c_plotmarker
and (not(handle_schematics.moresnow_installed) or not(moresnow) or node_content ~= moresnow.c_snow_top )) then
data[ a:index(ax, ay, az)] = cid.c_air;
end
else
local n = new_nodes[ t[1] ]; -- t[1]: id of the old node
if( not( n.ignore )) then
new_content = n.new_content;
else
new_content = node_content;
end
-- replace all dirt and dirt with grass at that x,z coordinate with the stored ground grass node;
if( n.is_grass and keep_ground) then
new_content = ground_type;
end
if( n.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
-- do not overwrite plotmarkers
if( new_content ~= cid.c_air or node_content ~= cid.c_plotmarker ) then
data[ a:index(ax, ay, az)] = new_content;
end
-- store that a tree is to be grown there
if( n.is_tree ) then
table.insert( extra_calls.trees, {x=ax, y=ay, z=az, typ=new_content, snow=has_snow});
-- we're dealing with a chest that might need filling
elseif( n.is_chestlike ) then
table.insert( extra_calls.chests, {x=ax, y=ay, z=az, typ=new_content, bpos_i=building_nr_in_bpos, typ_name=n.special_chest});
-- the sign may require some text to be written on it
elseif( n.is_sign ) then
table.insert( extra_calls.signs, {x=ax, y=ay, z=az, typ=new_content, bpos_i=building_nr_in_bpos});
end
-- handle rotation
if( n.paramtype2 ) then
local param2 = t[2];
if( n.change_param2 and n.change_param2[ t[2] ]) then
param2 = n.change_param2[ param2 ];
end
local np2 = 0;
if( mirror_x ) then
np2 = handle_schematics.rotation_table[ n.paramtype2 ][ param2+1 ][ pos.brotate+1 ][ 2 ];
elseif( mirror_z ) then
np2 = handle_schematics.rotation_table[ n.paramtype2 ][ param2+1 ][ pos.brotate+1 ][ 3 ];
else
np2 = handle_schematics.rotation_table[ n.paramtype2 ][ param2+1 ][ pos.brotate+1 ][ 1 ];
end
--[[
local param2list = handle_schematics.get_param2_rotated( n.paramtype2, param2);
local np2 = param2list[ pos.brotate + 1];
-- mirror
if( mirror_x ) then
if( #param2list==5) then
np2 = handle_schematics.mirror_facedir[ ((pos.brotate+1)%2)+1 ][ np2+1 ];
elseif( #param2list<5
and ((pos.brotate%2==1 and (np2==4 or np2==5))
or (pos.brotate%2==0 and (np2==2 or np2==3)))) then
np2 = param2list[ (pos.brotate + 2)%4 +1];
end
elseif( mirror_z ) then
if( #param2list==5) then
np2 = handle_schematics.mirror_facedir[ (pos.brotate %2)+1 ][ np2+1 ];
elseif( #param2list<5
and ((pos.brotate%2==0 and (np2==4 or np2==5))
or (pos.brotate%2==1 and (np2==2 or np2==3)))) then
np2 = param2list[ (pos.brotate + 2)%4 +1];
end
end
--]]
param2_data[a:index(ax, ay, az)] = np2;
else
param2_data[a:index(ax, ay, az)] = t[2];
end
end
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 = handle_schematics.mg_drop_moresnow( ax, az, y_top, y_bottom-1, a, data, param2_data);
if( res and (data[ a:index(ax, res.height, az)]==cid.c_air
or data[ a:index(ax, res.height, az)]==cid.c_water )) 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
-- actually place the buildings (at least those which came as .we files; .mts files are handled later on)
-- this code was also responsible for tree placement;
-- place_buildings is used by mg_villages exclusively. It calls the local function generate_building and
-- therefore resides in this file.
handle_schematics.place_buildings = function(village, minp, maxp, data, param2_data, a, cid, village_id)
-- this function is only relevant for mg_villages
if( not( mg_villages )) then
return;
end
local vx, vz, vs, vh = village.vx, village.vz, village.vs, village.vh
local village_type = village.village_type;
local bpos = village.to_add_data.bpos;
local replacements = mg_villages.get_replacement_table( village.village_type, nil, village.to_add_data.replacements );
cid.c_chest = handle_schematics.get_content_id_replaced( 'default:chest', replacements );
cid.c_chest_locked = handle_schematics.get_content_id_replaced( 'default:chest_locked', replacements );
cid.c_chest_shelf = handle_schematics.get_content_id_replaced( 'cottages:shelf', replacements );
cid.c_chest_ash = handle_schematics.get_content_id_replaced( 'trees:chest_ash', replacements );
cid.c_chest_aspen = handle_schematics.get_content_id_replaced( 'trees:chest_aspen', replacements );
cid.c_chest_birch = handle_schematics.get_content_id_replaced( 'trees:chest_birch', replacements );
cid.c_chest_maple = handle_schematics.get_content_id_replaced( 'trees:chest_maple', replacements );
cid.c_chest_chestnut = handle_schematics.get_content_id_replaced( 'trees:chest_chestnut', replacements );
cid.c_chest_pine = handle_schematics.get_content_id_replaced( 'trees:chest_pine', replacements );
cid.c_chest_spruce = handle_schematics.get_content_id_replaced( 'trees:chest_spruce', replacements );
cid.c_sign = handle_schematics.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
local extranodes = {}
local extra_calls = { on_constr = {}, trees = {}, chests = {}, signs = {}, traders = {} };
for i, pos in ipairs(bpos) do
-- roads are only placed if there are at least mg_villages.MINIMAL_BUILDUNGS_FOR_ROAD_PLACEMENT buildings in the village
if( not(pos.btype) or pos.btype ~= 'road' or village.anz_buildings > mg_villages.MINIMAL_BUILDUNGS_FOR_ROAD_PLACEMENT )then
-- replacements are in table format for mapgen-based building spawning
local road_material = mg_villages.road_node;
if( pos.road_material ) then
road_material = pos.road_material;
end
generate_building(pos, minp, maxp, data, param2_data, a, extranodes, replacements, cid, extra_calls, i, village_id, nil, road_material, true )
end
end
-- replacements are in list format for minetest.place_schematic(..) type spawning
return { extranodes = extranodes, bpos = bpos, replacements = replacements.list, dirt_roads = village.to_add_data.dirt_roads,
plantlist = village.to_add_data.plantlist, extra_calls = extra_calls };
end
-- place a schematic manually
--
-- pos needs to contain information about how to place the building:
-- pos.x, pos.y, pos.z where the building is to be placed
-- pos.btype determines which building will be placed; if not set, binfo_extra needs to be provided
-- pos.brotate contains a value of 0-3, which determines the rotation of the building
-- pos.bsizex size of the building in x direction
-- pos.bsizez size of the building in z direction
-- pos.mirror if set, the building will be mirrored
-- pos.no_plotmarker optional; needs to be set in order to avoid the generation of a plotmarker
-- building_nr optional; used for plotmarker
-- village_id optional; used for plotmarker
-- pos.fruit optional; determines the fruit a farm is going to grow (if binfo.farming_plus is set)
-- binfo contains general information about a building:
-- binfo.sizex size of the building in x direction
-- binfo.sizez
-- binfo.ysize
-- binfo.yoff how deep is the building burried?
-- binfo.nodenames list of the node names beeing used by the building
-- binfo.scm name of the file containing the schematic; only needed for an error message
-- binfo.scm_data_cache contains actual information about the nodes beeing used (the data)
-- binfo.is_mts optional; if set to 1, the function will abort
-- binfo.farming_plus optional; if set, pos.fruit needs to be set as well
-- binfo.axis optional; relevant for some mirroring operations
--
-- replacement_list contains replacements in the same list format as place_schematic uses
--
handle_schematics.place_building_using_voxelmanip = function( pos, binfo, replacement_list)
if( not( replacement_list ) or type( replacement_list ) ~= 'table' ) then
return;
end
-- if not defined, the building needs to start at pos.x,pos.y,pos.z - without offset
if( not( binfo.yoff )) then
binfo.yoff = 0;
end
-- TODO: calculate the end position from the given data
-- get a suitable voxelmanip object
-- (taken from minetest_game/mods/default/trees.lua)
local vm = minetest.get_voxel_manip()
local minp, maxp = vm:read_from_map(
{x = pos.x, y = pos.y, z = pos.z},
{x = pos.x+pos.bsizex, y = pos.y+binfo.ysize, z = pos.z+pos.bsizez} -- TODO
)
local a = VoxelArea:new({MinEdge = minp, MaxEdge = maxp})
local data = vm:get_data()
local param2_data = vm:get_param2_data();
-- translate the replacement_list into replacements.ids and replacements.table format
-- the first two parameters are nil because we do not want a new replacement list to be generated
local replacements = handle_schematics.get_replacement_table( nil, nil, replacement_list );
-- only very few nodes are actually used from the cid table (content ids)
local cid = {};
cid.c_air = minetest.get_content_id( 'air' );
cid.c_dirt = handle_schematics.get_content_id_replaced( 'default:dirt', replacements );
cid.c_dirt_with_grass = handle_schematics.get_content_id_replaced( 'default:dirt_with_grass',replacements );
cid.c_sapling = handle_schematics.get_content_id_replaced( 'default:sapling', replacements );
cid.c_jsapling = handle_schematics.get_content_id_replaced( 'default:junglesapling', replacements );
cid.c_psapling = handle_schematics.get_content_id_replaced( 'default:pine_sapling', replacements );
cid.c_savannasapling = handle_schematics.get_content_id_replaced( 'mg:savannasapling', replacements );
cid.c_pinesapling = handle_schematics.get_content_id_replaced( 'mg:pinesapling', replacements );
cid.c_plotmarker = handle_schematics.get_content_id_replaced( 'mg_villages:plotmarker', replacements );
cid.c_chest = handle_schematics.get_content_id_replaced( 'default:chest', replacements );
cid.c_chest_locked = handle_schematics.get_content_id_replaced( 'default:chest_locked', replacements );
cid.c_chest_shelf = handle_schematics.get_content_id_replaced( 'cottages:shelf', replacements );
cid.c_chest_ash = handle_schematics.get_content_id_replaced( 'trees:chest_ash', replacements );
cid.c_chest_aspen = handle_schematics.get_content_id_replaced( 'trees:chest_aspen', replacements );
cid.c_chest_birch = handle_schematics.get_content_id_replaced( 'trees:chest_birch', replacements );
cid.c_chest_maple = handle_schematics.get_content_id_replaced( 'trees:chest_maple', replacements );
cid.c_chest_chestnut = handle_schematics.get_content_id_replaced( 'trees:chest_chestnut', replacements );
cid.c_chest_pine = handle_schematics.get_content_id_replaced( 'trees:chest_pine', replacements );
cid.c_chest_spruce = handle_schematics.get_content_id_replaced( 'trees:chest_spruce', replacements );
cid.c_sign = handle_schematics.get_content_id_replaced( 'default:sign_wall', replacements );
-- for roads
cid.c_sign = handle_schematics.get_content_id_replaced( 'default:gravel', replacements );
local extranodes = {}
local extra_calls = { on_constr = {}, trees = {}, chests = {}, signs = {}, traders = {} };
-- last parameter false -> place dirt nodes instead of trying to keep the ground nodes
generate_building(pos, minp, maxp, data, param2_data, a, extranodes, replacements, cid, extra_calls, pos.building_nr, pos.village_id, binfo, cid.c_gravel, false);
-- store the changed map data
vm:set_data(data);
vm:set_param2_data(param2_data);
vm:write_to_map();
vm:update_liquids();
vm:update_map();
-- TODO: do the calls for the extranodes as well
-- replacements are in list format for minetest.place_schematic(..) type spawning
return { extranodes = extranodes, replacements = replacements.list, extra_calls = extra_calls };
end
-- places a building read from file "building_name" on the map between start_pos and end_pos using luavoxelmanip
-- returns error message on failure and nil on success
handle_schematics.place_building_from_file = function( start_pos, end_pos, building_name, replacement_list, rotate, axis, mirror, no_plotmarker )
if( not( building_name )) then
return "No file name given. Cannot find the schematic.";
end
local binfo = handle_schematics.analyze_file( building_name, nil, nil );
if( not( binfo )) then
return "Failed to import schematic. Only .mts and .we are supported!";
end
-- nodenames and scm_data_cache can be used directly;
-- the size dimensions need to be renamed
binfo.sizex = binfo.size.x;
binfo.sizez = binfo.size.z;
binfo.ysize = binfo.size.y;
-- this value has already been taken care of when determining start_pos
binfo.yoff = 0;
-- file name of the scm; only used for error messages
binfo.scm = building_name;
-- this is relevant for mirroring operations
binfo.axis = axis;
if( not( rotate ) or rotate=="0" ) then
start_pos.brotate = 0;
elseif( rotate=="90" ) then
start_pos.brotate = 1;
elseif( rotate=="180" ) then
start_pos.brotate = 2;
elseif( rotate=="270" ) then
start_pos.brotate = 3;
end
if( start_pos.brotate > 3 ) then
start_pos.brotate = start_pos.brotate % 4;
end
-- determine the size of the bulding from the place we assigned to it...
start_pos.bsizex = math.abs(end_pos.x - start_pos.x)+1;
start_pos.bsizez = math.abs(end_pos.z - start_pos.z)+1;
-- otpional; if set, the building will be mirrored
start_pos.mirror = mirror;
-- do not generate a plot marker as this is not part of a village;
-- otherwise, building_nr and village_id would have to be provided
start_pos.no_plotmarker = no_plotmarker;
-- all those calls to on_construct need to be done now
local res = handle_schematics.place_building_using_voxelmanip( start_pos, binfo, replacement_list);
if( not(res) or not( res.extra_calls )) then
return;
end
-- call on_construct where needed;
-- trees, chests and signs receive no special treatment here
for k, v in pairs( res.extra_calls.on_constr ) do
local node_name = minetest.get_name_from_content_id( k );
if( minetest.registered_nodes[ node_name ].on_construct ) then
for _, pos in ipairs(v) do
minetest.registered_nodes[ node_name ].on_construct( pos );
end
end
end
if( binfo.metadata ) then
-- if it is a .we/.wem file, metadata was included directly
handle_schematics.restore_meta( nil, binfo.metadata, start_pos, end_pos, start_pos.brotate, mirror);
else
-- .mts files come with extra .meta file (if such a .meta file was created)
-- TODO: restore metadata for .mts files
--handle_schematics.restore_meta( filename, nil, binfo.metadata, start_pos, end_pos, start_pos.brotate, mirror);
end
end
-- add the dirt roads
handle_schematics.place_dirt_roads = function(village, minp, maxp, data, param2_data, a, c_road_node)
local c_air = minetest.get_content_id( 'air' );
for _, pos in ipairs(village.to_add_data.dirt_roads) do
handle_schematics.place_road( minp, maxp, data, param2_data, a, c_road_node, pos, c_air );
end
end
handle_schematics.place_road = function(minp, maxp, data, param2_data, a, c_road_node, pos, c_air )
local param2 = 0;
if( pos.bsizex > 2 and pos.bsizex > pos.bsizez) then
param2 = 1;
end
--[[
local is_main_road = false;
local c_road_node = minetest.get_content_id('default:coalblock');
local c_middle_wool = minetest.get_content_id('default:clay');
local slab_stone = minetest.get_content_id('stairs:slab_stone');
if( pos.bsizex > 2 and pos.bsizez > 2 ) then
is_main_road = true;
end
--]]
if( not(pos.y >= minp.y and pos.y <= maxp.y-2)) then
return;
end
for x = math.max( pos.x, minp.x ), math.min( pos.x+pos.bsizex-1, maxp.x ) do
for z = math.max( pos.z, minp.z ), math.min( pos.z+pos.bsizez-1, maxp.z ) do
-- roads have a height of 1 block
data[ a:index( x, pos.y, z)] = c_road_node;
param2_data[ a:index( x, pos.y, z)] = param2;
-- ...with air above
data[ a:index( x, pos.y+1, z)] = c_air;
data[ a:index( x, pos.y+2, z)] = c_air;
--[[
if( (param2==0 and (x==pos.x or x==pos.x+8) and is_main_road)
or (param2==1 and (z==pos.z or z==pos.z+8) and is_main_road)) then
data[ a:index( x, pos.y+1, z )] = slab_stone;
elseif((param2==0 and (x==pos.x+4 ) and is_main_road)
or (param2==1 and (z==pos.z+4 ) and is_main_road)) then
data[ a:index( x, pos.y, z )] = c_middle_wool;
end
--]]
end
end
end
if( minetest.get_modpath('moresnow' )) then
handle_schematics.moresnow_installed = true;
end

View File

@ -0,0 +1,171 @@
replacements_group['farming'] = {}
-- this contains a list of all found/available nodenames that may act as a replacement frming nodes
replacements_group['farming'].found = {};
-- contains a list of *all* known farming names - even of mods that may not be installed
replacements_group['farming'].all = {};
-- contains information about how a particular node is called if a particular farming mod is used;
replacements_group['farming'].data = {};
-- names of traders for the diffrent fruits
replacements_group['farming'].traders = {};
replacements_group['farming'].replace_material = function( replacements, old_material, new_material )
if( not( old_material ) or not( replacements_group['farming'].data[ old_material ])
or not( new_material ) or not( replacements_group['farming'].data[ new_material ])
or old_material == new_material ) then
return replacements;
end
local old_nodes = replacements_group['farming'].data[ old_material ];
local new_nodes = replacements_group['farming'].data[ new_material ];
for i=1,#old_nodes do
local old = old_nodes[i];
local new = old;
if( i<=#new_nodes and new_nodes[i] and minetest.registered_nodes[ new_nodes[i]] ) then
new = new_nodes[i];
local found = false;
for i,v in ipairs(replacements) do
if( v and v[1]==old ) then
v[2] = new;
found = true;
end
end
if( not( found )) then
table.insert( replacements, { old, new });
end
-- default to the last growth stage
elseif( i>#new_nodes and minetest.registered_nodes[ new_nodes[ #new_nodes ]]) then
table.insert( replacements, { old, new_nodes[ #new_nodes ] });
end
end
return replacements;
end
---------------------
-- internal functions
---------------------
replacements_group['farming'].add_material = function( fruit, fruit_item, prefix, seperator, postfix )
local is_loaded = false;
if( minetest.registered_items[ fruit_item ]
and minetest.registered_nodes[ prefix..fruit..seperator.."1"..postfix ] ) then
is_loaded = true;
table.insert( replacements_group['farming'].found, fruit_item );
end
table.insert( replacements_group['farming'].all, fruit_item );
local data = {};
-- handle seeds
if( minetest.registered_items[ prefix..fruit..'_seed' ]) then
data[1] = prefix..fruit..'_seed';
elseif( minetest.registered_items[ prefix..fruit..'seed' ]) then
data[1] = prefix..fruit..'seed';
else
data[1] = fruit_item;
end
for i=1,8 do
local node_name = prefix..fruit..seperator..tostring(i)..postfix;
if( is_loaded and minetest.registered_nodes[ node_name ]) then
table.insert( data, node_name );
-- if the mod is not loaded, we do not know how many growth stages it has;
-- in order to be on the safe side, store them all
elseif( not( is_loaded )) then
table.insert( data, node_name );
end
end
-- the last plant stage (the one that gives the fruit) usually has no number
local node_name = prefix..fruit;
if( is_loaded and minetest.registered_nodes[ node_name ]) then
table.insert( data, node_name );
elseif( not( is_loaded )) then
table.insert( data, node_name );
end
replacements_group['farming'].data[ fruit_item ] = data;
-- farming nodes do not count as ground (except for soil - which is not handled here)
local c_ignore = minetest.get_content_id( 'ignore' );
for _,v in ipairs( data ) do
local id = minetest.get_content_id( v );
if( id and id ~= c_ignore ) then
replacements_group.node_is_ground[ id ] = false;
end
end
if( is_loaded and minetest.get_modpath('mobf_trader') and mobf_trader and mobf_trader.add_trader ) then
-- TODO: use replacements for the payments where needed
local goods = {
{ fruit_item.." 1", "default:coal_lump 3", "default:wood 8"},
{ fruit_item.." 10", "default:steel_ingot 2", "default:chest_locked 1"}};
if( fruit_item ~= data[1] ) then
table.insert( goods, { data[1].." 1", "farming:scarecrow", "farming:scarecrow_light 1"});
table.insert( goods, { data[1].." 2", "default:dirt 20", "default:bucket_water", "default:steel_ingot 4", "default:leaves 99" });
end
table.insert( goods, {"farming:hoe_wood 1","default:wood 10", "default:cobble 10"});
mobf_trader.add_trader( mobf_trader.npc_trader_prototype,
"farmer growing "..fruit.."s", -- not always the right grammatical form
fruit.."_farmer_v",
goods,
{ "farmer" },
{'kuhhaendler.png', 'bauer_in_sonntagskleidung.png', 'baeuerin.png', 'wheat_farmer_by_addi.png', 'tomatenhaendler.png'}
);
replacements_group['farming'].traders[ fruit_item ] = fruit..'_farmer_v';
end
end
-- create a list of all available fruit types
replacements_group['farming'].construct_farming_type_list = function()
-- farming from minetest_game
replacements_group['farming'].add_material( 'wheat', 'farming:wheat', 'farming:', '_', '' );
replacements_group['farming'].add_material( 'cotton', 'farming:cotton', 'farming:', '_', '' );
replacements_group['farming'].add_material( 'pumpkin','farming:pumpkin', 'farming:', '_', '' );
-- RealTest
replacements_group['farming'].add_material( 'flax', 'farming:string', 'farming:', '_', '' );
replacements_group['farming'].add_material( 'spelt', 'farming:wheat', 'farming:', '_', '' );
replacements_group['farming'].add_material( 'soy', 'farming:soy', 'farming:', '_', '' );
-- diffrent versions of farming_plus:
-- PilzAdam: https://forum.minetest.net/viewtopic.php?t=2787
-- TenPlus1: https://forum.minetest.net/viewtopic.php?t=9019
-- MTDad: https://forum.minetest.net/viewtopic.php?t=10187
local fruits = { 'strawberry', 'raspberry',
'carrot', 'rhubarb', 'cucumber',
'pumpkin', 'melon',
'orange', 'lemon', 'peach', 'walnut',
'potato','potatoe', -- diffrent mods spell them diffrently
'tomato', 'corn'
};
for i,fruit in ipairs( fruits ) do
if( minetest.registered_nodes[ 'farming_plus:'..fruit ]
and minetest.registered_nodes[ 'farming_plus:'..fruit..'_1' ]
and minetest.registered_items[ 'farming_plus:'..fruit..'_item' ] ) then
replacements_group['farming'].add_material( fruit, 'farming_plus:'..fruit..'_item', 'farming_plus:', '_', '' );
end
end
-- coffee beans from farming_plus/farming_plusplus
replacements_group['farming'].add_material( 'coffee', 'farming_plus:coffee_beans', 'farming_plus:', '_', '' );
-- Docfarming: https://forum.minetest.net/viewtopic.php?t=3948
fruits = {'carrot','corn','potato','raspberry'};
for i,fruit in ipairs( fruits ) do
replacements_group['farming'].add_material( fruit, 'docfarming:'..fruit, 'docfarming:', '', '' );
end
end
-- create the list of known farming fruits
replacements_group['farming'].construct_farming_type_list();

View File

@ -0,0 +1,21 @@
-- mapgen based replacements work best using a table, while minetest.place_schematic(..) based spawning needs a list
handle_schematics.get_replacement_table = function( housetype, pr, replacements )
local rtable = {};
local ids = {};
if( not( replacements ) and mg_villages and mg_villages.get_replacement_list) then
replacements = mg_villages.get_replacement_list( housetype, pr );
end
-- it is very problematic if the torches on houses melt snow and cause flooding; thus, we use a torch that is not hot
if( minetest.registered_nodes[ 'mg_villages:torch']) then
table.insert( replacements, {'default:torch', 'mg_villages:torch'});
end
for i,v in ipairs( replacements ) do
if( v and #v == 2 ) then
rtable[ v[1] ] = v[2];
ids[ minetest.get_content_id( v[1] )] = minetest.get_content_id( v[2] );
end
end
return { table = rtable, list = replacements, ids = ids };
end

View File

@ -0,0 +1,82 @@
replacements_group['realtest'] = {}
-- parameter: replacements, name_in_default, name_in_realtest, to_realtest=true/false
replacements_group['realtest'].stairs = function( repl, def, rt, to_realtest)
if( to_realtest ) then
if( def ~= rt ) then
table.insert( repl, {'default:'..def, 'default:'..rt});
end
table.insert( repl, {'stairs:stair_'..def, 'default:'..rt..'_stair'});
table.insert( repl, {'stairs:slab_'..def, 'default:'..rt..'_slab'});
else
if( def ~= rt ) then
table.insert( repl, {'default:'..rt, 'default:'..def});
end
table.insert( repl, {'default:'..rt..'_stair', 'stairs:stair_'..def});
table.insert( repl, {'default:'..rt..'_stair_upside_down','stairs:stair_'..def});
-- upside-down-slab
table.insert( repl, {'default:'..rt..'_slab_r', 'stairs:slab_'..def});
table.insert( repl, {'default:'..rt..'_slab', 'stairs:slab_'..def});
end
return repl;
end
replacements_group['realtest'].replace = function( replacements )
local repl = {};
local to_realtest = false;
if( not( minetest.registered_nodes[ 'default:furnace' ])
and minetest.registered_nodes[ 'oven:oven' ]) then
to_realtest = true;
elseif( minetest.registered_nodes[ 'default:furnace' ]
and not( minetest.registered_nodes[ 'oven:oven' ])) then
to_realtest = false;
else
-- else no replacements required
return;
end
replacements_group['realtest'].stairs( repl, 'stone', 'stone', to_realtest );
replacements_group['realtest'].stairs( repl, 'cobble', 'stone_flat', to_realtest );
replacements_group['realtest'].stairs( repl, 'stonebrick', 'stone_bricks', to_realtest );
replacements_group['realtest'].stairs( repl, 'desert_stone', 'desert_stone', to_realtest );
replacements_group['realtest'].stairs( repl, 'desert_cobble', 'desert_stone_flat', to_realtest );
replacements_group['realtest'].stairs( repl, 'desert_stonebrick', 'desert_stone_bricks',to_realtest );
replacements_group['realtest'].stairs( repl, 'brick', 'brick', to_realtest );
if( to_realtest ) then
table.insert( repl, {'default:furnace', 'oven:oven'});
table.insert( repl, {'default:clay', 'grounds:clay'});
-- Realtest does not know about these nodes yet
table.insert( repl, {'farming:soil_wet', 'farming:soil'});
table.insert( repl, {'farming:desert_sand_soil', 'farming:soil'});
table.insert( repl, {'farming:desert_sand_soil_wet','farming:soil'});
for i=1,5 do
table.insert( repl, {'default:grass_'..i,'air' });
end
table.insert( repl, {'default:apple', 'air' });
table.insert( repl, {'default:obsidian_glass', 'default:glass' });
else
table.insert( repl, {'oven:oven', 'default:furnace'});
table.insert( repl, {'grounds:clay', 'default:clay'});
table.insert( repl, {'farming:soil', 'farming:soil_wet'});
end
for i,v in ipairs( repl ) do
if( v and v[2] and minetest.registered_nodes[ v[2]] ) then
local found = false;
for j,w in ipairs( replacements ) do
if( w and w[1] and w[1]==v[1] ) then
w[2] = v[2];
found = true;
end
end
if( not( found )) then
table.insert( replacements, {v[1],v[2]} );
end
end
end
return replacements;
end

View File

@ -0,0 +1,110 @@
replacements_group['roof'] = {}
-- this contains a list of all found/available nodenames that may act as a replacement frming nodes
replacements_group['roof'].found = {};
-- contains a list of *all* known roof names - even of mods that may not be installed
replacements_group['roof'].all = {};
-- contains information about how a particular node is called if a particular roof mod is used;
replacements_group['roof'].data = {};
replacements_group['roof'].replace_material = function( replacements, old_material, new_material )
if( not( old_material ) or not( replacements_group['roof'].data[ old_material ])
or not( new_material ) or not( replacements_group['roof'].data[ new_material ])
or old_material == new_material ) then
return replacements;
end
local old_nodes = replacements_group['roof'].data[ old_material ];
local new_nodes = replacements_group['roof'].data[ new_material ];
for i=1,#old_nodes do
local old = old_nodes[i];
local new = old;
if( i<=#new_nodes and new_nodes[i] and minetest.registered_nodes[ new_nodes[i]] ) then
new = new_nodes[i];
local found = false;
for i,v in ipairs(replacements) do
if( v and v[1]==old ) then
v[2] = new;
found = true;
end
end
if( not( found )) then
table.insert( replacements, { old, new });
end
end
end
return replacements;
end
---------------------
-- internal functions
---------------------
replacements_group['roof'].add_material = function( nodelist )
local is_loaded = false;
if( minetest.registered_items[ nodelist[1] ] ) then
is_loaded = true;
table.insert( replacements_group['roof'].found, nodelist[1] );
end
table.insert( replacements_group['roof'].all, nodelist[1]);
replacements_group['roof'].data[ nodelist[1] ] = nodelist;
end
-- create a list of all available fruit types
replacements_group['roof'].construct_roof_type_list = function()
-- roof from cottages
local roofs = {'straw', 'reet', 'wood', 'slate', 'red', 'brown', 'black'};
for i,v in ipairs( roofs ) do
replacements_group['roof'].add_material( {
'cottages:roof_connector_'..v,
'cottages:roof_flat_'..v,
'', -- no full block available
'cottages:roof_'..v
} );
end
-- from dryplants
roofs = {'reed', 'wetreed'};
for i,v in ipairs( roofs ) do
replacements_group['roof'].add_material( {
'dryplants:'..v..'_roof',
'dryplants:'..v..'_slab',
'dryplants:'..v,
'dryplants:'..v..'_roof',
'dryplants:'..v..'_roof_corner',
'dryplants:'..v..'_roof_corner_2'
} );
end
-- roof from homedecor
roofs = {'wood', 'terracotta', 'asphalt', 'glass'};
for i,v in ipairs( roofs ) do
replacements_group['roof'].add_material( {
'homedecor:shingle_side_'..v,
'homedecor:shingles_'..v,
'',
'homedecor:shingles_'..v,
'homedecor:shingle_inner_corner_'..v,
'homedecor:shingle_outer_corner_'..v,
} );
end
replacements_group['roof'].data[ 'homedecor:shingle_side_glass' ][2] = 'homedecor:skylight';
replacements_group['roof'].data[ 'homedecor:shingle_side_glass' ][4] = 'homedecor:skylight';
replacements_group['roof'].data[ 'homedecor:shingle_side_asphalt'][3] = 'streets:asphalt';
-- TODO: slopes from technic or other slopes mods?
end
-- create the list of known roof fruits
replacements_group['roof'].construct_roof_type_list();

View File

@ -0,0 +1,234 @@
replacements_group['wood'] = {}
-- this contains a list of all found/available nodenames that may act as a replacement for default:wood
replacements_group['wood'].found = {};
-- contains a list of *all* known wood names - even of mods that may not be installed
replacements_group['wood'].all = {};
-- contains information about how a particular node is called if a particular wood is used;
replacements_group['wood'].data = {};
-- names of traders for the diffrent wood types
replacements_group['wood'].traders = {};
------------------------------------------------------------------------------
-- external function; call it in order to replace old_wood with new_wood;
-- other nodes (trees, saplings, fences, doors, ...) are replaced accordingly,
-- depending on what new_wood has to offer
------------------------------------------------------------------------------
replacements_group['wood'].replace_material = function( replacements, old_wood, new_wood )
if( not( old_wood ) or not( replacements_group['wood'].data[ old_wood ])
or not( new_wood ) or not( replacements_group['wood'].data[ new_wood ])
or old_wood == new_wood ) then
return replacements;
end
local old_nodes = replacements_group['wood'].data[ old_wood ];
local new_nodes = replacements_group['wood'].data[ new_wood ];
for i=3,#old_nodes do
local old = old_nodes[i];
local new = old;
if( i<=#new_nodes and new_nodes[i] and minetest.registered_nodes[ new_nodes[i]] ) then
new = new_nodes[i];
local found = false;
for i,v in ipairs(replacements) do
if( v and v[1]==old ) then
v[2] = new;
found = true;
end
end
if( not( found )) then
table.insert( replacements, { old, new });
end
end
end
return replacements;
end
---------------------
-- internal functions
---------------------
-- wood (and its corresponding tree trunk) is a very good candidate for replacement in most houses
-- helper function for replacements_group['wood'].get_wood_type_list
replacements_group['wood'].add_material = function( candidate_list, mod_prefix, w_pre, w_post, t_pre, t_post, l_pre, l_post,
s_pre, s_post, stair_pre, stair_post, slab_pre, slab_post,
fence_pre, fence_post, gate_pre, gate_post )
if( not( candidate_list )) then
return;
end
for _,v in ipairs( candidate_list ) do
local is_loaded = false;
local wood_name = mod_prefix..w_pre..v..w_post;
-- create a complete list of all possible wood names
table.insert( replacements_group['wood'].all, wood_name );
-- create a list of all *installed* wood types
if( minetest.registered_nodes[ wood_name ]) then
table.insert( replacements_group['wood'].found, wood_name );
is_loaded = true;
end
-- there is no check if the node names created here actually exist
local data = { v, -- 1. base name of the node
mod_prefix, -- 2. mod name
wood_name, -- 3. replacement for default:wood
mod_prefix..t_pre..v..t_post, -- 4. " " for default:tree
mod_prefix..l_pre..v..l_post, -- 5. " " for default:leaves
mod_prefix..s_pre..v..s_post, -- 6. " " for default:sapling
stair_pre..v..stair_post, -- 7. " " for stairs:stair_wood
slab_pre..v..slab_post, -- 8. " " for stairs:slab_wood
fence_pre..v..fence_post, -- 9. " " for default:fence_wood
gate_pre..v..gate_post..'_open', -- 10. " " for cottages:gate_open
gate_pre..v..gate_post..'_closed',-- 11. " " for cottages:gate_closed
};
-- normal wood does have a number of nodes which might get replaced by more specialized wood types
if( mod_prefix=='default:' and v=='' ) then
local w = 'wood';
data[10] = 'cottages:gate_open';
data[11] = 'cottages:gate_closed';
data[12] = 'default:ladder';
data[13] = 'doors:door_'..w..'_t_1';
data[14] = 'doors:door_'..w..'_t_2';
data[15] = 'doors:door_'..w..'_b_1';
data[16] = 'doors:door_'..w..'_b_2';
data[17] = 'default:bookshelf';
data[18] = 'default:chest';
data[19] = 'default:chest_locked';
data[20] = 'stairs:stair_'..w..'upside_down';
data[21] = 'stairs:slab_'..w..'upside_down';
data[22] = 'doors:trapdoor_open';
data[23] = 'doors:trapdoor';
-- realtest has some further replacements
elseif( mod_prefix=='trees:' and w_post=='_planks' and t_post=='_log' ) then
data[12] = 'trees:'..v..'_ladder';
data[13] = 'doors:door_'..v..'_t_1';
data[14] = 'doors:door_'..v..'_t_2';
data[15] = 'doors:door_'..v..'_b_1';
data[16] = 'doors:door_'..v..'_b_2';
data[17] = 'decorations:bookshelf_'..v;
data[18] = 'trees:'..v..'_chest';
data[19] = 'trees:'..v..'_chest_locked';
data[20] = 'trees:'..v..'_planks_stair_upside_down';
data[21] = 'trees:'..v..'_planks_slab_upside_down';
data[22] = 'hatches:'..v..'_hatch_opened_top';
data[23] = 'hatches:'..v..'_hatch_opened_bottom';
end
replacements_group['wood'].data[ wood_name ] = data;
-- none of the wood nodes counts as ground
local c_ignore = minetest.get_content_id( 'ignore' );
for _,v in ipairs( data ) do
local id = minetest.get_content_id( v );
if( id and id ~= c_ignore ) then
replacements_group.node_is_ground[ id ] = false;
end
end
if( is_loaded and minetest.get_modpath('mobf_trader') and mobf_trader and mobf_trader.add_trader ) then
-- TODO: check if all offered payments exist
local goods = {
{ data[3].." 4", "default:dirt 24", "default:cobble 24"},
{ data[4].." 4", "default:apple 2", "default:coal_lump 4"},
{ data[4].." 8", "default:pick_stone 1", "default:axe_stone 1"},
{ data[4].." 12", "default:cobble 80", "default:steel_ingot 1"},
{ data[4].." 36", "bucket:bucket_empty 1", "bucket:bucket_water 1"},
{ data[4].." 42", "default:axe_steel 1", "default:mese_crystal 4"},
{ data[6].." 1", "default:mese 10", "default:steel_ingot 48"},
-- leaves are a cheaper way of getting saplings
{ data[5].." 10", "default:cobble 1", "default:dirt 2"}
};
mobf_trader.add_trader( mobf_trader.npc_trader_prototype,
"Trader of "..( v or "unknown" ).." wood",
v.."_wood_v",
goods,
{ "lumberjack" },
{ 'holzfaeller.png' }
);
replacements_group['wood'].traders[ wood_name ] = v..'_wood_v';
end
end
end
-- TODO: there are also upside-down variants sometimes
-- TODO: moreblocks - those may be installed and offer further replacements
-- create a list of all available wood types
replacements_group['wood'].construct_wood_type_list = function()
-- https://github.com/minetest/minetest_game
-- default tree and jungletree; no gates available
replacements_group['wood'].add_material( {'', 'jungle' }, 'default:', '','wood','', 'tree', '','leaves', '','sapling',
'stairs:stair_', 'wood', 'stairs:slab_', 'wood', 'default:fence_','wood', 'NONE', '' );
-- default:pine_needles instead of leaves; no gates available
replacements_group['wood'].add_material( {'pine' }, 'default:', '','wood','', 'tree', '','_needles','','_sapling',
'stairs:stair_', 'wood', 'stairs:slab_', 'wood', 'default:fence_','wood', 'NONE','' );
-- https://github.com/Novatux/mg
-- trees from nores mapgen
replacements_group['wood'].add_material( {'savanna', 'pine' },'mg:', '','wood','', 'tree', '','leaves', '','sapling',
'stairs:stair_','wood', 'stairs:slab_','wood', 'NONE','', 'NONE','');
-- https://github.com/VanessaE/moretrees
-- minus the jungletree (already in default)
local moretrees_treelist = {"beech","apple_tree","oak","sequoia","birch","palm","spruce","pine","willow","acacia","rubber_tree","fir" };
replacements_group['wood'].add_material( moretrees_treelist, 'moretrees:', '', '_planks', '','_trunk', '','_leaves','','_sapling',
'moretrees:stair_','_planks', 'moretrees:slab_','_planks', 'NONE','', 'NONE','');
-- https://github.com/tenplus1/ethereal
-- ethereal does not have a common naming convention for leaves
replacements_group['wood'].add_material( {'acacia','redwood'},'ethereal:', '','_wood', '','_trunk', '','_leaves', '','_sapling',
'stairs:stair_','_wood', 'stairs:slab_','_wood', 'ethereal:fence_','', 'ethereal:','gate');
-- frost has another sapling type...
replacements_group['wood'].add_material( {'frost'}, 'ethereal:', '','_wood', '','_tree', '','_leaves', '','_tree_sapling',
'stairs:stair_','_wood', 'stairs:slab_','_wood', 'ethereal:fence_','wood', 'ethereal:','woodgate' );
-- those tree types do not use typ_leaves, but typleaves instead...
replacements_group['wood'].add_material( {'yellow'}, 'ethereal:', '','_wood', '','_trunk', '','leaves', '','_tree_sapling',
'stairs:stair_','_wood', 'stairs:slab_','_wood', 'ethereal:fence_','wood', 'ethereal:','gate' );
-- banana has a diffrent fence type....
replacements_group['wood'].add_material( {'banana'}, 'ethereal:', '','_wood', '','_trunk', '','leaves', '','_tree_sapling',
'stairs:stair_','_wood', 'stairs:slab_','_wood', 'ethereal:fence_', '', 'ethereal:','gate' );
-- palm has another name for the sapling again...
replacements_group['wood'].add_material( {'palm'}, 'ethereal:', '','_wood', '','_trunk', '','leaves', '','_sapling',
'stairs:stair_','_wood', 'stairs:slab_','_wood', 'ethereal:fence_', '', 'ethereal:','gate' );
-- the leaves are called willow_twig here...
replacements_group['wood'].add_material( {'willow'}, 'ethereal:', '','_wood', '','_trunk', '','_twig', '','_sapling',
'stairs:stair_','_wood', 'stairs:slab_','_wood', 'ethereal:fence_', '', 'ethereal:','gate' );
-- mushroom has its own name; it works quite well as a wood replacement; the red cap is used as leaves
-- the stairs are also called slightly diffrently (end in _trunk instead of _wood)
replacements_group['wood'].add_material( {'mushroom'}, 'ethereal:', '','_pore', '','_trunk', '','', '','_sapling',
'stairs:stair_','_trunk', 'stairs:slab_','_trunk', 'ethereal:fence_', '', 'ethereal:','gate' );
-- https://github.com/VanessaE/realtest_game
local realtest_trees = {'ash','aspen','birch','maple','chestnut','pine','spruce'};
replacements_group['wood'].add_material( realtest_trees, 'trees:', '','_planks', '','_log', '','_leaves', '','_sapling',
'trees:','_planks_stair', 'trees:','_planks_slab', 'fences:','_fence', 'NONE','' );
-- https://github.com/Gael-de-Sailly/Forest
local forest_trees = {'oak','birch','willow','fir','mirabelle','cherry','plum','beech','ginkgo','lavender'};
replacements_group['wood'].add_material( forest_trees, 'forest:', '', '_wood', '','_tree', '','_leaves', '','_sapling',
'stairs:stair_','_wood', 'stairs:slab_','_wood', 'NONE','', 'NONE','' );
-- https://github.com/bas080/trees
replacements_group['wood'].add_material( {'mangrove','palm','conifer'},'trees:', 'wood_','', 'tree_','', 'leaves_','', 'sapling_','',
'stairs:stair_','_wood', 'stairs:slab_','_wood', 'NONE','', 'NONE','' );
-- https://github.com/PilzAdam/farming_plus
-- TODO: this does not come with its own wood... banana and cocoa trees (only leaves, sapling and fruit)
-- TODO: farming_plus:TREETYP_sapling farming_plus:TREETYP_leaves farming_plus:TREETYP
-- TODO: in general: add fruits as replacements for apples
end
-- actually construct the data structure once
replacements_group['wood'].construct_wood_type_list();

View File

@ -0,0 +1,114 @@
local rotate_facedir = function(facedir)
return ({1, 2, 3, 0,
13, 14, 15, 12,
17, 18, 19, 16,
9, 10, 11, 8,
5, 6, 7, 4,
21, 22, 23, 20})[facedir+1]
end
-- accessd through handle_schematics.mirror_facedir[ (rotation%2)+1 ][ facedir+1 ]
handle_schematics.mirror_facedir =
{{ 2, 1, 0, 3, -- 0, 1, 2, 3
8, 9, 10, 11, -- 4, 5, 6, 7
4, 5, 6, 7, -- 8, 9,10,11
12, 13, 14, 15, --12,13,14,15
16, 17, 18, 19, --16,17,18,19
22, 21, 20, 23 --20,21,22,23
},
{ 0, 3, 2, 1, -- 0, 1, 2, 3
4, 7, 6, 5, -- 4, 5, 6, 7
8, 9, 10, 11, -- 8, 9,10,11
16, 17, 18, 19, --12,13,14,15
12, 15, 14, 13, --16,17,18,19
20, 23, 22, 21 --20,21,22,23
}};
local rotate_wallmounted = function(wallmounted)
return ({0, 1, 5, 4, 2, 3})[wallmounted+1]
end
handle_schematics.get_param2_rotated = function( paramtype2, p2 )
local p2r = {};
p2r[ 1 ] = p2;
if( paramtype2 == 'wallmounted' ) then
for i = 2,4 do
p2r[ i ] = rotate_wallmounted( p2r[ i-1 ]);
end
elseif( paramtype2 == 'facedir' ) then
for i = 2,4 do
p2r[ i ] = rotate_facedir( p2r[ i-1 ]);
end
p2r[5]=1; -- indicate that it is wallmounted
else
return { p2, p2, p2, p2 };
end
return p2r;
end
handle_schematics.mirrored_node = {};
handle_schematics.add_mirrored_node_type = function( name, mirrored_name )
handle_schematics.mirrored_node[ name ] = mirrored_name;
local id = minetest.get_content_id( name );
local id_mi = minetest.get_content_id( mirrored_name );
local c_ignore = minetest.get_content_id( 'ignore' );
if( id and id_mi and id ~= c_ignore and id_mi ~= c_ignore ) then
handle_schematics.mirrored_node[ id ] = id_mi;
end
end
local door_materials = {'wood','steel','glass','obsidian_glass'};
for _,material in ipairs( door_materials ) do
handle_schematics.add_mirrored_node_type( 'doors:door_'..material..'_b_1', 'doors:door_'..material..'_b_2' );
handle_schematics.add_mirrored_node_type( 'doors:door_'..material..'_t_1', 'doors:door_'..material..'_t_2' );
handle_schematics.add_mirrored_node_type( 'doors:door_'..material..'_b_2', 'doors:door_'..material..'_b_1' );
handle_schematics.add_mirrored_node_type( 'doors:door_'..material..'_t_2', 'doors:door_'..material..'_t_1' );
end
handle_schematics.rotation_table = {};
handle_schematics.rotation_table[ 'facedir' ] = {};
handle_schematics.rotation_table[ 'wallmounted' ] = {};
for paramtype2,v in pairs( handle_schematics.rotation_table ) do
for param2 = 0,23 do
if( param2 < 6 or paramtype2 == 'facedir' ) then
local param2list = handle_schematics.get_param2_rotated( paramtype2, param2);
handle_schematics.rotation_table[ paramtype2 ][ param2+1 ] = {};
for rotation = 0,3 do
local np2 = param2list[ rotation + 1];
local mirror_x = np2;
local mirror_z = np2;
-- mirror_x
if( #param2list==5) then
mirror_x = handle_schematics.mirror_facedir[ (( rotation +1)%2)+1 ][ np2+1 ];
elseif( #param2list<5
and (( rotation%2==1 and (np2==4 or np2==5))
or ( rotation%2==0 and (np2==2 or np2==3)))) then
mirror_x = param2list[ ( rotation + 2)%4 +1];
end
-- mirror_z
if( #param2list==5) then
mirror_z = handle_schematics.mirror_facedir[ (rotation %2)+1 ][ np2+1 ];
elseif( #param2list<5
and (( rotation%2==0 and (np2==4 or np2==5))
or ( rotation%2==1 and (np2==2 or np2==3)))) then
mirror_z = param2list[ ( rotation + 2)%4 +1];
end
handle_schematics.rotation_table[ paramtype2 ][ param2+1 ][ rotation+1 ] = { np2, mirror_x, mirror_z };
end
end
end
end

View File

@ -0,0 +1,91 @@
-- reserve the namespace
save_restore = {}
-- TODO: if this gets more versatile, add sanity checks for filename
-- TODO: apart from allowing filenames, schems/<filename> also needs to be allowed
-- TODO: save and restore ought to be library functions and not implemented in each individual mod!
save_restore.save_data = function( filename, data )
local path = minetest.get_worldpath()..'/'..filename;
local file = io.open( path, 'w' );
if( file ) then
file:write( minetest.serialize( data ));
file:close();
else
print("[save_restore] Error: Savefile '"..tostring( path ).."' could not be written.");
end
end
save_restore.restore_data = function( filename )
local path = minetest.get_worldpath()..'/'..filename;
local file = io.open( path, 'r' );
if( file ) then
local data = file:read("*all");
file:close();
return minetest.deserialize( data );
else
print("[save_restore] Error: Savefile '"..tostring( path ).."' not found.");
return {}; -- return empty table
end
end
save_restore.file_exists = function( filename )
local path = minetest.get_worldpath()..'/'..filename;
local file = save_restore.file_access( path, 'r' );
if( file ) then
file:close();
return true;
end
return;
end
save_restore.create_directory = function( filename )
local path = minetest.get_worldpath()..'/'..filename;
if( not( save_restore.file_exists( filename ))) then
if( minetest.mkdir ) then
minetest.mkdir( minetest.get_worldpath().."/schems");
else
os.execute("mkdir \""..minetest.get_worldpath().."/schems".. "\"");
end
end
end
-- we only need the function io.open in a version that can read schematic files from diffrent places,
-- even if a secure environment is enforced; this does require an exception for the mod
local ie_io_open = io.open;
if( minetest.request_insecure_environment ) then
local ie, req_ie = _G, minetest.request_insecure_environment
if req_ie then ie = req_ie() end
if ie then
ie_io_open = ie.io.open;
end
end
-- only a certain type of files can be read and written
save_restore.file_access = function( path, params )
if( (params=='r' or params=='rb')
and ( string.find( path, '.mts', -4 )
or string.find( path, '.schematic', -11 )
or string.find( path, '.we', -3 )
or string.find( path, '.wem', -4 ) )) then
return ie_io_open( path, params );
elseif( (params=='w' or params=='wb')
and ( string.find( path, '.mts', -4 )
or string.find( path, '.schematic', -11 )
or string.find( path, '.we', -3 )
or string.find( path, '.wem', -4 ) )) then
return ie_io_open( path, params );
end
end

Binary file not shown.

After

Width:  |  Height:  |  Size: 261 B

View File

@ -0,0 +1,402 @@
-- based on:
-- # Minecraft to Minetest WE schematic MCEdit filter
-- # by sfan5
-- #blockdata -1 means ignore
local P2_IGNORE = -1;
-- #blockdata -2 means copy without change
local P2_COPY = -2;
-- #blockdata -3 means copy and convert the mc facedir value to mt facedir
local P2_CONVERT= -3;
-- #blockdata -4 is for stairs to support upside down ones
local P2_STAIR = -4;
-- #blockdata selects one of the listed subtypes
local P2_SELECT = -5;
-- #Reference MC: http://media-mcw.cursecdn.com/8/8c/DataValuesBeta.png
-- #Reference MT:
-- # https://github.com/minetest/common/blob/master/mods/default/init.lua
-- # https://github.com/minetest/common/blob/master/mods/wool/init.lua
-- # https://github.com/minetest/common/blob/master/mods/stairs/init.lua
local conversionTable = {
-- #blockid blockdata minetest-nodename
-- [0] = {P2_IGNORE, "air"},
[1] = {P2_IGNORE, "default:stone"},
-- 0: stone; 1: granite; 2: polished granite;
-- 3: diorite; 4: polished diorite; 5: andesite;
-- 6: polished andesite
[2] = {P2_IGNORE, "default:dirt_with_grass"},
[3] = {P2_IGNORE, "default:dirt"},
-- 0: dirt; 1: coarse dirt; 2: podzol
[4] = {P2_IGNORE, "default:cobble"},
[5] = {P2_SELECT, { [0]="default:wood",
[1]="moretrees:spruce_planks",
[2]="moretrees:birch_planks",
[3]="default:junglewood",
[4]="moretrees:acacia_planks",
[5]="moretrees:oak_planks"}},
[6] = {P2_SELECT, { [0]="default:wood",
[1]="moretrees:spruce_sapling",
[2]="moretrees:birch_sapling",
[3]="default:junglesapling",
[4]="moretrees:acacia_sapling",
[5]="moretrees:oak_sapling"}},
[7] = {P2_IGNORE, "minecraft:bedrock"}, --# FIXME Bedrock
[8] = {P2_IGNORE, "default:water_flowing"},
[9] = {P2_IGNORE, "default:water_source"},
[10] = {P2_IGNORE, "default:lava_flowing"},
[11] = {P2_IGNORE, "default:lava_source"},
[12] = {P2_SELECT, { [0]="default:sand",
[1]="default:desert_sand"}},
[13] = {P2_IGNORE, "default:gravel"},
[14] = {P2_IGNORE, "default:stone_with_gold"},
[15] = {P2_IGNORE, "default:stone_with_iron"},
[16] = {P2_IGNORE, "default:stone_with_coal"},
-- TODO: the trees have facedir
[17] = {P2_SELECT, { [0]="default:tree",
[1]="moretrees:spruce_trunk",
[2]="moretrees:birch_trunk",
[3]="default:jungletree",
[4]="moretrees:acacia_trunk",
[5]="moretrees:oak_trunk"}},
[18] = {P2_SELECT, { [0]="default:leaves",
[1]="moretrees:spruce_leaves",
[2]="moretrees:birch_leaves",
[3]="default:jungleleaves",
[4]="default:leaves",
[5]="moretrees:spruce_leaves",
[6]="moretrees:birch_leaves",
[7]="default:jungleleaves",
[8]="default:leaves",
[9]="moretrees:spruce_leaves",
[10]="moretrees:birch_leaves",
[11]="default:jungleleaves",
[12]="default:leaves",
[13]="moretrees:spruce_leaves",
[14]="moretrees:birch_leaves",
[15]="default:jungleleaves"}},
[19] = {P2_CONVERT,"minecraft:sponge"},
[20] = {P2_IGNORE, "default:glass"},
[21] = {P2_IGNORE, "default:stone_with_copper"}, -- Lapis Lazuli Ore
[22] = {P2_IGNORE, "default:copperblock"}, -- Lapis Lazuli Block
[23] = {P2_CONVERT,"minecraft:dispenser"},
[24] = {P2_SELECT, { [0]="default:sandstone",
[1]="default:sandstonebrick",
[2]="default:sandstone"}},
[25] = {P2_CONVERT,"minecraft:nodeblock"},
[26] = {P2_CONVERT,"beds:bed"}, -- TODO: might require special handling?
[27] = {P2_CONVERT,"minecraft:golden_rail"},
[28] = {P2_CONVERT,"minecraft:detector_rail"},
-- 29: sticky piston
[30] = {P2_CONVERT,"minecraft:web"},
[31] = {P2_SELECT, { [0]="default:dry_shrub",
[1]="default:grass_4",
[2]="ferns:fern_02"}},
[32] = {P2_IGNORE, "default:dry_shrub"},
-- 34: piston head
[35] = {P2_SELECT, { [0]="wool:white",
[1]="wool:orange",
[2]="wool:magenta",
[3]="wool:light_blue",
[4]="wool:yellow",
[5]="wool:green",
[6]="wool:pink",
[7]="wool:dark_grey",
[8]="wool:grey",
[9]="wool:cyan",
[10]="wool:violet",
[11]="wool:blue",
[12]="wool:brown",
[13]="wool:dark_green",
[14]="wool:red",
[15]="wool:black"}},
-- 36: piston extension
[37] = {P2_IGNORE, "flowers:dandelion_yellow"},
[38] = {P2_SELECT, { [0]="flowers:rose",
[1]="flowers:geranium",
[2]="flowers:viola",
[3]="flowers:dandelion_white",
[4]="tulips:red",
[5]="flowers:tulip",
[6]="tulips:white",
[7]="tulips:pink",
[8]="tulips:black"}},
[41] = {P2_IGNORE, "default:goldblock"},
[42] = {P2_IGNORE, "default:steelblock"},
-- double stone slabs...full blocks?
[43] = {P2_SELECT, { [0]="default:stone",
[1]="default:sandstonebrick",
[2]="default:wood",
[3]="default:cobble",
[4]="default:brick",
[5]="default:stonebrick",
[6]="nether:brick",
[7]="quartz:quartz",
[8]="moreblocks:split_stone_tile",
[9]="default:sandstone"}},
[44] = {P2_SELECT, { [0]="stairs:slab_stone",
[1]="stairs:slab_sandstone",
[2]="stairs:slab_wood",
[3]="stairs:slab_cobble",
[4]="stairs:slab_brick",
[5]="stairs:slab_stonebrick",
[6]="stairs:slab_nether",
[7]="stairs:slab_quartz",
[8]="stairs:slab_stoneupside_down",
[9]="stairs:slab_sandstoneupside_down",
[10]="stairs:slab_woodupside_down",
[11]="stairs:slab_cobbleupside_down",
[12]="stairs:slab_brickupside_down",
[13]="stairs:slab_stonebrickupside_down",
[14]="stairs:slab_netzerupside_down",
[15]="stairs:slab_quartzupside_down"}},
[45] = {P2_IGNORE, "default:brick"},
[46] = {P2_CONVERT,"tnt:tnt"},
[47] = {P2_IGNORE, "default:bookshelf"},
[48] = {P2_IGNORE, "default:mossycobble"},
[49] = {P2_IGNORE, "default:obsidian"},
[50] = {P2_CONVERT,"default:torch"},
[51] = {P2_IGNORE, "fire:basic_flame"},
[52] = {P2_CONVERT,"minecraft:mob_spawner"},
[53] = {P2_STAIR, "stairs:stair_wood"},
[54] = {P2_IGNORE, "default:chest"},
[56] = {P2_IGNORE, "default:stone_with_diamond"},
[57] = {P2_IGNORE, "default:diamondblock"},
[58] = {P2_CONVERT,"minecraft:crafting_table"},
[59] = {P2_IGNORE, "farming:wheat_8"},
[60] = {P2_IGNORE, "farming:soil_wet"},
[61] = {P2_IGNORE, "default:furnace"},
[62] = {P2_IGNORE, "default:furnace_active"},
[63] = {P2_IGNORE, "default:sign_wall"},
[64] = {P2_IGNORE, "doors:door_wood_t_1"},
[65] = {P2_IGNORE, "default:ladder"},
[66] = {P2_IGNORE, "default:rail"},
[67] = {P2_STAIR, "stairs:stair_cobble"},
[68] = {P2_CONVERT,"default:sign_wall"},
[71] = {P2_IGNORE, "doors:door_steel_t_1"},
[78] = {P2_IGNORE, "default:snow"},
[79] = {P2_IGNORE, "default:ice"},
[80] = {P2_IGNORE, "default:snowblock"},
[81] = {P2_IGNORE, "default:cactus"},
[82] = {P2_IGNORE, "default:clay"},
[83] = {P2_IGNORE, "default:papyrus"},
[84] = {P2_CONVERT,"minecraft:jukebox"},
[85] = {P2_IGNORE, "default:fence_wood"},
[86] = {P2_CONVERT,"farming:pumpkin"},
[91] = {P2_CONVERT,"farming:pumpkin_face_light"},
[92] = {P2_CONVERT,"minecraft:cake"},
[95] = {P2_IGNORE, "minecraft:stained_glass"}, -- TODO
[96] = {P2_CONVERT,"doors:trapdoor"},
[97] = {P2_IGNORE, "minecraft:monster_egg"},
[98] = {P2_IGNORE, "default:stonebrick"},
[108]= {P2_STAIR, "stairs:stair_brick"},
[109]= {P2_CONVERT,"stairs:stair_stonebrick"},
-- TODO: double ... wood slab...
[125]= {P2_SELECT, { [0]="default:wood",
[1]="moretrees:spruce_planks",
[2]="moretrees:birch_planks",
[3]="default:junglewood",
[4]="moretrees:acacia_planks",
[5]="moretrees:oak_planks"}},
[125]= {P2_IGNORE, "default:wood"},
[126]= {P2_SELECT, { [0]="stairs:slab_wood",
[1]="stairs:slab_spruce_planks",
[2]="stairs:slab_birch_planks",
[3]="stairs:slab_junglewood",
[4]="stairs:slab_acacia_planks",
[5]="stairs:slab_oak_planks",
[8]="stairs:slab_woodupside_down",
[9]="stairs:slab_spruce_planksupside_down",
[10]="stairs:slab_birch_planksupside_down",
[11]="stairs:slab_junglewoodupside_down",
[12]="stairs:slab_acacia_planksupside_down",
[13]="stairs:slab_oak_planksupside_down"}},
[126]= {P2_IGNORE, "stairs:slab_wood"},
[128]= {P2_STAIR, "stairs:stair_sandstone"},
[129]= {P2_IGNORE, "default:stone_with_mese"},
[133]= {P2_IGNORE, "default:mese"},
[134]= {P2_STAIR, "stairs:stair_wood"},
[135]= {P2_STAIR, "stairs:stair_wood"},
[136]= {P2_STAIR, "stairs:stair_junglewood"},
-- #Mesecons section
-- # Reference: https://github.com/Jeija/minetest-mod-mesecons/blob/master/mesecons_alias/init.lua
[25] = {P2_IGNORE, "mesecons_noteblock:noteblock"},
[29] = {P2_CONVERT,"mesecons_pistons:piston_sticky_off"},
[33] = {P2_CONVERT,"mesecons_pistons:piston_normal_off"},
[55] = {P2_IGNORE, "mesecons:wire_00000000_off"},
[69] = {P2_CONVERT,"mesecons_walllever:wall_lever_off"},
[70] = {P2_IGNORE, "mesecons_pressureplates:pressure_plate_stone_off"},
[72] = {P2_IGNORE, "mesecons_pressureplates:pressure_plate_wood_off"},
[73] = {P2_IGNORE, "default:stone_with_mese"},
[74] = {P2_IGNORE, "default:stone_with_mese"},
[75] = {P2_CONVERT,"mesecons_torch:torch_off"},
[76] = {P2_CONVERT,"mesecons_torch:torch_on"},
[77] = {P2_CONVERT,"mesecons_button:button_off"},
[93] = {P2_CONVERT,"mesecons_delayer:delayer_off_1"},
[94] = {P2_CONVERT,"mesecons_delayer:delayer_on_1"},
-- see mod https://github.com/doyousketch2/stained_glass
[95] = {P2_SELECT, { [0]="default:glass", -- TODO
[1]="stained_glass:orange__",
[2]="stained_glass:magenta__",
[3]="stained_glass:skyblue__",
[4]="stained_glass:yellow__",
[5]="stained_glass:lime__",
[6]="stained_glass:redviolet__",
[7]="stained_glass:dark_grey__", -- TODO
[8]="stained_glass:grey__", -- TODO
[9]="stained_glass:cyan__",
[10]="stained_glass:violet__",
[11]="stained_glass:blue__",
[12]="stained_glass:orange_dark_",
[13]="stained_glass:green__",
[14]="stained_glass:red__",
[15]="stained_glass:black__"}}, -- TODO
[101]= {P2_CONVERT,"xpanes:bar"},
[102]= {P2_CONVERT,"xpanes:pane"},
[103]= {P2_IGNORE, "farming:melon"},
[104]= {P2_IGNORE, "minecraft:pumpkin_stem"},
[105]= {P2_IGNORE, "minecraft:melon_stem"},
[106]= {P2_CONVERT,"vines:vine"},
[107]= {P2_CONVERT,"minecraft:fence_gate"},
[108]= {P2_STAIR, "stairs:stair_brick"},
[109]= {P2_STAIR, "stairs:stair_stonebrick"},
[110]= {P2_CONVERT,"minecraft:mycelium"},
[111]= {P2_CONVERT,"flowers:waterlily"},
[112]= {P2_CONVERT,"minecraft:nether_brick"},
[113]= {P2_CONVERT,"minecraft:nether_brick_fence"},
[114]= {P2_CONVERT,"minecraft:nether_brick_stairs"},
[115]= {P2_CONVERT,"minecraft:nether_wart"},
[116]= {P2_CONVERT,"minecraft:enchanting_table"},
[117]= {P2_CONVERT,"minecraft:brewing_stand"},
[118]= {P2_CONVERT,"minecraft:cauldron"},
[119]= {P2_CONVERT,"minecraft:end_portal"},
[120]= {P2_CONVERT,"minecraft:end_portal_frame"},
[121]= {P2_CONVERT,"minecraft:end_stone"},
[122]= {P2_CONVERT,"minecraft:dragon_egg"},
[123]= {P2_IGNORE, "mesecons_lightstone_red_off"},
[124]= {P2_IGNORE, "mesecons_lightstone_red_on"},
[125]= {P2_CONVERT,"minecraft:double_wooden_slab"},
[126]= {P2_CONVERT,"stairs:slab_wood"},
[127]= {P2_CONVERT,"farming_plus:cocoa"},
[137]= {P2_IGNORE, "mesecons_commandblock:commandblock_off"},
[151]= {P2_IGNORE, "mesecons_solarpanel:solar_panel_off"},
[152]= {P2_IGNORE, "default:mese"},
-- see mod https://github.com/tenplus1/bakedclay
[159] = {P2_SELECT, { [0]="bakedclay:white",
[1]="bakedclay:orange",
[2]="bakedclay:magenta",
[3]="bakedclay:light_blue", -- TODO
[4]="bakedclay:yellow",
[5]="bakedclay:green",
[6]="bakedclay:pink",
[7]="bakedclay:dark_grey",
[8]="bakedclay:grey",
[9]="bakedclay:cyan",
[10]="bakedclay:violet",
[11]="bakedclay:blue",
[12]="bakedclay:brown",
[13]="bakedclay:dark_green",
[14]="bakedclay:red",
[15]="bakedclay:black"}},
-- see mod mccarpet https://forum.minetest.net/viewtopic.php?t=7419
[171] = {P2_SELECT, { [0]="mccarpet:white",
[1]="mccarpet:orange",
[2]="mccarpet:magenta",
[3]="mccarpet:light_blue", -- TODO
[4]="mccarpet:yellow",
[5]="mccarpet:green",
[6]="mccarpet:pink",
[7]="mccarpet:dark_grey",
[8]="mccarpet:grey",
[9]="mccarpet:cyan",
[10]="mccarpet:violet",
[11]="mccarpet:blue",
[12]="mccarpet:brown",
[13]="mccarpet:dark_green",
[14]="mccarpet:red",
[15]="mccarpet:black"}},
[181] = {P2_SELECT, { [0]="default:desert_stonebrick",
[1]="default:desertstone"}},
-- #Nether section
-- # Reference: https://github.com/PilzAdam/nether/blob/master/init.lua
[43] = {P2_IGNORE, "nether:brick"},
[87] = {P2_IGNORE, "nether:rack"},
[88] = {P2_IGNORE, "nether:sand"},
[89] = {P2_IGNORE, "nether:glowstone"},
[90] = {P2_CONVERT,"nether:portal"},
-- #Riesenpilz Section
-- # Reference: https://github.com/HybridDog/riesenpilz/blob/master/init.lua
[39] = {P2_IGNORE, "riesenpilz:brown"},
[40] = {P2_IGNORE, "riesenpilz:red"},
[99] = {P2_CONVERT,"riesenpilz:head_brown"},
[100]= {P2_CONVERT,"riesenpilz:head_brown"},
}
local mc2mtFacedir = function(blockdata)
-- #Minetest
-- # x+ = 2
-- # x- = 3
-- # z+ = 1
-- # z- = 0
-- #Minecraft
-- # x+ = 3
-- # x- = 1
-- # z+ = 0
-- # z- = 2
local tbl = {
[3]= 2,
[1]= 1,
[0]= 3,
[2]= 0,
}
if( tbl[ blockdata ] ) then
return tbl[ blockdata ];
-- this happens with i.e. wallmounted torches...
else
return blockdata;
end
end
local mc2mtstairs = function( name, blockdata)
if blockdata >= 4 then
return {name.. "upside_down", mc2mtFacedir(blockdata - 4)}
else
return {name, mc2mtFacedir(blockdata)}
end
end
-- returns {translated_node_name, translated_param2}
handle_schematics.findMC2MTConversion = function(blockid, blockdata)
if (blockid == 0 ) then
return {"air",0};
-- fallback
elseif( not( conversionTable[ blockid ])) then
return { "minecraft:"..tostring( blockid )..'_'..tostring( blockdata ), 0};
end
local conv = conversionTable[ blockid ];
if( conv[1] == P2_IGNORE ) then
return { conv[2], 0};
elseif( conv[1] == P2_COPY ) then
return { conv[2], blockdata};
elseif( conv[1] == P2_CONVERT) then
return { conv[2], mc2mtFacedir(blockdata)};
elseif( conv[1] == P2_STAIR ) then
return mc2mtstairs(conv[2], blockdata);
elseif( conv[1] == P2_SELECT
and conv[2][ blockdata ] ) then
return { conv[2][ blockdata ], 0};
elseif( conv[1] == P2_SELECT
and not(conv[2][ blockdata ] )) then
return { conv[2][0], 0};
else
return { conv[2], 0 };
end
return {air, 0};
end

View File

@ -0,0 +1,138 @@
------------------------------------------------------------------------------------------
-- This is the file
-- https://github.com/Uberi/Minetest-WorldEdit/blob/master/worldedit/serialization.lua
-- Changes:
-- * worldedit namespace renamed to worldeit_file
-- * eliminiated functions that are not needed
-- * made function load_schematic non-local
-- * originx, originy and originz are now passed as parameters to worldedit_file.load_schematic;
-- they are required for an old file format
------------------------------------------------------------------------------------------
worldedit_file = {} -- add the namespace
--- Schematic serialization and deserialiation.
-- @module worldedit.serialization
worldedit_file.LATEST_SERIALIZATION_VERSION = 5
local LATEST_SERIALIZATION_HEADER = worldedit_file.LATEST_SERIALIZATION_VERSION .. ":"
--[[
Serialization version history:
1: Original format. Serialized Lua table with a weird linked format...
2: Position and node seperated into sub-tables in fields `1` and `2`.
3: List of nodes, one per line, with fields seperated by spaces.
Format: <X> <Y> <Z> <Name> <Param1> <Param2>
4: Serialized Lua table containing a list of nodes with `x`, `y`, `z`,
`name`, `param1`, `param2`, and `meta` fields.
5: Added header and made `param1`, `param2`, and `meta` fields optional.
Header format: <Version>,<ExtraHeaderField1>,...:<Content>
--]]
--- Reads the header of serialized data.
-- @param value Serialized WorldEdit data.
-- @return The version as a positive natural number, or 0 for unknown versions.
-- @return Extra header fields as a list of strings, or nil if not supported.
-- @return Content (data after header).
function worldedit_file.read_header(value)
if value:find("^[0-9]+[%-:]") then
local header_end = value:find(":", 1, true)
local header = value:sub(1, header_end - 1):split(",")
local version = tonumber(header[1])
table.remove(header, 1)
local content = value:sub(header_end + 1)
return version, header, content
end
-- Old versions that didn't include a header with a version number
if value:find("([+-]?%d+)%s+([+-]?%d+)%s+([+-]?%d+)") and not value:find("%{") then -- List format
return 3, nil, value
elseif value:find("^[^\"']+%{%d+%}") then
if value:find("%[\"meta\"%]") then -- Meta flat table format
return 2, nil, value
end
return 1, nil, value -- Flat table format
elseif value:find("%{") then -- Raw nested table format
return 4, nil, value
end
return nil
end
--- Loads the schematic in `value` into a node list in the latest format.
-- Contains code based on [table.save/table.load](http://lua-users.org/wiki/SaveTableToFile)
-- by ChillCode, available under the MIT license.
-- @return A node list in the latest format, or nil on failure.
function worldedit_file.load_schematic(value, we_origin)
local version, header, content = worldedit_file.read_header(value)
local nodes = {}
if version == 1 or version == 2 then -- Original flat table format
local tables = minetest.deserialize(content)
if not tables then return nil end
-- Transform the node table into an array of nodes
for i = 1, #tables do
for j, v in pairs(tables[i]) do
if type(v) == "table" then
tables[i][j] = tables[v[1]]
end
end
end
nodes = tables[1]
if version == 1 then --original flat table format
for i, entry in ipairs(nodes) do
local pos = entry[1]
entry.x, entry.y, entry.z = pos.x, pos.y, pos.z
entry[1] = nil
local node = entry[2]
entry.name, entry.param1, entry.param2 = node.name, node.param1, node.param2
entry[2] = nil
end
end
elseif version == 3 or version=="3" then -- List format
if( not( we_origin ) or #we_origin <3) then
we_origin = { 0, 0, 0 };
end
for x, y, z, name, param1, param2 in content:gmatch(
"([+-]?%d+)%s+([+-]?%d+)%s+([+-]?%d+)%s+" ..
"([^%s]+)%s+(%d+)%s+(%d+)[^\r\n]*[\r\n]*") do
param1, param2 = tonumber(param1), tonumber(param2)
table.insert(nodes, {
x = we_origin[1] + tonumber(x),
y = we_origin[2] + tonumber(y),
z = we_origin[3] + tonumber(z),
name = name,
param1 = param1 ~= 0 and param1 or nil,
param2 = param2 ~= 0 and param2 or nil,
})
end
elseif version == 4 or version == 5 then -- Nested table format
if not jit then
-- This is broken for larger tables in the current version of LuaJIT
nodes = minetest.deserialize(content)
else
-- XXX: This is a filthy hack that works surprisingly well - in LuaJIT, `minetest.deserialize` will fail due to the register limit
nodes = {}
content = content:gsub("return%s*{", "", 1):gsub("}%s*$", "", 1) -- remove the starting and ending values to leave only the node data
local escaped = content:gsub("\\\\", "@@"):gsub("\\\"", "@@"):gsub("(\"[^\"]*\")", function(s) return string.rep("@", #s) end)
local startpos, startpos1, endpos = 1, 1
while true do -- go through each individual node entry (except the last)
startpos, endpos = escaped:find("},%s*{", startpos)
if not startpos then
break
end
local current = content:sub(startpos1, startpos)
local entry = minetest.deserialize("return " .. current)
table.insert(nodes, entry)
startpos, startpos1 = endpos, endpos
end
local entry = minetest.deserialize("return " .. content:sub(startpos1)) -- process the last entry
table.insert(nodes, entry)
end
else
return nil
end
return nodes
end

View File

@ -0,0 +1,187 @@
-----------------------------------------------------------------------------
-- configuration values which you can adjust according to your liking
-----------------------------------------------------------------------------
-- set to false if you do not want to have any villages spawning
mg_villages.ENABLE_VILLAGES = true;
-- generate one random building for each mg_villages.INVERSE_HOUSE_DENSITY th mapchunk;
-- set to 0 in order to disable spawning of these lone buildings outside villages
mg_villages.INVERSE_HOUSE_DENSITY = 4;
-- cover some villages with artificial snow; probability: 1/mg_villages.artificial_snow_probability
mg_villages.artificial_snow_probability = 10;
-- if set to true, soil around villaes will get special soil-snow instead of plant + snow cover
mg_villages.use_soil_snow = false;
-- only place roads if there are at least that many buildings in the village
mg_villages.MINIMAL_BUILDUNGS_FOR_ROAD_PLACEMENT = 4;
-- players without the mg_villages priv can only see villages which are less than that many blocks away
-- from them when using the /vmap command
mg_villages.VILLAGE_DETECT_RANGE = 400;
-- if set to true, only players which have the mg_villages priv can use the "/visit <village nr>"
-- command which allows teleporting to the village with the given number
mg_villages.REQUIRE_PRIV_FOR_TELEPORT = false;
-- if set to true, players cannot modify spawned villages without buying the house from the village first
mg_villages.ENABLE_PROTECTION = true;
-- the first village - the one the player spawns in - will be of this type
mg_villages.FIRST_VILLAGE_TYPE = 'medieval';
-- the mapgen will disregard mapchunks where min.y > mg_villages.MAX_HEIGHT_TREATED;
-- you can set this value to 64 if you have a slow machine and a mapgen which does not create extreme mountains
-- (or if you don't care if extreme mountains may create burried villages occasionally)
mg_villages.MAX_HEIGHT_TREATED = 200;
-- choose the debug level you want
mg_villages.DEBUG_LEVEL = mg_villages.DEBUG_LEVEL_NORMAL
-- if set to true (or anything else but nil or false), highlandpools by paramat (see
-- https://forum.minetest.net/viewtopic.php?t=8400) will be created
mg_villages.CREATE_HIGHLANDPOOLS = true
-- background image for the /vmap command
-- RealTest comes with a diffrent texture
if( minetest.get_modpath('grounds') and minetest.get_modpath('joiner_table')) then
mg_villages.MAP_BACKGROUND_IMAGE = "default_dirt_grass.png";
elseif( minetest.registered_nodes[ 'default:dirt_with_grass'] ) then
mg_villages.MAP_BACKGROUND_IMAGE = "default_grass.png";
else
mg_villages.MAP_BACKGROUND_IMAGE = "";
end
-- if set to true, the outer buildings in medieval villages will be fields; this is not very convincing yet
-- currently not really used; does not look as good as expected
mg_villages.medieval_subtype = false;
-- set this to true if you want to use normal lava - but beware: charachoal villages may cause bushfires!
--mg_villages.use_normal_unsafe_lava = false;
-----------------------------------------------------------------------------
-- decrese these values slightly if you want MORE trees around your villages;
-- increase it if you want to DECREASE the amount of trees around villages
-----------------------------------------------------------------------------
-- on average, every n.th node inside a village area 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
mg_villages.sapling_probability[ minetest.get_content_id( 'default:pinesapling' ) ] = 30;
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
mg_villages.moretrees_treelist = nil;
if( minetest.get_modpath( 'moretrees' )) then
mg_villages.moretrees_treelist = moretrees.treelist;
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
-----------------------------------------------------------------------------
-- no need to change this, unless you add new farming_plus fruits
-----------------------------------------------------------------------------
-- the schematics for buildings of type 'farm_tiny' grow cotton; the farming_plus fruits would be far more fitting
mg_villages.fruit_list = {'carrot','potatoe','orange','rhubarb','strawberry','tomato','cotton'};
-- is farming_plus available? If not, we can't use this
if( not( minetest.get_modpath("farming_plus"))) then
mg_villages.fruit_list = nil;
end
-----------------------------------------------------------------------------
-- players can buy plots in villages with houses on for this price;
-- set according to your liking
-----------------------------------------------------------------------------
-- how much does the player have to pay for a plot with a building?
mg_villages.prices = {
empty = "default:copper_ingot 1", -- plot to build on
-- building types which usually have inhabitants (and thus allow the player
-- who bought the building to modifiy the entire village area minus other
-- buildings)
tent = "default:copper_ingot 1",
hut = "default:copper_ingot 1",
farm_full = "default:gold_ingot 4",
farm_tiny = "default:gold_ingot 2",
lumberjack = "default:gold_ingot 2",
house = "default:gold_ingot 2",
house_large = "default:gold_ingot 4",
tavern = "default:gold_ingot 12",
trader = "default:gold_ingot 2",
-- more or less community buildings
well = "default:gold_ingot 1",
village_square = "default:goldblock 1",
secular = "default:goldblock 2", -- secular buildings, such as libraries ec.
church = "default:goldblock 10",
-- places for mobs to work at; usually without inhabitants
tower = "default:copper_ingot 1",
shed = "default:copper_ingot 2",
pit = "default:copper_ingot 3", -- claytrader pit
mill = "default:gold_ingot 10",
forge = "default:gold_ingot 10",
bakery = "default:gold_ingot 10",
shop = "default:gold_ingot 20",
sawmill = "default:gold_ingot 30",
-- decoration
wagon = "default:tree 10",
bench = "default:tree 4",
-- seperate fields
pasture = "default:copper_ingot 2",
field = "default:copper_ingot 2",
-- chateaus are expensive
chateau = "default:diamondblock 5",
}
-----------------------------------------------------------------------------
-- The values below seldom need adjustment; don't change them unless you
-- know exactly what you are doing.
-----------------------------------------------------------------------------
-- 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;
-- internal variables for village generation
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
-- min and max size are only used in case of them beeing not provided by the village type (see buildings.lua)
mg_villages.VILLAGE_MIN_SIZE = 25
mg_villages.VILLAGE_MAX_SIZE = 90 --55
mg_villages.FIRST_ROADSIZE = 3
mg_villages.BIG_ROAD_CHANCE = 0
-- Enable that for really big villages (there are also really slow to generate)
--[[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]]

View File

@ -0,0 +1,3 @@
This is a continuation of my (Sokomines) fork of Nores mg mapgen.
The fork can be found under https://github.com/Sokomine/mg

View File

@ -0,0 +1,430 @@
-- scm="bla" Name of the file that holds the buildings' schematic. Supported types: .we and .mts (omit the extension!)
-- sizex, sizez, ysize: obsolete
-- yoff=0 how deep is the building burried?
-- pervillage=1 Never generate more than this amount of this building and this type (if set) of building per village.
-- axis=1 Building needs to be mirrored along the x-axis instead of the z-axis because it is initially rotated
-- inh=2 maximum amount of inhabitants the building may hold (usually amount of beds present)
-- if set to i.e. -1, this indicates that a mob is WORKING, but not LIVING here
-- we_origin Only needed for very old .we files (savefile format version 3) which do not start at 0,0,0 but have an offset.
-- price Stack that has to be paid in order to become owner of the plot the building stands on and the building;
-- overrides mg_villages.prices[ building_typ ].
mg_villages.all_buildings_list = {}
local buildings = {
-- the houses the mod came with
{yoff= 0, scm="house", orients={2}, typ='house', weight={nore=1, single=2 }, inh=4},
{yoff= 0, scm="wheat_field", typ='field', weight={nore=1 }, inh=-1},
{yoff= 0, scm="cotton_field", typ='field', weight={nore=1 }, inh=-1},
{yoff= 1, scm="lamp", no_rotate=true, typ='deco', weight={nore=1/5 }},
{yoff=-5, scm="well", no_rotate=true, pervillage=1, typ='well', weight={nore=1 }},
{yoff= 0, scm="fountain", pervillage=3, typ='fountain', weight={nore=1/4 }, axis=1},
{yoff= 0, scm="small_house", orients={3}, typ='house', weight={nore=1, single=2 }, axis=1, inh=2},
{yoff= 0, scm="house_with_garden", orients={1}, typ='house', weight={nore=1, single=2 }, axis=1, inh=3},
{yoff= 0, scm="church", orients={3}, pervillage=1, typ='church', weight={nore=1 }, axis=1, inh=-1},
{yoff= 0, scm="tower", orients={0}, typ='tower', weight={nore=1/7, single=1 }, inh=-1},
{yoff= 0, scm="forge", orients={0}, pervillage=2, typ='forge', weight={nore=1, single=1/3 }, inh=-1},
{yoff= 0, scm="library", orients={1}, pervillage=2, typ='secular', weight={nore=1 }, axis=1, inh=-1},
{yoff= 0, scm="inn", orients={1}, pervillage=4, typ='tavern', weight={nore=1/2, single=1/3 }, axis=1, inh=-1}, -- has room for 4 guests
{yoff= 0, scm="pub", orients={3}, pervillage=2, typ='tavern', weight={nore=1/3, single=1/3 }, axis=1, inh=-1},
-- log cabins by Sokomine (requiring cottages, glasspanes)
{yoff= 0, scm="logcabin1", orients={1}, weight={logcabin=1, single=1}, axis=1, inh=2, typ='hut'},
{yoff= 0, scm="logcabin2", orients={1}, weight={logcabin=1, single=1}, axis=1, inh=2, typ='hut'},
{yoff= 0, scm="logcabin3", orients={1}, weight={logcabin=1, single=1}, axis=1, inh=3, typ='hut'},
{yoff= 0, scm="logcabin4", orients={1}, weight={logcabin=1, single=1}, axis=1, inh=3, typ='hut'},
{yoff= 0, scm="logcabin5", orients={1}, weight={logcabin=1, single=1}, axis=1, inh=1, typ='hut'},
{yoff= 0, scm="logcabin6", orients={1}, weight={logcabin=1, single=1}, axis=1, inh=1, typ='hut'},
{yoff= 0, scm="logcabin7", orients={1}, weight={logcabin=1, single=1}, axis=1, inh=2, typ='hut'},
{yoff= 0, scm="logcabin8", orients={1}, weight={logcabin=1, single=1}, axis=1, inh=2, typ='hut'},
{yoff= 0, scm="logcabin9", orients={1}, weight={logcabin=1, single=1}, axis=1, inh=1, typ='hut'},
{yoff= 0, scm="logcabin10", orients={2}, weight={logcabin=1, single=1}, inh=3, typ='hut'},
{yoff= 0, scm="logcabin11", orients={2}, weight={logcabin=1, single=1}, inh=6, typ='hut'},
{yoff= 0, scm="logcabinpub1", orients={1}, weight={logcabin=1/6, single=1}, pervillage=1, typ='tavern', axis=1, inh=1}, -- +5 guests
{yoff= 0, scm="logcabinpub2", orients={1}, weight={logcabin=1/6, single=1}, pervillage=1, typ='tavern', axis=1, inh=2}, -- +8 guests
{yoff= 0, scm="logcabinpub3", orients={1}, weight={logcabin=1/6, single=1}, pervillage=1, typ='tavern', axis=1, inh=2}, -- +12 guest
-- grass huts (requiring cottages, dryplants, cavestuff/undergrowth, plantlife)
{yoff= 0, scm="grasshut1", orients={2}, weight={grasshut=1, single=1}, inh=3, typ='hut'},
{yoff= 0, scm="grasshut2", orients={2}, weight={grasshut=1, single=1}, inh=10, typ='hut'}, -- community hut for meetings
{yoff= 0, scm="grasshut3", orients={2}, weight={grasshut=1, single=1}, inh=3, typ='hut'},
{yoff= 0, scm="grasshut4", orients={2}, weight={grasshut=1, single=1}, inh=3, typ='hut'},
{yoff= 0, scm="grasshut5", orients={2}, weight={grasshut=1, single=1}, inh=1, typ='hut'},
{yoff= 0, scm="grasshut6", orients={2}, weight={grasshut=1, single=1}, inh=3, typ='hut'},
{yoff= 0, scm="grasshutcenter", orients={2}, pervillage=1, weight={grasshut=2}, typ = 'tavern'}, -- open meeting place
-- for the buildings below, sizex, sizez and ysize are read from the file directly;
-- schematics from Sokomines villages mod (requires cottages)
{scm="church_1", yoff= 0, orients={0}, farming_plus=0, avoid='', typ='church', weight={medieval=4 }, pervillage=1, inh=-1},
-- {scm="church_2_twoelk", yoff= 0, orients={0}, farming_plus=0, avoid='', typ='church', weight={medieval=4}, pervillage=1},
{scm="forge_1", yoff= 0, orients={0}, farming_plus=0, avoid='', typ='forge', weight={medieval=2, single=1/2}, pervillage=1, inh=-1},
{scm="mill_1", yoff= 0, orients={0}, farming_plus=0, avoid='', typ='mill', weight={medieval=2 }, pervillage=1, inh=-1},
{scm="watermill_1", yoff=-3, orients={1}, farming_plus=0, avoid='', typ='mill', weight={medieval=2 }, pervillage=1, inh=-2},
{scm="hut_1", yoff= 0, orients={0}, farming_plus=0, avoid='', typ='hut', weight={medieval=1, single=1 }, inh=1},
{scm="hut_2", yoff= 0, orients={0}, farming_plus=0, avoid='', typ='hut', weight={medieval=1, single=1 }, inh=2},
{scm="farm_full_1", yoff= 0, orients={0}, farming_plus=0, avoid='', typ='farm_full', weight={medieval=1/4, single=1 }, inh=2},
{scm="farm_full_2", yoff= 0, orients={0}, farming_plus=0, avoid='', typ='farm_full', weight={medieval=1/4, single=1 }, inh=5},
{scm="farm_full_3", yoff= 0, orients={0}, farming_plus=0, avoid='', typ='farm_full', weight={medieval=1/4, single=1 }, inh=5},
{scm="farm_full_4", yoff= 0, orients={0}, farming_plus=0, avoid='', typ='farm_full', weight={medieval=1/4, single=1 }, inh=8},
{scm="farm_full_5", yoff= 0, orients={0}, farming_plus=0, avoid='', typ='farm_full', weight={medieval=1/4, single=1 }, inh=5},
{scm="farm_full_6", yoff= 0, orients={0}, farming_plus=0, avoid='', typ='farm_full', weight={medieval=1/4, single=1 }, inh=5},
{scm="farm_tiny_1", yoff= 0, orients={0}, farming_plus=1, avoid='', typ='farm_tiny', weight={medieval=1, single=1 }, inh=2},
{scm="farm_tiny_2", yoff= 0, orients={0}, farming_plus=1, avoid='', typ='farm_tiny', weight={medieval=1, single=1 }, inh=6},
{scm="farm_tiny_3", yoff= 0, orients={0}, farming_plus=1, avoid='', typ='farm_tiny', weight={medieval=1, single=1 }, inh=4},
{scm="farm_tiny_4", yoff= 0, orients={0}, farming_plus=1, avoid='', typ='farm_tiny', weight={medieval=1, single=1 }, inh=4},
{scm="farm_tiny_5", yoff= 0, orients={0}, farming_plus=1, avoid='', typ='farm_tiny', weight={medieval=1, single=1 }, inh=4},
{scm="farm_tiny_6", yoff= 0, orients={0}, farming_plus=1, avoid='', typ='farm_tiny', weight={medieval=1, single=1 }, inh=4},
{scm="farm_tiny_7", yoff= 0, orients={0}, farming_plus=1, avoid='', typ='farm_tiny', weight={medieval=1, single=1 }, inh=7},
{scm="taverne_1", yoff= 0, orients={0}, farming_plus=1, avoid='', typ='tavern', weight={medieval=1/2, single=1 }, pervillage=1, inh=6}, -- 19 beds: 10 guest, 3 worker, 6 family
{scm="taverne_2", yoff= 0, orients={0}, farming_plus=0, avoid='', typ='tavern', weight={medieval=1/2, single=1/3}, pervillage=1, inh=2}, -- no guests
{scm="taverne_3", yoff= 0, orients={0}, farming_plus=0, avoid='', typ='tavern', weight={medieval=1/2, single=1/3}, pervillage=1, inh=2}, -- no guests
{scm="taverne_4", yoff= 0, orients={0}, farming_plus=0, avoid='', typ='tavern', weight={medieval=1/2, single=1/3}, pervillage=1, inh=1}, -- no guests
{scm="well_1", yoff= 0, orients={0}, farming_plus=0, avoid='well', typ='well', weight={medieval=1/12, single=1/2}, pervillage=4},
{scm="well_2", yoff= 0, orients={0}, farming_plus=0, avoid='well', typ='well', weight={medieval=1/12, single=1/2}, pervillage=4},
{scm="well_3", yoff= 0, orients={0}, farming_plus=0, avoid='well', typ='well', weight={medieval=1/12, single=1/2}, pervillage=4},
{scm="well_4", yoff= 0, orients={0}, farming_plus=0, avoid='well', typ='well', weight={medieval=1/12, single=1/2}, pervillage=4},
{scm="well_5", yoff= 0, orients={0}, farming_plus=0, avoid='well', typ='well', weight={medieval=1/12, single=1/2}, pervillage=4},
{scm="well_6", yoff= 0, orients={0}, farming_plus=0, avoid='well', typ='well', weight={medieval=1/12, single=1/2}, pervillage=4},
{scm="well_7", yoff= -1, orients={0}, farming_plus=0, avoid='well', typ='well', weight={medieval=1/12, single=1/2}, pervillage=4},
{scm="well_8", yoff= 0, orients={0}, farming_plus=0, avoid='well', typ='well', weight={medieval=1/12, single=1/2}, pervillage=4},
{scm="allmende_3_90", yoff=-2, orients={0}, farming_plus=0, avoid='', typ='allmende', weight={medieval=3,taoki=3,nore=3,logcabin=1,grasshut=1}, pervillage=1},
{scm="tree_place_1", yoff= 1, orients={0}, farming_plus=0, avoid='', typ='village_square', weight={medieval=1/12}, pervillage=1},
{scm="tree_place_2", yoff= 1, orients={0}, farming_plus=0, avoid='', typ='village_square', weight={medieval=1/12}, pervillage=1},
{scm="tree_place_3", yoff= 1, orients={0}, farming_plus=0, avoid='', typ='village_square', weight={medieval=1/12}, pervillage=1},
{scm="tree_place_4", yoff= 1, orients={0}, farming_plus=0, avoid='', typ='village_square', weight={medieval=1/12}, pervillage=1},
{scm="tree_place_5", yoff= 1, orients={0}, farming_plus=0, avoid='', typ='village_square', weight={medieval=1/12}, pervillage=1},
{scm="tree_place_6", yoff= 1, orients={0}, farming_plus=0, avoid='', typ='village_square', weight={medieval=1/12}, pervillage=1},
{scm="tree_place_7", yoff= 1, orients={0}, farming_plus=0, avoid='', typ='village_square', weight={medieval=1/12}, pervillage=1},
{scm="tree_place_8", yoff= 1, orients={0}, farming_plus=0, avoid='', typ='village_square', weight={medieval=1/12}, pervillage=1},
{scm="tree_place_9", yoff= 1, orients={0}, farming_plus=0, avoid='', typ='village_square', weight={medieval=1/12}, pervillage=1},
{scm="tree_place_10", yoff= 1, orients={0}, farming_plus=0, avoid='', typ='village_square', weight={medieval=1/12}, pervillage=1},
{scm="wagon_1", yoff= 0, orients={0,1,2,3}, farming_plus=0, avoid='', typ='wagon', weight={medieval=1/12,tent=1/3}, axis=1},
{scm="wagon_2", yoff= 0, orients={0,1,2,3}, farming_plus=0, avoid='', typ='wagon', weight={medieval=1/12,tent=1/3}, axis=1},
{scm="wagon_3", yoff= 0, orients={0,1,2,3}, farming_plus=0, avoid='', typ='wagon', weight={medieval=1/12,tent=1/3}, axis=1},
{scm="wagon_4", yoff= 0, orients={0,1,2,3}, farming_plus=0, avoid='', typ='wagon', weight={medieval=1/12,tent=1/3}, axis=1},
{scm="wagon_5", yoff= 0, orients={0,1,2,3}, farming_plus=0, avoid='', typ='wagon', weight={medieval=1/12,tent=1/3}, axis=1},
{scm="wagon_6", yoff= 0, orients={0,1,2,3}, farming_plus=0, avoid='', typ='wagon', weight={medieval=1/12,tent=1/3}, axis=1},
{scm="wagon_7", yoff= 0, orients={0,1,2,3}, farming_plus=0, avoid='', typ='wagon', weight={medieval=1/12,tent=1/3}, axis=1},
{scm="wagon_8", yoff= 0, orients={0,1,2,3}, farming_plus=0, avoid='', typ='wagon', weight={medieval=1/12,tent=1/3}, axis=1},
{scm="wagon_9", yoff= 0, orients={0,1,2,3}, farming_plus=0, avoid='', typ='wagon', weight={medieval=1/12,tent=1/3}, axis=1},
{scm="wagon_10", yoff= 0, orients={0,1,2,3}, farming_plus=0, avoid='', typ='wagon', weight={medieval=1/12,tent=1/3}, axis=1},
{scm="wagon_11", yoff= 0, orients={0,1,2,3}, farming_plus=0, avoid='', typ='wagon', weight={medieval=1/12,tent=1/3}, axis=1},
{scm="wagon_12", yoff= 0, orients={0,1,2,3}, farming_plus=0, avoid='', typ='wagon', weight={medieval=1/12,tent=1/3}, axis=1},
{scm="bench_1", yoff= 0, orients={0,1,2}, farming_plus=0, avoid='', typ='bench', weight={medieval=1/12}, nomirror=1},
{scm="bench_2", yoff= 0, orients={0,1,2}, farming_plus=0, avoid='', typ='bench', weight={medieval=1/12}, nomirror=1},
{scm="bench_3", yoff= 0, orients={0,1,2}, farming_plus=0, avoid='', typ='bench', weight={medieval=1/12}, nomirror=1},
{scm="bench_4", yoff= 0, orients={0,1,2}, farming_plus=0, avoid='', typ='bench', weight={medieval=1/12}, nomirror=1},
{scm="shed_1", yoff= 0, orients={0,1,2}, farming_plus=0, avoid='', typ='shed', weight={medieval=1/10}},
{scm="shed_2", yoff= 0, orients={0,1,2}, farming_plus=0, avoid='', typ='shed', weight={medieval=1/10}},
{scm="shed_3", yoff= 0, orients={0,1,2}, farming_plus=0, avoid='', typ='shed', weight={medieval=1/10}},
{scm="shed_5", yoff= 0, orients={0,1,2}, farming_plus=0, avoid='', typ='shed', weight={medieval=1/10}},
{scm="shed_6", yoff= 0, orients={0,1,2}, farming_plus=0, avoid='', typ='shed', weight={medieval=1/10}},
{scm="shed_7", yoff= 0, orients={0,1,2}, farming_plus=0, avoid='', typ='shed', weight={medieval=1/10}},
{scm="shed_8", yoff= 0, orients={0,1,2}, farming_plus=0, avoid='', typ='shed', weight={medieval=1/10}},
{scm="shed_9", yoff= 0, orients={0,1,2}, farming_plus=0, avoid='', typ='shed', weight={medieval=1/10}},
{scm="shed_10", yoff= 0, orients={0,1,2}, farming_plus=0, avoid='', typ='shed', weight={medieval=1/10}},
{scm="shed_11", yoff= 0, orients={0,1,2}, farming_plus=0, avoid='', typ='shed', weight={medieval=1/10}},
{scm="shed_12", yoff= 0, orients={0,1,2}, farming_plus=0, avoid='', typ='shed', weight={medieval=1/10}},
{scm="weide_1", yoff= 0, orients={0,1,2,3}, farming_plus=0, avoid='pasture', typ='pasture', weight={medieval=1/6}, pervillage=8},
{scm="weide_2", yoff= 0, orients={0,1,2,3}, farming_plus=0, avoid='pasture', typ='pasture', weight={medieval=1/6}, pervillage=8},
{scm="weide_3", yoff= 0, orients={0,1,2,3}, farming_plus=0, avoid='pasture', typ='pasture', weight={medieval=1/6}, pervillage=8},
{scm="weide_4", yoff= 0, orients={0,1,2,3}, farming_plus=0, avoid='pasture', typ='pasture', weight={medieval=1/6}, pervillage=8},
{scm="weide_5", yoff= 0, orients={0,1,2,3}, farming_plus=0, avoid='pasture', typ='pasture', weight={medieval=1/6}, pervillage=8},
{scm="weide_6", yoff= 0, orients={0,1,2,3}, farming_plus=0, avoid='pasture', typ='pasture', weight={medieval=1/6}, pervillage=8},
{scm="field_1", yoff=-2, orients={0,1,2,3}, farming_plus=0, avoid='field', typ='field', weight={medieval=1/6}, pervillage=8},
{scm="field_2", yoff=-2, orients={0,1,2,3}, farming_plus=0, avoid='field', typ='field', weight={medieval=1/6}, pervillage=8},
{scm="field_3", yoff=-2, orients={0,1,2,3}, farming_plus=0, avoid='field', typ='field', weight={medieval=1/6}, pervillage=8},
{scm="field_4", yoff=-2, orients={0,1,2,3}, farming_plus=0, avoid='field', typ='field', weight={medieval=1/6}, pervillage=8},
-- hut and hills for charachoal burners; perhaps they could live together with lumberjacks?
{scm="charachoal_hut", yoff= 0, orients={0,1,2}, farming_plus=0, avoid='', typ='hut', weight={charachoal=1, single=5}, inh=2, nomirror=1},
{scm="charachoal_hill", yoff= 0, orients={0,1,2,3}, farming_plus=0, avoid='', typ='hut', weight={charachoal=2 }, inh=-1, nomirror=1},
-- lumberjacks; they require the cottages mod
{scm="lumberjack_1", yoff= 1, orients={1}, avoid='', typ='lumberjack', weight={lumberjack=1, single=3}, axis=1, inh=3},
{scm="lumberjack_2", yoff= 1, orients={1}, avoid='', typ='lumberjack', weight={lumberjack=1, single=3}, axis=1, inh=4},
{scm="lumberjack_3", yoff= 1, orients={1,2,3}, avoid='', typ='lumberjack', weight={lumberjack=1, single=3}, inh=3},
{scm="lumberjack_4", yoff= 1, orients={1}, avoid='', typ='lumberjack', weight={lumberjack=1, single=3}, axis=1, inh=4},
{scm="lumberjack_5", yoff= 1, orients={1}, avoid='', typ='lumberjack', weight={lumberjack=1, single=3}, axis=1, inh=9},
{scm="lumberjack_6", yoff= 1, orients={1}, avoid='', typ='lumberjack', weight={lumberjack=1, single=3}, axis=1, inh=5},
{scm="lumberjack_7", yoff= 1, orients={1}, avoid='', typ='lumberjack', weight={lumberjack=1, single=3}, axis=1, inh=5},
{scm="lumberjack_8", yoff= 1, orients={1}, avoid='', typ='lumberjack', weight={lumberjack=1, single=3}, axis=1, inh=9},
{scm="lumberjack_9", yoff= 1, orients={1}, avoid='', typ='lumberjack', weight={lumberjack=1, single=3}, axis=1, inh=5},
{scm="lumberjack_10", yoff= 1, orients={1}, avoid='', typ='lumberjack', weight={lumberjack=1, single=3}, axis=1, inh=2},
{scm="lumberjack_11", yoff= 0, orients={1}, avoid='', typ='lumberjack', weight={lumberjack=1, single=3}, axis=1, inh=2},
{scm="lumberjack_12", yoff= 1, orients={1}, avoid='', typ='lumberjack', weight={lumberjack=1, single=3}, axis=1, inh=3},
{scm="lumberjack_13", yoff= 1, orients={1}, avoid='', typ='lumberjack', weight={lumberjack=1, single=3}, axis=1, inh=3},
{scm="lumberjack_14", yoff= 1, orients={1}, avoid='', typ='lumberjack', weight={lumberjack=1, single=3}, axis=1, inh=2},
{scm="lumberjack_15", yoff= 1, orients={1}, avoid='', typ='lumberjack', weight={lumberjack=1, single=3}, axis=1, inh=2},
{scm="lumberjack_16", yoff= 0, orients={1}, avoid='', typ='lumberjack', weight={lumberjack=1, single=3}, axis=1, inh=2},
{scm="lumberjack_school", yoff= 1, orients={1}, avoid='', typ='school', weight={lumberjack=2 }, axis=1, inh=1},
{scm="lumberjack_stable", yoff= 0, orients={3}, avoid='', typ='lumberjack', weight={lumberjack=1, single=3}, axis=1, inh=-1},
{scm="lumberjack_pub_1", yoff= 1, orients={1}, avoid='', typ='tavern', weight={lumberjack=3, single=1}, pervillage=1, axis=1, inh=-1},
{scm="lumberjack_church_1", yoff= 1, orients={1}, avoid='', typ='church', weight={lumberjack=3}, pervillage=1, axis=1, inh=-1},
{scm="lumberjack_hotel_1", yoff= 1, orients={1}, avoid='', typ='house', weight={lumberjack=1, single=1}, axis=1, inh=16}, -- all 16 are guests
{scm="lumberjack_shop_1", yoff= 1, orients={1}, avoid='', typ='shop', weight={lumberjack=1}, pervillage=1, axis=1, inh=-1},
{scm="lumberjack_sawmill_1",yoff=-7, orients={1}, avoid='', typ='sawmill', weight={lumberjack=2, single=1}, pervillage=1, axis=1, inh=-1},
-- {scm="cow_trader_1", yoff= 0, orients={4}, avoid='', typ='trader', weight={lumberjack=1}},
-- clay traders depend on cottages as well
{scm="trader_clay_1", yoff= 1, orients={1}, avoid='', typ='trader', weight={claytrader=3, single=3}, axis=1, inh=1}, -- poor guy who has to live in that small thing
{scm="trader_clay_2", yoff= 1, orients={3}, avoid='', typ='trader', weight={claytrader=3, single=3}, axis=1, inh=1}, -- not that he'll live very comftable there...
{scm="trader_clay_3", yoff= 1, orients={0}, avoid='', typ='trader', weight={claytrader=3, single=3}, inh=2},
{scm="trader_clay_4", yoff= 1, orients={2}, avoid='', typ='trader', weight={claytrader=3, single=3}, inh=2},
{scm="trader_clay_5", yoff= 1, orients={1}, avoid='', typ='trader', weight={claytrader=3, single=3}, axis=1, inh=2},
{scm="clay_pit_1", yoff=-3, orients={0,1,2,3}, avoid='', typ='pit', weight={claytrader=1}},
{scm="clay_pit_2", yoff=-2, orients={0,1,2,3}, avoid='', typ='pit', weight={claytrader=1}},
{scm="clay_pit_3", yoff=-7, orients={0,1,2,3}, avoid='', typ='pit', weight={claytrader=1}},
{scm="clay_pit_4", yoff= 0, orients={0,1,2,3}, avoid='', typ='pit', weight={claytrader=1}},
{scm="clay_pit_5", yoff= 1, orients={0,1,2,3}, avoid='', typ='pit', weight={claytrader=1}},
-- Houses from Taokis Structure I/O Mod (see https://forum.minetest.net/viewtopic.php?id=5524)
{scm="default_town_farm", yoff= -1, orients={1}, farming_plus=0, avoid='', typ='field', weight={taoki=1, single=1}, axis=1},
{scm="default_town_house_large_1", yoff= -4, orients={1}, farming_plus=0, avoid='', typ='house', weight={taoki=1/4, single=1}, axis=1, inh=10},
{scm="default_town_house_large_2", yoff= -4, orients={1}, farming_plus=0, avoid='', typ='house', weight={taoki=1/4, single=1}, axis=1, inh=8},
{scm="default_town_house_medium", yoff= -4, orients={1}, farming_plus=0, avoid='', typ='house', weight={taoki=1/2, single=1}, axis=1, inh=6},
{scm="default_town_house_small", yoff= -4, orients={1}, farming_plus=0, avoid='', typ='house', weight={taoki=1, single=1}, axis=1, inh=4},
{scm="default_town_house_tiny_1", yoff= 1, orients={1}, farming_plus=0, avoid='', typ='house', weight={taoki=1, single=1}, axis=1, inh=3},
{scm="default_town_house_tiny_2", yoff= 1, orients={1}, farming_plus=0, avoid='', typ='house', weight={taoki=1, single=1}, axis=1, inh=3},
{scm="default_town_house_tiny_3", yoff= 1, orients={1}, farming_plus=0, avoid='', typ='house', weight={taoki=1, single=1}, axis=1, inh=2},
{scm="default_town_park", yoff= 1, orients={1}, farming_plus=0, avoid='', typ='deco', weight={taoki=1 }, axis=1},
{scm="default_town_tower", yoff= 1, orients={1}, farming_plus=0, avoid='', typ='tower', weight={taoki=1/6, single=1}, axis=1, inh=-1},
{scm="default_town_well", yoff= -6, orients={1}, farming_plus=0, avoid='', typ='well', weight={taoki=1/4 }, axis=1},
{scm="default_town_fountain", yoff= 1, orients={1}, farming_plus=0, avoid='', typ='fountain',weight={taoki=1/4 }, axis=1},
-- the hotel seems to be only the middle section of the building; it's build for another spawning algorithm
-- {scm="default_town_hotel", yoff= -1, orients={1}, farming_plus=0, avoid='', typ='house', weight={taoki=1/5}},
{scm="tent_tiny_1", yoff=0, orients={3}, farming_plus=0, avoid='', typ='tent', weight={tent=1, single=1}, inh=1},
{scm="tent_tiny_2", yoff=0, orients={3}, farming_plus=0, avoid='', typ='tent', weight={tent=1, single=1}, inh=1},
{scm="tent_big_1", yoff=0, orients={3}, farming_plus=0, avoid='', typ='tent', weight={tent=1, single=1}}, -- no sleeping place
{scm="tent_big_2", yoff=0, orients={3}, farming_plus=0, avoid='', typ='tent', weight={tent=1, single=1}, inh=2},
{scm="tent_medium_1", yoff=0, orients={3}, farming_plus=0, avoid='', typ='tent', weight={tent=1/2, single=1}, inh=3},
{scm="tent_medium_2", yoff=0, orients={3}, farming_plus=0, avoid='', typ='tent', weight={tent=1/2, single=1}, inh=3},
{scm="tent_medium_3", yoff=0, orients={3}, farming_plus=0, avoid='', typ='tent', weight={tent=1/2, single=1}, inh=3},
{scm="tent_medium_4", yoff=0, orients={3}, farming_plus=0, avoid='', typ='tent', weight={tent=1/2, single=1}, inh=3},
{scm="tent_open_1", yoff=0, orients={3}, farming_plus=0, avoid='', typ='tent', weight={tent=1/5}},
{scm="tent_open_2", yoff=0, orients={3}, farming_plus=0, avoid='', typ='tent', weight={tent=1/5}},
{scm="tent_open_3", yoff=0, orients={3}, farming_plus=0, avoid='', typ='tent', weight={tent=1/5}},
{scm="tent_open_big_1", yoff=0, orients={3}, farming_plus=0, avoid='', typ='tent', weight={tent=1/5}},
{scm="tent_open_big_2", yoff=0, orients={3}, farming_plus=0, avoid='', typ='tent', weight={tent=1/5}},
{scm="tent_open_big_3", yoff=0, orients={3}, farming_plus=0, avoid='', typ='tent', weight={tent=1/5}},
{scm="hochsitz_1", yoff=0, orients={0,1,2,3}, farming_plus=0, avoid='', typ='tower', weight={tower=1, single=1/3}, nomirror=1},
{scm="hochsitz_2", yoff=0, orients={0,1,2,3}, farming_plus=0, avoid='', typ='tower', weight={tower=1, single=1/3}, nomirror=1},
{scm="hochsitz_3", yoff=0, orients={0,1,2,3}, farming_plus=0, avoid='', typ='tower', weight={tower=1, single=1/3}, nomirror=1},
{scm="hochsitz_4", yoff=0, orients={0,1,2,3}, farming_plus=0, avoid='', typ='tower', weight={tower=1, single=1/3}, nomirror=1},
{scm="chateau_without_garden", yoff=-1,orients={0,1,2,3}, farming_plus=0, avoid='', typ='chateau', weight={chateau=1,single=8}, pervillage=1, inh=8},
{scm="baking_house_1", yoff=0, orients={0}, farming_plus=0, avoid='', typ='bakery', weight={medieval=1/4}, pervillage=1, inh=-1},
{scm="baking_house_2", yoff=0, orients={0}, farming_plus=0, avoid='', typ='bakery', weight={medieval=1/4}, pervillage=1, inh=-1},
{scm="baking_house_3", yoff=0, orients={0}, farming_plus=0, avoid='', typ='bakery', weight={medieval=1/4}, pervillage=1, inh=-1},
{scm="baking_house_4", yoff=0, orients={0}, farming_plus=0, avoid='', typ='bakery', weight={medieval=1/4}, pervillage=1, inh=-1},
{scm="empty_1", yoff=0, typ='empty', inh=0, pervillage=2,
weight={nore=1/8,taoki=1/8,medieval=1/8,charachoal=1/8,lumberjack=1/8,claytrader=1/8,logcabin=1/8,canadian=1/8,grasshut=1/8,tent=1/8}},
{scm="empty_2", yoff=0, typ='empty', inh=0, pervillage=2,
weight={nore=1/8,taoki=1/8,medieval=1/8,charachoal=1/8,lumberjack=1/8,claytrader=1/8,logcabin=1/8,canadian=1/8,grasshut=1/8,tent=1/8}},
{scm="empty_3", yoff=0, typ='empty', inh=0, pervillage=2,
weight={nore=1/8,taoki=1/8,medieval=1/8,charachoal=1/8,lumberjack=1/8,claytrader=1/8,logcabin=1/8,canadian=1/8,grasshut=1/8,tent=1/8}},
{scm="empty_4", yoff=0, typ='empty', inh=0, pervillage=2,
weight={nore=1/8,taoki=1/8,medieval=1/8,charachoal=1/8,lumberjack=1/8,claytrader=1/8,logcabin=1/8,canadian=1/8,grasshut=1/8,tent=1/8}},
{scm="empty_5", yoff=0, typ='empty', inh=0, pervillage=2,
weight={nore=1/8,taoki=1/8,medieval=1/8,charachoal=1/8,lumberjack=1/8,claytrader=1/8,logcabin=1/8,canadian=1/8,grasshut=1/8,tent=1/8}},
}
-- read the data files and fill in information like size and nodes that need on_construct to be called after placing;
-- skip buildings that cannot be used due to missing mods
mg_villages.add_building = function( building_data )
local file_name = building_data.mts_path .. building_data.scm;
-- a building will only be used if it is used by at least one supported village type (=mods required for that village type are installed)
local is_used = false;
for typ,weight in pairs( building_data.weight ) do
if( typ and weight and typ ~= 'single' and mg_villages.village_type_data[ typ ] and mg_villages.village_type_data[ typ ].supported ) then
is_used = true;
end
-- add the building to the menu list for the build chest ("single" would be too many houses)
-- the empty plots are added to each village and of no intrest here
if( build_chest and build_chest.add_entry and typ and typ ~= 'single' and (not( building_data.typ ) or building_data.typ ~= 'empty')) then
build_chest.add_entry( {'main','mg_villages', typ, building_data.scm, file_name });
end
end
-- buildings as such may have a type as well
if( build_chest and build_chest.add_entry and building_data.typ ) then
build_chest.add_entry( {'main','mg_villages', building_data.typ, building_data.scm, file_name });
end
-- store information about all buildings - no matter weather they can be used or not - for later presentation in the build_chest's menu
if( build_chest and build_chest.add_building ) then
build_chest.add_building( file_name, building_data );
end
if( not( is_used )) then
-- do nothing; skip this file
mg_villages.print(mg_villages.DEBUG_LEVEL_INFO, 'SKIPPING '..tostring( building_data.scm )..' due to village type not supported.');
-- building cannot be used
building_data.not_available = 1;
return false;
end
-- determine the size of the building
local res = nil;
-- read the size of the building;
-- convert to .mts for later usage if necessary
res = handle_schematics.analyze_file( file_name, building_data.we_origin, building_data.mts_path .. building_data.scm );
if( not( res )) then
mg_villages.print(mg_villages.DEBUG_LEVEL_WARNING, 'SKIPPING '..tostring( building_data.scm )..' due to import failure.');
building_data.not_available = 1;
return false;
-- provided the file could be analyzed successfully (now covers both .mts and .we files)
elseif( res and res.size and res.size.x ) then
-- the file has to be placed with minetest.place_schematic(...)
building_data.is_mts = 1;
building_data.sizex = res.size.x;
building_data.sizez = res.size.z;
building_data.ysize = res.size.y;
-- some buildings may be rotated
if( not( building_data.orients ) and res.rotated ) then
building_data.orients = {};
if( res.rotated == 0 ) then
building_data.orients[1] = 0;
elseif( res.rotated == 90 ) then
building_data.axis = 1; -- important when mirroring
building_data.orients[1] = 1;
elseif( res.rotated == 180 ) then
building_data.orients[1] = 2;
elseif( res.rotated == 270 ) then
building_data.orients[1] = 3;
building_data.axis = 1; -- important when mirroring
end
end
if( not( building_data.yoff ) and res.burried ) then
building_data.yoff = 1-res.burried;
end
-- we do need at least the list of nodenames which will need on_constr later on
building_data.rotated = res.rotated;
building_data.nodenames = res.nodenames;
building_data.on_constr = res.on_constr;
building_data.after_place_node = res.after_place_node;
if( res.scm_data_cache ) then
building_data.scm_data_cache = res.scm_data_cache;
building_data.is_mts = 0;
end
-- missing data regarding building size - do not use this building for anything
elseif( not( building_data.sizex ) or not( building_data.sizez )
or building_data.sizex == 0 or building_data.sizez==0) then
-- no village will use it
mg_villages.print( mg_villages.DEBUG_LEVEL_INFO, 'No schematic found for building \''..tostring( building_data.scm )..'\'. Will not use that building.');
building_data.weight = {};
building_data.not_available = 1;
return false;
else
-- the file has to be handled by worldedit; it is no .mts file
building_data.is_mts = 0;
end
if( not( building_data.weight ) or type( building_data.weight ) ~= 'table' ) then
mg_villages.print( mg_villages.DEBUG_LEVEL_WARNING, 'SKIPPING '..tostring( building_data.scm )..' due to missing weight information.');
building_data.not_available = 1;
return false;
end
-- handle duplicates; make sure buildings always get the same number;
-- check if the building has been used in previous runs and got an ID there
-- create a not very unique, but for this case sufficient "id";
-- (buildings with the same size and name are considered to be drop-in-replacements
local building_id = building_data.sizex..'x'..building_data.sizez..'_'..building_data.scm;
-- if the building is new, it will get the next free id
local building_nr = #mg_villages.all_buildings_list + 1;
for i,v in ipairs( mg_villages.all_buildings_list ) do
if( v==building_id ) then
-- we found the building
building_nr = i;
end
end
-- if it is a new building, then save the list
if( building_nr == #mg_villages.all_buildings_list+1 ) then
mg_villages.all_buildings_list[ building_nr ] = building_id;
-- save information about previously imported buildings
save_restore.save_data( 'mg_villages_all_buildings_list.data', mg_villages.all_buildings_list );
end
-- determine the internal number for the building; this number is used as a key and can be found in the mg_all_villages.data file
if( not( mg_villages.BUILDINGS )) then
mg_villages.BUILDINGS = {};
end
-- actually store the building data
mg_villages.BUILDINGS[ building_nr ] = minetest.deserialize( minetest.serialize( building_data ));
-- create lists for all village types containing the buildings which may be used for that village
for typ, data in pairs( mg_villages.village_type_data ) do
local total_weight = 0;
if( not( data.building_list ) or not( data.max_weight_list )) then
data.building_list = {};
data.max_weight_list = {};
elseif( #data.max_weight_list > 0 ) then
-- get the last entry - that one will determine the current total_weight
total_weight = data.max_weight_list[ #data.max_weight_list ];
end
if( building_data.weight[ typ ] and building_data.weight[ typ ] > 0 ) then
local index = #data.building_list+1;
data.building_list[ index ] = building_nr;
data.max_weight_list[ index ] = total_weight + building_data.weight[ typ ];
end
end
-- print it for debugging usage
--print( building_data.scm .. ': '..tostring(building_data.sizex)..' x '..tostring(building_data.sizez)..' x '..tostring(building_data.ysize)..' h');
return true;
end
-- this list contains some information about previously imported buildings so that they will get the same id
mg_villages.all_buildings_list = save_restore.restore_data( 'mg_villages_all_buildings_list.data' );
-- import all the buildings
mg_villages.BUILDINGS = {};
local mts_path = mg_villages.modpath.."/schems/";
-- determine the size of the given houses and other necessary values
for i,v in ipairs( buildings ) do
v.mts_path = mts_path;
mg_villages.add_building( v, i );
end
buildings = nil;
-- roads are built in a diffrent way
mg_villages.BUILDINGS["road"] = {yoff = 0, ysize = 2, scm = {}}

View File

@ -0,0 +1,107 @@
minetest.register_privilege("mg_villages", { description = "Allows to teleport to villages via /vist <nr>", give_to_singleplayer = false});
-- this function is only used for the chat command currently
mg_villages.list_villages_formspec = function( player, formname, fields )
if( not( player ) or fields.quit) then
return
end
local pname = player:get_player_name();
local ppos = player:getpos();
local radius = 1000000;
-- without the special priv, players can only obtain informatoin about villages which are very close by
if( not( minetest.check_player_privs( pname, {mg_villages=true}))) then
radius = mg_villages.VILLAGE_DETECT_RANGE;
end
local formspec = 'size[12,12]'..
'button_exit[4.0,1.5;2,0.5;quit;Quit]'..
'tablecolumns[' ..
'text,align=right;'.. -- village number
'text,align=right;'.. -- distance from player
'text,align=center;'.. -- name of village
'text,align=center;'.. -- typ of village
'text,align=right;'.. -- x
'text,align=right;'.. -- y
'text,align=right;'.. -- z
'text,align=right;'.. -- size
'text,align=right;'.. -- #houses where inhabitants may live or work
'text,align=right]'..
'table[0.1,2.7;11.4,8.8;'..formname..';';
for k,v in pairs( mg_villages.all_villages ) do
local dx = math.abs( v.vx - ppos.x );
local dz = math.abs( v.vz - ppos.z );
-- distance in y direction is less relevant here and may be ignored
if( dx + dz < radius ) then
local dist = math.sqrt( dx * dx + dz * dz );
local is_full_village = 'village';
if( v.is_single_house ) then
is_full_village = '';
end
formspec = formspec..
v.nr..','..
tostring( math.floor( dist ))..','..
tostring( v.name or 'unknown' )..','..
v.village_type..','..
tostring( v.vx )..','..
tostring( v.vh )..','..
tostring( v.vz )..','..
tostring( v.vs )..','..
tostring( v.anz_buildings )..','..
tostring( is_full_village )..',';
end
end
formspec = formspec..';]'..
'tabheader[0.1,2.2;spalte;Nr,Dist,Name of village,Type of village,_X_,_H_,_Z_,Size,Buildings;;true;true]';
minetest.show_formspec( pname, formname, formspec );
end
minetest.register_chatcommand( 'villages', {
description = "Shows a list of all known villages.",
privs = {},
func = function(name, param)
mg_villages.list_villages_formspec( minetest.get_player_by_name( name ), "mg:village_list", {});
end
});
minetest.register_chatcommand( 'visit', {
description = "Teleports you to a known village.",
params = "<village number>",
privs = {},
func = function(name, param)
if( mg_villages.REQUIRE_PRIV_FOR_TELEPORT and not( minetest.check_player_privs( name, {mg_villages=true}))) then
minetest.chat_send_player( name, "You need the 'mg_villages' priv in order to teleport to villages using this command.");
return;
end
if( not( param ) or param == "" ) then
minetest.chat_send_player( name, "Which village do you want to visit? Please provide the village number!");
return;
end
local nr = tonumber( param );
for id, v in pairs( mg_villages.all_villages ) do
-- we have found the village
if( v and v.nr == nr ) then
minetest.chat_send_player( name, "Initiating transfer to village no. "..tostring( v.nr )..", called "..( tostring( v.name or 'unknown'))..".");
local player = minetest.get_player_by_name( name );
player:moveto( { x=v.vx, y=(v.vh+1), z=v.vz }, false);
return;
end
end
-- no village found
minetest.chat_send_player( name, "There is no village with the number "..tostring( param ).." (yet?).");
end
});

View File

@ -0,0 +1,187 @@
-----------------------------------------------------------------------------
-- configuration values which you can adjust according to your liking
-----------------------------------------------------------------------------
-- set to false if you do not want to have any villages spawning
mg_villages.ENABLE_VILLAGES = true;
-- generate one random building for each mg_villages.INVERSE_HOUSE_DENSITY th mapchunk;
-- set to 0 in order to disable spawning of these lone buildings outside villages
mg_villages.INVERSE_HOUSE_DENSITY = 0;
-- cover some villages with artificial snow; probability: 1/mg_villages.artificial_snow_probability
mg_villages.artificial_snow_probability = 0;
-- if set to true, soil around villaes will get special soil-snow instead of plant + snow cover
mg_villages.use_soil_snow = false;
-- only place roads if there are at least that many buildings in the village
mg_villages.MINIMAL_BUILDUNGS_FOR_ROAD_PLACEMENT = 4;
-- players without the mg_villages priv can only see villages which are less than that many blocks away
-- from them when using the /vmap command
mg_villages.VILLAGE_DETECT_RANGE = 400;
-- if set to true, only players which have the mg_villages priv can use the "/visit <village nr>"
-- command which allows teleporting to the village with the given number
mg_villages.REQUIRE_PRIV_FOR_TELEPORT = false;
-- if set to true, players cannot modify spawned villages without buying the house from the village first
mg_villages.ENABLE_PROTECTION = false;
-- the first village - the one the player spawns in - will be of this type
mg_villages.FIRST_VILLAGE_TYPE = 'ruins';
-- the mapgen will disregard mapchunks where min.y > mg_villages.MAX_HEIGHT_TREATED;
-- you can set this value to 64 if you have a slow machine and a mapgen which does not create extreme mountains
-- (or if you don't care if extreme mountains may create burried villages occasionally)
mg_villages.MAX_HEIGHT_TREATED = 64;
-- choose the debug level you want
mg_villages.DEBUG_LEVEL = mg_villages.DEBUG_LEVEL_NORMAL
-- if set to true (or anything else but nil or false), highlandpools by paramat (see
-- https://forum.minetest.net/viewtopic.php?t=8400) will be created
mg_villages.CREATE_HIGHLANDPOOLS = true
-- background image for the /vmap command
-- RealTest comes with a diffrent texture
if( minetest.get_modpath('grounds') and minetest.get_modpath('joiner_table')) then
mg_villages.MAP_BACKGROUND_IMAGE = "default_dirt_grass.png";
elseif( minetest.registered_nodes[ 'default:dirt_with_grass'] ) then
mg_villages.MAP_BACKGROUND_IMAGE = "default_grass.png";
else
mg_villages.MAP_BACKGROUND_IMAGE = "";
end
-- if set to true, the outer buildings in medieval villages will be fields; this is not very convincing yet
-- currently not really used; does not look as good as expected
mg_villages.medieval_subtype = false;
-- set this to true if you want to use normal lava - but beware: charachoal villages may cause bushfires!
mg_villages.use_normal_unsafe_lava = true;
-----------------------------------------------------------------------------
-- decrese these values slightly if you want MORE trees around your villages;
-- increase it if you want to DECREASE the amount of trees around villages
-----------------------------------------------------------------------------
-- on average, every n.th node inside a village area 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' ) ] = 10; -- suitable for a relatively dense forrest of normal trees
mg_villages.sapling_probability[ minetest.get_content_id( 'default:junglesapling' ) ] = 10; -- jungletrees are a bit bigger and need more space
mg_villages.sapling_probability[ minetest.get_content_id( 'default:pinesapling' ) ] = 10;
if( minetest.get_modpath( 'mg' )) then
mg_villages.sapling_probability[ minetest.get_content_id( 'mg:savannasapling' ) ] = 10;
mg_villages.sapling_probability[ minetest.get_content_id( 'mg:pinesapling' ) ] = 10;
end
mg_villages.moretrees_treelist = nil;
if( minetest.get_modpath( 'moretrees' )) then
mg_villages.moretrees_treelist = moretrees.treelist;
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
-----------------------------------------------------------------------------
-- no need to change this, unless you add new farming_plus fruits
-----------------------------------------------------------------------------
-- the schematics for buildings of type 'farm_tiny' grow cotton; the farming_plus fruits would be far more fitting
mg_villages.fruit_list = {'carrot','potatoe','orange','rhubarb','strawberry','tomato','cotton'};
-- is farming_plus available? If not, we can't use this
if( not( minetest.get_modpath("farming_plus"))) then
mg_villages.fruit_list = nil;
end
-----------------------------------------------------------------------------
-- players can buy plots in villages with houses on for this price;
-- set according to your liking
-----------------------------------------------------------------------------
-- how much does the player have to pay for a plot with a building?
mg_villages.prices = {
empty = "default:copper_ingot 1", -- plot to build on
-- building types which usually have inhabitants (and thus allow the player
-- who bought the building to modifiy the entire village area minus other
-- buildings)
tent = "default:copper_ingot 1",
hut = "default:copper_ingot 1",
farm_full = "default:gold_ingot 4",
farm_tiny = "default:gold_ingot 2",
lumberjack = "default:gold_ingot 2",
house = "default:gold_ingot 2",
house_large = "default:gold_ingot 4",
tavern = "default:gold_ingot 12",
trader = "default:gold_ingot 2",
-- more or less community buildings
well = "default:gold_ingot 1",
village_square = "default:goldblock 1",
secular = "default:goldblock 2", -- secular buildings, such as libraries ec.
church = "default:goldblock 10",
-- places for mobs to work at; usually without inhabitants
tower = "default:copper_ingot 1",
shed = "default:copper_ingot 2",
pit = "default:copper_ingot 3", -- claytrader pit
mill = "default:gold_ingot 10",
forge = "default:gold_ingot 10",
bakery = "default:gold_ingot 10",
shop = "default:gold_ingot 20",
sawmill = "default:gold_ingot 30",
-- decoration
wagon = "default:tree 10",
bench = "default:tree 4",
-- seperate fields
pasture = "default:copper_ingot 2",
field = "default:copper_ingot 2",
-- chateaus are expensive
chateau = "default:diamondblock 5",
}
-----------------------------------------------------------------------------
-- The values below seldom need adjustment; don't change them unless you
-- know exactly what you are doing.
-----------------------------------------------------------------------------
-- 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;
-- internal variables for village generation
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
-- min and max size are only used in case of them beeing not provided by the village type (see buildings.lua)
mg_villages.VILLAGE_MIN_SIZE = 25
mg_villages.VILLAGE_MAX_SIZE = 90 --55
mg_villages.FIRST_ROADSIZE = 3
mg_villages.BIG_ROAD_CHANCE = 0
-- Enable that for really big villages (there are also really slow to generate)
--[[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]]

View File

@ -0,0 +1,22 @@
handle_schematics
default
doors?
farming?
wool?
stairs?
cottages?
moretrees?
trees?
forest?
dryplants?
cavestuff?
snow?
moresnow?
darkage?
ethereal?
deco?
metals?
grounds?
moreblocks?
bell?
mg?

View File

@ -0,0 +1,228 @@
-- TODO: refill chest after some time?
-- TODO: alert NPC that something was taken
mg_villages.random_chest_content = {};
-- add random chest content
local ADD_RCC = function( data )
if( data and #data>3 and ( minetest.registered_nodes[ data[1] ] or minetest.registered_items[ data[1] ]) ) then
table.insert( mg_villages.random_chest_content, data );
end
end
-- things that can be found in private, not locked chests belonging to npc
-- contains tables of the following structure: { node_name, probability (in percent, 100=always, 0=never), max_amount, repeat (for more than one stack) }
mg_villages.random_chest_content = {};
ADD_RCC({"default:pick_stone", 10, 1, 3, farm_tiny=1, farm_full=1, shed=1, lumberjack=1, hut=1, chest_work=1, lumberjack=1 });
ADD_RCC({"default:pick_steel", 5, 1, 2, forge=1 });
ADD_RCC({"default:pick_mese", 2, 1, 2, forge=1, lumberjack=1 });
ADD_RCC({"default:shovel_stone", 5, 1, 3, farm_tiny=1, farm_full=1, shed=1, lumberjack=1, hut=1, chest_work=1 });
ADD_RCC({"default:shovel_steel", 5, 1, 2, forge=1 });
ADD_RCC({"default:axe_stone", 5, 1, 3, farm_tiny=1, farm_full=1, chest_work=1, lumberjack=1 });
ADD_RCC({"default:axe_steel", 5, 1, 2, forge=1, lumberjack=1 });
ADD_RCC({"default:sword_wood", 1, 1, 3, guard=1 });
ADD_RCC({"default:sword_stone", 1, 1, 3, guard=1 });
ADD_RCC({"default:sword_steel", 1, 1, 3, forge=1, guard=1 });
ADD_RCC({"default:stick", 20, 40, 2, church=1, library=1, chest_private=1, shelf=5, shed=1, lumberjack=1, hut=1 });
ADD_RCC({"default:torch", 50, 10, 4, church=1, library=1, chest_private=1, shelf=1, shed=1, lumberjack=1, hut=1 });
ADD_RCC({"default:book", 60, 1, 2, church=1, library=1 });
ADD_RCC({"default:paper", 60, 6, 4, church=1, library=1 });
ADD_RCC({"default:apple", 50, 10, 2, chest_storage=4, chest_private=1, shelf=5});
ADD_RCC({"default:ladder", 20, 1, 2, church=1, library=1, shed=1, lumberjack=1, hut=1 });
ADD_RCC({"default:coal_lump", 80, 30, 1, forge=1, shed=1, lumberjack=1, hut=1});
ADD_RCC({"default:steel_ingot", 30, 4, 2, forge=1 });
ADD_RCC({"default:mese_crystal_fragment", 10, 3, 1, forge=1, chest_storage=1 });
ADD_RCC({"bucket:bucket_empty", 10, 3, 2, chest_work=1, forge=1, shed=1, hut=1 });
ADD_RCC({"bucket:bucket_water", 5, 3, 2, chest_work=1, forge=1 });
ADD_RCC({"bucket:bucket_lava", 3, 3, 2, forge=1 });
ADD_RCC({"vessels:glass_bottle", 10, 10, 2, church=1, library=1, shelf=1 });
ADD_RCC({"vessels:drinking_glass", 20, 2, 1, church=1, library=1, shelf=1 });
ADD_RCC({"vessels:steel_bottle", 10, 1, 1, church=1, library=1, shelf=1 });
ADD_RCC({"wool:white", 60, 8, 2, church=1, library=1 });
-- that someone will hide valuable ingots in chests that are not locked is fairly unrealistic; thus, those items are rare
ADD_RCC({"moreores:gold_ingot", 1, 2, 1 });
ADD_RCC({"moreores:silver_ingot", 1, 2, 1 });
ADD_RCC({"moreores:copper_ingot", 30, 10, 1 });
ADD_RCC({"moreores:tin_ingot", 1, 5, 1 });
ADD_RCC({"moreores:bronze_ingot", 1, 1, 1 });
ADD_RCC({"moreores:mithril_ingot", 1, 1, 1 });
-- candles are a very likely content of chests
ADD_RCC({"candles:candle", 80, 12, 2, church=1, library=1, chest_private=1 });
ADD_RCC({"candles:candelabra_steel", 1, 1, 1, church=1, library=1, chest_private=1 });
ADD_RCC({"candles:candelabra_copper", 1, 1, 1, church=1, library=1, chest_private=1 });
ADD_RCC({"candles:honey", 50, 2, 1 });
-- our NPC have to spend their free time somehow...also adds food variety
ADD_RCC({"fishing:pole", 60, 1, 1 });
-- ropes are always useful
if( minetest.get_modpath("ropes") ~= nil ) then
ADD_RCC({"ropes:rope", 60, 5, 2, chest_work=1, shelf=1, chest_storage=1 });
elseif( minetest.get_modpath("farming") ~= nil ) then
ADD_RCC({"farming:string", 60, 5, 2, church=1, library=1, chest_work=1, shelf=1, chest_storage=1 });
elseif( minetest.get_modpath("moreblocks") ~= nil ) then
ADD_RCC({"moreblocks:rope", 60, 5, 2, chest_work=1, shelf=1, chest_storage=1 });
end
ADD_RCC({'bees:bottle_honey', 50, 4, 1, beekeeper=3, tavern=1, inn=1, chest_storage=1 });
ADD_RCC({'bees:extractor', 80, 1, 2, beekeeper=1 });
ADD_RCC({'bees:frame_empty', 50, 2, 5, beekeeper=1 });
ADD_RCC({'bees:frame_full', 80, 1, 1, beekeeper=1 });
ADD_RCC({'bees:grafting_tool', 50, 1, 3, beekeeper=1 });
ADD_RCC({'bees:hive_industrial', 100, 1, 1, beekeeper=1 });
ADD_RCC({'bees:honey_comb', 50, 2, 2, beekeeper=1 });
ADD_RCC({'bees:queen_bee', 50, 2, 3, beekeeper=1 });
ADD_RCC({'bees:smoker', 80, 1, 2, beekeeper=1 });
ADD_RCC({'bees:wax', 80, 3, 3, beekeeper=1 });
ADD_RCC({'bushes:blackberry', 80, 20, 4, bakery=1 });
ADD_RCC({'bushes:blackberry_pie_cooked', 80, 12, 4, bakery=1 });
ADD_RCC({'bushes:blueberry', 80, 20, 4, bakery=1 });
ADD_RCC({'bushes:blueberry_pie_cooked', 80, 12, 4, bakery=1 });
ADD_RCC({'bushes:gooseberry', 80, 20, 4, bakery=1 });
ADD_RCC({'bushes:gooseberry_pie_cooked', 80, 12, 4, bakery=1 });
ADD_RCC({'bushes:raspberry', 80, 20, 4, bakery=1 });
ADD_RCC({'bushes:raspberry_pie_cooked', 80, 12, 4, bakery=1 });
ADD_RCC({'bushes:mixed_berry_pie_cooked', 80, 12, 4, bakery=1 });
ADD_RCC({'bushes:sugar', 80, 99, 5, bakery=1, shelf=1 });
ADD_RCC({'carts:cart', 80, 1, 2, miner=1});
ADD_RCC({'castle:battleaxe', 50, 1, 1, guard=1, forge=1 });
ADD_RCC({'castle:ropebox', 50, 2, 2, guard=1 });
ADD_RCC({'castle:ropes', 50, 1, 1, guard=2, chest_private=1, chest_work=2 });
ADD_RCC({'castle:shield', 50, 1, 1, guard=1 });
ADD_RCC({'castle:shield_2', 50, 1, 1, guard=1 });
ADD_RCC({'castle:shield_3', 50, 1, 1, guard=1 });
ADD_RCC({'cottages:anvil', 80, 1, 2, forge=1 });
ADD_RCC({'currency:minegeld', 80, 10, 2, chest_private=1, chest_work=1 }); -- TODO: could be in any chest with a certain chance
ADD_RCC({'farming:hoe_stone', 80, 1, 2, farm_tiny=2, farm_full=2, chest_work=2 });
ADD_RCC({'homedecor:beer_mug', 50, 1, 2, tavern=5, inn=3});
ADD_RCC({'homedecor:book_blue', 50, 1, 2, church=1, library=1, chest_private=1});
ADD_RCC({'homedecor:book_red', 50, 1, 2, church=1, library=1, chest_private=1});
ADD_RCC({'homedecor:book_green', 50, 1, 2, church=1, library=1, chest_private=1});
ADD_RCC({'homedecor:bottle_brown', 50, 1, 2, tavern=3, inn=3, chest_private=1});
ADD_RCC({'homedecor:bottle_green', 50, 1, 2, tavern=3, inn=3, chest_private=1});
ADD_RCC({'homedecor:calendar', 50, 1, 1, church=1, library=1, chest_private=1, chest_work=1, chest_storage=1});
ADD_RCC({"homedecor:candle", 50, 2, 1, church=2, library=1, chest_private=1, chest_work=1, chest_storage=1 });
ADD_RCC({"homedecor:candle_thin", 50, 2, 1, church=1, library=1, chest_private=1, chest_work=1, chest_storage=1 });
ADD_RCC({"homedecor:copper_pans", 80, 1, 1, chest_work=1 });
ADD_RCC({"homedecor:dardboard", 50, 1, 1, tavern=1});
ADD_RCC({"homedecor:oil_extract", 80, 1, 3, church=1, library=1, chest_private=1, chest_work=1, chest_storage=1 });
ADD_RCC({"homedecor:oil_lamp", 50, 2, 1, church=1, library=1, chest_private=1, chest_work=1, chest_storage=1 });
ADD_RCC({"homedecor:torch_wall", 50, 2, 1, church=1, library=1, chest_private=1, chest_work=1, chest_storage=1 });
ADD_RCC({"locks:key", 50, 2, 1, chest_private=1, chest_work=1, chest_storage=1, forge=1 });
ADD_RCC({"locks:keychain", 50, 2, 1, chest_private=1, chest_work=1, chest_storage=1, forge=1 });
ADD_RCC({"moretrees:coconut_milk", 80, 5, 2, tavern=1, inn=1 });
ADD_RCC({"moretrees:raw_coconut", 80, 5, 2, tavern=1, inn=1 });
ADD_RCC({"moretrees:pine_nuts", 80, 99, 1, tavern=1, inn=1, chest_storage=3 });
ADD_RCC({"moretrees:spruce_nuts", 80, 99, 1, tavern=1, inn=1, chest_storage=3 });
ADD_RCC({"quartz:quartz_crystal", 80, 1, 1, library=1 });
ADD_RCC({"screwdriver:screwdriver", 80, 1, 1, chest_work=1 });
ADD_RCC({"unified_inventory:bag_large", 80, 1, 1, chest_private=1, chest_storage=2 });
ADD_RCC({"unified_inventory:bag_medium", 80, 1, 1, chest_private=1, chest_storage=2 });
ADD_RCC({"unified_inventory:bag_small", 80, 1, 1, tavern=1, inn=1, chest_work=1, chest_private=1 });
-- get some random content for a chest
mg_villages.fill_chest_random = function( pos, pr, building_nr, building_typ )
local building_data = mg_villages.BUILDINGS[ building_nr.btype ];
local meta = minetest.env:get_meta( pos );
local inv = meta:get_inventory();
local count = 0;
local typ = minetest.get_name_from_content_id( pos.typ );
if( pos.typ_name ) then
typ = pos.typ_name;
end
if( not( typ ) or (typ ~= 'cottages:shelf' and typ ~= 'cottages:chest_work' and typ ~= 'cottages:chest_storage' and typ ~= 'cottages:chest_private' )) then
typ = building_data.typ;
else
typ = string.sub( typ, 10 );
end
local typ2 = nil;
if( typ == 'cottages:chest_work' and building_data.typ ) then
typ2 = building_data.typ;
end
--print('FILLING chest of type '..tostring( typ )..' and '..tostring( typ2));
if( not( typ ) or typ=='' ) then
return;
end
local inv_size = inv:get_size('main');
for i,v in ipairs( mg_villages.random_chest_content ) do
-- repeat this many times
for count=1, v[ 4 ] do
-- to avoid too many things inside a chest, lower probability
if( count<30 -- make sure it does not get too much and there is still room for a new stack
and (v[ typ ] or (typ2 and v[ typ2 ]))
and inv_size and inv_size > 0 and v[ 2 ] > pr:next( 1, 200 )) then
--inv:add_item('main', v[ 1 ].." "..tostring( math.random( 1, tonumber(v[ 3 ]) )));
-- add itemstack at a random position in the chests inventory
inv:set_stack( 'main', pr:next( 1, inv:get_size( 'main' )), v[ 1 ].." "..tostring( pr:next( 1, tonumber(v[ 3 ]) )) );
count = count+1;
end
end
end
end
--[[
--the old code used by Nores mg mapgen
for _, n in pairs(village.to_add_data.extranodes) do
-- minetest.set_node(n.pos, n.node)
if n.meta ~= nil then
meta = minetest.get_meta(n.pos)
meta:from_table(n.meta)
if n.node.name == "default:chest" then
local inv = meta:get_inventory()
local items = inv:get_list("main")
for i=1, inv:get_size("main") do
inv:set_stack("main", i, ItemStack(""))
end
local numitems = pr:next(3, 20)
for i=1,numitems do
local ii = pr:next(1, #items)
local prob = items[ii]:get_count() % 2 ^ 8
local stacksz = math.floor(items[ii]:get_count() / 2 ^ 8)
if pr:next(0, prob) == 0 and stacksz>0 then
stk = ItemStack({name=items[ii]:get_name(), count=pr:next(1, stacksz), wear=items[ii]:get_wear(), metadata=items[ii]:get_metadata()})
local ind = pr:next(1, inv:get_size("main"))
while not inv:get_stack("main",ind):is_empty() do
ind = pr:next(1, inv:get_size("main"))
end
inv:set_stack("main", ind, stk)
end
end
end
end
end
--]]

View File

@ -0,0 +1,283 @@
--[[-- TODO: refill chest after some time?
-- TODO: alert NPC that something was taken
mg_villages.random_chest_content = {};
-- add random chest content
local ADD_RCC = function( data )
if( data and #data>3 and ( minetest.registered_nodes[ data[1] ] or minetest.registered_items[ data[1] ]) ) then
table.insert( mg_villages.random_chest_content, data );
end
end
-- things that can be found in private, not locked chests belonging to npc
-- contains tables of the following structure: { node_name, probability (in percent, 100=always, 0=never), max_amount, repeat (for more than one stack) }
mg_villages.random_chest_content = {};
ADD_RCC({"default:pick_stone", 10, 1, 3, farm_tiny=1, farm_full=1, shed=1, lumberjack=1, hut=1, chest_work=1, lumberjack=1 });
ADD_RCC({"default:pick_steel", 5, 1, 2, forge=1 });
ADD_RCC({"default:pick_mese", 2, 1, 2, forge=1, lumberjack=1 });
ADD_RCC({"default:shovel_stone", 5, 1, 3, farm_tiny=1, farm_full=1, shed=1, lumberjack=1, hut=1, chest_work=1 });
ADD_RCC({"default:shovel_steel", 5, 1, 2, forge=1 });
ADD_RCC({"default:axe_stone", 5, 1, 3, farm_tiny=1, farm_full=1, chest_work=1, lumberjack=1 });
ADD_RCC({"default:axe_steel", 5, 1, 2, forge=1, lumberjack=1 });
ADD_RCC({"default:sword_wood", 1, 1, 3, guard=1 });
ADD_RCC({"default:sword_stone", 1, 1, 3, guard=1 });
ADD_RCC({"default:sword_steel", 1, 1, 3, forge=1, guard=1 });
ADD_RCC({"default:stick", 20, 40, 2, church=1, library=1, chest_private=1, shelf=5, shed=1, lumberjack=1, hut=1 });
ADD_RCC({"default:torch", 50, 10, 4, church=1, library=1, chest_private=1, shelf=1, shed=1, lumberjack=1, hut=1 });
ADD_RCC({"default:book", 60, 1, 2, church=1, library=1 });
ADD_RCC({"default:paper", 60, 6, 4, church=1, library=1 });
ADD_RCC({"default:apple", 50, 10, 2, chest_storage=4, chest_private=1, shelf=5});
ADD_RCC({"default:ladder", 20, 1, 2, church=1, library=1, shed=1, lumberjack=1, hut=1 });
ADD_RCC({"default:coal_lump", 80, 30, 1, forge=1, shed=1, lumberjack=1, hut=1});
ADD_RCC({"default:steel_ingot", 30, 4, 2, forge=1 });
ADD_RCC({"default:mese_crystal_fragment", 10, 3, 1, forge=1, chest_storage=1 });
ADD_RCC({"bucket:bucket_empty", 10, 3, 2, chest_work=1, forge=1, shed=1, hut=1 });
ADD_RCC({"bucket:bucket_water", 5, 3, 2, chest_work=1, forge=1 });
ADD_RCC({"bucket:bucket_lava", 3, 3, 2, forge=1 });
ADD_RCC({"vessels:glass_bottle", 10, 10, 2, church=1, library=1, shelf=1 });
ADD_RCC({"vessels:drinking_glass", 20, 2, 1, church=1, library=1, shelf=1 });
ADD_RCC({"vessels:steel_bottle", 10, 1, 1, church=1, library=1, shelf=1 });
ADD_RCC({"wool:white", 60, 8, 2, church=1, library=1 });
-- that someone will hide valuable ingots in chests that are not locked is fairly unrealistic; thus, those items are rare
ADD_RCC({"moreores:gold_ingot", 1, 2, 1 });
ADD_RCC({"moreores:silver_ingot", 1, 2, 1 });
ADD_RCC({"moreores:copper_ingot", 30, 10, 1 });
ADD_RCC({"moreores:tin_ingot", 1, 5, 1 });
ADD_RCC({"moreores:bronze_ingot", 1, 1, 1 });
ADD_RCC({"moreores:mithril_ingot", 1, 1, 1 });
-- candles are a very likely content of chests
ADD_RCC({"candles:candle", 80, 12, 2, church=1, library=1, chest_private=1 });
ADD_RCC({"candles:candelabra_steel", 1, 1, 1, church=1, library=1, chest_private=1 });
ADD_RCC({"candles:candelabra_copper", 1, 1, 1, church=1, library=1, chest_private=1 });
ADD_RCC({"candles:honey", 50, 2, 1 });
-- our NPC have to spend their free time somehow...also adds food variety
ADD_RCC({"fishing:pole", 60, 1, 1 });
-- ropes are always useful
if( minetest.get_modpath("ropes") ~= nil ) then
ADD_RCC({"ropes:rope", 60, 5, 2, chest_work=1, shelf=1, chest_storage=1 });
elseif( minetest.get_modpath("farming") ~= nil ) then
ADD_RCC({"farming:string", 60, 5, 2, church=1, library=1, chest_work=1, shelf=1, chest_storage=1 });
elseif( minetest.get_modpath("moreblocks") ~= nil ) then
ADD_RCC({"moreblocks:rope", 60, 5, 2, chest_work=1, shelf=1, chest_storage=1 });
end
ADD_RCC({'bees:bottle_honey', 50, 4, 1, beekeeper=3, tavern=1, inn=1, chest_storage=1 });
ADD_RCC({'bees:extractor', 80, 1, 2, beekeeper=1 });
ADD_RCC({'bees:frame_empty', 50, 2, 5, beekeeper=1 });
ADD_RCC({'bees:frame_full', 80, 1, 1, beekeeper=1 });
ADD_RCC({'bees:grafting_tool', 50, 1, 3, beekeeper=1 });
ADD_RCC({'bees:hive_industrial', 100, 1, 1, beekeeper=1 });
ADD_RCC({'bees:honey_comb', 50, 2, 2, beekeeper=1 });
ADD_RCC({'bees:queen_bee', 50, 2, 3, beekeeper=1 });
ADD_RCC({'bees:smoker', 80, 1, 2, beekeeper=1 });
ADD_RCC({'bees:wax', 80, 3, 3, beekeeper=1 });
ADD_RCC({'bushes:blackberry', 80, 20, 4, bakery=1 });
ADD_RCC({'bushes:blackberry_pie_cooked', 80, 12, 4, bakery=1 });
ADD_RCC({'bushes:blueberry', 80, 20, 4, bakery=1 });
ADD_RCC({'bushes:blueberry_pie_cooked', 80, 12, 4, bakery=1 });
ADD_RCC({'bushes:gooseberry', 80, 20, 4, bakery=1 });
ADD_RCC({'bushes:gooseberry_pie_cooked', 80, 12, 4, bakery=1 });
ADD_RCC({'bushes:raspberry', 80, 20, 4, bakery=1 });
ADD_RCC({'bushes:raspberry_pie_cooked', 80, 12, 4, bakery=1 });
ADD_RCC({'bushes:mixed_berry_pie_cooked', 80, 12, 4, bakery=1 });
ADD_RCC({'bushes:sugar', 80, 99, 5, bakery=1, shelf=1 });
ADD_RCC({'carts:cart', 80, 1, 2, miner=1});
ADD_RCC({'castle:battleaxe', 50, 1, 1, guard=1, forge=1 });
ADD_RCC({'castle:ropebox', 50, 2, 2, guard=1 });
ADD_RCC({'castle:ropes', 50, 1, 1, guard=2, chest_private=1, chest_work=2 });
ADD_RCC({'castle:shield', 50, 1, 1, guard=1 });
ADD_RCC({'castle:shield_2', 50, 1, 1, guard=1 });
ADD_RCC({'castle:shield_3', 50, 1, 1, guard=1 });
ADD_RCC({'cottages:anvil', 80, 1, 2, forge=1 });
ADD_RCC({'currency:minegeld', 80, 10, 2, chest_private=1, chest_work=1 }); -- TODO: could be in any chest with a certain chance
ADD_RCC({'farming:hoe_stone', 80, 1, 2, farm_tiny=2, farm_full=2, chest_work=2 });
ADD_RCC({'homedecor:beer_mug', 50, 1, 2, tavern=5, inn=3});
ADD_RCC({'homedecor:book_blue', 50, 1, 2, church=1, library=1, chest_private=1});
ADD_RCC({'homedecor:book_red', 50, 1, 2, church=1, library=1, chest_private=1});
ADD_RCC({'homedecor:book_green', 50, 1, 2, church=1, library=1, chest_private=1});
ADD_RCC({'homedecor:bottle_brown', 50, 1, 2, tavern=3, inn=3, chest_private=1});
ADD_RCC({'homedecor:bottle_green', 50, 1, 2, tavern=3, inn=3, chest_private=1});
ADD_RCC({'homedecor:calendar', 50, 1, 1, church=1, library=1, chest_private=1, chest_work=1, chest_storage=1});
ADD_RCC({"homedecor:candle", 50, 2, 1, church=2, library=1, chest_private=1, chest_work=1, chest_storage=1 });
ADD_RCC({"homedecor:candle_thin", 50, 2, 1, church=1, library=1, chest_private=1, chest_work=1, chest_storage=1 });
ADD_RCC({"homedecor:copper_pans", 80, 1, 1, chest_work=1 });
ADD_RCC({"homedecor:dardboard", 50, 1, 1, tavern=1});
ADD_RCC({"homedecor:oil_extract", 80, 1, 3, church=1, library=1, chest_private=1, chest_work=1, chest_storage=1 });
ADD_RCC({"homedecor:oil_lamp", 50, 2, 1, church=1, library=1, chest_private=1, chest_work=1, chest_storage=1 });
ADD_RCC({"homedecor:torch_wall", 50, 2, 1, church=1, library=1, chest_private=1, chest_work=1, chest_storage=1 });
ADD_RCC({"locks:key", 50, 2, 1, chest_private=1, chest_work=1, chest_storage=1, forge=1 });
ADD_RCC({"locks:keychain", 50, 2, 1, chest_private=1, chest_work=1, chest_storage=1, forge=1 });
ADD_RCC({"moretrees:coconut_milk", 80, 5, 2, tavern=1, inn=1 });
ADD_RCC({"moretrees:raw_coconut", 80, 5, 2, tavern=1, inn=1 });
ADD_RCC({"moretrees:pine_nuts", 80, 99, 1, tavern=1, inn=1, chest_storage=3 });
ADD_RCC({"moretrees:spruce_nuts", 80, 99, 1, tavern=1, inn=1, chest_storage=3 });
ADD_RCC({"quartz:quartz_crystal", 80, 1, 1, library=1 });
ADD_RCC({"screwdriver:screwdriver", 80, 1, 1, chest_work=1 });
ADD_RCC({"unified_inventory:bag_large", 80, 1, 1, chest_private=1, chest_storage=2 });
ADD_RCC({"unified_inventory:bag_medium", 80, 1, 1, chest_private=1, chest_storage=2 });
ADD_RCC({"unified_inventory:bag_small", 80, 1, 1, tavern=1, inn=1, chest_work=1, chest_private=1 });
-- get some random content for a chest
mg_villages.fill_chest_random = function( pos, pr, building_nr, building_typ )
local building_data = mg_villages.BUILDINGS[ building_nr.btype ];
local meta = minetest.env:get_meta( pos );
local inv = meta:get_inventory();
local count = 0;
local typ = minetest.get_name_from_content_id( pos.typ );
if( pos.typ_name ) then
typ = pos.typ_name;
end
if( not( typ ) or (typ ~= 'cottages:shelf' and typ ~= 'cottages:chest_work' and typ ~= 'cottages:chest_storage' and typ ~= 'cottages:chest_private' )) then
typ = building_data.typ;
else
typ = string.sub( typ, 10 );
end
local typ2 = nil;
if( typ == 'cottages:chest_work' and building_data.typ ) then
typ2 = building_data.typ;
end
--print('FILLING chest of type '..tostring( typ )..' and '..tostring( typ2));
if( not( typ ) or typ=='' ) then
return;
end
local inv_size = inv:get_size('main');
for i,v in ipairs( mg_villages.random_chest_content ) do
-- repeat this many times
for count=1, v[ 4 ] do
-- to avoid too many things inside a chest, lower probability
if( count<30 -- make sure it does not get too much and there is still room for a new stack
and (v[ typ ] or (typ2 and v[ typ2 ]))
and inv_size and inv_size > 0 and v[ 2 ] > pr:next( 1, 200 )) then
--inv:add_item('main', v[ 1 ].." "..tostring( math.random( 1, tonumber(v[ 3 ]) )));
-- add itemstack at a random position in the chests inventory
inv:set_stack( 'main', pr:next( 1, inv:get_size( 'main' )), v[ 1 ].." "..tostring( pr:next( 1, tonumber(v[ 3 ]) )) );
count = count+1;
end
end
end
end]]
--[[
--the old code used by Nores mg mapgen
for _, n in pairs(village.to_add_data.extranodes) do
-- minetest.set_node(n.pos, n.node)
if n.meta ~= nil then
meta = minetest.get_meta(n.pos)
meta:from_table(n.meta)
if n.node.name == "default:chest" then
local inv = meta:get_inventory()
local items = inv:get_list("main")
for i=1, inv:get_size("main") do
inv:set_stack("main", i, ItemStack(""))
end
local numitems = pr:next(3, 20)
for i=1,numitems do
local ii = pr:next(1, #items)
local prob = items[ii]:get_count() % 2 ^ 8
local stacksz = math.floor(items[ii]:get_count() / 2 ^ 8)
if pr:next(0, prob) == 0 and stacksz>0 then
stk = ItemStack({name=items[ii]:get_name(), count=pr:next(1, stacksz), wear=items[ii]:get_wear(), metadata=items[ii]:get_metadata()})
local ind = pr:next(1, inv:get_size("main"))
while not inv:get_stack("main",ind):is_empty() do
ind = pr:next(1, inv:get_size("main"))
end
inv:set_stack("main", ind, stk)
end
end
end
end
end
--]]
--the code that works
-- adapted from the Mines mod
local chest_stuff = {
{name="default:apple", max = 3},
{name="farming:bread", max = 3},
{name="default:steel_ingot", max = 2},
{name="default:gold_ingot", max = 2},
{name="default:axe_steel", max = 1},
--{name="default:emerald", max = 5},
{name="default:pick_steel", max = 1},
{name="default:shovel_steel", max = 1},
{name="default:book", max = 3},
{name="default:torch", max = 13},
{name="default:stick", max = 7},
{name="default:coal_lump", max = 4},
{name="bucket:bucket_empty", max = 1},
{name="default:ladder", max = 10},
{name="default:mese_crystal_fragment", max = 2},
{name="vessels:glass_bottle", max = 1},
{name="wool:white", max = 11},
--{name="carpet:white", max = 11},
--{name="quartz:quartz_crystal", max = 5},
--{name="shears:shears", max = 1},
--{name="crops:melon_seed", max = 18},
--{name="mobs:saddle", max = 3},
{name="farming:carrot", max = 3},
{name="farming:corn", max = 3},
{name="farming:melon_slice", max = 3},
{name="farming:potato", max = 3},
{name="farming:raspberries", max = 3},
{name="farming:rhubarb", max = 3},
{name="farming:sugar", max = 3},
{name="farming:tomato", max = 3},
{name="farming:seed_wheat", max = 3},
{name="farming:cucumber", max = 3},
{name="farming:grapes", max = 3},
}
-- get some random content for a chest
mg_villages.fill_chest_random = function( pos, pr, building_nr, building_typ )
local meta = minetest.get_meta( pos )
local inv = meta:get_inventory()
inv:set_size("main", 8*4)
for i=0,pr:next(1,6),1 do
local stuff = chest_stuff[pr:next(1,#chest_stuff)]
local stack = {name=stuff.name, count = pr:next(1,stuff.max)}
if not inv:contains_item("main", stack) then
inv:set_stack("main", pr:next(1,32), stack)
end
end
end

View File

@ -0,0 +1,234 @@
-- highlandpools 0.1.1 by paramat
-- For latest stable Minetest back to 0.4.8
-- Depends default
-- Licenses: code WTFPL
-- Parameters
local YMAX = 33000 -- Maximum altitude for pools
local FLOW = 256
-- Stuff
highlandpools = {}
-- Functions
function highlandpools_remtree(x, y, z, area, data)
local c_tree = minetest.get_content_id("default:tree")
local c_apple = minetest.get_content_id("default:apple")
local c_leaves = minetest.get_content_id("default:leaves")
local c_air = minetest.get_content_id("air")
for j = 1, 7 do
for i = -2, 2 do
for k = -2, 2 do
local vi = area:index(x+i, y+j, z+k)
if data[vi] == c_tree
or data[vi] == c_apple
or data[vi] == c_leaves then
data[vi] = c_air
end
end
end
end
for j = 1, 7 do
for i = -2, 2 do
for k = -2, 2 do
local vi = area:index(x+i, y-j, z+k)
if data[vi] == c_tree
or data[vi] == c_apple
or data[vi] == c_leaves then
data[vi] = c_air
end
end
end
end
end
-- On generated function
mg_villages.do_highlandpools = function(minp, maxp, seed, vm, area, data, village_area)
local y0 = minp.y
if y0 < -32 or y0 > YMAX then
return
end
local x0 = minp.x
local z0 = minp.z
local x1 = maxp.x
local y1 = maxp.y
local z1 = maxp.z
local sidelen = x1 - x0 -- actually sidelen - 1
local c_air = minetest.get_content_id("air")
local c_ignore = minetest.get_content_id("ignore")
local c_watsour = minetest.get_content_id("default:river_water_source")
local c_grass = minetest.get_content_id("default:dirt_with_grass")
local c_tree = minetest.get_content_id("default:tree")
local c_apple = minetest.get_content_id("default:apple")
local c_leaves = minetest.get_content_id("default:leaves")
local c_dirt = minetest.get_content_id("default:dirt")
for xcen = x0 + 8, x1 - 7, 8 do
for zcen = z0 + 8, z1 - 7, 8 do
-- TODO: village_area[ x ][ z ][ 2 ] == 0 means: not inside any village area or terrain blend area
local yasurf = false -- y of above surface node
for y = y1, 2, -1 do
local vi = area:index(xcen, y, zcen)
local c_node = data[vi]
if y == y1 and c_node ~= c_air then -- if top node solid
break
elseif c_node == c_watsour then
break
elseif c_node == c_grass then
yasurf = y + 1
break
end
end
if yasurf then
local abort = false
for ser = 1, 80 do
local vi = area:index(xcen + ser, yasurf, zcen)
local c_node = data[vi]
if xcen + ser == x1 then
abort = true
elseif c_node ~= c_air
and c_node ~= c_tree
and c_node ~= c_leaves
and c_node ~= c_apple then
break
end
end
for ser = 1, 80 do
local vi = area:index(xcen - ser, yasurf, zcen)
local c_node = data[vi]
if xcen - ser == x0 then
abort = true
elseif c_node ~= c_air
and c_node ~= c_tree
and c_node ~= c_leaves
and c_node ~= c_apple then
break
end
end
for ser = 1, 80 do
local vi = area:index(xcen, yasurf, zcen + ser)
local c_node = data[vi]
if zcen + ser == z1 then
abort = true
elseif c_node ~= c_air
and c_node ~= c_tree
and c_node ~= c_leaves
and c_node ~= c_apple then
break
end
end
for ser = 1, 80 do
local vi = area:index(xcen, yasurf, zcen - ser)
local c_node = data[vi]
if zcen - ser == z0 then
abort = true
elseif c_node ~= c_air
and c_node ~= c_tree
and c_node ~= c_leaves
and c_node ~= c_apple then
break
end
end
if abort then
break
end
local vi = area:index(xcen, yasurf, zcen)
data[vi] = c_watsour
local flab = false -- flow abort
for flow = 1, FLOW do
for z = z0, z1 do
for x = x0, x1 do
local vif = area:index(x, yasurf, z)
if data[vif] == c_watsour then
if x == x0 or x == x1 or z == z0 or z == z1 then
flab = true -- if water at chunk edge abort flow
break
else -- flow water
local vie = area:index(x + 1, yasurf, z)
local viw = area:index(x - 1, yasurf, z)
local vin = area:index(x, yasurf, z + 1)
local vis = area:index(x, yasurf, z - 1)
if data[vie] == c_tree then
highlandpools_remtree(x + 1, yasurf, z, area, data)
data[vie] = c_watsour
elseif data[vie] == c_air
or data[vie] == c_apple
or data[vie] == c_leaves then
data[vie] = c_watsour
end
if data[viw] == c_tree then
highlandpools_remtree(x - 1, yasurf, z, area, data)
data[viw] = c_watsour
elseif data[viw] == c_air
or data[viw] == c_apple
or data[viw] == c_leaves then
data[viw] = c_watsour
end
if data[vin] == c_tree then
highlandpools_remtree(x, yasurf, z + 1, area, data)
data[vin] = c_watsour
elseif data[vin] == c_air
or data[vin] == c_apple
or data[vin] == c_leaves then
data[vin] = c_watsour
end
if data[vis] == c_tree then
highlandpools_remtree(x, yasurf, z - 1, area, data)
data[vis] = c_watsour
elseif data[vis] == c_air
or data[vis] == c_apple
or data[vis] == c_leaves then
data[vis] = c_watsour
end
end
end
end
if flab then
break
end
end
if flab then
break
end
end
if flab then -- erase water from this y level
for z = z0, z1 do
for x = x0, x1 do
local vi = area:index(x, yasurf, z)
if data[vi] == c_watsour then
data[vi] = c_air
end
end
end
else -- flow downwards add dirt
for z = z0, z1 do
for x = x0, x1 do
local vi = area:index(x, yasurf, z)
if data[vi] == c_watsour then
for y = yasurf - 1, y0, -1 do
local viu = area:index(x, y, z)
if data[viu] == c_air then
data[viu] = c_watsour
elseif data[viu] == c_grass then
data[viu] = c_dirt
break
else
break
end
end
end
end
end
end
end
end
end
end

View File

@ -0,0 +1,75 @@
-- reserve namespace for the villages
mg_villages = {}
mg_villages.all_villages = {}
mg_villages.mg_generated_map = {}
mg_villages.anz_villages = 0;
mg_villages.modpath = minetest.get_modpath( "mg_villages");
mg_villages.DEBUG_LEVEL_NONE = -1 -- -1: disable all printed messages
mg_villages.DEBUG_LEVEL_NORMAL = 0 -- 0: print information about which village spawned where plus important errors
mg_villages.DEBUG_LEVEL_WARNING = 1 -- 1: warnings/errors which may not be particulary helpful for non-developers
mg_villages.DEBUG_LEVEL_INFO = 2 -- 2: print even less important warnings
mg_villages.DEBUG_LEVEL_TIMING = 3 -- 3: detailled performance information
mg_villages.print = function( level, msg )
if( level <= mg_villages.DEBUG_LEVEL ) then
print( "[mg_villages] "..msg );
end
end
-- save_restore is now part of handle_schematics
--dofile(mg_villages.modpath.."/save_restore.lua")
mg_villages.all_villages = save_restore.restore_data( 'mg_all_villages.data' ); -- read mg_villages.all_villages data saved for this world from previous runs
mg_villages.mg_generated_map = save_restore.restore_data( 'mg_generated_map.data' );
dofile(mg_villages.modpath.."/config.lua")
-- adds a special gravel node which will neither fall nor be griefed by mapgen
dofile(mg_villages.modpath.."/nodes.lua")
-- the default game no longer provides helpful tree growing code
dofile(mg_villages.modpath.."/trees.lua")
dofile(mg_villages.modpath.."/replacements.lua")
-- multiple diffrent village types with their own sets of houses are supported
-- The function mg_villages.add_village_type( village_type_name, village_type_data )
-- allows other mods to add new village types.
dofile(mg_villages.modpath.."/village_types.lua")
-- Note: the "buildings" talbe is not in the mg_villages.* namespace
-- The function mg_villages.add_building( building_data ) allows other mods to add buildings.
dofile(mg_villages.modpath.."/buildings.lua")
-- mg_villages.init_weights() has to be called AFTER all village types and buildings have
-- been added using the functions above
dofile(mg_villages.modpath.."/init_weights.lua")
-- generate village names
dofile(mg_villages.modpath.."/name_gen.lua");
dofile(mg_villages.modpath.."/villages.lua")
-- adds a command that allows to teleport to a known village
dofile(mg_villages.modpath.."/chat_commands.lua")
-- protect villages from griefing
dofile(mg_villages.modpath.."/protection.lua")
-- create and show a map of the world
dofile(mg_villages.modpath.."/map_of_world.lua")
dofile(mg_villages.modpath.."/fill_chest.lua")
-- terrain blending for individual houses
dofile(mg_villages.modpath.."/terrain_blend.lua")
-- highlandpools
dofile(mg_villages.modpath.."/highlandpools.lua")
-- the interface for the mapgen;
-- also takes care of spawning the player
dofile(mg_villages.modpath.."/mapgen.lua")
dofile(mg_villages.modpath.."/spawn_player.lua")

View File

@ -0,0 +1,43 @@
-- this functions needs to be called once after *all* village types and buildings have been added
mg_villages.init_weights = function()
-- create a list of all used village types
mg_villages.village_types = {};
for k,v in pairs( mg_villages.village_type_data ) do
if( not( v.only_single ) and v.supported and v.building_list ) then
table.insert( mg_villages.village_types, k );
end
end
mg_villages.print(mg_villages.DEBUG_LEVEL_NORMAL,'Will create villages of the following types: '..minetest.serialize( mg_villages.village_types ));
mg_villages.village_types[ #mg_villages.village_types+1 ] = 'single';
mg_villages.village_types[ #mg_villages.village_types+1 ] = 'fields';
mg_villages.village_types[ #mg_villages.village_types+1 ] = 'tower';
for j,v in ipairs( mg_villages.village_types ) do
local total_weight = 0
for _, i in ipairs(mg_villages.BUILDINGS) do
if( not( i.max_weight )) then
i.max_weight = {};
end
if( i.weight and i.weight[ v ] and i.weight[ v ]>0 ) then
total_weight = total_weight+i.weight[ v ]
i.max_weight[v] = total_weight
end
end
local multiplier = 3000/total_weight
for _,i in ipairs(mg_villages.BUILDINGS) do
if( i.weight and i.weight[ v ] and i.weight[ v ]>0 ) then
i.max_weight[v] = i.max_weight[ v ]*multiplier
end
end
end
-- the fields do not exist as an independent type
mg_villages.village_types[ #mg_villages.village_types ] = nil;
-- neither does the tower type
mg_villages.village_types[ #mg_villages.village_types ] = nil;
-- and neither does the "single" type (==lone houses outside villages)
mg_villages.village_types[ #mg_villages.village_types ] = nil;
end

View File

@ -0,0 +1,193 @@
-- villages up to this many nodes in each direction are shown on the map
mg_villages.MAP_RANGE = 1000;
mg_villages.draw_tile = function( content_id, image, x, z, dx, dz, tile_nr )
if( not( image )) then
local node_name = minetest.get_name_from_content_id( content_id );
if( not( node_name )) then
return '';
end
local node_def = minetest.registered_nodes[ node_name ];
if( not( node_def )) then
return '';
end
local tiles = node_def.tiles;
local tile = nil;
if( tiles ~= nil ) then
if( not(tile_nr) or tile_nr > #tiles or tile_nr < 1 ) then
tile_nr = 1;
end
tile = tiles[tile_nr];
end
if type(tile)=="table" then
tile=tile["name"]
end
image = tile;
if( not( image )) then
image = "unknown_object.png";
end
end
return "image["..tostring(x)..",".. tostring(z) ..";"..dx..','..dz..";" .. image .."]";
end
mg_villages.map_of_world = function( pname )
local player = minetest.get_player_by_name( pname );
if( not( player )) then
return '';
end
local ppos = player:getpos();
-- also usable: diamond_block, sand, water
local formspec = "size[14.4,10]"..
"background[0,0;10,10;"..mg_villages.MAP_BACKGROUND_IMAGE.."]"..
"label[10,10;x axis]"..
"label[0,0;z axis]"..
"label[0,10;|]"..
"label[0.2,10;->]";
local r = mg_villages.MAP_RANGE;
local f1 = 10/(2*r);
local map_tiles_shown = math.floor( mg_villages.MAP_RANGE/80 );
local center_x = math.floor( ppos.x/80 );
local center_z = math.floor( ppos.z/80 );
for x = center_x - map_tiles_shown, center_x + map_tiles_shown do
for z = center_z - map_tiles_shown, center_z + map_tiles_shown do
if( mg_villages.mg_generated_map[ x ] and mg_villages.mg_generated_map[ x ][ z ] ) then
local surface_types = mg_villages.mg_generated_map[ x ][ z ];
local content_id = 0;
if( type( surface_types )=='table' ) then
content_id = surface_types[ 26 ];
else
content_id = surface_types;
end
local x1 = f1 * ((x*80) - ppos.x +r);
local z1 = f1 * ( (2*r) - ((z*80) - ppos.z + r));
local dx = f1 * 80;
local dz = f1 * 80;
formspec = formspec..mg_villages.draw_tile( content_id, nil, x1+0.5, z1-0.5, dx*1.25, dz*1.25, 1 );
-- if more detailed information is available, draw those tiles that differ from the most common tile
if( type( surface_types )=='table' and false) then -- TODO: disabled for now
dx = dx/5;
dz = dz/5;
for i,v in ipairs( surface_types ) do
if( v ~= content_id ) then
local x2 = x1+( math.floor( (i-1)/5 )*dx);
local z2 = z1+( math.floor( (i-1)%5 )*dz);
formspec = formspec..mg_villages.draw_tile( v, nil, x2+0.5, z2-0.5, dx*1.3, dz*1.3, 1);
end
end
end
end
end
end
local shown_villages = {};
r = mg_villages.MAP_RANGE;
f1 = 10/(2*r);
for name,v in pairs( mg_villages.all_villages ) do
local data = v; --minetest.deserialize( v );
local x = data.vx - ppos.x;
local z = data.vz - ppos.z;
-- show only villages which are at max mg_villages.MAP_RANGE away from player
if( x and z
and mg_villages.village_type_data[ data.village_type ]
and mg_villages.village_type_data[ data.village_type ].texture
and math.abs( x ) < r
and math.abs( z ) < r ) then
-- the village size determines the texture size
local dx = f1 * (data.vs*2) *1.25;
local dz = f1 * (data.vs*2) *1.0;
-- center the village texture
x = x - (data.vs/2);
z = z + (data.vs/2);
-- calculate the position for the village texture
x = f1 * (x+r);
z = f1 * ( (2*r) -(z+r));
formspec = formspec..
"label["..x..",".. z ..";"..tostring( data.nr ).."]"..mg_villages.draw_tile( nil, mg_villages.village_type_data[ data.village_type ].texture, x, z, dx, dz, 1 );
shown_villages[ #shown_villages+1 ] = tostring( data.nr )..". "..tostring( v.name or 'unknown' ).."]";
end
end
-- code and arrows taken from mapp mod
local yaw = player:get_look_yaw()
local rotate = 0;
if yaw ~= nil then
-- Find rotation and texture based on yaw.
yaw = math.deg(yaw)
yaw = math.fmod (yaw, 360)
if yaw<0 then yaw = 360 + yaw end
if yaw>360 then yaw = yaw - 360 end
if yaw < 90 then
rotate = 90
elseif yaw < 180 then
rotate = 180
elseif yaw < 270 then
rotate = 270
else
rotate = 0
end
yaw = math.fmod(yaw, 90)
yaw = math.floor(yaw / 10) * 10
end
-- show the players yaw
if rotate ~= 0 then
formspec = formspec.."image[".. 4.95 ..",".. 4.85 ..";0.4,0.4;d" .. yaw .. ".png^[transformFYR".. rotate .."]"
else
formspec = formspec.."image[".. 4.95 ..",".. 4.85 ..";0.4,0.4;d" .. yaw .. ".png^[transformFY]"
end
local i = 0.05;
formspec = formspec.."label[10,-0.4;Village types:]";
-- explain the meaning of the textures
if mg_villages.village_types ~= nil then
for _,typ in ipairs(mg_villages.village_types) do
formspec = formspec.."label[10.5,"..tostring(i)..";"..tostring( typ ).."]"..
"image[10.0,"..tostring(i+0.1)..";0.4,0.4;"..tostring( mg_villages.village_type_data[ typ ].texture ).."]";
i = i+0.45;
end
end
i = i+0.45;
formspec = formspec.."label[10.0,"..tostring(i)..";Villages shown on this map:]";
i = i+0.45;
local j = 1;
while (i<10.5 and j<=#shown_villages) do
formspec = formspec.."label[10.0,"..tostring(i)..";"..tostring( shown_villages[ j ] ).."]";
i = i+0.45;
j = j+1;
end
return formspec;
end
minetest.register_chatcommand( 'vmap', {
description = "Shows a map of all known villages withhin "..tostring( mg_villages.MAP_RANGE ).." blocks.",
privs = {},
func = function(name, param)
minetest.show_formspec( name, 'mg:world_map', mg_villages.map_of_world( name ));
end
});

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,90 @@
namegen = {};
namegen.prefixes = {'ac','ast','lang','pen','shep','ship'}
namegen.suffixes = {'beck','ey','ay','bury','burgh','brough','by','by','caster',
'cester','cum','den','don','field','firth','ford','ham','ham','ham',
'hope','ing','kirk','hill','law','leigh','mouth','ness','pool','shaw',
'stead','ster','tun','ton','ton','ton','ton','wold','worth','worthy',
'ville','river','forrest','lake'}
-- people/environmental features
namegen.silben = { 'a', 'an', 'ab', 'ac', 'am',
'be', 'ba', 'bi', 'bl', 'bm', 'bn', 'bo', 'br', 'bst', 'bu',
'ca', 'ce', 'ch', 'ci', 'ck', 'cl', 'cm', 'cn', 'co', 'cv',
'da', 'de', 'df', 'di', 'dl', 'dm', 'dn', 'do', 'dr', 'ds', 'dt', 'du', 'dv',
'do','ren','nav','ben','ada','min','org','san','pa','re','ne','en','er','ich',
'the','and','tha','ent','ing','ion','tio','for','nde',
'has','nce','edt','tis','oft','sth','mem',
'ich','ein','und','der','nde','sch','die','den','end','cht',
'the','and','tha','ent','ing','ion','for','de',
'has','ce','ed','is','ft','sth','mem',
'ch','ei','un','der','ie','den','end',
'do','ren','nav','ben','ada','min','org','san','pa','re','ne','en','er','ich',
'ta','bek','nik','le','lan','nem',
'bal','cir','da','en','fan','fir','fern','fa','oak','nut','gen','ga','hu','hi','hal',
'in','ig','ir','im','ja','je','jo','kla','kon','ker','log','lag','leg','lil',
'lon','las','leve','lere','mes','mir','mon','mm','mer','mig',
'na','nn','nerv','neu','oto','on','opt','oll','ome','ott',
'pen','par','pi','pa','po','pel','pig','qu','ren','rig','raf','res','ring',
'rib','rast','rost','ru','rum','rem','sem','sim','su','spring',
'cotton','cot','wood','palm',
'do','na','ik','ke','gen','bra','bn','lla','lle','st','aa','kir',
'nn','en','fo','fn','gi','ja','jn','ke','kr','kon','lis','on','ok','or','op',
'pp','p','qu','re','ra','rn','ri','so','sn','se','ti','tu',
'a','e','i','o','u',
're','ro','pe','pn','ci','co','cl',
'no','en','wi','we','er','en','ba','ki','nn','va','wu','x','tel','or',
'so','me','mi','em','en','eg','ge','kn'};
namegen.generate_village_name = function( pr )
local anz_silben = pr:next(2,5);
local name = '';
local prefix = '';
local postfix = '';
if( pr:next(1,8)==1) then
prefix = namegen.prefixes[ #namegen.prefixes ];
anz_silben = anz_silben -1;
end
if( pr:next(1,4)==1) then
postfix = name..namegen.suffixes[ #namegen.suffixes ];
anz_silben = anz_silben -2;
end
if( anz_silben < 2 ) then
anz_silben = 2;
end
for i = 1, anz_silben do
name = name..namegen.silben[ pr:next( 1, #namegen.silben )];
end
name = prefix..name..postfix;
name = string.upper( string.sub( name, 1, 1 ) )..string.sub( name, 2 );
return name;
end
namegen.generate_village_name_with_prefix = function( pr, village )
local name = namegen.generate_village_name( pr );
-- if a village consists of a single house, it gets a prefix depending on the house type
if( village.is_single_house and village.to_add_data and village.to_add_data.bpos ) then
-- the building got removed from mg_villages.BUILDINGS in the meantime
if( not( mg_villages.BUILDINGS[ village.to_add_data.bpos[1].btype] )) then
return 'Abandomed building';
end
local btyp = mg_villages.BUILDINGS[ village.to_add_data.bpos[1].btype].typ;
local bdata = mg_villages.village_type_data[ btyp ];
if( bdata and (bdata.name_prefix or bdata.name_postfix )) then
name = (bdata.name_prefix or '')..name..(bdata.name_postfix or '');
else
name = 'House '..name;
end
end
return name;
end

View File

@ -0,0 +1,142 @@
minetest.register_node("mg_villages:road", {
description = "village road",
tiles = {"default_gravel.png", "default_dirt.png"},
is_ground_content = true, -- will be removed by the cave generator
groups = {crumbly=2, falling_node = 1}, -- does fall
sounds = default.node_sound_dirt_defaults({
footstep = {name="default_gravel_footstep", gain=0.5},
dug = {name="default_gravel_footstep", gain=1.0},
}),
paramtype = "light",
paramtype2 = "facedir",
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = { { -0.5, -0.5, -0.5, 0.5, 0.5-2/16, 0.5}, },
},
})
mg_villages.road_node = minetest.get_content_id( 'mg_villages:road' );
-- do not drop snow on roads
if( moresnow ) then
moresnow.snow_cover[ mg_villages.road_node ] = moresnow.c_air;
end
minetest.register_node("mg_villages:soil", {
description = "Soil found on a field",
tiles = {"es_mud.png^farming_soil_wet.png", "es_mud.png"},
drop = "es:mud",
is_ground_content = true,
groups = {crumbly=3, not_in_creative_inventory=1, grassland = 1},
sounds = default.node_sound_dirt_defaults(),
})
minetest.register_node("mg_villages:desert_sand_soil", {
description = "Desert Sand",
tiles = {"default_desert_sand.png^farming_soil_wet.png", "default_desert_sand.png"},
is_ground_content = true,
drop = "default:desert_sand",
groups = {crumbly=3, not_in_creative_inventory = 1, sand=1, desert = 1},
sounds = default.node_sound_sand_defaults(),
})
-- This torch is not hot. It will not melt snow and cause no floodings in villages.
minetest.register_node("mg_villages:torch", {
description = "Torch-air",
drawtype = "torchlike",
--tiles = {"default_torch_on_floor.png", "default_torch_on_ceiling.png", "default_torch.png"},
tiles = {"default_invis.png"},
inventory_image = "default_torch_on_floor.png",
wield_image = "default_torch_on_floor.png",
paramtype = "light",
paramtype2 = "wallmounted",
sunlight_propagates = true,
is_ground_content = false,
walkable = false,
--light_source = LIGHT_MAX-1,
selection_box = {
type = "wallmounted",
wall_top = {-0.1, 0.5-0.6, -0.1, 0.1, 0.5, 0.1},
wall_bottom = {-0.1, -0.5, -0.1, 0.1, -0.5+0.6, 0.1},
wall_side = {-0.5, -0.3, -0.1, -0.5+0.3, 0.3, 0.1},
},
groups = {choppy=2,dig_immediate=3,flammable=1,attached_node=1},
legacy_wallmounted = true,
sounds = default.node_sound_defaults(),
drop = "default:torch",
})
minetest.register_node("mg_villages:plotmarker", {
description = "Plot marker",
drawtype = "nodebox",
tiles = {"default_stone_brick.png"},
paramtype = "light",
paramtype2 = "facedir",
node_box = {
type = "fixed",
fixed = {
{-0.5+2/16, -0.5, -0.5+2/16, 0.5-2/16, -0.5+3/16, 0.5-2/16},
},
},
groups = {cracky=3,stone=2},
on_rightclick = function( pos, node, clicker, itemstack, pointed_thing)
return mg_villages.plotmarker_formspec( pos, nil, {}, clicker )
end,
on_receive_fields = function(pos, formname, fields, sender)
return mg_villages.plotmarker_formspec( pos, formname, fields, sender );
end,
-- protect against digging
can_dig = function( pos, player )
local meta = minetest.get_meta( pos );
if( meta and meta:get_string( 'village_id' )~='' and meta:get_int( 'plot_nr' ) and meta:get_int( 'plot_nr' )>0) then
return false;
end
return true;
end
})
-- default to safe lava
if( not( mg_villages.use_normal_unsafe_lava )) then
local lava = minetest.registered_nodes[ "default:lava_source"];
if( lava ) then
-- a deep copy for the table would be more helpful...but, well, ...
local new_def = minetest.deserialize( minetest.serialize( lava ));
-- this lava does not cause fire to spread
new_def.name = nil;
new_def.groups.lava = nil;
new_def.groups.hot = nil;
new_def.groups.igniter = nil;
new_def.groups.lava_tamed = 3;
new_def.description = "Lava Source (tame)";
new_def.liquid_alternative_flowing = "mg_villages:lava_flowing_tamed";
new_def.liquid_alternative_source = "mg_villages:lava_source_tamed";
-- we create a NEW type of lava for this
minetest.register_node( "mg_villages:lava_source_tamed", new_def );
end
-- take care of the flowing variant as well
lava = minetest.registered_nodes[ "default:lava_flowing"];
if( lava ) then
-- a deep copy for the table would be more helpful...but, well, ...
local new_def = minetest.deserialize( minetest.serialize( lava ));
-- this lava does not cause fire to spread
new_def.name = nil;
new_def.groups.lava = nil;
new_def.groups.hot = nil;
new_def.groups.igniter = nil;
new_def.groups.lava_tamed = 3;
new_def.description = "Flowing Lava (tame)";
new_def.liquid_alternative_flowing = "mg_villages:lava_flowing_tamed";
new_def.liquid_alternative_source = "mg_villages:lava_source_tamed";
-- and a NEW type of flowing lava...
minetest.register_node( "mg_villages:lava_flowing_tamed", new_def );
end
end

View File

@ -0,0 +1,307 @@
-- get the id of the village pos lies in (or nil if outside of villages)
mg_villages.get_town_id_at_pos = function( pos )
for id, v in pairs( mg_villages.all_villages ) do
local size = v.vs * 3;
if( ( math.abs( pos.x - v.vx ) < size )
and ( math.abs( pos.z - v.vz ) < size )
and ( pos.y - v.vh < 40 and v.vh - pos.y < 10 )) then
local village_noise = minetest.get_perlin(7635, 3, 0.5, 16);
if( mg_villages.inside_village_area( pos.x, pos.z, v, village_noise)) then
local node = minetest.get_node( pos );
-- leaves can be digged in villages
if( node and node.name ) then
if( minetest.registered_nodes[ node.name ]
and minetest.registered_nodes[ node.name ].groups
and minetest.registered_nodes[ node.name ].groups.leaves ) then
return nil;
elseif( node.name=='default:snow' ) then
return nil;
-- bones can be digged in villages
elseif( node.name == 'bones:bones' ) then
return nil;
else
return id;
end
else
return id;
end
end
end
end
return nil;
end
local old_is_protected = minetest.is_protected
minetest.is_protected = function(pos, name)
if( not( mg_villages.ENABLE_PROTECTION )) then
return old_is_protected( pos, name );
end
local village_id = mg_villages.get_town_id_at_pos( pos );
if( village_id ) then
local is_houseowner = false;
for nr, p in ipairs( mg_villages.all_villages[ village_id ].to_add_data.bpos ) do
trustedusers = p.can_edit
trustedUser = false
if trustedusers ~= nil then
for _,trusted in ipairs(trustedusers) do
if trusted == name then
trustedUser = true
end
end
end
-- we have located the right plot; the player can build here if he owns this particular plot
if( p.x <= pos.x and (p.x + p.bsizex) >= pos.x
and p.z <= pos.z and (p.z + p.bsizez) >= pos.z) then
-- If player has been trusted by owner, can build
if (trustedUser) then
return false;
-- If player is owner, can build
elseif( p.owner and p.owner == name ) then
return false;
-- the allmende can be used by all
elseif( mg_villages.BUILDINGS[p.btype] and mg_villages.BUILDINGS[p.btype].typ=="allmende" ) then
return false;
-- the player cannot modify other plots, even though he may be house owner of another house and be allowed to modify common ground
else
return true;
end
-- if the player just owns another plot in the village, check if it's one where villagers may live
elseif( p.owner and p.owner == name or trustedUser) then
local btype = mg_villages.all_villages[ village_id ].to_add_data.bpos[ nr ].btype;
if( btype ~= 'road'
and mg_villages.BUILDINGS[btype]
and mg_villages.BUILDINGS[btype].inh
and mg_villages.BUILDINGS[btype].inh > 0 ) then
is_houseowner = true;
end
end
end
-- players who own a house in town where villagers may live (not only work!)
-- are allowed to modify common ground
if( is_houseowner ) then
return false;
end
return true;
end
return old_is_protected(pos, name);
end
minetest.register_on_protection_violation( function(pos, name)
if( not( mg_villages.ENABLE_PROTECTION )) then
return;
end
local found = mg_villages.get_town_id_at_pos( pos );
if( not( found ) or not( mg_villages.all_villages[ found ])) then
minetest.chat_send_player( name, 'Error: This area does not belong to a village.');
return;
end
minetest.chat_send_player( name, "You are inside of the area of the village "..
tostring( mg_villages.all_villages[ found ].name )..
". The inhabitants do not allow you any modifications.");
end );
mg_villages.plotmarker_formspec = function( pos, formname, fields, player )
-- if( not( mg_villages.ENABLE_PROTECTION )) then
-- return;
-- end
local meta = minetest.get_meta( pos );
if( not( meta )) then
return;
end
local village_id = meta:get_string('village_id');
local plot_nr = meta:get_int( 'plot_nr');
local pname = player:get_player_name();
if( not( village_id )
or not( mg_villages.all_villages )
or not( mg_villages.all_villages[ village_id ] )
or not( plot_nr )
or not( mg_villages.all_villages[ village_id ].to_add_data.bpos[ plot_nr ] )) then
minetest.chat_send_player( pname, 'Error. This plot marker is not configured correctly.'..minetest.serialize({village_id,plot_nr }));
return;
end
local owner = mg_villages.all_villages[ village_id ].to_add_data.bpos[ plot_nr ].owner;
local btype = mg_villages.all_villages[ village_id ].to_add_data.bpos[ plot_nr ].btype;
--minetest.chat_send_player( player:get_player_name(),'DATA FOR '..tostring(plot_nr)..': '..minetest.serialize( mg_villages.all_villages[ village_id ].to_add_data.bpos[ plot_nr ] ));
local original_formspec = "size[8,3]"..
"label[1.0,0.5;Plot No.: "..tostring( plot_nr ).."]"..
"label[2.5,0.5;Building:]"..
"label[3.5,0.5;"..tostring( mg_villages.BUILDINGS[btype].scm ).."]"..
"field[20,20;0.1,0.1;pos2str;Pos;"..minetest.pos_to_string( pos ).."]";
local formspec = "";
local ifinhabit = "";
-- Get Price
local price = "default:gold_ingot 2";
if (btype ~= 'road' and mg_villages.BUILDINGS[btype]) then
local plot_descr = 'Plot No. '..tostring( plot_nr ).. ' with '..tostring( mg_villages.BUILDINGS[btype].scm)
if (mg_villages.BUILDINGS[btype].price) then
price = mg_villages.BUILDINGS[btype].price;
elseif (mg_villages.BUILDINGS[btype].typ and mg_villages.prices[ mg_villages.BUILDINGS[btype].typ ]) then
price = mg_villages.prices[ mg_villages.BUILDINGS[btype].typ ];
end
-- Get if is inhabitant house
if (mg_villages.BUILDINGS[btype].inh and mg_villages.BUILDINGS[btype].inh > 0 ) then
ifinhabit = "label[1,1.5;Owners of this plot count as village inhabitants.]";
end
end
-- Determine price depending on building type
local price_stack= ItemStack( price );
-- If nobody owns the plot
if (not(owner) or owner=='') then
formspec = original_formspec ..
"label[1,1;You can buy this plot for]"..
"label[3.8,1;"..tostring( price_stack:get_count() ).." x ]"..
"item_image[4.3,0.8;1,1;"..( price_stack:get_name() ).."]"..
ifinhabit..
"button[2,2.5;1.5,0.5;buy;Buy plot]"..
"button_exit[4,2.5;1.5,0.5;abort;Exit]";
-- On Press buy button
if (fields['buy']) then
local inv = player:get_inventory();
if not mg_villages.all_villages[village_id].ownerlist then
mg_villages.all_villages[village_id].ownerlist = {}
end
-- Check if player already has a house in the village
if mg_villages.all_villages[village_id].ownerlist[pname] then
formspec = formspec.."label[1,1.9;Sorry. You already have a plot in this village.]";
-- Check if the price can be paid
elseif( inv and inv:contains_item( 'main', price_stack )) then
formspec = original_formspec..
"label[1,1;Congratulations! You have bought this plot.]"..
"button_exit[5.75,2.5;1.5,0.5;abort;Exit]";
mg_villages.all_villages[ village_id ].to_add_data.bpos[ plot_nr ].owner = pname;
if mg_villages.all_villages[village_id].ownerlist then
mg_villages.all_villages[village_id].ownerlist[pname] = true;
else
mg_villages.all_villages[village_id].ownerlist[pname] = true;
end
meta:set_string('infotext', 'Plot No. '..tostring( plot_nr ).. ' with '..tostring( mg_villages.BUILDINGS[btype].scm)..' (owned by '..tostring( pname )..')');
-- save the data so that it survives server restart
mg_villages.save_data();
-- substract the price from the players inventory
inv:remove_item( 'main', price_stack );
else
formspec = formspec.."label[1,1.9;Sorry. You are not able to pay the price.]";
end
end
-- If player is the owner of the plot
elseif (owner==pname) then
-- Check if inhabitant house
if(btype ~= 'road'
and mg_villages.BUILDINGS[btype]
and mg_villages.BUILDINGS[btype].inh
and mg_villages.BUILDINGS[btype].inh > 0 ) then
ifinhabit = "label[1,1.5;You are allowed to modify the common village area.]";
end
formspec = original_formspec.."size[8,3]"..
"label[1,1;This is your plot. You have bought it.]"..
"button[0.75,2.5;3,0.5;add_remove;Add/Remove Players]"..
ifinhabit..
"button_exit[3.75,2.5;2.0,0.5;abandon;Abandon plot]"..
"button_exit[5.75,2.5;1.5,0.5;abort;Exit]";
-- If Player wants to abandon plot
if(fields['abandon'] ) then
formspec = original_formspec..
"label[1,1;You have abandoned this plot.]"..
"button_exit[5.75,2.5;1.5,0.5;abort;Exit]";
mg_villages.all_villages[village_id].ownerlist[pname] = nil;
mg_villages.all_villages[ village_id ].to_add_data.bpos[ plot_nr ].can_edit = {}
mg_villages.all_villages[ village_id ].to_add_data.bpos[ plot_nr ].owner = nil;
-- Return price to player
local inv = player:get_inventory();
inv:add_item( 'main', price_stack );
meta:set_string('infotext', 'Plot No. '..tostring( plot_nr ).. ' with '..tostring( mg_villages.BUILDINGS[btype].scm) );
mg_villages.save_data();
end
-- If Player wants to add/remove trusted players
if (fields['add_remove']) then
local previousTrustees = mg_villages.all_villages[ village_id ].to_add_data.bpos[ plot_nr ].can_edit
local output = "";
if previousTrustees == nil then
previousTrustees = {}
else
for _, player in ipairs(previousTrustees) do
output = output..player.."\n"
end
end
formspec = "size[8,3]"..
"field[20,20;0.1,0.1;pos2str;Pos;"..minetest.pos_to_string( pos ).."]"..
"textarea[0.3,0.2;8,2.5;ownerplayers;Trusted Players;"..output.."]"..
"button[3.25,2.5;1.5,0.5;savetrustees;Save]";
mg_villages.save_data()
end
-- Save trusted players
if (fields["savetrustees"] == "Save") then
if not mg_villages.all_villages[ village_id ].to_add_data.bpos[ plot_nr ].can_edit then
mg_villages.all_villages[ village_id ].to_add_data.bpos[ plot_nr ].can_edit = {}
end
local x = 1;
for _, player in ipairs(fields.ownerplayers:split("\n")) do
mg_villages.all_villages[ village_id ].to_add_data.bpos[ plot_nr ].can_edit[x] = player
x = x + 1
end
mg_villages.save_data();
end
-- If A different Player owns plot
else
formspec = original_formspec.."label[1,1;"..tostring( owner ).." owns this plot.]"..
"button_exit[3,2.5;1.5,0.5;abort;Exit]";
end
minetest.show_formspec( pname, "mg_villages:plotmarker", formspec );
end
mg_villages.form_input_handler = function( player, formname, fields)
-- mg_villages.print(mg_villages.DEBUG_LEVEL_NORMAL,minetest.serialize(fields));
if( not( mg_villages.ENABLE_PROTECTION )) then
return false;
end
if( (formname == "mg_villages:plotmarker") and fields.pos2str and not( fields.abort )) then
local pos = minetest.string_to_pos( fields.pos2str );
mg_villages.plotmarker_formspec( pos, formname, fields, player );
return true;
end
return false;
end
minetest.register_on_player_receive_fields( mg_villages.form_input_handler )

View File

@ -0,0 +1,964 @@
-- ethereal comes with some intresting trees
if( minetest.get_modpath( 'ethereal' )) then
mg_villages.ethereal_trees = {'acacia','willow','redwood','frost','mushroom','yellow','palm','banana'};
end
if( minetest.get_modpath( 'forest' )) then
mg_villages.forest_trees = {'beech','birch','cherry','fir','ginkgo','lavender','mirabelle','oak','plum','willow'};
end
-- we are dealing with the TinyTrees mod from Bas080
if( minetest.get_modpath( 'trees' )
and minetest.registered_nodes[ 'trees:wood_mangrove' ] ) then
mg_villages.tinytrees_trees = {'mangrove','palm','conifer'};
end
-- The trees modname is not unique; there are other mods which bear that name.
-- If all the other mods are present as well, it's a strong indication for realtest beeing the game.
if( minetest.get_modpath( 'trees' )
and minetest.get_modpath( 'anvil')
and minetest.get_modpath( 'joiner_table')
and minetest.get_modpath( 'scribing_table' )) then
mg_villages.realtest_trees = {'ash','aspen','birch','maple','chestnut','pine','spruce'};
--print('REALTEST trees will be used.'); else print( 'NO REALTEST trees');
-- realtest is very special as far as stairs are concerned
mg_villages.realtest_stairs = {'default:stone','default:stone_flat','default:stone_bricks',
'default:desert_stone_flat','default:desert_stone_bricks'};
for i,v in ipairs(metals.list) do
table.insert( mg_villages.realtest_stairs, 'metals:'..v..'_block' );
end
-- the list of minteral names is local; so we can't add "decorations:"..mineral[1].."_block"
end
-- only the function mg_villages.get_replacement_table(..) is called from outside this file
mg_villages.replace_materials = function( replacements, pr, original_materials, prefixes, materials, old_material )
local postfixes = {};
local use_realtest_stairs = false;
-- handle realtest stairs/slabs
if( mg_villages.realtest_trees
and #prefixes==3
and prefixes[1]=='stairs:stair_' and prefixes[2]=='stairs:slab_' and prefixes[3]=='default:' ) then
prefixes = {''};
materials = mg_villages.realtest_stairs;
postfixes = {''};
use_realtest_stairs = true;
elseif( mg_villages.realtest_trees
and #prefixes==1
and prefixes[1]=='stairs:stair_') then
return;
else
for i,v in ipairs( prefixes ) do
postfixes[i] = '';
end
end
local known_materials = {};
local wood_found = false;
-- for all alternate materials
for i,m in ipairs( materials ) do
-- check if that material exists for each supplied prefix
for j,p in ipairs( prefixes ) do
-- if wood is present, later on try moretrees wood as well
if( 'default:wood' == m ) then
wood_found = true;
end
if( minetest.registered_nodes[ p..m..postfixes[j] ] ) then
table.insert( known_materials, m..postfixes[j] );
end
end
end
-- support wooden planks from moretrees
if( wood_found and mg_villages.moretrees_treelist ) then
for _,v in ipairs( mg_villages.moretrees_treelist ) do
if( minetest.registered_nodes[ "moretrees:"..v[1].."_planks"] ) then
table.insert( known_materials, "moretrees:"..v[1].."_planks" );
end
end
end
--[[
-- deco is used by BigFreakingDig; as that one lacks default nodes, it doesn't work out here
if( wood_found and minetest.get_modpath('deco')) then
local bfd_treelist = {'birch', 'cherry', 'evergreen', 'oak' };
for _,v in ipairs( bfd_treelist ) do
if( minetest.registered_nodes[ "deco:"..v.."_plank"] ) then
table.insert( known_materials, "deco:"..v.."_plank" );
end
end
end
--]]
if( wood_found and mg_villages.ethereal_trees ) then
for _,v in ipairs( mg_villages.ethereal_trees ) do
-- mushroom in ethereal is a pretty decorative material; increase its probability
if( v == 'mushroom' ) then
table.insert( known_materials, "ethereal:mushroom_pore" );
table.insert( known_materials, "ethereal:mushroom_pore" );
table.insert( known_materials, "ethereal:mushroom_pore" );
-- also increase probability for the decorative blueish wood
table.insert( known_materials, "ethereal:frost_wood" );
table.insert( known_materials, "ethereal:frost_wood" );
elseif( minetest.registered_nodes[ "ethereal:"..v.."_wood"] ) then
table.insert( known_materials, "ethereal:"..v.."_wood" );
end
end
end
if( wood_found and mg_villages.forest_trees ) then
for _,v in ipairs( mg_villages.forest_trees ) do
if( minetest.registered_nodes[ 'forest:'..v..'_wood'] ) then
table.insert( known_materials, 'forest:'..v..'_wood' );
end
end
end
if( wood_found and mg_villages.tinytrees_trees ) then
for _,v in ipairs( mg_villages.tinytrees_trees ) do
if( minetest.registered_nodes[ 'trees:wood_'..v] ) then
table.insert( known_materials, 'trees:wood_'..v );
end
end
end
if( wood_found and mg_villages.realtest_trees ) then
for _,v in ipairs( mg_villages.realtest_trees ) do
if( minetest.registered_nodes[ 'trees:'..v..'_planks'] ) then
table.insert( known_materials, 'trees:'..v..'_planks' );
end
end
end
-- nothing found which could be used
if( #known_materials < 1 ) then
return;
end
local new_material = known_materials[ pr:next( 1, #known_materials )];
if( use_realtest_stairs == true ) then
table.insert( replacements, { original_materials[ 1 ], new_material..'_stair' } );
table.insert( replacements, { original_materials[ 2 ], new_material..'_slab' } );
table.insert( replacements, { original_materials[ 3 ], new_material } );
table.insert( replacements, { original_materials[ 1 ]..'upside_down', new_material..'_stair_upside_down' } );
table.insert( replacements, { original_materials[ 2 ]..'upside_down', new_material..'_slab_upside_down' } );
return new_material;
end
-- no replacement necessary if we did choose the same material as before
if( new_material == old_material or old_material == (prefixes[1]..new_material)) then
return old_material;
end
for i,v in ipairs( prefixes ) do
table.insert( replacements, { original_materials[ i ], v..new_material } );
end
return new_material;
end
-- replace the tree trunk as well so that it fits to the wood type
mg_villages.replace_tree_trunk = function( replacements, wood_type )
if( wood_type == 'default:junglewood' ) then
table.insert( replacements, {'default:tree', 'default:jungletree'});
elseif( wood_type == 'default:pine_wood' ) then
table.insert( replacements, {'default:tree', 'default:pine_tree'});
elseif( wood_type == 'default:acacia_wood' ) then
table.insert( replacements, {'default:tree', 'default:acacia_tree'});
elseif( wood_type == 'mg:savannawood' ) then
table.insert( replacements, {'default:tree', 'mg:savannatree'});
elseif( wood_type == 'mg:pinewood' ) then
table.insert( replacements, {'default:tree', 'mg:pinetree'});
elseif( mg_villages.moretrees_treelist ) then
for _,v in ipairs( mg_villages.moretrees_treelist ) do
if( wood_type == "moretrees:"..v[1].."_planks" ) then
table.insert( replacements, {'default:tree', "moretrees:"..v[1].."_trunk"});
table.insert( replacements, {'default:leaves', "moretrees:"..v[1].."_leaves"});
end
end
elseif( wood_type == 'deco:birch_plank' ) then
table.insert( replacements, {'default:tree', "mapgen:birch_log"});
elseif( wood_type == 'deco:cherry_plank' ) then
table.insert( replacements, {'default:tree', "mapgen:cherry_log"});
elseif( wood_type == 'deco:evergreen_plank' ) then
table.insert( replacements, {'default:tree', "mapgen:evergreen_log"});
elseif( wood_type == 'deco:oak_plank' ) then
table.insert( replacements, {'default:tree', "mapgen:oak_log"});
elseif( wood_type == 'ethereal:frost_wood' ) then
table.insert( replacements, {'default:tree', "ethereal:frost_tree"});
elseif( wood_type == "ethereal:mushroom_pore" ) then
table.insert( replacements, {'default:tree', "ethereal:mushroom_trunk"});
elseif( mg_villages.ethereal_trees ) then
for _,v in ipairs( mg_villages.ethereal_trees ) do
if( wood_type == "ethereal:"..v.."_wood" ) then
table.insert( replacements, {'default:tree', "ethereal:"..v.."_trunk"});
end
end
elseif( mg_villages.forest_trees ) then
for _,v in ipairs( mg_villages.forest_trees ) do
if( wood_type == "forest:"..v.."_wood" ) then
table.insert( replacements, {'default:tree', "forest:"..v.."_tree"});
end
end
elseif( mg_villages.tinytrees_trees ) then
for _,v in ipairs( mg_villages.tinytrees_trees ) do
if( wood_type == "trees:wood_"..v ) then
table.insert( replacements, {'default:tree', "trees:tree_"..v});
end
end
elseif( mg_villages.realtest_trees ) then
for _,v in ipairs( mg_villages.realtest_trees ) do
if( wood_type == 'trees:'..v..'_planks' ) then
table.insert( replacements, {'default:tree', "trees:"..v..'_log'});
-- realtest does not have most of the nodes from default, so we need to replace them as well
table.insert( replacements, {'default:wood', 'trees:'..v..'_planks'});
table.insert( replacements, {'default:leaves', 'trees:'..v..'_leaves'});
table.insert( replacements, {'default:ladder', 'trees:'..v..'_ladder'});
table.insert( replacements, {'default:chest', 'trees:'..v..'_chest'});
table.insert( replacements, {'default:chest_locked', 'trees:'..v..'_chest_locked'});
table.insert( replacements, {'default:fence_wood', 'fences:'..v..'_fence'});
table.insert( replacements, {'default:bookshelf', 'decorations:bookshelf_'..v});
table.insert( replacements, {'doors:door_wood_t_1', 'doors:door_'..v..'_t_1'});
table.insert( replacements, {'doors:door_wood_b_1', 'doors:door_'..v..'_b_1'});
table.insert( replacements, {'doors:door_wood_t_2', 'doors:door_'..v..'_t_2'});
table.insert( replacements, {'doors:door_wood_b_2', 'doors:door_'..v..'_b_2'});
-- not really wood-realted, but needs to be replaced as well
table.insert( replacements, {'default:furnace', 'oven:oven'});
-- farming is also handled diffrently
table.insert( replacements, {'farming:soil_wet', 'farming:soil'});
table.insert( replacements, {'farming:cotton_1', 'farming:flax_1'});
table.insert( replacements, {'farming:cotton_2', 'farming:flax_1'});
table.insert( replacements, {'farming:cotton_3', 'farming:flax_2'});
table.insert( replacements, {'farming:cotton_4', 'farming:flax_2'});
table.insert( replacements, {'farming:cotton_5', 'farming:flax_3'});
table.insert( replacements, {'farming:cotton_6', 'farming:flax_3'});
table.insert( replacements, {'farming:cotton_7', 'farming:flax_4'});
table.insert( replacements, {'farming:cotton_8', 'farming:flax_4'});
-- stairs and slabs made out of default wood
table.insert( replacements, {'stairs:stair_wood', 'trees:'..v..'_planks_stair'});
table.insert( replacements, {'stairs:slab_wood', 'trees:'..v..'_planks_slab'});
table.insert( replacements, {'stairs:stair_woodupside_down','trees:'..v..'_planks_stair_upside_down' } );
table.insert( replacements, {'stairs:slab_woodupside_down', 'trees:'..v..'_planks_slab_upside_down' } );
end
end
else
return nil;
end
return wood_type;
-- TODO if minetest.get_modpath("moreblocks") and moretrees.enable_stairsplus the
end
-- if buildings are made out of a certain wood type, people might expect trees of that type nearby
mg_villages.replace_saplings = function( replacements, wood_type )
if( wood_type == 'default:junglewood' ) then
table.insert( replacements, {'default:sapling', 'default:junglesapling'});
elseif( wood_type == 'default:pine_wood' ) then
table.insert( replacements, {'default:sapling', 'default:pine_sapling'});
elseif( wood_type == 'default:acacia_wood' ) then
table.insert( replacements, {'default:sapling', 'default:acacia_sapling'});
elseif( wood_type == 'mg:savannawood' ) then
table.insert( replacements, {'default:sapling', 'mg:savannasapling'});
elseif( wood_type == 'mg:pinewood' ) then
table.insert( replacements, {'default:sapling', 'mg:pinesapling'});
elseif( mg_villages.moretrees_treelist ) then
for _,v in ipairs( mg_villages.moretrees_treelist ) do
if( wood_type == "moretrees:"..v[1].."_planks" ) then
table.insert( replacements, {'default:sapling', "moretrees:"..v[1].."_sapling_ongen"});
end
end
elseif( mg_villages.ethereal_trees ) then
for _,v in ipairs( mg_villages.ethereal_trees ) do
if( wood_type == "ethereal:"..v.."_wood" ) then
table.insert( replacements, {'default:sapling', "ethereal:"..v.."_sapling"});
end
end
elseif( mg_villages.forest_trees ) then
for _,v in ipairs( mg_villages.forest_trees ) do
if( wood_type == "forest:"..v.."_wood" ) then
table.insert( replacements, {'default:sapling', "forest:"..v.."_sapling"});
end
end
elseif( mg_villages.tinytrees_trees ) then
for _,v in ipairs( mg_villages.tinytrees_trees ) do
if( wood_type == "trees:wood_"..v ) then
table.insert( replacements, {'default:sapling', "trees:sapling_"..v});
end
end
elseif( mg_villages.realtest_trees ) then
for _,v in ipairs( mg_villages.realtest_trees ) do
if( wood_type == 'trees:'..v..'_planks' ) then
table.insert( replacements, {'default:sapling', "trees:"..v.."_sapling"});
table.insert( replacements, {'default:junglesapling', "trees:"..v.."_sapling"});
table.insert( replacements, {'default:pine_sapling', "trees:"..v.."_sapling"});
end
end
elseif( wood_type == 'deco:birch_plank' ) then
table.insert( replacements, {'default:sapling', "mapgen:birch_sapling"});
elseif( wood_type == 'deco:cherry_plank' ) then
table.insert( replacements, {'default:sapling', "mapgen:cherry_sapling"});
elseif( wood_type == 'deco:evergreen_plank' ) then
table.insert( replacements, {'default:sapling', "mapgen:evergreen_sapling"});
elseif( wood_type == 'deco:oak_plank' ) then
table.insert( replacements, {'default:sapling', "mapgen:oak_sapling"});
end
end
-- Note: This function is taken from the villages mod (by Sokomine)
-- 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)
mg_villages.get_replacement_list = function( housetype, pr )
local replacements = {};
-- else some grass would never (re)grow (if it's below a roof)
-- table.insert( replacements, {'default:dirt', dirt_with_grass_replacement });
-- table.insert( replacements, {'default:dirt_with_grass', dirt_with_grass_replacement });
table.insert( replacements, {'default:dirt', 'default:dirt_with_grass' });
-- realtest lacks quite a lot from default
if( mg_villages.realtest_trees ) then
for i=1,8 do
table.insert( replacements, {'farming:wheat_'..i, 'farming:spelt_'..tostring( (i+(i%2))/2) });
table.insert( replacements, {'farming:cotton_'..i, 'farming:flax_' ..tostring( (i+(i%2))/2) });
end
for i=1,5 do
table.insert( replacements, {'default:grass_'..i, 'air' });
end
table.insert( replacements, {'default:apple', 'air' });
table.insert( replacements, {'default:cobble', 'default:stone_macadam' });
table.insert( replacements, {'default:obsidian_glass', 'default:glass' });
end
if( housetype and mg_villages.village_type_data[ housetype ] and mg_villages.village_type_data[ housetype ].replacement_function ) then
return mg_villages.village_type_data[ housetype ].replacement_function( housetype, pr, replacements );
end
return replacements;
end
-- Taokis houses from structure i/o
mg_villages.replacements_taoki = function( housetype, pr, replacements )
local wood_type = 'default:wood';
if( mg_villages.realtest_trees ) then
wood_type = mg_villages.replace_materials( replacements, pr,
{'default:wood'},
{''},
{'default:wood'},
'default:wood');
table.insert( replacements, {'stairs:stair_cobble', 'default:stone_bricks_stair' });
table.insert( replacements, {'stairs:slab_cobble', 'default:stone_bricks_slab' });
table.insert( replacements, {'stairs:stair_stone', 'default:stone_flat_stair' });
table.insert( replacements, {'stairs:slab_stone', 'default:stone_flat_slab' });
else
-- the main body of the houses in the .mts files is made out of wood
wood_type = mg_villages.replace_materials( replacements, pr,
{'default:wood'},
{''},
{'default:wood', 'default:junglewood', 'default:pine_wood', 'default:acacia_wood', 'mg:pinewood', 'mg:savannawood',
'default:clay', 'default:brick', 'default:sandstone',
'default:stonebrick', 'default:desert_stonebrick','default:sandstonebrick', 'default:sandstone','default:stone','default:desert_stone',
'default:coalblock','default:steelblock','default:goldblock', 'default:bronzeblock', 'default:copperblock', 'wool:white',
'default:stone_flat', 'default:desert_stone_flat', -- realtest
'darkage:adobe', 'darkage:basalt', 'darkage:basalt_cobble', 'darkage:chalk',
'darkage:gneiss', 'darkage:gneiss_cobble', 'darkage:marble', 'darkage:marble_tile',
'darkage:mud', 'darkage:ors', 'darkage:ors_cobble',
'darkage:schist', 'darkage:serpentine', 'darkage:shale', 'darkage:silt', 'darkage:slate',
'mapgen:mese_stone', 'mapgen:soap_stone'},
'default:wood');
end
-- tree trunks are seldom used in these houses; let's change them anyway
mg_villages.replace_tree_trunk( replacements, wood_type );
-- all this comes in variants for stairs and slabs as well
mg_villages.replace_materials( replacements, pr,
{'stairs:stair_stonebrick', 'stairs:slab_stonebrick', 'default:stonebrick'},
{'stairs:stair_', 'stairs:slab_', 'default:' },
{ 'stonebrick', 'stone', 'sandstone', 'cobble'},
'stonebrick');
-- decorative slabs above doors etc.
mg_villages.replace_materials( replacements, pr,
{'stairs:stair_wood'},
{'stairs:stair_'},
{'stonebrick', 'stone', 'sandstone', 'cobble', 'wood', 'junglewood', 'pine_wood', 'acaica_wood' },
'wood');
-- brick roofs are a bit odd; but then...
-- all three shapes of roof parts have to fit together
mg_villages.replace_materials( replacements, pr,
{'stairs:stair_brick', 'stairs:slab_brick', 'default:brick'},
{'stairs:stair_', 'stairs:slab_', 'default:' },
{ 'brick', 'stone', 'cobble', 'stonebrick', 'wood', 'junglewood', 'pine_wood', 'acacia_wood', 'sandstone' },
'brick' );
return replacements;
end
mg_villages.replacements_nore = function( housetype, pr, replacements )
mg_villages.replace_materials( replacements, pr,
-- {'default:stonebrick'},
-- {'default:'},
{'stairs:stair_stonebrick', 'stairs:slab_stonebrick', 'default:stonebrick'},
{'stairs:stair_', 'stairs:slab_', 'default:' },
{'stonebrick', 'desert_stonebrick','sandstonebrick', 'sandstone','stone','desert_stone','stone_flat','desert_stone_flat','stone_bricks','desert_strone_bricks'},
'stonebrick');
-- replace the wood as well
local wood_type = mg_villages.replace_materials( replacements, pr,
{'default:wood'},
{''},
{ 'default:wood', 'default:junglewood', 'default:pine_wood', 'default:acacia_wood', 'mg:savannawood', 'mg:pinewood' },
'default:wood');
mg_villages.replace_tree_trunk( replacements, wood_type );
mg_villages.replace_saplings( replacements, wood_type );
if( pr:next(1,3)==1 and not( mg_villages.realtest_trees)) then
table.insert( replacements, {'default:glass', 'default:obsidian_glass'});
end
if( mg_villages.realtest_trees ) then
table.insert( replacements, {'stairs:stair_cobble', 'default:stone_bricks_stair' });
table.insert( replacements, {'stairs:slab_cobble', 'default:stone_bricks_slab' });
end
return replacements;
end
mg_villages.replacements_lumberjack = function( housetype, pr, replacements )
-- replace the wood - those are lumberjacks after all
local wood_type = mg_villages.replace_materials( replacements, pr,
{'default:wood'},
{''},
{ 'default:wood', 'default:junglewood', 'default:pine_wood', 'default:acacia_wood', 'mg:savannawood', 'mg:pinewood' },
'default:wood');
mg_villages.replace_tree_trunk( replacements, wood_type );
mg_villages.replace_saplings( replacements, wood_type );
if( not( minetest.get_modpath('bell' ))) then
table.insert( replacements, {'bell:bell', 'default:goldblock' });
end
if( mg_villages.realtest_trees ) then
table.insert( replacements, {'stairs:stair_cobble', 'default:stone_bricks_stair' });
table.insert( replacements, {'stairs:slab_cobble', 'default:stone_bricks_slab' });
end
return replacements;
end
mg_villages.replacements_logcabin = function( housetype, pr, replacements )
-- for logcabins, wood is the most likely type of roof material
local roof_type = mg_villages.replace_materials( replacements, pr,
{'stairs:stair_cobble', 'stairs:slab_cobble' },
{'cottages:roof_connector_', 'cottages:roof_flat_' },
{'straw', 'wood', 'wood', 'wood', 'reet', 'slate', 'red', 'brown', 'black'},
'' );
-- some houses have junglewood roofs
if( roof_type ) then
table.insert( replacements, {'stairs:stair_junglewood', 'cottages:roof_connector_'..roof_type });
table.insert( replacements, {'stairs:slab_junglewood', 'cottages:roof_flat_'..roof_type });
table.insert( replacements, {'cottages:roof_connector_wood', 'cottages:roof_connector_'..roof_type });
table.insert( replacements, {'cottages:roof_flat_wood', 'cottages:roof_flat_'..roof_type });
-- realtest does not have normal stairs
elseif( mg_villages.realtest_trees ) then
table.insert( replacements, {'stairs:stair_junglewood', 'trees:aspen_planks_stair' });
table.insert( replacements, {'stairs:slab_junglewood', 'trees:aspen_planks_slab' });
end
if( mg_villages.realtest_trees ) then
local wood_type = mg_villages.replace_materials( replacements, pr,
{'default:wood'},
{''},
{ 'default:wood' },
'default:wood');
mg_villages.replace_tree_trunk( replacements, wood_type );
mg_villages.replace_saplings( replacements, wood_type );
table.insert( replacements, {'default:stonebrick', 'default:stone_bricks' }); -- used for chimneys
table.insert( replacements, {'stairs:stair_stonebrick', 'default:stone_bricks_stair' });
-- table.insert( replacements, {'default:junglewood', wood_type }); -- replace the floor
-- replace the floor with another type of wood (looks better than the same type as above)
mg_villages.replace_materials( replacements, pr,
{'default:junglewood'},
{''},
{ 'default:wood' },
'default:junglewood');
end
return replacements;
end
mg_villages.replacements_chateau = function( housetype, pr, replacements )
if( minetest.get_modpath( 'cottages' )) then
-- straw is the most likely building material for roofs for historical buildings
mg_villages.replace_materials( replacements, pr,
-- all three shapes of roof parts have to fit together
{ 'cottages:roof_straw', 'cottages:roof_connector_straw', 'cottages:roof_flat_straw' },
{ 'cottages:roof_', 'cottages:roof_connector_', 'cottages:roof_flat_'},
{'straw', 'straw', 'straw', 'straw', 'straw',
'reet', 'reet', 'reet',
'slate', 'slate',
'wood', 'wood',
'red',
'brown',
'black'},
'straw');
else
mg_villages.replace_materials( replacements, pr,
-- all three shapes of roof parts have to fit together
{ 'cottages:roof_straw', 'cottages:roof_connector_straw', 'cottages:roof_flat_straw' },
{ 'stairs:stair_', 'stairs:stair_', 'stairs:slab_'},
{'cobble', 'stonebrick', 'desert_cobble', 'desert_stonebrick', 'stone'},
'stonebrick');
table.insert( replacements, { 'cottages:glass_pane', 'default:glass' });
end
local wood_type = mg_villages.replace_materials( replacements, pr,
{'default:wood'},
{''},
{ 'default:wood', 'default:junglewood', 'default:pine_wood', 'default:acacia_wood', 'mg:savannawood', 'mg:pinewood'}, --, 'default:brick', 'default:sandstone', 'default:desert_cobble' },
'default:wood');
mg_villages.replace_tree_trunk( replacements, wood_type );
mg_villages.replace_saplings( replacements, wood_type );
if( mg_villages.realtest_trees ) then
-- replace the floor with another type of wood (looks better than the same type as above)
mg_villages.replace_materials( replacements, pr,
{'stairs:stair_junglewood', 'stairs:slab_junglewood', 'default:junglewood'},
{'stairs:stair_', 'stairs:slab_', 'default:' },
{ 'default:wood' },
'wood' );
end
local mfs2 = mg_villages.replace_materials( replacements, pr,
{'stairs:stair_cobble', 'stairs:slab_cobble', 'default:cobble'},
{'stairs:stair_', 'stairs:slab_', 'default:' },
{ 'cobble', 'brick', 'clay', 'desert_cobble', 'desert_stone', 'desert_stonebrick', 'loam', 'sandstone', 'sandstonebrick', 'stonebrick' },
'cobble');
return replacements;
end
mg_villages.replacements_tent = function( housetype, pr, replacements )
table.insert( replacements, { "glasspanes:wool_pane", "cottages:wool_tent" });
table.insert( replacements, { "default:gravel", "default:sand" });
-- realtest needs diffrent fence posts and doors
if( mg_villages.realtest_trees ) then
local wood_type = mg_villages.replace_materials( replacements, pr,
{'default:wood'},
{''},
{ 'default:wood' },
'default:wood');
mg_villages.replace_tree_trunk( replacements, wood_type );
mg_villages.replace_saplings( replacements, wood_type );
end
return replacements;
end
mg_villages.replacements_grasshut = function( housetype, pr, replacements )
table.insert( replacements, {'moreblocks:fence_jungle_wood', 'default:fence' });
if( pr:next( 1, 4) == 1 ) then
table.insert( replacements, {'dryplants:reed_roof', 'cottages:roof_straw'});
table.insert( replacements, {'dryplants:reed_slab', 'cottages:roof_flat_straw' });
table.insert( replacements, {'dryplants:wetreed_roof', 'cottages:roof_reet' });
table.insert( replacements, {'dryplants:wetreed_slab', 'cottages:roof_flat_reet' });
else -- replace the straw and cobble one of the huts uses
table.insert( replacements, {'cottages:straw', 'dryplants:wetreed' });
table.insert( replacements, {'stairs:slab_cobble', 'dryplants:reed_slab' });
end
if( pr:next( 1, 4) == 1 ) then
table.insert( replacements, {'dryplants:wetreed_roof_corner', 'default:wood' });
table.insert( replacements, {'dryplants:wetreed_roof_corner_2', 'default:junglewood' });
end
if( not( minetest.get_modpath( 'cavestuff' ))) then
table.insert( replacements, {'cavestuff:desert_pebble_2', 'default:slab_cobble' });
end
table.insert( replacements, {'default:desert_sand', 'default:dirt_with_grass' });
return replacements;
end
mg_villages.replacements_claytrader = function( housetype, pr, replacements )
-- the walls of the clay trader houses are made out of brick
mg_villages.replace_materials( replacements, pr,
{ 'stairs:stair_brick', 'stairs:slab_brick', 'default:brick' }, -- default_materials
{ 'stairs:stair_', 'stairs:slab_', 'default:' }, -- prefixes (for new materials)
{ 'brick', 'stone', 'sandstone', 'sandstonebrick', 'desert_stone', 'desert_cobble', 'desert_stonebrick' }, -- new materials
'brick' ); -- original material
-- material for the floor
mg_villages.replace_materials( replacements, pr,
{'default:stone'},
{'default:'},
{ 'brick', 'stone', 'sandstone', 'sandstonebrick', 'clay', 'desert_stone', 'desert_cobble', 'desert_stonebrick',
'default:stone_flat', 'default:desert_stone_flat', -- realtest
},
'stone');
-- the clay trader homes come with stone stair roofs; slabs are used in other places as well (but those replacements here are ok)
mg_villages.replace_materials( replacements, pr,
{'stairs:stair_stone', 'stairs:slab_stone' },
{'cottages:roof_connector_', 'cottages:roof_flat_' },
{'straw', 'straw', 'straw', 'straw', 'straw',
'reet', 'reet', 'reet',
'slate', 'slate',
'wood', 'wood',
'red',
'brown',
'black'},
'');
-- hills and pits that contain the materials clay traders dig for
mg_villages.replace_materials( replacements, pr,
{'default:stone_with_coal'},
{'default:'},
{'sand', 'sandstone', 'clay'},
'');
if( mg_villages.realtest_trees ) then
local wood_type = mg_villages.replace_materials( replacements, pr,
{'default:wood'},
{''},
{ 'default:wood' },
'default:wood');
mg_villages.replace_tree_trunk( replacements, wood_type );
mg_villages.replace_saplings( replacements, wood_type );
table.insert( replacements, {'default:clay', 'default:dirt_with_clay'});
local mfs2 = mg_villages.replace_materials( replacements, pr,
{'stairs:stair_cobble', 'stairs:slab_cobble', 'default:cobble'},
{'stairs:stair_', 'stairs:slab_', 'default:' },
{ 'stone' }, -- will be replaced by mg_villages.realtest_stairs
'sandstone');
end
return replacements;
end
mg_villages.replacements_charachoal = function( housetype, pr, replacements )
if( mg_villages.realtest_trees ) then
local wood_type = mg_villages.replace_materials( replacements, pr,
{'default:wood'},
{''},
{ 'default:wood' },
'default:wood');
mg_villages.replace_tree_trunk( replacements, wood_type );
mg_villages.replace_saplings( replacements, wood_type );
table.insert( replacements, {'stairs:slab_loam', 'cottages:loam'});
table.insert( replacements, {'stairs:stair_loam', 'cottages:loam'});
end
return replacements;
end
-- wells can get the same replacements as the sourrounding village; they'll get a fitting roof that way
mg_villages.replacements_medieval = function( housetype, pr, replacements )
if( not( minetest.get_modpath('bell' ))) then
table.insert( replacements, {'bell:bell', 'default:goldblock' });
end
-- glass that served as a marker got copied accidently; there's usually no glass in cottages
table.insert( replacements, {'default:glass', 'air'});
-- some plants started growing while the buildings where saved - eliminate them
table.insert( replacements, {'junglegrass:medium', 'air'});
table.insert( replacements, {'junglegrass:short', 'air'});
table.insert( replacements, {'poisonivy:seedling', 'air'});
-- 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( pr:next( 1, 2 ) == 2 ) then
-- table.insert( replacements, {'cottages:glass_pane', 'default:fence_wood'});
local gp = mg_villages.replace_materials( replacements, pr,
{'cottages:glass_pane'},
{''},
{'xpanes:pane', 'default:glass', 'default:obsidian_glass', 'default:fence_wood',
'darkage:medieval_glass', 'darkage:iron_bars', 'darkage:iron_grille', 'darkage:wood_bars',
'darkage:wood_frame', 'darkage:wood_grille'},
'cottages:glass_pane');
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:pine_wood', 'default:acacia_wood', 'default:sandstone',
'default:desert_stone','default:brick','default:cobble','default:stonebrick',
'default:desert_stonebrick','default:sandstonebrick','default:stone',
'mg:savannawood', 'mg:savannawood', 'mg:savannawood', 'mg:savannawood',
'mg:pinewood', 'mg:pinewood', 'mg:pinewood', 'mg:pinewood',
'default:stone_flat', 'default:desert_stone_flat', -- realtest
'darkage:adobe', 'darkage:basalt', 'darkage:basalt_cobble', 'darkage:chalk',
'darkage:gneiss', 'darkage:gneiss_cobble', 'darkage:marble', 'darkage:marble_tile',
'darkage:mud', 'darkage:ors', 'darkage:ors_cobble', 'darkage:reinforced_chalk',
'darkage:reinforced_wood', 'darkage:reinforced_wood_left', 'darkage:reinforced_wood_right',
'darkage:schist', 'darkage:serpentine', 'darkage:shale', 'darkage:silt', 'darkage:slate',
'darkage:slate_cobble', 'darkage:slate_tile', 'darkage:stone_brick',
'mapgen:mese_stone', 'mapgen:soap_stone'};
-- what is sandstone (the floor) may be turned into something else
local mfs = mg_villages.replace_materials( replacements, pr,
{'default:sandstone'},
{''},
materials,
'default:sandstone');
if( mg_villages.realtest_trees ) then
table.insert( replacements, {'stairs:slab_sandstone', 'default:stone_slab'});
local mfs2 = mg_villages.replace_materials( replacements, pr,
{'stairs:stair_sandstone', 'stairs:slab_sandstone', 'default:sandstone'},
{'stairs:stair_', 'stairs:slab_', 'default:' },
{ 'stone' }, -- will be replaced by mg_villages.realtest_stairs
'sandstone');
elseif( mfs and mfs ~= 'default:sandstone' ) then
if( mfs == 'cottages:loam' or mfs == 'default:clay' or mfs == 'mg:savannawood' or mfs == 'mg:pinewood') then
mfs = 'default:wood';
elseif( mfs =='default:sandstonebrick' or mfs == 'default:desert_stone' or mfs == 'default:desert_stonebrick'
or not( minetest.registered_nodes[ 'stairs:slab_'..string.sub( mfs, 9 )] )) then
mfs = '';
end
if( mfs and mfs ~= '' ) then
-- realtest needs special treatment
table.insert( replacements, {'stairs:slab_sandstone', 'stairs:slab_'..string.sub( mfs, 9 )});
end
end
-- except for the floor, everything else may be glass
table.insert( materials, 'default:glass' );
local uses_wood = false;
-- bottom part of the house (usually ground floor from outside)
local replace_clay = mg_villages.replace_materials( replacements, pr,
{'default:clay'},
{''},
materials,
'default:clay');
if( replace_clay and replace_clay ~= 'default:clay' ) then
uses_wood = mg_villages.replace_tree_trunk( replacements, replace_clay );
mg_villages.replace_saplings( replacements, replace_clay );
end
-- upper part of the house (may be the same as the material for the lower part)
local replace_loam = mg_villages.replace_materials( replacements, pr,
{'cottages:loam'},
{''},
materials,
'cottages:loam');
-- if the bottom was not replaced by wood, perhaps the top is
if( not( uses_wood ) and replace_loam ) then
mg_villages.replace_tree_trunk( replacements, replace_loam );
mg_villages.replace_saplings( replacements, replace_loam );
elseif( mg_villages.realtest_trees ) then
local wood_type = mg_villages.replace_materials( replacements, pr,
{'default:wood'},
{''},
{ 'default:wood' },
'default:wood');
mg_villages.replace_tree_trunk( replacements, wood_type );
mg_villages.replace_saplings( replacements, wood_type );
end
-- replace cobble; for these nodes, a stony material is needed (used in wells as well)
-- mossycobble is fine here as well
local mcs = mg_villages.replace_materials( replacements, pr,
{'default:cobble'},
{'default:'},
{'sandstone', 'desert_stone', 'desert_cobble',
'cobble', 'cobble',
'stonebrick', 'stonebrick', 'stonebrick', -- more common than other materials
'mossycobble', 'mossycobble','mossycobble',
'stone', 'stone',
'desert_stonebrick','sandstonebrick'},
'cobble');
-- set a fitting material for the slabs; mossycobble uses the default cobble slabs
if( mg_villages.realtest_trees ) then
local mcs2 = mg_villages.replace_materials( replacements, pr,
{'stairs:stair_cobble', 'stairs:slab_cobble', 'default:cobble'},
{'stairs:stair_', 'stairs:slab_', 'default:' },
{ 'stone' }, -- will be replaced by mg_villages.realtest_stairs
'cobble');
table.insert( replacements, {'moreblocks:slab_cobble', 'default:'..mcs..'_slab'});
elseif( mcs ~= 'mossycobble' and mcs ~= 'cobble') then
-- if no slab exists, use sandstone slabs
if( not( mcs ) or not( minetest.registered_nodes[ 'stairs:slab_'..mcs ])) then
mcs = 'sandstone';
end
table.insert( replacements, {'stairs:slab_cobble', 'stairs:slab_'..mcs});
table.insert( replacements, {'moreblocks:slab_cobble', 'stairs:slab_'..mcs});
else
table.insert( replacements, {'moreblocks:slab_cobble', 'stairs:slab_'..mcs});
end
-- straw is the most likely building material for roofs for historical buildings
mg_villages.replace_materials( replacements, pr,
-- all three shapes of roof parts have to fit together
{ 'cottages:roof_straw', 'cottages:roof_connector_straw', 'cottages:roof_flat_straw' },
{ 'cottages:roof_', 'cottages:roof_connector_', 'cottages:roof_flat_'},
{'straw', 'straw', 'straw', 'straw', 'straw',
'reet', 'reet', 'reet',
'slate', 'slate',
'wood', 'wood',
'red',
'brown',
'black'},
'straw');
--print('REPLACEMENTS used: '..minetest.serialize( replacements ));
return replacements;
end
mg_villages.replacements_tower = function( housetype, pr, replacements )
-- replace the wood - this is needed in particular for the fences
local wood_type = mg_villages.replace_materials( replacements, pr,
{'default:wood'},
{''},
{ 'default:wood', 'default:junglewood', 'mg:savannawood', 'mg:pinewood' },
'default:wood');
mg_villages.replace_tree_trunk( replacements, wood_type );
mg_villages.replace_saplings( replacements, wood_type );
mg_villages.replace_materials( replacements, pr,
{'stairs:stair_cobble', 'stairs:slab_cobble', 'default:cobble'},
{'stairs:stair_', 'stairs:slab_', 'default:' },
{'stonebrick', 'desert_stonebrick','sandstonebrick', 'sandstone','stone','desert_stone','stone_flat','desert_stone_flat','stone_bricks','desert_strone_bricks'},
'stonebrick');
return replacements;
end
-- Translate replacement function from above (which aims at place_schematic) for the villages in Nores mapgen
mg_villages.get_replacement_ids = function( housetype, pr )
local replace = {};
local replacements = mg_villages.get_replacement_list( housetype, pr );
for i,v in ipairs( replacements ) do
if( v and #v == 2 ) then
replace[ minetest.get_content_id( v[1] )] = minetest.get_content_id( v[2] );
end
end
return replace;
end
-- mapgen based replacements work best using a table, while minetest.place_schematic(..) based spawning needs a list
mg_villages.get_replacement_table = function( housetype, pr, replacements )
local rtable = {};
local ids = {};
if( not( replacements )) then
replacements = mg_villages.get_replacement_list( housetype, pr );
end
-- it is very problematic if the torches on houses melt snow and cause flooding; thus, we use a torch that is not hot
table.insert( replacements, {'default:torch', 'mg_villages:torch'});
-- make charachoal villages safe from spreading fire
if( not( mg_villages.use_normal_unsafe_lava )) then
table.insert( replacements, {'default:lava_source', 'mg_villages:lava_source_tamed'});
table.insert( replacements, {'default:lava_flowing', 'mg_villages:lava_flowing_tamed'});
end
for i,v in ipairs( replacements ) do
if( v and #v == 2 ) then
rtable[ v[1] ] = v[2];
ids[ minetest.get_content_id( v[1] )] = minetest.get_content_id( v[2] );
end
end
return { table = rtable, list = replacements, ids = ids };
end
mg_villages.get_content_id_replaced = function( node_name, replacements )
if( not( node_name ) or not( replacements ) or not(replacements.table )) then
return minetest.get_content_id( 'ignore' );
end
if( replacements.table[ node_name ]) then
return minetest.get_content_id( replacements.table[ node_name ] );
else
return minetest.get_content_id( node_name );
end
end
-- 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
replacements.table[ old_name ] = new_name;
end
end
end

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More