moved handling of schematics into a seperate mod
This commit is contained in:
parent
a9e72bf3c1
commit
d206b90ebc
@ -1,273 +0,0 @@
|
||||
|
||||
--[[ 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 = io.open(path..'.mts', "rb")
|
||||
if (file == nil) then
|
||||
return nil
|
||||
end
|
||||
--print('[mg_villages] Analyzing .mts file '..tostring( path..'.mts' ));
|
||||
--if( not( string.byte )) then
|
||||
-- print( '[mg_villages] 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}; -- TODO: handle possible meta values contained in another file
|
||||
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 = io.open(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 );
|
||||
-- convert to .mts for later usage
|
||||
if( res and store_as_mts ) then
|
||||
handle_schematics.store_mts_file( store_as_mts, res );
|
||||
end
|
||||
end
|
||||
return res;
|
||||
end
|
@ -1,83 +0,0 @@
|
||||
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 = io.open( scm..".we", "r")
|
||||
if not f then
|
||||
f, err = io.open( 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
|
||||
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
|
||||
if ent.meta == nil then
|
||||
ent.meta = {fields={}, inventory={}}
|
||||
end
|
||||
|
||||
scm[ent.y][ent.x][ent.z] = { nodenames_id[ ent.name ], ent.param2 }; --TODO ent.meta
|
||||
|
||||
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 };
|
||||
end
|
932
build_chest.lua
932
build_chest.lua
@ -1,932 +0,0 @@
|
||||
-----------------------------------------------------------------------------------------------------------------
|
||||
-- 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");
|
||||
|
||||
|
||||
|
||||
-- helper function; sorts by the second element of the table
|
||||
local function build_chest_comp(a,b)
|
||||
if (a[2] > b[2]) then
|
||||
return true;
|
||||
end
|
||||
end
|
||||
|
||||
-- create a statistic about how frequent each node name occoured
|
||||
build_chest.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, build_chest_comp );
|
||||
return statistic;
|
||||
end
|
||||
|
||||
|
||||
|
||||
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;
|
||||
-- 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 = build_chest.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
|
||||
|
||||
|
||||
|
||||
|
||||
-- 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
|
||||
|
||||
|
||||
|
||||
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.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 ) ));
|
||||
|
||||
|
||||
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:]";
|
||||
|
||||
|
||||
if( building_name and building_name ~= '' and build_chest.building[ building_name ] and build_chest.building[ building_name ].size) then
|
||||
local size = build_chest.building[ building_name ].size;
|
||||
formspec = formspec..
|
||||
-- show which building has been selected
|
||||
"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
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
|
||||
-- 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
|
||||
-- TODO: also show size and such
|
||||
-- do replacements for realtest where necessary (this needs to be done only once)
|
||||
local replacements = {};
|
||||
replacements_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
|
||||
|
||||
-- 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.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
|
||||
-- <worldname>/backup_PLAYERNAME_x_y_z_burried_rotation.mts
|
||||
filename = minetest.get_worldpath()..'/backup_'..
|
||||
meta:get_string('owner')..'_'..
|
||||
tostring( start_pos.x )..':'..tostring( start_pos.y )..':'..tostring( start_pos.z )..'_'..
|
||||
'0_0.mts';
|
||||
|
||||
-- TODO: handle metadata
|
||||
-- store a backup of the original landscape
|
||||
minetest.create_schematic( start_pos, end_pos, nil, filename, nil);
|
||||
meta:set_string('backup', filename );
|
||||
|
||||
minetest.chat_send_player( pname, 'CREATING backup schematic for this place in '..tostring( filename )..'.');
|
||||
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 );
|
||||
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
|
||||
minetest.place_schematic( start_pos, backup_file, "0", {}, true );
|
||||
meta:set_string('backup', nil );
|
||||
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: what if there is no schems folder in that directory?
|
||||
-- TODO: forbid overwriting existing files?
|
||||
local worldpath = minetest.get_worldpath();
|
||||
local filename_complete = worldpath..'/schems/'..filename..'.mts';
|
||||
-- really save it with probability_list and slice_prob_list both as nil
|
||||
minetest.create_schematic( p1, p2, nil, filename_complete, nil);
|
||||
|
||||
-- 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
|
||||
|
||||
meta:set_string( 'formspec', build_chest.update_formspec( pos, 'main', player, fields ));
|
||||
end
|
||||
|
||||
|
||||
|
||||
minetest.register_node("mg_villages: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
|
||||
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,
|
||||
|
||||
})
|
||||
|
||||
|
@ -1,55 +0,0 @@
|
||||
|
||||
|
||||
build_chest_add_to_menu = function( path, add_path )
|
||||
local file,error = 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 );
|
||||
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_to_menu( minetest.get_modpath( minetest.get_current_modname()).."/list_of_schematics.txt", "");
|
@ -1,219 +0,0 @@
|
||||
-------------------------------------------------------------
|
||||
--- 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]";
|
||||
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
|
||||
|
||||
|
||||
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
|
||||
|
@ -1,215 +0,0 @@
|
||||
|
||||
|
||||
-- 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 mg_villages.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..mg_villages.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..mg_villages.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
|
@ -1,3 +1,4 @@
|
||||
handle_schematics
|
||||
default
|
||||
doors
|
||||
farming
|
||||
|
18
init.lua
18
init.lua
@ -28,13 +28,6 @@ mg_villages.mg_generated_map = save_restore.restore_data( 'mg_generated_map.data
|
||||
|
||||
dofile(mg_villages.modpath.."/config.lua")
|
||||
|
||||
-- read size from schematics files directly
|
||||
-- analyze_mts_file.lua uses handle_schematics.* namespace
|
||||
dofile(mg_villages.modpath.."/worldedit_file.lua") -- deserialize worldedit savefiles
|
||||
dofile(mg_villages.modpath.."/analyze_mts_file.lua")
|
||||
dofile(mg_villages.modpath.."/analyze_we_file.lua")
|
||||
dofile(mg_villages.modpath.."/rotate.lua")
|
||||
|
||||
-- adds a special gravel node which will neither fall nor be griefed by mapgen
|
||||
dofile(mg_villages.modpath.."/nodes.lua")
|
||||
|
||||
@ -44,12 +37,6 @@ dofile(mg_villages.modpath.."/trees.lua")
|
||||
-- the replacement groups do add some non-ground nodes
|
||||
mg_villages.node_is_ground = {};
|
||||
|
||||
-- replace some materials for entire villages randomly
|
||||
replacements_group = {};
|
||||
dofile(mg_villages.modpath.."/replacements_wood.lua")
|
||||
dofile(mg_villages.modpath.."/replacements_realtest.lua")
|
||||
dofile(mg_villages.modpath.."/replacements_farming.lua")
|
||||
dofile(mg_villages.modpath.."/replacements_roof.lua")
|
||||
dofile(mg_villages.modpath.."/replacements.lua")
|
||||
|
||||
-- multiple diffrent village types with their own sets of houses are supported
|
||||
@ -57,9 +44,6 @@ dofile(mg_villages.modpath.."/replacements.lua")
|
||||
-- allows other mods to add new village types.
|
||||
dofile(mg_villages.modpath.."/village_types.lua")
|
||||
|
||||
-- a chest for spawning buildings manually
|
||||
dofile(mg_villages.modpath.."/build_chest.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")
|
||||
@ -71,8 +55,6 @@ dofile(mg_villages.modpath.."/init_weights.lua")
|
||||
-- generate village names
|
||||
dofile(mg_villages.modpath.."/name_gen.lua");
|
||||
|
||||
dofile(mg_villages.modpath.."/village_traders.lua")
|
||||
dofile(mg_villages.modpath.."/place_buildings.lua")
|
||||
dofile(mg_villages.modpath.."/villages.lua")
|
||||
|
||||
-- adds a command that allows to teleport to a known village
|
||||
|
@ -1,808 +0,0 @@
|
||||
-- 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( 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
|
||||
|
||||
-- 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_chest_private
|
||||
or new_content == cid.c_chest_work
|
||||
or new_content == cid.c_chest_storage ) then
|
||||
-- we're dealing with a chest that might need filling
|
||||
new_nodes[ i ].is_chestlike = 1;
|
||||
-- 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;
|
||||
|
||||
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)
|
||||
|
||||
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 ) then
|
||||
mg_villages.get_fruit_replacements( replacements, pos.fruit);
|
||||
end
|
||||
|
||||
local traders = {};
|
||||
if( handle_schematics.choose_traders ) then
|
||||
local village_type = "";
|
||||
if( village_id and mg_villages.all_villages and mg_villages.all_villages[ village_id ] ) then
|
||||
village_type = mg_villages.all_villages[ village_id ].village_type;
|
||||
end
|
||||
local building_type = "";
|
||||
if( binfo.typ ) then
|
||||
building_type = binfo.typ;
|
||||
end
|
||||
if( not( pos.traders )) then
|
||||
traders = handle_schematics.choose_traders( village_type, building_type, replacements )
|
||||
end
|
||||
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(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 ) 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});
|
||||
|
||||
-- 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
|
||||
|
||||
-- determine suitable positions for the traders
|
||||
if( handle_schematics.choose_trader_pos and #traders>0) then
|
||||
extra_calls.traders = handle_schematics.choose_trader_pos(pos, minp, maxp, data, param2_data, a, extranodes, replacements, cid, extra_calls, building_nr_in_bpos, village_id, binfo_extra, road_node, traders);
|
||||
print('TRADERS CHOOSEN FOR '..tostring( binfo.scm )..': '..minetest.serialize( extra_calls.traders ));
|
||||
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
|
||||
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_private = handle_schematics.get_content_id_replaced( 'cottages:chest_private', replacements );
|
||||
cid.c_chest_work = handle_schematics.get_content_id_replaced( 'cottages:chest_work', replacements );
|
||||
cid.c_chest_storage = handle_schematics.get_content_id_replaced( 'cottages:chest_storage', 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
|
||||
generate_building(pos, minp, maxp, data, param2_data, a, extranodes, replacements, cid, extra_calls, i, village_id, nil, mg_villages.road_node )
|
||||
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 = mg_villages.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_private = handle_schematics.get_content_id_replaced( 'cottages:chest_private', replacements );
|
||||
cid.c_chest_work = handle_schematics.get_content_id_replaced( 'cottages:chest_work', replacements );
|
||||
cid.c_chest_storage = handle_schematics.get_content_id_replaced( 'cottages:chest_storage', 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 = {} };
|
||||
|
||||
generate_building(pos, minp, maxp, data, param2_data, a, extranodes, replacements, cid, extra_calls, pos.building_nr, pos.village_id, binfo, cid.c_gravel);
|
||||
|
||||
-- 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
|
||||
-- TODO: handle metadata (if any is provided)
|
||||
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 ) then
|
||||
param2 = 1;
|
||||
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;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if( minetest.get_modpath('moresnow' )) then
|
||||
handle_schematics.moresnow_installed = true;
|
||||
end
|
@ -1,167 +0,0 @@
|
||||
|
||||
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)
|
||||
if( mg_villages and mg_villages.node_is_ground ) then
|
||||
for _,v in ipairs( data ) do
|
||||
mg_villages.node_is_ground[ v ] = false;
|
||||
end
|
||||
end
|
||||
|
||||
if( is_loaded 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" },
|
||||
""
|
||||
);
|
||||
|
||||
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:', '_', '' );
|
||||
|
||||
-- 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();
|
@ -1,82 +0,0 @@
|
||||
replacements_realtest = {}
|
||||
|
||||
-- parameter: replacements, name_in_default, name_in_realtest, to_realtest=true/false
|
||||
replacements_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_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_realtest.stairs( repl, 'stone', 'stone', to_realtest );
|
||||
replacements_realtest.stairs( repl, 'cobble', 'stone_flat', to_realtest );
|
||||
replacements_realtest.stairs( repl, 'stonebrick', 'stone_bricks', to_realtest );
|
||||
replacements_realtest.stairs( repl, 'desert_stone', 'desert_stone', to_realtest );
|
||||
replacements_realtest.stairs( repl, 'desert_cobble', 'desert_stone_flat', to_realtest );
|
||||
replacements_realtest.stairs( repl, 'desert_stonebrick', 'desert_stone_bricks',to_realtest );
|
||||
replacements_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
|
||||
|
@ -1,110 +0,0 @@
|
||||
|
||||
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();
|
@ -1,233 +0,0 @@
|
||||
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
|
||||
if( mg_villages and mg_villages.node_is_ground ) then
|
||||
for _,v in ipairs( data ) do
|
||||
mg_villages.node_is_ground[ v ] = false;
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if( is_loaded 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" },
|
||||
""
|
||||
);
|
||||
|
||||
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();
|
||||
|
114
rotate.lua
114
rotate.lua
@ -1,114 +0,0 @@
|
||||
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
|
@ -1,146 +0,0 @@
|
||||
|
||||
|
||||
handle_schematics.choose_traders = function( village_type, building_type, replacements )
|
||||
|
||||
if( not( building_type ) or not( village_type )) then
|
||||
return;
|
||||
end
|
||||
|
||||
-- some jobs are obvious
|
||||
if( building_type == 'mill' ) then
|
||||
return { 'miller' };
|
||||
elseif( building_type == 'bakery' ) then
|
||||
return { 'baker' };
|
||||
elseif( building_type == 'school' ) then
|
||||
return { 'teacher' };
|
||||
elseif( building_type == 'forge' ) then
|
||||
local traders = {'blacksmith', 'bronzesmith' };
|
||||
return { traders[ math.random(#traders)] };
|
||||
elseif( building_type == 'shop' ) then
|
||||
local traders = {'seeds','flowers','misc','default','ore', 'fruit trader', 'wood'};
|
||||
return { traders[ math.random(#traders)] };
|
||||
-- there are no traders for these jobs - they'd require specialized mobs
|
||||
elseif( building_type == 'tower'
|
||||
or building_type == 'church'
|
||||
or building_type == 'secular'
|
||||
or building_type == 'tavern' ) then
|
||||
return {};
|
||||
end
|
||||
|
||||
if( village_type == 'charachoal' ) then
|
||||
return { 'charachoal' };
|
||||
elseif( village_type == 'claytrader' ) then
|
||||
return { 'clay' };
|
||||
end
|
||||
|
||||
local res = {};
|
||||
if( building_type == 'shed'
|
||||
or building_type == 'farm_tiny'
|
||||
or building_type == 'house'
|
||||
or building_type == 'house_large'
|
||||
or building_type=='hut') then
|
||||
local traders = { 'stonemason', 'stoneminer', 'carpenter', 'toolmaker',
|
||||
'doormaker', 'furnituremaker', 'stairmaker', 'cooper', 'wheelwright',
|
||||
'saddler', 'roofer', 'iceman', 'potterer', 'bricklayer', 'dyemaker',
|
||||
'dyemakerl', 'glassmaker' }
|
||||
-- sheds and farms both contain craftmen
|
||||
res = { traders[ math.random( #traders )] };
|
||||
if( building_type == 'shed'
|
||||
or building_type == 'house'
|
||||
or building_type == 'house_large'
|
||||
or building_type == 'hut' ) then
|
||||
return res;
|
||||
end
|
||||
end
|
||||
|
||||
if( building_type == 'field'
|
||||
or building_type == 'farm_full'
|
||||
or building_type == 'farm_tiny' ) then
|
||||
|
||||
local fruit = 'farming:cotton';
|
||||
if( 'farm_full' ) then
|
||||
-- RealTest
|
||||
fruit = 'farming:wheat';
|
||||
if( replacements_group['farming'].traders[ 'farming:soy']) then
|
||||
fruit_item = 'farming:soy';
|
||||
end
|
||||
if( minetest.get_modpath("mobf") ) then
|
||||
local animal_trader = {'animal_cow', 'animal_sheep', 'animal_chicken', 'animal_exotic'};
|
||||
res[1] = animal_trader[ math.random( #animal_trader )];
|
||||
end
|
||||
return { res[1], replacements_group['farming'].traders[ fruit_item ]};
|
||||
elseif( #replacements_group['farming'].found > 0 ) then
|
||||
-- get a random fruit to grow
|
||||
fruit = replacements_group['farming'].found[ math.random( #replacements_group['farming'].found) ];
|
||||
return { res[1], replacements_group['farming'].traders[ fruit_item ]};
|
||||
else
|
||||
return res;
|
||||
end
|
||||
end
|
||||
|
||||
if( building_type == 'pasture' and minetest.get_modpath("mobf")) then
|
||||
local animal_trader = {'animal_cow', 'animal_sheep', 'animal_chicken', 'animal_exotic'};
|
||||
return { animal_trader[ math.random( #animal_trader )] };
|
||||
end
|
||||
|
||||
|
||||
-- TODO: banana,cocoa,rubber from farming_plus?
|
||||
-- TODO: sawmill
|
||||
if( building_type == 'lumberjack' or village_type == 'lumberjack' ) then
|
||||
-- TODO: limit this to single houses
|
||||
if( replacements.table and replacements.table[ 'default:wood' ] ) then
|
||||
return { replacements_group['wood'].traders[ replacements.table[ 'default:wood' ]] };
|
||||
elseif( #replacements_group['wood'].traders > 0 ) then
|
||||
return { replacements_group['wood'].traders[ math.random( #replacements_group['wood'].traders) ]};
|
||||
else
|
||||
return { 'common_wood'};
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- tent, chateau: places for living at; no special jobs associated
|
||||
-- nore,taoki,medieval,lumberjack,logcabin,canadian,grasshut,tent: further village types
|
||||
|
||||
return res;
|
||||
end
|
||||
|
||||
|
||||
handle_schematics.choose_trader_pos = function(pos, minp, maxp, data, param2_data, a, extranodes, replacements, cid, extra_calls, building_nr_in_bpos, village_id, binfo_extra, road_node, traders)
|
||||
|
||||
local trader_pos = {};
|
||||
-- determine spawn positions for the mobs
|
||||
for i,tr in ipairs( traders ) do
|
||||
local tries = 0;
|
||||
local found = false;
|
||||
local pt = {x=pos.x, y=pos.y, z=pos.z};
|
||||
while( tries < 10 and not(found)) do
|
||||
-- get a random position for the trader
|
||||
pt.x = pos.x+math.random(pos.bsizex);
|
||||
pt.z = pos.z+math.random(pos.bsizez);
|
||||
-- check if it is inside the area contained in data
|
||||
if (pt.x >= minp.x and pt.x <= maxp.x) and (pt.y >= minp.y and pt.y <= maxp.y) and (pt.z >= minp.z and pt.z <= maxp.z) then
|
||||
|
||||
while( pt.y < maxp.y
|
||||
and (data[ a:index( pt.x, pt.y, pt.z)]~=cid.c_air
|
||||
or data[ a:index( pt.x, pt.y+1, pt.z)]~=cid.c_air )) do
|
||||
pt.y = pt.y + 1;
|
||||
end
|
||||
|
||||
-- TODO: check if this position is really suitable? traders standing on the roof are a bit odd
|
||||
found = true;
|
||||
end
|
||||
tries = tries+1;
|
||||
|
||||
-- check if this position has already been assigned to another trader
|
||||
for j,t in ipairs( trader_pos ) do
|
||||
if( t.x==pt.x and t.y==pt.y and t.z==pt.z ) then
|
||||
found = false;
|
||||
end
|
||||
end
|
||||
end
|
||||
if( found ) then
|
||||
table.insert( trader_pos, {x=pt.x, y=pt.y, z=pt.z, typ=tr, bpos_i = building_nr_in_bpos} );
|
||||
end
|
||||
end
|
||||
return trader_pos;
|
||||
end
|
@ -1,138 +0,0 @@
|
||||
------------------------------------------------------------------------------------------
|
||||
-- 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
|
Loading…
x
Reference in New Issue
Block a user