Switch to mgv7, add mg_villages, add magic flower, finish potion textures, remove moretrees

master
Brandon 2015-07-18 15:15:49 -05:00
parent 825115ea31
commit 76f76cc2e6
425 changed files with 10926 additions and 5401 deletions

View File

@ -1,17 +1,41 @@
default_privs = interact, shout, fast, money
#
# Physics stuff
#
movement_acceleration_default = 1.4
movement_acceleration_default = 1.2
movement_acceleration_air = 2.1
movement_acceleration_fast = 2.4
movement_speed_walk = 4.2
movement_speed_crouch = 2.1
movement_speed_fast = 6.2
movement_speed_fast = 6.5
movement_speed_climb = 2.2
movement_speed_jump = 6.7
movement_speed_jump = 6.9
movement_speed_descend = 6
movement_liquid_fluidity = 2
movement_liquid_fluidity_smooth = 0.5
movement_liquid_sink = 15
movement_gravity = 9.75
#
# Mapgen Stuff
#
cloud_height = 160
mg_flags = trees, caves, dungeons, light
enable_floating_dungeons = false
mgv7_spflags = mountains, ridges
mgv7_np_terrain_base = 4, 70, (600, 600, 600), 82341, 5, 0.6, 2.0
mgv7_np_terrain_alt = 4, 25, (600, 600, 600), 5934, 5, 0.6, 2.0
mgv7_np_terrain_persist = 0.6, 0.12, (2000, 2000, 2000), 539, 3, 0.5, 2.0
mgv7_np_height_select = -12, 24, (500, 500, 500), 4213, 6, 0.69, 2.0
mgv7_np_filler_depth = 0, 1.2, (150, 150, 150), 261, 4, 0.7, 2.0
mgv7_np_mount_height = 675, 350, (3000, 2000, 3000), 72449, 3, 0.5, 2.0
mgv7_np_ridge_uwater = 0, 1, (1000, 1000, 1000), 85039, 5, 0.6, 2.0
mgv7_np_mountain = -0.6, 1, (850, 1450, 850), 5333, 5, 0.68, 2.0
mgv7_np_ridge = 0, 1, (100, 100, 100), 6467, 4, 0.75, 2.0
mgv7_np_cave1 = 0, 12, (100, 100, 100), 52534, 4, 0.5, 2.0
mgv7_np_cave2 = 0, 12, (100, 100, 100), 10325, 4, 0.5, 2.0

View File

@ -60,3 +60,10 @@ local function adventuretest_placenode(pos, node, placer)
hunger.handle_node_actions(pos,node,placer)
end
minetest.register_on_placenode(adventuretest_placenode)
local function on_generated(minp,maxp,seed)
quests.treasure.on_generated(minp,maxp)
end
minetest.register_on_generated(on_generated)

View File

@ -1551,7 +1551,7 @@ minetest.register_node("default:ice", {
is_ground_content = true,
paramtype = "light",
freezemelt = "default:water_source",
groups = {cracky=3, melts=1},
groups = {choppy=3,cracky=3, melts=1},
sounds = default.node_sound_glass_defaults(),
})

View File

@ -30,6 +30,25 @@ minetest.register_node("flowers:dandelion_white", {
},
})
minetest.register_node("flowers:magic", {
description = "Magic Flower",
drawtype = "plantlike",
tiles = { "flowers_magic.png" },
inventory_image = "flowers_magic.png",
wield_image = "flowers_magic.png",
sunlight_propagates = true,
paramtype = "light",
light_source = 12,
walkable = false,
buildable_to = true,
groups = {snappy=3,flammable=2,flower=1,flora=1,attached_node=1,color_white=1},
sounds = default.node_sound_leaves_defaults(),
selection_box = {
type = "fixed",
fixed = { -0.15, -0.5, -0.15, 0.15, 0.2, 0.15 },
},
})
minetest.register_node("flowers:dandelion_yellow", {
description = "Yellow Dandelion",
drawtype = "plantlike",
@ -120,6 +139,27 @@ minetest.register_node("flowers:viola", {
},
})
-- regrow some magic flowers
minetest.register_abm({
nodenames = {"default:dirt_with_snow","default:snowblock"},
interval = 80,
chance = 600,
action = function(pos, node)
if pos.y < 300 then
return
end
local light = minetest.get_node_light(pos)
if not light or light < 10 then
return
end
local pos2 = {x=pos.x,y=(pos.y+1),z=pos.z}
local above = minetest.get_node(pos2).name
if above == "default:snow" or above == "air" then
minetest.set_node(pos2,"flowers:magic")
end
end,
})
minetest.register_abm({
nodenames = {"group:flora"},
neighbors = {"default:dirt_with_grass", "default:desert_sand"},

Binary file not shown.

After

Width:  |  Height:  |  Size: 313 B

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

64
mods/handle_schematics/init.lua Executable file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,234 @@
replacements_group['wood'] = {}
-- this contains a list of all found/available nodenames that may act as a replacement for default:wood
replacements_group['wood'].found = {};
-- contains a list of *all* known wood names - even of mods that may not be installed
replacements_group['wood'].all = {};
-- contains information about how a particular node is called if a particular wood is used;
replacements_group['wood'].data = {};
-- names of traders for the diffrent wood types
replacements_group['wood'].traders = {};
------------------------------------------------------------------------------
-- external function; call it in order to replace old_wood with new_wood;
-- other nodes (trees, saplings, fences, doors, ...) are replaced accordingly,
-- depending on what new_wood has to offer
------------------------------------------------------------------------------
replacements_group['wood'].replace_material = function( replacements, old_wood, new_wood )
if( not( old_wood ) or not( replacements_group['wood'].data[ old_wood ])
or not( new_wood ) or not( replacements_group['wood'].data[ new_wood ])
or old_wood == new_wood ) then
return replacements;
end
local old_nodes = replacements_group['wood'].data[ old_wood ];
local new_nodes = replacements_group['wood'].data[ new_wood ];
for i=3,#old_nodes do
local old = old_nodes[i];
local new = old;
if( i<=#new_nodes and new_nodes[i] and minetest.registered_nodes[ new_nodes[i]] ) then
new = new_nodes[i];
local found = false;
for i,v in ipairs(replacements) do
if( v and v[1]==old ) then
v[2] = new;
found = true;
end
end
if( not( found )) then
table.insert( replacements, { old, new });
end
end
end
return replacements;
end
---------------------
-- internal functions
---------------------
-- wood (and its corresponding tree trunk) is a very good candidate for replacement in most houses
-- helper function for replacements_group['wood'].get_wood_type_list
replacements_group['wood'].add_material = function( candidate_list, mod_prefix, w_pre, w_post, t_pre, t_post, l_pre, l_post,
s_pre, s_post, stair_pre, stair_post, slab_pre, slab_post,
fence_pre, fence_post, gate_pre, gate_post )
if( not( candidate_list )) then
return;
end
for _,v in ipairs( candidate_list ) do
local is_loaded = false;
local wood_name = mod_prefix..w_pre..v..w_post;
-- create a complete list of all possible wood names
table.insert( replacements_group['wood'].all, wood_name );
-- create a list of all *installed* wood types
if( minetest.registered_nodes[ wood_name ]) then
table.insert( replacements_group['wood'].found, wood_name );
is_loaded = true;
end
-- there is no check if the node names created here actually exist
local data = { v, -- 1. base name of the node
mod_prefix, -- 2. mod name
wood_name, -- 3. replacement for default:wood
mod_prefix..t_pre..v..t_post, -- 4. " " for default:tree
mod_prefix..l_pre..v..l_post, -- 5. " " for default:leaves
mod_prefix..s_pre..v..s_post, -- 6. " " for default:sapling
stair_pre..v..stair_post, -- 7. " " for stairs:stair_wood
slab_pre..v..slab_post, -- 8. " " for stairs:slab_wood
fence_pre..v..fence_post, -- 9. " " for default:fence_wood
gate_pre..v..gate_post..'_open', -- 10. " " for cottages:gate_open
gate_pre..v..gate_post..'_closed',-- 11. " " for cottages:gate_closed
};
-- normal wood does have a number of nodes which might get replaced by more specialized wood types
if( mod_prefix=='default:' and v=='' ) then
local w = 'wood';
data[10] = 'cottages:gate_open';
data[11] = 'cottages:gate_closed';
data[12] = 'default:ladder';
data[13] = 'doors:door_'..w..'_t_1';
data[14] = 'doors:door_'..w..'_t_2';
data[15] = 'doors:door_'..w..'_b_1';
data[16] = 'doors:door_'..w..'_b_2';
data[17] = 'default:bookshelf';
data[18] = 'default:chest';
data[19] = 'default:chest_locked';
data[20] = 'stairs:stair_'..w..'upside_down';
data[21] = 'stairs:slab_'..w..'upside_down';
data[22] = 'doors:trapdoor_open';
data[23] = 'doors:trapdoor';
-- realtest has some further replacements
elseif( mod_prefix=='trees:' and w_post=='_planks' and t_post=='_log' ) then
data[12] = 'trees:'..v..'_ladder';
data[13] = 'doors:door_'..v..'_t_1';
data[14] = 'doors:door_'..v..'_t_2';
data[15] = 'doors:door_'..v..'_b_1';
data[16] = 'doors:door_'..v..'_b_2';
data[17] = 'decorations:bookshelf_'..v;
data[18] = 'trees:'..v..'_chest';
data[19] = 'trees:'..v..'_chest_locked';
data[20] = 'trees:'..v..'_planks_stair_upside_down';
data[21] = 'trees:'..v..'_planks_slab_upside_down';
data[22] = 'hatches:'..v..'_hatch_opened_top';
data[23] = 'hatches:'..v..'_hatch_opened_bottom';
end
replacements_group['wood'].data[ wood_name ] = data;
-- none of the wood nodes counts as ground
local c_ignore = minetest.get_content_id( 'ignore' );
for _,v in ipairs( data ) do
local id = minetest.get_content_id( v );
if( id and id ~= c_ignore ) then
replacements_group.node_is_ground[ id ] = false;
end
end
if( is_loaded and 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
mods/handle_schematics/rotate.lua Executable file
View File

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

View File

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

View File

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

View File

@ -0,0 +1,146 @@
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

View File

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

1
mods/mg/.gitignore vendored
View File

@ -1 +0,0 @@
*~

194
mods/mg/biomes.lua Executable file
View File

@ -0,0 +1,194 @@
minetest.clear_registered_biomes()
minetest.register_biome({
name = "plains",
node_top = "default:dirt_with_grass",
depth_top = 1,
node_filler = "default:dirt",
depth_filler = 15,
node_dust = "air",
node_underwater = "default:dirt",
y_min = 3,
y_max = 230,
node_shore_filler = "default:sand",
heat_point = 45,
humidity_point = 45,
})
minetest.register_biome({
name = "hot_plains",
node_top = "default:dirt_with_grass",
depth_top = 1,
node_filler = "default:dirt",
depth_filler = 15,
node_dust = "air",
node_underwater = "default:dirt",
y_min = 3,
y_max = 230,
node_shore_filler = "default:sand",
heat_point = 70,
humidity_point = 45,
})
minetest.register_biome({
name = "beach",
node_top = "default:sand",
depth_top = 1,
node_filler = "default:sandstone",
depth_filler = 15,
node_dust = "air",
node_underwater = "default:sand",
y_min = -25,
y_max = 3,
node_shore_filler = "default:sand",
heat_point = 70,
humidity_point = 45,
})
minetest.register_biome({
name = "gravelbar",
node_top = "default:gravel",
depth_top = 1,
node_filler = "default:gravel",
depth_filler = 2,
node_dust = "air",
node_underwater = "default:dirt",
y_min = -25,
y_max = 3,
node_shore_filler = "default:gravel",
heat_point = 45,
humidity_point = 45,
})
minetest.register_biome({
name = "forest",
node_top = "default:dirt_with_grass",
depth_top = 1,
node_filler = "default:dirt",
depth_filler = 15,
node_dust = "air",
node_underwater = "default:dirt",
y_min = 2,
y_max = 230,
node_shore_filler = "default:sand",
heat_point = 45,
humidity_point = 65,
})
minetest.register_biome({
name = "forest_gravelbar",
node_top = "default:gravel",
depth_top = 1,
node_filler = "default:gravel",
depth_filler = 2,
node_dust = "air",
node_underwater = "default:dirt",
y_min = -25,
y_max = 2,
node_shore_filler = "default:gravel",
heat_point = 45,
humidity_point = 65,
})
minetest.register_biome({
name = "mtn_top",
node_top = "default:snowblock",
depth_top = 1,
node_filler = "default:snowblock",
depth_filler = 5,
node_dust = "default:snow",
node_underwater = "default:dirt",
y_min = 230,
y_max = 32000,
node_shore_filler = "default:snowblock",
--heat_point = 45,
--humidity_point = 45,
})
minetest.register_biome({
name = "desert",
--node_dust = "",
node_top = "default:desert_sand",
depth_top = 1,
node_filler = "default:desert_sand",
depth_filler = 1,
node_stone = "default:desert_stone",
--node_water_top = "",
--depth_water_top = ,
--node_water = "",
y_min = 1,
y_max = 230,
heat_point = 95,
humidity_point = 10,
})
minetest.register_biome({
name = "savanna",
--node_dust = "",
node_top = "mg:dirt_with_dry_grass",
depth_top = 1,
node_filler = "default:dirt",
depth_filler = 4,
node_stone = "default:stone",
--node_water_top = "",
--depth_water_top = ,
--node_water = "",
y_min = 1,
y_max = 230,
heat_point = 95,
humidity_point = 45,
})
minetest.register_biome({
name = "snowy",
node_dust = "default:snow",
node_top = "default:snowblock",
depth_top = 1,
node_filler = "default:dirt",
depth_filler = 2,
node_stone = "default:stone",
node_water_top = "default:ice",
depth_water_top = 2,
--node_water = "",
y_min = 1,
y_max = 230,
heat_point = 10,
humidity_point = 70,
})
minetest.register_biome({
name = "tundra",
--node_dust = "",
node_top = "default:dirt_with_snow",
depth_top = 1,
node_filler = "default:dirt",
depth_filler = 4,
node_stone = "default:stone",
node_water_top = "default:ice",
depth_water_top = 2,
--node_water = "",
y_min = 1,
y_max = 230,
heat_point = 10,
humidity_point = 40,
})
minetest.register_biome({
name = "jungle",
--node_dust = "",
node_top = "default:dirt_with_grass",
depth_top = 1,
node_filler = "default:dirt",
depth_filler = 6,
node_stone = "default:stone",
--node_water_top = "default:ice",
--depth_water_top = 2,
--node_water = "",
y_min = 1,
y_max = 230,
heat_point = 90,
humidity_point = 90,
})

View File

@ -1,61 +0,0 @@
buildings = {
{sizex= 7, sizez= 7, yoff= 0, ysize= 9, scm="house", orients={2}},
{sizex= 9, sizez= 9, yoff= 0, ysize= 2, scm="wheat_field"},
{sizex= 9, sizez= 9, yoff= 0, ysize= 2, scm="cotton_field"},
{sizex= 3, sizez= 3, yoff= 1, ysize= 4, scm="lamp", weight=1/5, no_rotate=true},
{sizex= 4, sizez= 4, yoff=-5, ysize=11, scm="well", no_rotate=true, pervillage=1},
{sizex= 7, sizez= 7, yoff= 0, ysize=11, scm="fountain", weight=1/4, pervillage=3},
{sizex= 5, sizez= 5, yoff= 0, ysize= 6, scm="small_house", orients={3}},
{sizex= 6, sizez=12, yoff= 0, ysize= 7, scm="house_with_garden", orients={1}},
{sizex=16, sizez=19, yoff=-1, ysize=14, scm="church", orients={3}, pervillage=1},
{sizex= 8, sizez= 9, yoff= 0, ysize= 6, scm="forge", orients={0}, pervillage=2},
{sizex=12, sizez=13, yoff= 0, ysize= 6, scm="library", orients={1}, pervillage=2},
{sizex=15, sizez= 7, yoff= 0, ysize=12, scm="inn", orients={1}, pervillage=4, weight=1/2},
{sizex=22, sizez=17, yoff= 0, ysize= 7, scm="pub", orients={3}, pervillage=1, weight=1/3},
{sizex=11, sizez=13, yoff= 1, ysize=12, scm="pub2", orients={3}, pervillage=1, weight=1/3},
{sizex=12, sizez=10, yoff= 1, ysize= 9, scm="house1",orients={4}},
{sizex=13, sizez=10, yoff= 1, ysize= 9, scm="house2",orients={4}},
{sizex=13, sizez=11, yoff= 1, ysize= 8, scm="house3",orients={4}},
{sizex=14, sizez=12, yoff= 1, ysize= 8, scm="farmtiny1",orients={4}},
{sizex=12, sizez=26, yoff= 1, ysize=19, scm="largechurch", orients={1},pervillage=1},
}
local gravel = minetest.get_content_id("default:gravel")
local rgravel = {}
for i = 1, 2000 do
rgravel[i] = gravel
end
local rgravel2 = {}
for i = 1, 2000 do
rgravel2[i] = rgravel
end
local rair = {}
for i = 1, 2000 do
rair[i] = c_air
end
local rair2 = {}
for i = 1, 2000 do
rair2[i] = rair
end
local road_scm = {rgravel2, rair2}
buildings["road"] = {yoff = 0, ysize = 2, scm = road_scm}
local rwall = {{minetest.get_content_id("default:stonebrick")}}
local wall = {}
for i = 1, 6 do
wall[i] = rwall
end
buildings["wall"] = {yoff = 1, ysize = 6, scm = wall}
local total_weight = 0
for _, i in ipairs(buildings) do
if i.weight == nil then i.weight = 1 end
total_weight = total_weight+i.weight
i.max_weight = total_weight
end
local multiplier = 3000/total_weight
for _,i in ipairs(buildings) do
i.max_weight = i.max_weight*multiplier
end

257
mods/mg/decorations.lua Executable file
View File

@ -0,0 +1,257 @@
minetest.register_decoration({
deco_type = "simple",
place_on = {"default:desert_sand"},
sidelen = 80,
noise_params = {
offset = -0.0005,
scale = 0.0015,
spread = {x=200, y=200, z=200},
seed = 230,
octaves = 3,
persist = 0.6
},
biomes = {"desert"},
y_min = 2,
y_max = 70,
decoration = "default:cactus",
height = 2,
height_max = 4,
})
minetest.register_decoration({
deco_type = "simple",
place_on = {"default:desert_sand"},
sidelen = 80,
noise_params = {
offset = -0.0005,
scale = 0.0015,
spread = {x=200, y=200, z=200},
seed = 230,
octaves = 3,
persist = 0.6
},
biomes = {"desert"},
y_min = 2,
y_max = 155,
decoration = "default:cactus",
height = 2,
height_max = 5,
})
minetest.register_decoration({
deco_type = "simple",
place_on = {"default:dirt_with_snow","default:snowblock"},
sidelen = 40,
fill_ratio = 0.001,
biomes = {"mtn_top"},
y_min = 300,
y_max = 32000,
decoration = "flowers:magic",
})
minetest.register_decoration({
deco_type = "simple",
place_on = {"mg:dirt_with_dry_grass"},
sidelen = 40,
fill_ratio = 0.0008,
biomes = {"savanna"},
y_min = 2,
y_max = 40,
decoration = "mg:savannasapling_ongen",
})
minetest.register_decoration({
deco_type = "simple",
place_on = {"mg:dirt_with_dry_grass","default:desert_sand"},
sidelen = 40,
fill_ratio = 0.05,
biomes = {"savanna","desert"},
y_min = 2,
y_max = 40,
decoration = "default:dry_shrub",
})
-- Apple tree
minetest.register_decoration({
deco_type = "schematic",
place_on = {"default:dirt_with_grass"},
sidelen = 80,
noise_params = {
offset = 0.04,
scale = 0.02,
spread = {x=250, y=250, z=250},
seed = 2,
octaves = 3,
persist = 0.66
},
biomes = {"forest"},
y_min = 1,
y_max = 155,
schematic = minetest.get_modpath("mg").."/schematics/apple_tree.mts",
flags = "place_center_x, place_center_z",
})
minetest.register_decoration({
deco_type = "schematic",
place_on = {"default:dirt_with_grass"},
sidelen = 80,
fill_ratio = 0.0005,
biomes = {"plains","hot_plains"},
y_min = 1,
y_max = 155,
schematic = minetest.get_modpath("mg").."/schematics/apple_tree.mts",
flags = "place_center_x, place_center_z",
})
-- Jungle tree
minetest.register_decoration({
deco_type = "schematic",
place_on = {"default:dirt_with_grass"},
sidelen = 80,
fill_ratio = 0.08,
biomes = {"jungle"},
y_min = -1,
y_max = 155,
schematic = minetest.get_modpath("mg").."/schematics/jungle_tree.mts",
flags = "place_center_x, place_center_z",
})
minetest.register_decoration({
deco_type = "simple",
place_on = {"default:dirt_with_grass"},
sidelen = 80,
fill_ratio = 0.9,
biomes = {
"jungle",
},
y_min = 0,
y_max = 155,
decoration = "default:junglegrass",
})
minetest.register_decoration({
deco_type = "schematic",
place_on = {"default:desert_sand"},
sidelen = 80,
noise_params = {
offset = -0.0005,
scale = 0.0015,
spread = {x=200, y=200, z=200},
seed = 230,
octaves = 3,
persist = 0.6
},
biomes = {"desert"},
y_min = 2,
y_max = 31000,
schematic = minetest.get_modpath("mg").."/schematics/large_cactus.mts",
flags = "place_center_x",
rotation = "random",
})
minetest.register_decoration({
deco_type = "simple",
place_on = {"default:dirt_with_grass"},
sidelen = 16,
noise_params = {
offset = -0.03,
scale = 0.09,
spread = {x=200, y=200, z=200},
seed = 329,
octaves = 3,
persist = 0.6
},
biomes = {
"plains","forest","hot_plains",
},
y_min = 3,
y_max = 155,
decoration = "default:grass_5",
})
minetest.register_decoration({
deco_type = "simple",
place_on = {"default:dirt_with_grass"},
sidelen = 16,
noise_params = {
offset = -0.015,
scale = 0.075,
spread = {x=200, y=200, z=200},
seed = 329,
octaves = 3,
persist = 0.6
},
biomes = {
"plains","forest","hot_plains",
},
y_min = 3,
y_max = 155,
decoration = "default:grass_4",
})
minetest.register_decoration({
deco_type = "simple",
place_on = {"default:dirt_with_grass"},
sidelen = 16,
noise_params = {
offset = 0,
scale = 0.06,
spread = {x=200, y=200, z=200},
seed = 329,
octaves = 3,
persist = 0.6
},
biomes = {
"plains","forest","hot_plains",
},
y_min = 3,
y_max = 155,
decoration = "default:grass_3",
})
minetest.register_decoration({
deco_type = "simple",
place_on = {"default:dirt_with_grass"},
sidelen = 16,
noise_params = {
offset = 0.015,
scale = 0.045,
spread = {x=200, y=200, z=200},
seed = 329,
octaves = 3,
persist = 0.6
},
biomes = {
"plains","forest","hot_plains",
},
y_min = 3,
y_max = 155,
decoration = "default:grass_2",
})
minetest.register_decoration({
deco_type = "simple",
place_on = {"default:dirt_with_grass"},
sidelen = 16,
noise_params = {
offset = 0.03,
scale = 0.03,
spread = {x=200, y=200, z=200},
seed = 329,
octaves = 3,
persist = 0.6
},
biomes = {
"plains","forest","hot_plains",
},
y_min = 3,
y_max = 155,
decoration = "default:grass_1",
})

View File

@ -1,5 +1 @@
default
farming
wool
stairs
moretrees?
default

View File

@ -1,2 +0,0 @@
This mod changes the default map generation: it changes the way ores are placed, add new biomes and villages.
Note: do NOT use it in an already generated world!!!

View File

@ -1,11 +1,7 @@
mg = {}
local mgpath = minetest.get_modpath("mg")
local ENABLE_SNOW = false
dofile(mgpath.."/nodes.lua")
local DMAX = 60
local AREA_SIZE = 256
dofile(minetest.get_modpath(minetest.get_current_modname()).."/nodes.lua")
c_air = minetest.get_content_id("air")
c_grass = minetest.get_content_id("default:dirt_with_grass")
c_dry_grass = minetest.get_content_id("mg:dirt_with_dry_grass")
@ -43,753 +39,13 @@ c_jungle_grass = minetest.get_content_id("default:junglegrass")
c_dry_shrub = minetest.get_content_id("default:dry_shrub")
c_papyrus = minetest.get_content_id("default:papyrus")
minetest.register_on_mapgen_init(function(mgparams)
minetest.set_mapgen_params({mgname="singlenode", flags="nolight"})
end)
local cache = {}
dofile(mgpath.."/trees.lua")
dofile(mgpath.."/biomes.lua")
dofile(mgpath.."/decorations.lua")
local function cliff(x, n)
return 0.2*x*x - x + n*x - n*n*x*x - 0.01 * math.abs(x*x*x) + math.abs(x)*100*n*n*n*n
end
local function get_base_surface_at_point(x, z, vn, vh, ni, noise1, noise2, noise3, noise4)
local index = 65536*x+z
if cache[index] ~= nil then return cache[index] end
cache[index] = 25*noise1[ni]+noise2[ni]*noise3[ni]/3
if noise4[ni] > 0.8 then
cache[index] = cliff(cache[index], noise4[ni]-0.8)
end
if vn<40 then
cache[index] = vh
elseif vn<200 then
cache[index] = (vh*(200-vn) + cache[index]*(vn-40))/160
end
return cache[index]
end
local function surface_at_point(x, z, ...)
return get_base_surface_at_point(x, z, unpack({...}))
end
local SMOOTHED = AREA_SIZE+2*DMAX
local HSMOOTHED = AREA_SIZE+DMAX
local INSIDE = AREA_SIZE-DMAX
local function smooth(x, z, ...)
local s=0
local w=0
for xi=-DMAX, DMAX do
for zi=-DMAX, DMAX do
local d2=xi*xi+zi*zi
if d2<DMAX*DMAX then
local w1 = 1-d2/(DMAX*DMAX)
local w2 = 15/16*w1*w1
w = w+w2
s=s+w2*surface_at_point(x+xi, z+zi, unpack({...}))
end
end
end
return s/w
end
local function smooth_surface(x, z, vnoise, vx, vz, vs, vh, ...)
local vn
if vs ~= 0 then
vn = (vnoise:get2d({x=x, y=z})-2)*20+(40/(vs*vs))*((x-vx)*(x-vx)+(z-vz)*(z-vz))
else
vn = 1000
end
return surface_at_point(x, z, vn, vh, unpack({...}))
end
function inside_village(x, z, vx, vz, vs, vnoise)
return ((vnoise:get2d({x=x, y=z})-2)*20+(40/(vs*vs))*((x-vx)*(x-vx)+(z-vz)*(z-vz)))<=40
end
minetest.register_on_mapgen_init(function(mgparams)
wseed = math.floor(mgparams.seed/10000000000)
end)
function get_bseed(minp)
return wseed + math.floor(5*minp.x/47) + math.floor(873*minp.z/91)
end
function get_bseed2(minp)
return wseed + math.floor(87*minp.x/47) + math.floor(73*minp.z/91) + math.floor(31*minp.y/12)
end
c_air = minetest.get_content_id("air")
c_ignore = minetest.get_content_id("ignore")
c_water = minetest.get_content_id("default:water_source")
local function add_leaves(data, vi, c_leaves, c_snow)
if data[vi]==c_air or data[vi]==c_ignore or data[vi] == c_snow then
data[vi] = c_leaves
end
end
function add_tree(data, a, x, y, z, minp, maxp, pr)
local th = pr:next(3, 4)
for yy=math.max(minp.y, y), math.min(maxp.y, y+th) do
local vi = a:index(x, yy, z)
data[vi] = c_tree
end
local maxy = y+th
for xx=math.max(minp.x, x-1), math.min(maxp.x, x+1) do
for yy=math.max(minp.y, maxy-1), math.min(maxp.y, maxy+1) do
for zz=math.max(minp.z, z-1), math.min(maxp.z, z+1) do
add_leaves(data, a:index(xx, yy, zz), c_leaves)
end
end
end
for i=1,8 do
local xi = pr:next(x-2, x+1)
local yi = pr:next(maxy-1, maxy+1)
local zi = pr:next(z-2, z+1)
for xx=math.max(minp.x, xi), math.min(maxp.x, xi+1) do
for yy=math.max(minp.y, yi), math.min(maxp.y, yi+1) do
for zz=math.max(minp.z, zi), math.min(maxp.z, zi+1) do
add_leaves(data, a:index(xx, yy, zz), c_leaves)
end
end
end
end
end
function add_jungletree(data, a, x, y, z, minp, maxp, pr)
local th = pr:next(7, 11)
for yy=math.max(minp.y, y), math.min(maxp.y, y+th) do
local vi = a:index(x, yy, z)
data[vi] = c_jungletree
end
local maxy = y+th
for xx=math.max(minp.x, x-1), math.min(maxp.x, x+1) do
for yy=math.max(minp.y, maxy-1), math.min(maxp.y, maxy+1) do
for zz=math.max(minp.z, z-1), math.min(maxp.z, z+1) do
add_leaves(data, a:index(xx, yy, zz), c_jungleleaves)
end
end
end
for i=1,30 do
local xi = pr:next(x-3, x+2)
local yi = pr:next(maxy-2, maxy+1)
local zi = pr:next(z-3, z+2)
for xx=math.max(minp.x, xi), math.min(maxp.x, xi+1) do
for yy=math.max(minp.y, yi), math.min(maxp.y, yi+1) do
for zz=math.max(minp.z, zi), math.min(maxp.z, zi+1) do
add_leaves(data, a:index(xx, yy, zz), c_jungleleaves)
end
end
end
end
end
function add_savannatree(data, a, x, y, z, minp, maxp, pr)
local th = pr:next(7, 11)
for yy=math.max(minp.y, y), math.min(maxp.y, y+th) do
local vi = a:index(x, yy, z)
data[vi] = c_savannatree
end
local maxy = y+th
for xx=math.max(minp.x, x-1), math.min(maxp.x, x+1) do
for yy=math.max(minp.y, maxy-1), math.min(maxp.y, maxy+1) do
for zz=math.max(minp.z, z-1), math.min(maxp.z, z+1) do
add_leaves(data, a:index(xx, yy, zz), c_savannaleaves)
end
end
end
for i=1,20 do
local xi = pr:next(x-3, x+2)
local yi = pr:next(maxy-2, maxy)
local zi = pr:next(z-3, z+2)
for xx=math.max(minp.x, xi), math.min(maxp.x, xi+1) do
for yy=math.max(minp.y, yi), math.min(maxp.y, yi+1) do
for zz=math.max(minp.z, zi), math.min(maxp.z, zi+1) do
add_leaves(data, a:index(xx, yy, zz), c_savannaleaves)
end
end
end
end
for i=1,15 do
local xi = pr:next(x-3, x+2)
local yy = pr:next(maxy-6, maxy-5)
local zi = pr:next(z-3, z+2)
for xx=math.max(minp.x, xi), math.min(maxp.x, xi+1) do
for zz=math.max(minp.z, zi), math.min(maxp.z, zi+1) do
if minp.y<=yy and maxp.y>=yy then
add_leaves(data, a:index(xx, yy, zz), c_savannaleaves)
end
end
end
end
end
function add_savannabush(data, a, x, y, z, minp, maxp, pr)
local bh = pr:next(1, 2)
local bw = pr:next(2, 4)
for xx=math.max(minp.x, x-bw), math.min(maxp.x, x+bw) do
for zz=math.max(minp.z, z-bw), math.min(maxp.z, z+bw) do
for yy=math.max(minp.y, y-bh), math.min(maxp.y, y+bh) do
if pr:next(1, 100) < 95 and math.abs(xx-x) < pr:next(bh, bh+2)-math.abs(y-yy) and math.abs(zz-z) < pr:next(bh, bh+2)-math.abs(y-yy) then
add_leaves(data, a:index(xx, yy, zz), c_savannaleaves)
for yyy=math.max(minp.y, yy-2), yy do
add_leaves(data, a:index(xx, yyy, zz), c_savannaleaves)
end
end
end
end
end
if x<=maxp.x and x>=minp.x and y<=maxp.y and y>=minp.y and z<=maxp.z and z>=minp.z then
local vi = a:index(x, y, z)
data[vi] = c_savannatree
end
end
function add_pinetree(data, a, x, y, z, minp, maxp, pr, snow)
if snow == nil then snow = c_snow end
local th = pr:next(9, 13)
for yy=math.max(minp.y, y), math.min(maxp.y, y+th) do
local vi = a:index(x, yy, z)
data[vi] = c_pinetree
end
local maxy = y+th
for xx=math.max(minp.x, x-3), math.min(maxp.x, x+3) do
for yy=math.max(minp.y, maxy-1), math.min(maxp.y, maxy-1) do
for zz=math.max(minp.z, z-3), math.min(maxp.z, z+3) do
if pr:next(1, 100) < 80 then
add_leaves(data, a:index(xx, yy, zz), c_pineleaves, snow)
add_leaves(data, a:index(xx, yy+1, zz), snow)
end
end
end
end
for xx=math.max(minp.x, x-2), math.min(maxp.x, x+2) do
for yy=math.max(minp.y, maxy), math.min(maxp.y, maxy) do
for zz=math.max(minp.z, z-2), math.min(maxp.z, z+2) do
if pr:next(1, 100) < 85 then
add_leaves(data, a:index(xx, yy, zz), c_pineleaves, snow)
add_leaves(data, a:index(xx, yy+1, zz), snow)
end
end
end
end
for xx=math.max(minp.x, x-1), math.min(maxp.x, x+1) do
for yy=math.max(minp.y, maxy+1), math.min(maxp.y, maxy+1) do
for zz=math.max(minp.z, z-1), math.min(maxp.z, z+1) do
if pr:next(1, 100) < 90 then
add_leaves(data, a:index(xx, yy, zz), c_pineleaves, snow)
add_leaves(data, a:index(xx, yy+1, zz), snow)
end
end
end
end
if maxy+1<=maxp.y and maxy+1>=minp.y then
add_leaves(data, a:index(x, maxy+1, z), c_pineleaves, snow)
add_leaves(data, a:index(x, maxy+2, z), snow)
end
local my = 0
for i=1,20 do
local xi = pr:next(x-3, x+2)
local yy = pr:next(maxy-6, maxy-5)
local zi = pr:next(z-3, z+2)
if yy > my then
my = yy
end
for xx=math.max(minp.x, xi), math.min(maxp.x, xi+1) do
for zz=math.max(minp.z, zi), math.min(maxp.z, zi+1) do
if minp.y<=yy and maxp.y>=yy then
add_leaves(data, a:index(xx, yy, zz), c_pineleaves, snow)
add_leaves(data, a:index(xx, yy+1, zz), snow)
end
end
end
end
for xx=math.max(minp.x, x-2), math.min(maxp.x, x+2) do
for yy=math.max(minp.y, my+1), math.min(maxp.y, my+1) do
for zz=math.max(minp.z, z-2), math.min(maxp.z, z+2) do
if pr:next(1, 100) < 85 then
add_leaves(data, a:index(xx, yy, zz), c_pineleaves, snow)
add_leaves(data, a:index(xx, yy+1, zz), snow)
end
end
end
end
for xx=math.max(minp.x, x-1), math.min(maxp.x, x+1) do
for yy=math.max(minp.y, my+2), math.min(maxp.y, my+2) do
for zz=math.max(minp.z, z-1), math.min(maxp.z, z+1) do
if pr:next(1, 100) < 90 then
add_leaves(data, a:index(xx, yy, zz), c_pineleaves, snow)
add_leaves(data, a:index(xx, yy+1, zz), snow)
end
end
end
end
end
dofile(minetest.get_modpath(minetest.get_current_modname()).."/we.lua")
dofile(minetest.get_modpath(minetest.get_current_modname()).."/rotate.lua")
dofile(minetest.get_modpath(minetest.get_current_modname()).."/buildings.lua")
dofile(minetest.get_modpath(minetest.get_current_modname()).."/villages.lua")
dofile(minetest.get_modpath(minetest.get_current_modname()).."/ores.lua")
function get_biome_table(minp, humidity, temperature, range)
if range == nil then range = 1 end
local l = {}
for xi = -range, range do
for zi = -range, range do
local mnp, mxp = {x=minp.x+xi*80,z=minp.z+zi*80}, {x=minp.x+xi*80+80,z=minp.z+zi*80+80}
local pr = PseudoRandom(get_bseed(mnp))
local bxp, bzp = pr:next(mnp.x, mxp.x), pr:next(mnp.z, mxp.z)
local h, t = humidity:get2d({x=bxp, y=bzp}), temperature:get2d({x=bxp, y=bzp})
l[#l+1] = {x=bxp, z=bzp, h=h, t=t}
end
end
return l
end
local function get_distance(x1, x2, z1, z2)
return (x1-x2)*(x1-x2)+(z1-z2)*(z1-z2)
end
function get_nearest_biome(biome_table, x, z)
local m = math.huge
local k = 0
for key, bdef in ipairs(biome_table) do
local dist = get_distance(bdef.x, x, bdef.z, z)
if dist<m then
m=dist
k=key
end
end
return biome_table[k]
end
local function get_perlin_map(seed, octaves, persistance, scale, minp, maxp)
local sidelen = maxp.x - minp.x +1
local pm = minetest.get_perlin_map(
{offset=0, scale=1, spread={x=scale, y=scale, z=scale}, seed=seed, octaves=octaves, persist=persistance},
{x=sidelen, y=sidelen, z=sidelen}
)
return pm:get2dMap_flat({x = minp.x, y = minp.z, z = 0})
end
local function copytable(t)
local t2 = {}
for key, val in pairs(t) do
t2[key] = val
end
return t2
end
local function mg_generate(minp, maxp, emin, emax, vm)
local b_start = os.clock()
minetest.log("verbose","mg_generate")
local a = VoxelArea:new{
MinEdge={x=emin.x, y=emin.y, z=emin.z},
MaxEdge={x=emax.x, y=emax.y, z=emax.z},
}
local treemin = {x=emin.x, y=minp.y, z=emin.z}
local treemax = {x=emax.x, y=maxp.y, z=emax.z}
local sidelen = maxp.x-minp.x+1
local noise1 = get_perlin_map(12345, 6, 0.5, 256, minp, maxp)
local noise2 = get_perlin_map(56789, 6, 0.5, 256, minp, maxp)
local noise3 = get_perlin_map(42, 3, 0.5, 96, minp, maxp)
local noise4 = get_perlin_map(6957, 8, 0.5, 1024, minp, maxp) -- water?
local noise1raw = minetest.get_perlin(12345, 6, 0.5, 256)
--local noise2 = minetest.get_perlin(56789, 6, 0.5, 256)
--local noise3 = minetest.get_perlin(42, 3, 0.5, 32)
--local noise4 = minetest.get_perlin(8954, 8, 0.5, 1024)
local vcr = VILLAGE_CHECK_RADIUS
local vx,vz,vs,vh
local exitloop = false
for xi = -vcr, vcr do
for zi = -vcr, vcr do
--print(tostring(xi)..","..tostring(zi))
vx,vz,vs,vh = village_at_point({x=minp.x+xi*80,z=minp.z+zi*80}, noise1raw)
if vs ~= 0 then
--goto out
exitloop = true
break
end
end
if exitloop then break end
end
--::out::
local pr = PseudoRandom(get_bseed(minp))
local village_noise = minetest.get_perlin(7635, 3, 0.5, 16)
--local noise_top_layer = minetest.get_perlin(654, 6, 0.5, 256)
--local noise_second_layer = minetest.get_perlin(123, 6, 0.5, 256)
local noise_top_layer = get_perlin_map(654, 6, 0.5, 256, minp, maxp)
local noise_second_layer = get_perlin_map(123, 6, 0.5, 256, minp, maxp)
local noise_temperature_raw = minetest.get_perlin(763, 7, 0.5, 1024)
local noise_humidity_raw = minetest.get_perlin(834, 7, 0.5, 1024)
--local noise_beach = minetest.get_perlin(452, 6, 0.5, 256)
local noise_temperature = get_perlin_map(763, 7, 0.5, 2048, minp, maxp)
local noise_humidity = get_perlin_map(834, 7, 0.5, 2048, minp, maxp)
local noise_beach = get_perlin_map(452, 6, 0.5, 256, minp, maxp)
local biome_table = get_biome_table(minp, noise_humidity_raw, noise_temperature_raw)
local data = vm:get_data()
local ni = 1
local above_top
local liquid_top
local top
local top_layer
local second_layer
local humidity
local temperature
local villages_to_grow = {}
local ni = 0
for z = minp.z, maxp.z do
for x = minp.x, maxp.x do
ni = ni + 1
local y=math.floor(smooth_surface(x, z, village_noise, vx, vz, vs, vh, ni, noise1, noise2, noise3, noise4))
humidity = noise_humidity[ni]
temperature = noise_temperature[ni] - math.max(y, 0)/50
local biome = get_nearest_biome(biome_table, x, z)
local biome_humidity = biome.h
local biome_temperature = biome.t
if biome_temperature<-0.4 then
liquid_top = c_ice
else
liquid_top = c_water
end
if y < -1 then
above_top = c_air
top = c_dirt
top_layer = c_dirt
second_layer = c_stone
elseif y < 3 and noise_beach[ni]<0.2 then
above_top = c_air
top = c_sand
top_layer = c_sand
second_layer = c_sandstone
else
above_top = c_air
if biome_temperature>0.4 then
if biome_humidity<-0.4 then
top = c_desert_sand
top_layer = c_desert_sand
second_layer = c_desert_stone
elseif biome_humidity<0.4 then
top = c_dry_grass
top_layer = c_dirt
second_layer = c_stone
else
top = c_grass
top_layer = c_dirt
second_layer = c_stone
end
elseif biome_temperature<-0.4 then
above_top = c_snow
top = c_dirt_snow
top_layer = c_dirt
second_layer = c_stone
else
top = c_grass
top_layer = c_dirt
second_layer = c_stone
end
end
if y>=100 then
above_top = c_air
top = c_snow
top_layer = c_snowblock
end
if y<0 then
above_top = c_air
end
if y<=maxp.y and y>=minp.y then
local vi = a:index(x, y, z)
if y >= 0 then
data[vi] = top
else
data[vi] = top_layer
end
end
local add_above_top = true
for id, tree in ipairs(mg.registered_trees) do
if tree.min_humidity <= humidity and humidity <= tree.max_humidity
and tree.min_temperature <= temperature and temperature <= tree.max_temperature
and tree.min_biome_humidity <= biome_humidity and biome_humidity <= tree.max_biome_humidity
and tree.min_biome_temperature <= biome_temperature and biome_temperature <= tree.max_biome_temperature
and tree.min_height <= y+1 and y+1 <= tree.max_height
and ((not tree.grows_on) or tree.grows_on == top)
and pr:next(1, tree.chance) == 1 then
if inside_village(x, z, vx, vz, vs, village_noise) and not tree.can_be_in_village then
villages_to_grow[#villages_to_grow+1] = {x=x, y=y+1, z=z, id=id}
else
tree.grow(data, a, x, y+1, z, minp, maxp, pr)
end
add_above_top = false
break
end
end
if add_above_top and y+1<=maxp.y and y+1>=minp.y then
local vi = a:index(x, y+1, z)
data[vi] = above_top
end
if y<0 and minp.y<=0 and maxp.y>y then
for yy = math.max(y+1, minp.y), math.min(0, maxp.y) do
local vi = a:index(x, yy, z)
data[vi] = c_water
end
if maxp.y>=0 then
data[a:index(x, 0, z)] = liquid_top
end
end
local tl = math.floor((noise_top_layer[ni]+2.5)*2)
if y-tl-1<=maxp.y and y-1>=minp.y then
for yy = math.max(y-tl-1, minp.y), math.min(y-1, maxp.y) do
local vi = a:index(x, yy, z)
data[vi] = top_layer
end
end
local sl = math.floor((noise_second_layer[ni]+5)*3)
if y-sl-1<=maxp.y and y-tl-2>=minp.y then
for yy = math.max(y-sl-1, minp.y), math.min(y-tl-2, maxp.y) do
local vi = a:index(x, yy, z)
data[vi] = second_layer
end
end
if y-sl-2>=minp.y then
for yy = minp.y, math.min(y-sl-2, maxp.y) do
local vi = a:index(x, yy, z)
data[vi] = c_stone
end
end
end
end
local va = VoxelArea:new{MinEdge=minp, MaxEdge=maxp}
--[[
local og_start = os.clock()
for _, ore_sheet in ipairs(mg.registered_ore_sheets) do
local sidelen = maxp.x - minp.x + 1
local np = copytable(ore_sheet.noise_params)
np.seed = np.seed + minp.y
local pm = minetest.get_perlin_map(np, {x=sidelen, y=sidelen, z=1})
local map = pm:get2dMap_flat({x = minp.x, y = minp.z})
local ni = 0
local trh = ore_sheet.threshhold
local wherein = minetest.get_content_id(ore_sheet.wherein)
local ore = minetest.get_content_id(ore_sheet.name)
local hmin = ore_sheet.height_min
local hmax = ore_sheet.height_max
local tmin = ore_sheet.tmin
local tmax = ore_sheet.tmax
for z = minp.z, maxp.z do
for x = minp.x, maxp.x do
ni = ni+1
local noise = map[ni]
if noise > trh then
local thickness = pr:next(tmin, tmax)
local y0 = math.floor(minp.y + (noise-trh)*4)
for y = math.max(y0, hmin), math.min(y0+thickness-1, hmax) do
local vi = a:index(x, y, z)
if data[vi] == wherein or wherein == c_ignore then
data[vi] = ore
end
end
end
end
end
end
local og_end = os.clock()
--print("Ore gen: " .. tostring(og_end - og_start))
]]
for _, ore in ipairs(mg.registered_ores) do
local o_start = os.clock()
generate_vein(minetest.get_content_id(ore.name), minetest.get_content_id(ore.wherein), minp, maxp, ore.seeddiff, ore, data, a, va)
local o_end = os.clock()
--print("Ore "..ore.name.. " gen: "..tostring(o_end-o_start))
end
local to_add = generate_village(vx, vz, vs, vh, minp, maxp, data, a, village_noise, villages_to_grow)
vm:set_data(data)
vm:calc_lighting(
{x=minp.x-16, y=minp.y, z=minp.z-16},
{x=maxp.x+16, y=maxp.y, z=maxp.z+16}
)
local ul = os.clock()
vm:update_liquids()
local eul = os.clock()
--print("Update liquids: "..tostring(eul-ul))
vm:write_to_map(data)
quests.treasure.on_generated(minp,maxp,emin,emax,vm)
local meta
for _, n in pairs(to_add) do
if n.node.name ~= "air" then
minetest.set_node(n.pos, n.node)
if n.meta ~= nil then
meta = minetest.get_meta(n.pos)
meta:from_table(n.meta)
if n.node.name == "default:chest" or n.node.name == "default:npc_chest" then
local inv = meta:get_inventory()
local items = inv:get_list("main")
for i=1, inv:get_size("main") do
inv:set_stack("main", i, ItemStack(""))
end
local numitems = pr:next(3, 20)
for i=1,numitems do
local ii = pr:next(1, #items)
local prob = items[ii]:get_count() % 2 ^ 8
local stacksz = math.floor(items[ii]:get_count() / 2 ^ 8)
if pr:next(0, prob) == 0 and stacksz>0 then
stk = ItemStack({name=items[ii]:get_name(), count=pr:next(1, stacksz), wear=items[ii]:get_count(), metadata=items[ii]:get_metadata()})
local ind = pr:next(1, inv:get_size("main"))
while not inv:get_stack("main",ind):is_empty() do
ind = pr:next(1, inv:get_size("main"))
end
inv:set_stack("main", ind, stk)
end
end
-- insert some experience into every chest, this gives incentive to explore villages
local cexp = math.random(0,25)
local expitem = experience.exp_to_items(cexp)
for _,e in pairs(expitem) do
inv:add_item("main",e)
end
end
end
if n.node.name == "mobs:spawner" then
meta = minetest.get_meta(n.pos)
meta:set_string("entity",n.mob)
meta:set_int("active_objects",2)
meta:set_int("active_objects_wider",7)
meta:set_string("infotext",n.mob)
end
end
end
local b_end = os.clock()
--print((tostring(b_end - b_start)))
end
minetest.register_on_generated(function(minp, maxp, seed)
local vm, emin, emax = minetest.get_mapgen_object("voxelmanip")
mg_generate(minp, maxp, emin, emax, vm)
end)
function mg_regenerate(pos, name)
local minp = {x = 80*math.floor((pos.x+32)/80)-32,
y = 80*math.floor((pos.y+32)/80)-32,
z = 80*math.floor((pos.z+32)/80)-32}
local maxp = {x = minp.x+79, y = minp.y+79, z = minp.z+79}
local vm = minetest.get_voxel_manip()
local emin, emax = vm:read_from_map(minp, maxp)
local data = {}
for i = 1, (maxp.x-minp.x+1)*(maxp.y-minp.y+1)*(maxp.z-minp.z+1) do
data[i] = c_air
end
vm:set_data(data)
vm:write_to_map()
mg_generate(minp, maxp, emin, emax, vm)
if name ~= nil then
minetest.chat_send_player(name, "Regenerating done, fixing lighting. This may take a while...")
end
-- Fix lighting
local nodes = minetest.find_nodes_in_area(minp, maxp, "air")
local nnodes = #nodes
local p = math.floor(nnodes/5)
local dig_node = minetest.dig_node
for _, pos in ipairs(nodes) do
dig_node(pos)
if _%p == 0 then
if name ~= nil then
minetest.chat_send_player(name, math.floor(_/nnodes*100).."%")
end
end
end
if name ~= nil then
minetest.chat_send_player(name, "Done")
end
end
minetest.register_chatcommand("mg_regenerate", {
privs = {server = true},
func = function(name, param)
local player = minetest.get_player_by_name(name)
if player then
local pos = player:getpos()
mg_regenerate(pos, name)
end
end,
})
mg.registered_ores = {}
function mg.register_ore(oredef)
if oredef.wherein == nil then
oredef.wherein = "ignore"
end
if DEBUG then
oredef.wherein = "ignore"
oredef.maxheight = 31000
end
mg.registered_ores[#mg.registered_ores+1] = oredef
end
mg.registered_ore_sheets = {}
function mg.register_ore_sheet(oredef)
if oredef.wherein == nil then
oredef.wherein = "ignore"
end
if DEBUG then
oredef.wherein = "ignore"
oredef.height_max = 31000
end
mg.registered_ore_sheets[#mg.registered_ore_sheets+1] = oredef
end
mg.registered_trees = {}
function mg.register_tree(treedef)
if treedef.min_humidity == nil then
treedef.min_humidity = -2
end
if treedef.max_humidity == nil then
treedef.max_humidity = 2
end
if treedef.min_biome_humidity == nil then
treedef.min_biome_humidity = -2
end
if treedef.max_biome_humidity == nil then
treedef.max_biome_humidity = 2
end
if treedef.min_temperature == nil then
treedef.min_temperature = -2
end
if treedef.max_temperature == nil then
treedef.max_temperature = 2
end
if treedef.min_biome_temperature == nil then
treedef.min_biome_temperature = -2
end
if treedef.max_biome_temperature == nil then
treedef.max_biome_temperature = 2
end
mg.registered_trees[#mg.registered_trees+1] = treedef
end
dofile(minetest.get_modpath(minetest.get_current_modname()).."/oredesc.lua")
dofile(minetest.get_modpath(minetest.get_current_modname()).."/trees.lua")
if ENABLE_SNOW then
dofile(minetest.get_modpath(minetest.get_current_modname()).."/snow.lua")
end
minetest.register_on_mapgen_init(function(params)
minetest.set_mapgen_params({
mgname = "v7"
})
end)

View File

@ -61,6 +61,23 @@ minetest.register_node("mg:savannasapling", {
sounds = default.node_sound_leaves_defaults(),
})
minetest.register_node("mg:savannasapling_ongen", {
description = "Savannawood Sapling",
drawtype = "plantlike",
visual_scale = 1.0,
tiles = {"mg_dry_sapling.png"},
drop = {
max_items = 1,
items = {
{
items = {'mg:savannasapling'},
},
}
},
})
minetest.register_abm({
nodenames = {"mg:savannasapling"},
interval = 10,
@ -77,6 +94,22 @@ minetest.register_abm({
end
})
minetest.register_abm({
nodenames = {"mg:savannasapling_ongen"},
interval = 1,
chance = 1,
action = function(pos, node)
local vm = minetest.get_voxel_manip()
local minp, maxp = vm:read_from_map({x=pos.x-10, y=pos.y, z=pos.z-10}, {x=pos.x+10, y=pos.y+20, z=pos.z+10})
local a = VoxelArea:new{MinEdge=minp, MaxEdge=maxp}
local data = vm:get_data()
add_savannatree(data, a, pos.x, pos.y, pos.z, minp, maxp, PseudoRandom(math.random(1,100000)))
vm:set_data(data)
vm:write_to_map(data)
vm:update_map()
end
})
minetest.register_node("mg:dirt_with_dry_grass", {
description = "Dry Grass",
tiles = {"mg_dry_grass.png", "default_dirt.png", "default_dirt.png^mg_dry_grass_side.png"},
@ -209,5 +242,7 @@ minetest.register_node("mg:ignore", {
description = "MG Ignore",
drawtype = "airlike",
sunlight_propagates = true,
walkable=false,
pointable=false,
groups = {snappy=2,not_in_creative_inventory=1},
})

View File

@ -1,333 +0,0 @@
mg.register_ore({
name = "default:stone_with_iron",
wherein = "default:stone",
seeddiff = 2,
maxhdistance = 15,
maxvdistance = 6,
maxheight = -22,
seglenghtn = 14,
seglenghtdev = 6,
segincln = 0.45,
segincldev = 0.6,
turnangle = 37,
forkturnangle = 82,
numperblock = 12,
numbranchesn = 1,
numbranchesdev = 0,
mothersizen = -1,
mothersizedev = 0,
sizen = 100,
sizedev = 20,
radius = 2,
})
mg.register_ore({
name = "default:stone_with_coal",
wherein = "default:stone",
seeddiff = 1,
maxvdistance = 10,
sizen = 54,
sizedev = 27,
maxheight = 64,
seglenghtn = 15,
seglenghtdev = 6,
segincln = 0,
segincldev = 0.36,
turnangle = 57,
forkturnangle = 57,
radius = 1,
numperblock = 6
})
mg.register_ore({
name = "glowcrystals:glowcrystal_ore",
wherein = "default:stone",
seeddiff = 9,
maxvdistance = 10.5,
maxheight = -25,
seglenghtn = 15,
seglenghtdev = 6,
segincln = 0,
segincldev = 0.6,
turnangle = 57,
forkturnangle = 57,
numperblock = 2.5
})
mg.register_ore({
name = "default:stone_with_mese",
wherein = "default:stone",
seeddiff = 2,
maxvdistance = 50,
sizen = 7,
sizedev = 3,
maxheight = -128,
seglenghtn = 2,
seglenghtdev = 1,
segincln = 4,
segincldev = 1,
turnangle = 57,
forkturnangle = 57,
numperblock = 0.8,
numbranchesn = 2,
numbranchesdev = 1,
fork_chance = 0.1,
mothersizen = 0,
mothersizedev = 0
})
mg.register_ore({
name = "default:mese",
wherein = "default:stone",
seeddiff = 3,
maxvdistance = 50,
sizen = 3,
sizedev = 1,
maxheight = -1024,
seglenghtn = 2,
seglenghtdev = 1,
segincln = 4,
segincldev = 1,
turnangle = 57,
forkturnangle = 57,
numbranchesn = 2,
numbranchesdev = 1,
fork_chance = 0.1,
radius = 1
})
mg.register_ore({ -- Same parameters exactly as the previous one so it spawns inside
name = "default:lava_source",
wherein = "default:mese",
seeddiff = 3,
maxvdistance = 50,
sizen = 3,
sizedev = 1,
maxheight = -1024,
seglenghtn = 2,
seglenghtdev = 1,
segincln = 4,
segincldev = 1,
turnangle = 57,
forkturnangle = 57,
numbranchesn = 2,
numbranchesdev = 1,
fork_chance = 0.1,
mothersizen = 0,
mothersizedev = 0
})
mg.register_ore({
name = "default:stone_with_copper",
wherein = "default:stone",
seeddiff = 4,
maxvdistance = 10.5,
maxheight = -16,
seglenghtn = 15,
seglenghtdev = 6,
segincln = 0,
segincldev = 0.6,
turnangle = 57,
forkturnangle = 57,
numperblock = 2
})
mg.register_ore({
name = "default:stone_with_diamond",
wherein = "default:stone",
seeddiff = 5,
maxvdistance = 50,
sizen = 7,
sizedev = 3,
maxheight = -1024,
seglenghtn = 2,
seglenghtdev = 1,
segincln = 4,
segincldev = 1,
turnangle = 57,
forkturnangle = 57,
numperblock = 1.8,
numbranchesn = 2,
numbranchesdev = 1,
fork_chance = 0.1,
mothersizen = 0,
mothersizedev = 0
})
mg.register_ore({
name = "default:stone_with_diamond",
wherein = "default:stone",
seeddiff = 5,
maxvdistance = 50,
sizen = 7,
sizedev = 3,
maxheight = -256,
seglenghtn = 2,
seglenghtdev = 1,
segincln = 4,
segincldev = 1,
turnangle = 57,
forkturnangle = 57,
numperblock = 0.8,
numbranchesn = 2,
numbranchesdev = 1,
fork_chance = 0.1,
mothersizen = 0,
mothersizedev = 0
})
mg.register_ore({
name = "default:stone_with_gold",
wherein = "default:stone",
seeddiff = 17,
maxvdistance = 10,
sizen = 30,
sizedev = 8,
maxheight = -256,
seglenghtn = 8,
seglenghtdev = 4,
segincln = 0.6,
segincldev = 0.4,
turnangle = 57,
forkturnangle = 57,
numbranchesn = 2,
numbranchesdev = 1,
fork_chance = 0.1,
radius = 1
})
mg.register_ore({
name = "default:clay",
wherein = "default:dirt",
seeddiff = 6,
maxvdistance = 10.5,
maxheight = 0,
minheight = -50,
sizen = 50,
sizedev = 20,
seglenghtn = 15,
seglenghtdev = 6,
segincln = 0,
segincldev = 0.6,
turnangle = 57,
forkturnangle = 57,
numperblock = 1,
radius = 1.5
})
mg.register_ore({
name = "default:lava_source",
seeddiff = 7,
maxhdistance = 20,
maxvdistance = 70,
maxheight = -100,
seglenghtn = 2,
seglenghtdev = 1,
segincln = -5,
segincldev = 2,
turnangle = 57,
forkturnangle = 57,
numperblock = 1,
numbranchesn = 2,
numbranchesdev = 1,
mothersizen = 5,
mothersizedev = 3,
sizen = 8,
sizedev = 2,
radius = 2.3
})
mg.register_ore({
name = "air",
wherein = "default:stone",
seeddiff = 1236,
maxhdistance = 40,
maxvdistance = 40,
maxheight = -3,
seglenghtn = 15,
seglenghtdev = 6,
segincln = 0.2,
segincldev = 0.9,
turnangle = 66,
forkturnangle = 71,
numperblock = 5,
numbranchesn = 2,
numbranchesdev = 0,
mothersizen = -1,
mothersizedev = 0,
sizen = 100,
sizedev = 20,
radius = 3
})
mg.register_ore({
name = "air",
wherein = "default:dirt",
seeddiff = 1236,
maxhdistance = 40,
maxvdistance = 40,
maxheight = -3,
seglenghtn = 15,
seglenghtdev = 6,
segincln = 0.2,
segincldev = 0.9,
turnangle = 66,
forkturnangle = 71,
numperblock = 5,
numbranchesn = 2,
numbranchesdev = 0,
mothersizen = -1,
mothersizedev = 0,
sizen = 100,
sizedev = 20,
radius = 3
})
mg.register_ore({
name = "air",
wherein = "default:sand",
seeddiff = 1236,
maxhdistance = 40,
maxvdistance = 40,
maxheight = -3,
seglenghtn = 15,
seglenghtdev = 6,
segincln = 0.2,
segincldev = 0.9,
turnangle = 66,
forkturnangle = 71,
numperblock = 5,
numbranchesn = 2,
numbranchesdev = 0,
mothersizen = -1,
mothersizedev = 0,
sizen = 100,
sizedev = 20,
radius = 3
})
mg.register_ore({
name = "air",
wherein = "default:sandstone",
seeddiff = 1236,
maxhdistance = 40,
maxvdistance = 40,
maxheight = -3,
seglenghtn = 15,
seglenghtdev = 6,
segincln = 0.2,
segincldev = 0.9,
turnangle = 66,
forkturnangle = 71,
numperblock = 5,
numbranchesn = 2,
numbranchesdev = 0,
mothersizen = -1,
mothersizedev = 0,
sizen = 100,
sizedev = 20,
radius = 3
})

View File

@ -1,166 +0,0 @@
function in_cuboid(pos, minp, maxp)
return (pos.x>=minp.x and pos.y>= minp.y and pos.z>=minp.z and pos.x<=maxp.x and pos.y<=maxp.y and pos.z<=maxp.z)
end
function round_pos(p)
return {x=math.floor(p.x+0.5),y=math.floor(p.y+0.5),z=math.floor(p.z+0.5)}
end
function draw_sphere(name, wherein, center, radius, data, a, insideva)
local rad2=radius*radius
local pr_rv = PseudoRandom(center.x+center.y+center.z)
local variant = pr_rv:next(-4,2)
if math.ceil(radius)+variant > 0 then
radius = math.ceil(radius)+variant
end
local pos0={}
for x=-radius, radius do
pos0.x=center.x+x
for y=-radius, radius do
pos0.y=center.y+y
for z=-radius, radius do
pos0.z=center.z+z
if x*x+y*y+z*z<=rad2 and insideva:containsp(pos0) and data[a:indexp(pos0)] ~= c_water and ((wherein == c_ignore and data[a:indexp(pos0)] ~= c_water) or data[a:indexp(pos0)] == wherein) then
data[a:indexp(pos0)] = name
end
end
end
end
end
function place_segment(name, wherein, pos1, pos2, minp, maxp, radius, data, a, insideva)
local d={x=pos2.x-pos1.x, y=pos2.y-pos1.y, z=pos2.z-pos1.z}
local N=math.max(math.abs(d.x),math.abs(d.y),math.abs(d.z))
local s={x=d.x/N,y=d.y/N,z=d.z/N}
local p=pos1
draw_sphere(name,wherein,pos1,radius, data, a, insideva)
for i=1,N do
p={x=p.x+s.x,y=p.y+s.y,z=p.z+s.z}
local p0=round_pos(p)
if not in_cuboid(p0, minp, maxp) then return end
draw_sphere(name,wherein,p0,radius, data, a, insideva)
end
end
function generate_vein_segment(name, wherein, minp, maxp, pr, pos, angle, rem_size, options, data, a, insideva)
if rem_size<=0 then return end
local Ln=options.seglenghtn
local Ldev=options.seglenghtdev
local L=pr:next(Ln-Ldev,Ln+Ldev)
local incln=options.segincln*100
local incldev=options.segincldev*100
local incl=pr:next(incln-incldev,incln+incldev)/100
local turnangle=options.turnangle
local forkturnangle=options.forkturnangle
local fork_chance=options.fork_chance*100
local forkmultn=options.forkmultn*100
local forkmultdev=options.forkmultdev*100
local radius=options.radius
local end_pos={x=pos.x+L*math.cos(angle), y=pos.y-L*incl, z=pos.z+L*math.sin(angle)}
place_segment(name, wherein, round_pos(pos), round_pos(end_pos), minp, maxp, radius, data, a, insideva)
if not in_cuboid(end_pos, minp, maxp) then return end
local new_angle=(math.pi*pr:next(-turnangle,turnangle)/180)+angle
generate_vein_segment(name, wherein, minp, maxp, pr, end_pos, new_angle, rem_size-L, options, data, a, insideva)
local numforks=math.floor(fork_chance/100)+1
fork_chance=fork_chance/numforks
if pr:next(1,100)<=fork_chance then
for f=1,numforks do
local new_angle=(math.pi*pr:next(-forkturnangle,forkturnangle)/180)+angle
local forkmult=pr:next(forkmultn-forkmultdev, forkmultn+forkmultdev)/100
if forkmult>1 then forkmult=1 end
generate_vein_segment(name, wherein, minp, maxp, pr, end_pos, new_angle, forkmult*(rem_size-L), options, data, a, insideva)
end
end
end
function generate_vein(name, wherein, minp, maxp, seeddiff, options, data, a, insideva, second_call)
local seed = get_bseed2(minp)+seeddiff
options=get_or_default(options)
local numperblock=options.numperblock*1000
local maxhdistance=options.maxhdistance
local maxvdistance=options.maxvdistance
local numbranchesn=options.numbranchesn
local numbranchesdev=options.numbranchesdev
local mothersizen=options.mothersizen*10
local mothersizedev=options.mothersizedev*10
local sizen=options.sizen
local sizedev=options.sizedev
if second_call==nil then
local hblocks=math.floor(maxhdistance/80)+1
local vblocks=math.floor(maxvdistance/80)+1
for xblocksdiff=-hblocks,hblocks do
for yblocksdiff=-vblocks,vblocks do
for zblocksdiff=-hblocks,hblocks do
if xblocksdiff~=0 or yblocksdiff~=0 or zblocksdiff~=0 then
local new_minp={x=minp.x+xblocksdiff*80,y=minp.y+yblocksdiff*80,z=minp.z+zblocksdiff*80}
local new_maxp={x=maxp.x+xblocksdiff*80,y=maxp.y+yblocksdiff*80,z=maxp.z+zblocksdiff*80}
generate_vein(name, wherein, new_minp, new_maxp, seeddiff, options, data, a, insideva, true)
end
end
end
end
end
local pr = PseudoRandom(seed)
local numveins=math.floor(numperblock/1000)
numperblock=numperblock-1000*numveins
if pr:next(1,1000)<=numperblock then
numveins=numveins+1
end
if numveins>0 then
local min_y=math.max(options.minheight,minp.y)
local max_y=math.min(options.maxheight,maxp.y)
if min_y>max_y then return end
for v=1,numveins do
local vein_pos={x=pr:next(minp.x,maxp.x),y=pr:next(min_y,max_y),z=pr:next(minp.z,maxp.z)}
local numbranches=pr:next(numbranchesn-numbranchesdev,numbranchesn+numbranchesdev)
local mothersize=pr:next(mothersizen-mothersizedev,mothersizen+mothersizedev)/10
if mothersize>=0 then
draw_sphere(name, wherein,vein_pos, mothersize, data, a, insideva)
end
local minpos = {x=vein_pos.x-maxhdistance,y=vein_pos.y-maxvdistance,z=vein_pos.z-maxhdistance}
local maxpos = {x=vein_pos.x+maxhdistance,y=vein_pos.y+maxvdistance,z=vein_pos.z+maxhdistance}
for i=1,numbranches do
local start_angle=math.pi*pr:next(0,359)/180
local size=pr:next(sizen-sizedev,sizen+sizedev)
generate_vein_segment(name, wherein, minpos, maxpos, pr, vein_pos, start_angle, size, options, data, a, insideva)
end
end
end
end
function get_or_default(options)
if options.numperblock==nil then options.numperblock=0.3 end
if options.maxhdistance==nil then options.maxhdistance=32 end
if options.maxvdistance==nil then options.maxvdistance=32 end
if options.numbranchesn==nil then options.numbranchesn=3 end
if options.numbranchesdev==nil then options.numbranchesdev=2 end
if options.mothersizen==nil then options.mothersizen=1 end
if options.mothersizedev==nil then options.mothersizedev=0.5 end
if options.sizen==nil then options.sizen=120 end
if options.sizedev==nil then options.sizedev=60 end
if options.seglenghtn==nil then options.seglenghtn=6 end
if options.seglenghtdev==nil then options.seglenghtdev=2 end
if options.segincln==nil then options.segincln=0.2 end
if options.segincldev==nil then options.segincldev=0.1 end
if options.turnangle==nil then options.turnangle=20 end
if options.forkturnangle==nil then options.forkturnangle=90 end
if options.fork_chance==nil then options.fork_chance=0.2 end
if options.forkmultn==nil then options.forkmultn=0.75 end
if options.forkmultdev==nil then options.forkmultdev=0.25 end
if options.minheight==nil then options.minheight=-31000 end
if options.maxheight==nil then options.maxheight=31000 end
if options.radius==nil then options.radius=0 end
return options
end

View File

@ -1,58 +0,0 @@
function deepcopy(orig)
return minetest.deserialize(minetest.serialize(orig))
end
function rotate_facedir(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
function rotate_wallmounted(wallmounted)
return ({0, 1, 5, 4, 2, 3})[wallmounted+1]
end
function rotate_scm(scm)
local ysize = #scm
local xsize = #scm[1]
local zsize = #scm[1][1]
local new_scm = {}
for i=1, ysize do
new_scm[i] = {}
for j=1, zsize do
new_scm[i][j] = {}
end
end
for y = 1, ysize do
for x = 1, xsize do
for z = 1, zsize do
local old = scm[y][x][z]
local newx = z
local newz = xsize-x+1
if type(old) ~= "table" or old.rotation == nil then
new_scm[y][newx][newz] = old
elseif old.rotation == "wallmounted" then
local new = deepcopy(old)
new.node.param2 = rotate_wallmounted(new.node.param2)
new_scm[y][newx][newz] = new
elseif old.rotation == "facedir" then
local new = deepcopy(old)
new.node.param2 = rotate_facedir(new.node.param2)
new_scm[y][newx][newz] = new
end
end
end
end
return new_scm
end
function rotate(scm, times)
for i=1, times do
scm = rotate_scm(scm)
end
return scm
end

BIN
mods/mg/schematics/apple_tree.mts Executable file

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,475 +0,0 @@
2 1 3 stairs:slab_wood 157 0
2 1 4 stairs:slab_wood 173 0
2 1 5 stairs:slab_wood 157 0
2 1 6 stairs:slab_wood 173 0
2 1 7 stairs:slab_wood 157 0
3 1 2 stairs:slab_wood 125 0
3 1 3 default:cobble 0 0
3 1 4 cottages:loam 0 0
3 1 5 cottages:loam 0 0
3 1 6 cottages:loam 0 0
3 1 7 default:cobble 0 0
3 1 8 stairs:slab_cobble 157 0
3 1 9 stairs:slab_cobble 157 0
3 1 10 stairs:slab_cobble 173 0
3 1 11 default:cobble 0 0
3 2 3 default:tree 0 0
3 2 4 default:fence_wood 205 0
3 2 6 default:fence_wood 205 0
3 2 7 default:tree 0 0
3 2 11 default:tree 0 0
3 3 3 default:tree 0 0
3 3 4 default:torch 0 1
3 3 6 default:torch 0 1
3 3 7 default:tree 0 0
3 3 11 default:torch 15 1
3 4 2 cottages:roof_flat_straw 173 1
3 4 3 cottages:roof_flat_straw 189 1
3 4 4 cottages:roof_flat_straw 205 1
3 4 5 cottages:roof_flat_straw 189 1
3 4 6 cottages:roof_flat_straw 205 1
3 4 7 cottages:roof_flat_straw 189 1
3 4 8 cottages:roof_flat_straw 173 1
4 1 2 stairs:slab_wood 141 0
4 1 3 cottages:loam 0 0
4 1 4 cottages:loam 0 0
4 1 5 cottages:loam 0 0
4 1 6 cottages:loam 0 0
4 1 7 default:cobble 0 0
4 1 8 default:cobble 0 0
4 1 9 default:cobble 0 0
4 1 10 default:cobble 0 0
4 1 11 default:cobble 0 0
4 1 12 stairs:slab_cobble 157 0
4 4 2 cottages:roof_flat_straw 157 3
4 4 3 cottages:roof_flat_straw 173 3
4 4 4 cottages:roof_flat_straw 189 3
4 4 5 cottages:roof_flat_straw 173 3
4 4 6 cottages:roof_flat_straw 189 3
4 4 7 cottages:roof_flat_straw 173 3
4 4 8 cottages:roof_flat_straw 189 3
5 1 2 stairs:slab_wood 124 0
5 1 3 cottages:loam 0 0
5 1 4 cottages:loam 0 0
5 1 5 cottages:loam 0 0
5 1 6 cottages:loam 0 0
5 1 7 default:cobble 0 0
5 1 8 default:cobble 0 0
5 1 9 default:cobble 0 0
5 1 10 default:cobble 0 0
5 1 11 default:cobble 0 0
5 1 12 stairs:slab_cobble 140 0
5 2 7 cottages:bench 188 1
5 2 8 cottages:bench 205 1
5 2 10 cottages:bench 173 1
5 2 11 cottages:bench 173 1
5 3 1 cottages:roof_straw 141 1
5 3 8 default:torch 0 2
5 3 10 cottages:roof_straw 189 1
5 3 11 cottages:roof_straw 189 1
5 3 12 cottages:roof_straw 173 1
5 3 13 cottages:roof_straw 157 1
5 4 2 cottages:roof_flat_straw 141 1
5 4 3 cottages:roof_flat_straw 157 1
5 4 4 cottages:roof_flat_straw 173 1
5 4 5 cottages:roof_flat_straw 157 1
5 4 6 cottages:roof_flat_straw 173 1
5 4 7 cottages:roof_flat_straw 189 1
5 4 8 cottages:roof_flat_straw 205 1
6 1 2 stairs:slab_wood 108 0
6 1 3 default:cobble 0 0
6 1 4 cottages:loam 0 0
6 1 5 cottages:loam 0 0
6 1 6 cottages:loam 0 0
6 1 7 default:cobble 0 0
6 1 8 default:cobble 0 0
6 1 9 default:cobble 0 0
6 1 10 default:cobble 0 0
6 1 11 default:cobble 0 0
6 1 12 stairs:slab_cobble 124 0
6 2 3 default:tree 0 0
6 2 4 default:fence_wood 155 0
6 2 6 default:fence_wood 171 0
6 2 7 default:tree 0 0
6 2 8 default:wood 0 0
6 2 9 doors:door_wood_b_1 173 1
6 2 10 default:wood 0 0
6 2 11 default:tree 0 0
6 3 3 default:tree 0 0
6 3 4 default:fence_wood 172 0
6 3 6 default:fence_wood 188 0
6 3 7 default:tree 0 0
6 3 8 default:wood 0 0
6 3 9 doors:door_wood_t_1 189 1
6 3 10 default:wood 0 0
6 3 11 default:tree 0 0
6 4 1 cottages:roof_straw 109 1
6 4 2 cottages:roof_connector_straw 125 1
6 4 3 cottages:roof_straw 141 1
6 4 4 cottages:roof_straw 157 1
6 4 5 cottages:roof_straw 157 1
6 4 6 cottages:roof_straw 173 1
6 4 7 cottages:roof_straw 189 1
6 4 8 cottages:roof_connector_straw 189 1
6 4 9 cottages:roof_straw 173 1
6 4 10 cottages:roof_straw 157 1
6 4 11 cottages:roof_straw 157 1
6 4 12 cottages:roof_connector_straw 141 1
6 4 13 cottages:roof_straw 125 1
7 1 2 default:cobble 0 0
7 1 3 cottages:loam 0 0
7 1 4 cottages:loam 0 0
7 1 5 cottages:loam 0 0
7 1 6 cottages:loam 0 0
7 1 7 cottages:loam 0 0
7 1 8 default:cobble 0 0
7 1 9 default:cobble 0 0
7 1 10 default:cobble 0 0
7 1 11 default:cobble 0 0
7 1 12 default:cobble 0 0
7 2 2 default:wood 0 0
7 2 7 default:chest 0 0
7 2 8 default:wood 0 0
7 2 10 cottages:bench 187 3
7 2 11 cottages:bench 204 3
7 2 12 default:wood 0 0
7 3 2 default:wood 0 0
7 3 7 default:torch 0 4
7 3 8 default:wood 0 0
7 3 11 default:torch 0 4
7 3 12 default:wood 0 0
7 4 2 default:wood 0 0
7 4 8 default:wood 0 0
7 4 9 default:wood 0 0
7 4 10 default:wood 0 0
7 4 11 default:wood 0 0
7 4 12 default:wood 0 0
7 5 1 cottages:roof_straw 93 1
7 5 2 cottages:roof_connector_straw 109 1
7 5 3 cottages:roof_straw 125 1
7 5 4 cottages:roof_straw 141 1
7 5 5 cottages:roof_straw 157 1
7 5 6 cottages:roof_straw 173 1
7 5 7 cottages:roof_straw 189 1
7 5 8 cottages:roof_connector_straw 173 1
7 5 9 cottages:roof_straw 157 1
7 5 10 cottages:roof_straw 141 1
7 5 11 cottages:roof_straw 125 1
7 5 12 cottages:roof_connector_straw 109 1
7 5 13 cottages:roof_straw 125 1
8 1 2 default:cobble 0 0
8 1 3 cottages:loam 0 0
8 1 4 cottages:loam 0 0
8 1 5 cottages:loam 0 0
8 1 6 cottages:loam 0 0
8 1 7 cottages:loam 0 0
8 1 8 default:cobble 0 0
8 1 9 default:cobble 0 0
8 1 10 default:cobble 0 0
8 1 11 default:cobble 0 0
8 1 12 default:cobble 0 0
8 2 2 default:wood 0 0
8 2 5 default:ladder 153 2
8 2 8 doors:door_wood_b_1 170 0
8 2 11 default:furnace 0 0
8 2 12 default:wood 0 0
8 3 2 default:fence_wood 124 0
8 3 5 default:ladder 170 2
8 3 8 doors:door_wood_t_1 187 0
8 3 12 default:glass 188 0
8 4 2 default:wood 0 0
8 4 8 default:wood 0 0
8 4 9 default:wood 0 0
8 4 10 default:wood 0 0
8 4 11 default:wood 0 0
8 4 12 default:wood 0 0
8 5 2 default:wood 0 0
8 5 3 default:fence_wood 108 0
8 5 4 default:fence_wood 124 0
8 5 6 default:fence_wood 156 0
8 5 7 default:fence_wood 172 0
8 5 8 default:wood 0 0
8 5 9 default:chest 0 2
8 5 11 cottages:bed_head 140 3
8 5 12 default:wood 0 0
8 6 1 cottages:roof_straw 61 1
8 6 2 cottages:roof_connector_straw 77 1
8 6 3 cottages:roof_straw 93 1
8 6 4 cottages:roof_straw 109 1
8 6 5 cottages:roof_straw 125 1
8 6 6 cottages:roof_straw 141 1
8 6 7 cottages:roof_straw 157 1
8 6 8 cottages:roof_straw 173 1
8 6 9 cottages:roof_straw 189 1
8 6 10 cottages:roof_straw 173 1
8 6 11 cottages:roof_straw 157 1
8 6 12 cottages:roof_connector_straw 141 1
8 6 13 cottages:roof_straw 125 1
9 1 2 default:cobble 0 0
9 1 3 cottages:loam 0 0
9 1 4 cottages:loam 0 0
9 1 5 cottages:loam 0 0
9 1 6 cottages:loam 0 0
9 1 7 cottages:loam 0 0
9 1 8 default:cobble 0 0
9 1 9 default:cobble 0 0
9 1 10 default:cobble 0 0
9 1 11 default:cobble 0 0
9 1 12 default:cobble 0 0
9 2 2 default:wood 0 0
9 2 3 default:fence_wood 105 0
9 2 4 default:fence_wood 120 0
9 2 5 default:fence_wood 136 0
9 2 6 default:fence_wood 153 0
9 2 7 default:fence_wood 170 0
9 2 8 default:wood 0 0
9 2 9 default:ladder 170 5
9 2 10 cottages:bench 187 1
9 2 11 cottages:bench 204 1
9 2 12 default:wood 0 0
9 3 2 default:wood 0 0
9 3 3 default:fence_wood 122 0
9 3 5 default:fence_wood 153 0
9 3 7 default:fence_wood 187 0
9 3 8 default:wood 0 0
9 3 9 default:ladder 187 5
9 3 11 default:torch 0 4
9 3 12 default:wood 0 0
9 4 2 default:wood 0 0
9 4 3 stairs:slab_wood 106 0
9 4 4 stairs:slab_wood 122 0
9 4 6 stairs:slab_wood 154 0
9 4 7 stairs:slab_wood 170 0
9 4 8 default:wood 0 0
9 4 9 default:ladder 170 5
9 4 10 default:wood 0 0
9 4 11 default:wood 0 0
9 4 12 default:wood 0 0
9 5 2 default:wood 0 0
9 5 8 default:wood 0 0
9 5 9 default:ladder 187 5
9 5 11 cottages:bed_foot 155 3
9 5 12 default:wood 0 0
9 6 2 default:wood 0 0
9 6 8 default:wood 0 0
9 6 9 default:ladder 204 5
9 6 12 default:wood 0 0
9 7 1 cottages:roof_straw 61 1
9 7 2 cottages:roof_connector_straw 77 1
9 7 3 cottages:roof_straw 93 1
9 7 4 cottages:roof_straw 109 1
9 7 5 cottages:roof_straw 125 1
9 7 6 cottages:roof_straw 141 1
9 7 7 cottages:roof_straw 157 1
9 7 8 cottages:roof_straw 173 1
9 7 9 cottages:roof_straw 189 1
9 7 10 cottages:roof_straw 173 1
9 7 11 cottages:roof_straw 157 1
9 7 12 cottages:roof_connector_straw 141 1
9 7 13 cottages:roof_straw 125 1
10 1 2 default:cobble 0 0
10 1 3 cottages:loam 0 0
10 1 4 cottages:loam 0 0
10 1 5 cottages:loam 0 0
10 1 6 cottages:loam 0 0
10 1 7 cottages:loam 0 0
10 1 8 default:cobble 0 0
10 1 9 default:cobble 0 0
10 1 10 default:cobble 0 0
10 1 11 default:cobble 0 0
10 1 12 default:cobble 0 0
10 2 2 default:wood 0 0
10 2 5 default:fence_wood 120 0
10 2 8 default:wood 0 0
10 2 9 doors:door_wood_b_1 170 1
10 2 10 default:wood 0 0
10 2 11 default:wood 0 0
10 2 12 default:wood 0 0
10 3 2 default:wood 0 0
10 3 5 default:fence_wood 137 0
10 3 8 default:wood 0 0
10 3 9 doors:door_wood_t_1 187 1
10 3 10 default:wood 0 0
10 3 11 default:wood 0 0
10 3 12 default:wood 0 0
10 4 2 default:wood 0 0
10 4 3 stairs:slab_wood 90 0
10 4 4 stairs:slab_wood 106 0
10 4 5 stairs:slab_wood 122 0
10 4 6 stairs:slab_wood 138 0
10 4 7 stairs:slab_wood 154 0
10 4 8 default:wood 0 0
10 4 9 default:wood 0 0
10 4 10 default:wood 0 0
10 4 11 default:wood 0 0
10 4 12 default:wood 0 0
10 5 2 default:fence_wood 60 0
10 5 8 default:wood 0 0
10 5 12 default:wood 0 0
10 6 2 default:wood 0 0
10 6 8 default:wood 0 0
10 6 9 default:torch 0 5
10 6 12 default:fence_wood 172 0
10 7 1 cottages:roof_straw 77 3
10 7 2 cottages:roof_connector_straw 93 3
10 7 3 cottages:roof_straw 109 3
10 7 4 cottages:roof_straw 125 3
10 7 5 cottages:roof_straw 141 3
10 7 6 cottages:roof_straw 157 3
10 7 7 cottages:roof_straw 173 3
10 7 8 cottages:roof_straw 189 3
10 7 9 cottages:roof_straw 205 3
10 7 10 cottages:roof_straw 189 3
10 7 11 cottages:roof_straw 173 3
10 7 12 cottages:roof_connector_straw 157 3
10 7 13 cottages:roof_straw 141 3
11 1 2 default:cobble 0 0
11 1 3 cottages:loam 0 0
11 1 4 cottages:loam 0 0
11 1 5 cottages:loam 0 0
11 1 6 cottages:loam 0 0
11 1 7 cottages:loam 0 0
11 1 8 default:cobble 0 0
11 1 9 default:cobble 0 0
11 1 10 default:cobble 0 0
11 1 11 default:cobble 0 0
11 1 12 default:cobble 0 0
11 2 2 default:wood 0 0
11 2 5 default:fence_wood 105 0
11 2 8 default:wood 0 0
11 2 12 default:wood 0 0
11 3 2 default:fence_wood 76 0
11 3 5 default:fence_wood 122 0
11 3 8 default:wood 0 0
11 3 12 default:glass 156 0
11 4 2 default:wood 0 0
11 4 3 stairs:slab_wood 75 0
11 4 4 stairs:slab_wood 91 0
11 4 5 stairs:slab_wood 107 0
11 4 6 stairs:slab_wood 123 0
11 4 7 stairs:slab_wood 139 0
11 4 8 default:wood 0 0
11 4 9 default:wood 0 0
11 4 10 default:wood 0 0
11 4 11 default:wood 0 0
11 4 12 default:wood 0 0
11 5 2 default:wood 0 0
11 5 8 default:wood 0 0
11 5 9 default:chest 0 1
11 5 10 cottages:bed_foot 172 0
11 5 11 cottages:bed_head 156 0
11 5 12 default:wood 0 0
11 6 1 cottages:roof_straw 77 3
11 6 2 cottages:roof_connector_straw 93 3
11 6 3 cottages:roof_straw 109 3
11 6 4 cottages:roof_straw 125 3
11 6 5 cottages:roof_straw 141 3
11 6 6 cottages:roof_straw 157 3
11 6 7 cottages:roof_straw 173 3
11 6 8 cottages:roof_straw 189 3
11 6 9 cottages:roof_straw 205 3
11 6 10 cottages:roof_straw 189 3
11 6 11 cottages:roof_straw 173 3
11 6 12 cottages:roof_connector_straw 157 3
11 6 13 cottages:roof_straw 141 3
12 1 2 default:cobble 0 0
12 1 3 cottages:loam 0 0
12 1 4 cottages:loam 0 0
12 1 5 cottages:loam 0 0
12 1 6 cottages:loam 0 0
12 1 7 cottages:loam 0 0
12 1 8 default:cobble 0 0
12 1 9 default:cobble 0 0
12 1 10 default:cobble 0 0
12 1 11 default:cobble 0 0
12 1 12 default:cobble 0 0
12 2 2 default:wood 0 0
12 2 5 default:fence_wood 90 0
12 2 8 default:wood 0 0
12 2 9 default:furnace 0 1
12 2 10 default:cobble 0 0
12 2 11 stairs:slab_wood 170 0
12 2 12 default:wood 0 0
12 3 2 default:wood 0 0
12 3 5 default:fence_wood 107 0
12 3 8 default:wood 0 0
12 3 9 default:torch 0 2
12 3 11 stairs:slab_wood 187 0
12 3 12 default:wood 0 0
12 4 2 default:wood 0 0
12 4 3 stairs:slab_wood 60 0
12 4 4 stairs:slab_wood 76 0
12 4 5 stairs:slab_wood 92 0
12 4 6 stairs:slab_wood 108 0
12 4 7 stairs:slab_wood 124 0
12 4 8 default:wood 0 0
12 4 9 default:wood 0 0
12 4 10 default:wood 0 0
12 4 11 default:wood 0 0
12 4 12 default:wood 0 0
12 5 1 cottages:roof_straw 45 3
12 5 2 cottages:roof_connector_straw 61 3
12 5 3 cottages:roof_straw 77 3
12 5 4 cottages:roof_straw 93 3
12 5 5 cottages:roof_straw 109 3
12 5 6 cottages:roof_straw 125 3
12 5 7 cottages:roof_straw 141 3
12 5 8 cottages:roof_connector_straw 157 3
12 5 9 cottages:roof_straw 173 3
12 5 10 cottages:roof_straw 157 3
12 5 11 cottages:roof_straw 141 3
12 5 12 cottages:roof_connector_straw 125 3
12 5 13 cottages:roof_straw 109 3
13 1 3 default:cobble 0 0
13 1 4 default:cobble 0 0
13 1 5 default:cobble 0 0
13 1 6 default:cobble 0 0
13 1 7 default:cobble 0 0
13 1 8 default:cobble 0 0
13 1 9 default:cobble 0 0
13 1 10 default:cobble 0 0
13 1 11 default:cobble 0 0
13 2 3 default:tree 0 0
13 2 4 default:wood 0 0
13 2 5 default:wood 0 0
13 2 6 default:wood 0 0
13 2 7 default:tree 0 0
13 2 8 default:wood 0 0
13 2 9 default:wood 0 0
13 2 10 default:wood 0 0
13 2 11 default:tree 0 0
13 3 3 default:tree 0 0
13 3 4 default:wood 0 0
13 3 5 default:wood 0 0
13 3 6 default:wood 0 0
13 3 7 default:tree 0 0
13 3 8 default:wood 0 0
13 3 9 default:wood 0 0
13 3 10 default:wood 0 0
13 3 11 default:tree 0 0
13 4 1 cottages:roof_straw 13 3
13 4 2 cottages:roof_connector_straw 29 3
13 4 3 cottages:roof_straw 45 3
13 4 4 cottages:roof_straw 61 3
13 4 5 cottages:roof_straw 77 3
13 4 6 cottages:roof_straw 93 3
13 4 7 cottages:roof_straw 109 3
13 4 8 cottages:roof_connector_straw 125 3
13 4 9 cottages:roof_straw 141 3
13 4 10 cottages:roof_straw 125 3
13 4 11 cottages:roof_straw 109 3
13 4 12 cottages:roof_connector_straw 93 3
13 4 13 cottages:roof_straw 93 3
14 3 1 cottages:roof_straw 13 3
14 3 2 cottages:roof_straw 13 3
14 3 3 cottages:roof_straw 13 3
14 3 4 cottages:roof_straw 29 3
14 3 5 cottages:roof_straw 45 3
14 3 6 cottages:roof_straw 61 3
14 3 7 cottages:roof_straw 77 3
14 3 8 cottages:roof_straw 93 3
14 3 9 cottages:roof_straw 109 3
14 3 10 cottages:roof_straw 93 3
14 3 11 cottages:roof_straw 77 3
14 3 12 cottages:roof_straw 77 3
14 3 13 cottages:roof_straw 93 3

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

View File

@ -1,176 +0,0 @@
local function getmp(x)
return (80 * math.floor((x + 32) / 80)) - 32
end
local function get_minp(pos)
return {x=getmp(pos.x), y=getmp(pos.y), z=getmp(pos.z)}
end
--[[local snow_timer = 0
local snow_range = 1
minetest.register_globalstep(function(dtime)
snow_timer = snow_timer + dtime
if snow_timer < 1 then return end
snow_timer = 0
local noise_temperature_raw = minetest.get_perlin(763, 7, 0.5, 512)
local noise_humidity_raw = minetest.get_perlin(834, 7, 0.5, 512)
for _, player in ipairs(minetest.get_connected_players()) do
local pos = player:getpos()
if pos.y > -50 and pos.y < 150 then
--local to_update = {}
--local to_update_index = 1
local minp = get_minp(pos)
local biome_table = get_biome_table(minp, noise_humidity_raw, noise_temperature_raw, snow_range+1)
local vm = minetest.get_voxel_manip()
local eminp, emaxp = vm:read_from_map({x=minp.x-80*snow_range, y=-50, z=minp.z-80*snow_range}, {x=minp.x+79+80*snow_range, y=150, z=minp.z+79+80*snow_range})
--print(dump(eminp), dump(emaxp))
local a = VoxelArea:new{MinEdge=eminp, MaxEdge=emaxp}
local data = vm:get_data()
for i = 1, 80*(snow_range+1)*(snow_range+1) do
local x = math.random(eminp.x, emaxp.x)
local z = math.random(eminp.z, emaxp.z)
local biome = get_nearest_biome(biome_table, x, z)
if biome.t < -0.4 then
local y = emaxp.y
while y >= eminp.y and data[a:index(x, y, z)] == c_air do
y = y - 1
end
if y >= eminp.y and y < emaxp.y then
if data[a:index(x, y, z)] == c_snow then
if minetest.add_node_level({x=x, y=y, z=z}, 7) ~= 0 then
--data[a:index(x, y+1, z)] = c_snow
minetest.set_node({x=x, y=y+1, z=z}, {name = "default:snow"})
end
else
--data[a:index(x, y+1, z)] = c_snow
minetest.set_node({x=x, y=y+1, z=z}, {name = "default:snow"})
end
--to_update[to_update_index] = {x=x,y=y+1,z=z}
--to_update_index = to_update_index+1
end
end
end
--vm:set_data(data)
--vm:write_to_map(data)
--for _, pos in ipairs(to_update) do
-- nodeupdate(pos)
--end
end
end
end)]]
local function add_snow_level(pos)
local level = minetest.get_node_level(pos)
if level <= 28 then
minetest.add_node_level(pos, 7)
return 0
else
return 7
end
end
local is_snowing = false
local to_snow = {}
local to_snow_index = 0
local SNOW_RANGE = 16
local SNOW_BLOCK_SIZE = 4
local SNOW_STEPS = 10
local SNOW_TIMES_PER_BLOCK = 4
minetest.register_globalstep(function(dtime)
if math.random(1, 5000) == 1 then
is_snowing = not is_snowing
end
if to_snow_index == 0 then
if is_snowing then
for _, player in ipairs(minetest.get_connected_players()) do
local pos = player:getpos()
if pos.y > -50 and pos.y < 150 then
local x = SNOW_BLOCK_SIZE*math.floor((pos.x/SNOW_BLOCK_SIZE)+0.5)
local z = SNOW_BLOCK_SIZE*math.floor((pos.z/SNOW_BLOCK_SIZE)+0.5)
for xi = -SNOW_RANGE, SNOW_RANGE do
for zi = -SNOW_RANGE, SNOW_RANGE do
to_snow_index = to_snow_index + 1
to_snow[to_snow_index] = {x=x+SNOW_BLOCK_SIZE*xi, y=0, z=z+SNOW_BLOCK_SIZE*zi}
end
end
end
end
end
else
local noise_temperature_raw = minetest.get_perlin(763, 7, 0.5, 512)
local noise_humidity_raw = minetest.get_perlin(834, 7, 0.5, 512)
for s = 1, math.min(SNOW_STEPS, to_snow_index) do
local pos = to_snow[to_snow_index]
--print(dump(pos))
to_snow[to_snow_index] = nil
to_snow_index = to_snow_index - 1
local minp = get_minp(pos)
local biome_table = get_biome_table(minp, noise_humidity_raw, noise_temperature_raw)
local vm = minetest.get_voxel_manip()
local eminp, emaxp = vm:read_from_map({x=pos.x, y=-50, z=pos.z}, {x=pos.x + SNOW_BLOCK_SIZE - 1, y=150, z=pos.z + SNOW_BLOCK_SIZE - 1})
--print(eminp.y, emaxp.y)
--print(dump(eminp),dump(emaxp))
local a = VoxelArea:new{MinEdge=eminp, MaxEdge=emaxp}
local data = vm:get_data()
for i = 1, SNOW_TIMES_PER_BLOCK do
local x = math.random(pos.x, pos.x + SNOW_BLOCK_SIZE - 1)
local z = math.random(pos.z, pos.z + SNOW_BLOCK_SIZE - 1)
local biome = get_nearest_biome(biome_table, x, z)
if biome.t < -0.4 then
local y = emaxp.y
while y >= eminp.y and (data[a:index(x, y, z)] == c_air or data[a:index(x, y, z)] == c_ignore) do
y = y - 1
end
if y >= eminp.y and y < emaxp.y then
if data[a:index(x, y, z)] == c_snow then
--if minetest.add_node_level({x=x, y=y, z=z}, 7) ~= 0 then
if add_snow_level({x=x, y=y, z=z}) ~= 0 then
-- do nothing, max stack
--minetest.set_node({x=x, y=y, z=z}, {name = "default:snowblock"})
end
elseif data[a:index(x, y, z)] == c_snowblock or data[a:index(x, y, z)] == c_water then
-- do nothing
else
minetest.set_node({x=x, y=y+1, z=z}, {name = "default:snow"})
end
end
end
end
end
end
end)
minetest.register_abm({
nodenames = {"mg:pineleaves"},
interval = 2,
chance = 4,
action = function(pos, node)
local above = {x=pos.x, y=pos.y+1, z=pos.z}
if minetest.get_node(above).name == "default:snow" then
local level = minetest.get_node_level(above)
if level >= 14 then
for i = 1, 15 do
local p = {x=pos.x, y=pos.y-i, z=pos.z}
local n = minetest.get_node(p).name
if n ~= "air" and n ~= "mg:pineleaves" then
if n == "default:snow" then
--if minetest.add_node_level(p, 7) == 0 then
if add_snow_level(p) == 0 then
minetest.add_node_level(above, -7)
end
else
local above_p = {x=p.x, y=p.y+1, z=p.z}
if minetest.get_node(above_p).name == "air" then
minetest.set_node(above_p, {name = "default:snow"})
minetest.add_node_level(above, -7)
end
end
return
end
end
end
end
end
})

View File

@ -1,222 +1,205 @@
mg.register_tree({
max_biome_humidity = -0.4,
min_biome_temperature = 0.4,
min_height = 4,
max_height = 40,
grows_on = c_desert_sand,
chance = 50,
grow = function(data, a, x, y, z, minp, maxp, pr)
-- Cactus
local ch = pr:next(1, 4)
for yy = math.max(y, minp.y), math.min(y+ch-1, maxp.y) do
data[a:index(x, yy, z)] = c_cactus
end
local function add_leaves(data, vi, c_leaves, c_snow)
if data[vi]==c_air or data[vi]==c_ignore or data[vi] == c_snow then
data[vi] = c_leaves
end
})
end
mg.register_tree({
max_biome_humidity = -0.4,
min_biome_temperature = 0.4,
min_height = 2,
max_height = 30,
grows_on = c_desert_sand,
chance = 50,
can_be_in_village = true,
grow = function(data, a, x, y, z, minp, maxp, pr)
-- Dry shrub
if minp.y <= y and y <= maxp.y then
data[a:index(x, y, z)] = c_dry_shrub
end
function add_tree(data, a, x, y, z, minp, maxp, pr)
local th = pr:next(3, 4)
for yy=math.max(minp.y, y), math.min(maxp.y, y+th) do
local vi = a:index(x, yy, z)
data[vi] = c_tree
end
})
mg.register_tree({
min_biome_humidity = -0.4,
max_biome_humidity = 0.4,
min_biome_temperature = 0.4,
min_height = 2,
max_height = 12,
grows_on = c_dry_grass,
chance = 1000,
grow = add_savannatree
})
mg.register_tree({
min_biome_humidity = -0.4,
max_biome_humidity = 0.4,
min_biome_temperature = 0.4,
min_height = 13,
max_height = 30,
grows_on = c_dry_grass,
chance = 250,
grow = add_savannatree
})
mg.register_tree({
min_biome_humidity = -0.4,
max_biome_humidity = 0.4,
min_biome_temperature = 0.4,
min_height = 2,
max_height = 40,
grows_on = c_dry_grass,
chance = 1500,
grow = add_savannabush
})
mg.register_tree({
min_biome_humidity = 0.4,
min_biome_temperature = 0.4,
min_height = 1,
max_height = 40,
grows_on = c_grass,
chance = 14,
grow = add_tree
})
mg.register_tree({
min_biome_humidity = 0.4,
min_biome_temperature = 0.4,
min_height = 1,
max_height = 25,
grows_on = c_grass,
chance = 16,
grow = add_jungletree
})
mg.register_tree({
min_biome_humidity = 0.4,
min_biome_temperature = 0.4,
min_height = 1,
max_height = 25,
grows_on = c_grass,
chance = 30,
can_be_in_village = true,
grow = function(data, a, x, y, z, minp, maxp, pr)
-- Jungle grass
if minp.y <= y and y <= maxp.y then
data[a:index(x, y, z)] = c_jungle_grass
end
local maxy = y+th
for xx=math.max(minp.x, x-1), math.min(maxp.x, x+1) do
for yy=math.max(minp.y, maxy-1), math.min(maxp.y, maxy+1) do
for zz=math.max(minp.z, z-1), math.min(maxp.z, z+1) do
add_leaves(data, a:index(xx, yy, zz), c_leaves)
end
})
mg.register_tree({
min_biome_humidity = -0.4,
max_biome_temperature = -0.4,
min_height = 3,
max_height = 55,
grows_on = c_dirt_snow,
chance = 40,
grow = add_pinetree
})
mg.register_tree({
max_biome_humidity = -0.4,
max_biome_temperature = -0.4,
min_height = 3,
max_height = 55,
grows_on = c_dirt_snow,
chance = 500,
grow = add_pinetree
})
mg.register_tree({
max_biome_humidity = -0.4,
min_biome_temperature = -0.4,
max_biome_temperature = 0.4,
min_height = 1,
max_height = 40,
grows_on = c_grass,
chance = 250,
grow = add_tree
})
mg.register_tree({
max_biome_humidity = -0.4,
min_biome_temperature = -0.4,
max_biome_temperature = 0.4,
min_height = 1,
max_height = 40,
grows_on = c_grass,
chance = 60,
can_be_in_village = false,
grow = function(data, a, x, y, z, minp, maxp, pr)
-- Grass 1-4
if minp.y <= y and y <= maxp.y then
data[a:index(x, y, z)] = c_grasses[pr:next(1, 4)]
end
end
})
mg.register_tree({
min_biome_humidity = 0.4,
min_biome_temperature = -0.4,
max_biome_temperature = 0.4,
min_height = 1,
max_height = 40,
grows_on = c_grass,
chance = 250,
grow = add_tree
})
mg.register_tree({
min_biome_humidity = 0.4,
min_biome_temperature = -0.4,
max_biome_temperature = 0.4,
min_height = 3,
max_height = 40,
grows_on = c_grass,
chance = 3,
can_be_in_village = false,
grow = function(data, a, x, y, z, minp, maxp, pr)
-- Grass 3-5
if minp.y <= y and y <= maxp.y then
data[a:index(x, y, z)] = c_grasses[pr:next(3, 5)]
end
end
})
mg.register_tree({
min_biome_humidity = -0.4,
max_biome_humidity = 0.4,
min_biome_temperature = -0.4,
max_biome_temperature = 0.4,
min_height = 3,
max_height = 40,
grows_on = c_grass,
chance = 20,
grow = add_tree
})
mg.register_tree({
min_height = 1,
max_height = 1,
grows_on = c_grass,
chance = 10,
grow = function(data, a, x, y, z, minp, maxp, pr)
-- Papyrus
local ph = pr:next(2, 4)
for yy = math.max(y, minp.y), math.min(y+ph-1, maxp.y) do
data[a:index(x, yy, z)] = c_papyrus
for i=1,8 do
local xi = pr:next(x-2, x+1)
local yi = pr:next(maxy-1, maxy+1)
local zi = pr:next(z-2, z+1)
for xx=math.max(minp.x, xi), math.min(maxp.x, xi+1) do
for yy=math.max(minp.y, yi), math.min(maxp.y, yi+1) do
for zz=math.max(minp.z, zi), math.min(maxp.z, zi+1) do
add_leaves(data, a:index(xx, yy, zz), c_leaves)
end
end
end
})
function mg.get_spawn_tree_func(treedef)
return function(data, a, x, y, z, minp, maxp, pr)
if minp.y <= y and y <= maxp.y then
minetest.after(0, minetest.spawn_tree, {x=x, y=y, z=z}, treedef)
end
end
end
if minetest.get_modpath("moretrees") then
mg.register_tree({
min_humidity = 0,
min_temperature = 0.2,
min_height = 1,
max_height = 5,
grows_on = c_grass,
chance = 800,
grow = mg.get_spawn_tree_func(moretrees.rubber_tree_model)
})
function add_jungletree(data, a, x, y, z, minp, maxp, pr)
local th = pr:next(7, 11)
for yy=math.max(minp.y, y), math.min(maxp.y, y+th) do
local vi = a:index(x, yy, z)
data[vi] = c_jungletree
end
local maxy = y+th
for xx=math.max(minp.x, x-1), math.min(maxp.x, x+1) do
for yy=math.max(minp.y, maxy-1), math.min(maxp.y, maxy+1) do
for zz=math.max(minp.z, z-1), math.min(maxp.z, z+1) do
add_leaves(data, a:index(xx, yy, zz), c_jungleleaves)
end
end
end
for i=1,30 do
local xi = pr:next(x-3, x+2)
local yi = pr:next(maxy-2, maxy+1)
local zi = pr:next(z-3, z+2)
for xx=math.max(minp.x, xi), math.min(maxp.x, xi+1) do
for yy=math.max(minp.y, yi), math.min(maxp.y, yi+1) do
for zz=math.max(minp.z, zi), math.min(maxp.z, zi+1) do
add_leaves(data, a:index(xx, yy, zz), c_jungleleaves)
end
end
end
end
end
function add_savannatree(data, a, x, y, z, minp, maxp, pr)
local th = pr:next(7, 11)
for yy=math.max(minp.y, y), math.min(maxp.y, y+th) do
local vi = a:index(x, yy, z)
data[vi] = c_savannatree
end
local maxy = y+th
for xx=math.max(minp.x, x-1), math.min(maxp.x, x+1) do
for yy=math.max(minp.y, maxy-1), math.min(maxp.y, maxy+1) do
for zz=math.max(minp.z, z-1), math.min(maxp.z, z+1) do
add_leaves(data, a:index(xx, yy, zz), c_savannaleaves)
end
end
end
for i=1,20 do
local xi = pr:next(x-3, x+2)
local yi = pr:next(maxy-2, maxy)
local zi = pr:next(z-3, z+2)
for xx=math.max(minp.x, xi), math.min(maxp.x, xi+1) do
for yy=math.max(minp.y, yi), math.min(maxp.y, yi+1) do
for zz=math.max(minp.z, zi), math.min(maxp.z, zi+1) do
add_leaves(data, a:index(xx, yy, zz), c_savannaleaves)
end
end
end
end
for i=1,15 do
local xi = pr:next(x-3, x+2)
local yy = pr:next(maxy-6, maxy-5)
local zi = pr:next(z-3, z+2)
for xx=math.max(minp.x, xi), math.min(maxp.x, xi+1) do
for zz=math.max(minp.z, zi), math.min(maxp.z, zi+1) do
if minp.y<=yy and maxp.y>=yy then
add_leaves(data, a:index(xx, yy, zz), c_savannaleaves)
end
end
end
end
end
function add_savannabush(data, a, x, y, z, minp, maxp, pr)
local bh = pr:next(1, 2)
local bw = pr:next(2, 4)
for xx=math.max(minp.x, x-bw), math.min(maxp.x, x+bw) do
for zz=math.max(minp.z, z-bw), math.min(maxp.z, z+bw) do
for yy=math.max(minp.y, y-bh), math.min(maxp.y, y+bh) do
if pr:next(1, 100) < 95 and math.abs(xx-x) < pr:next(bh, bh+2)-math.abs(y-yy) and math.abs(zz-z) < pr:next(bh, bh+2)-math.abs(y-yy) then
add_leaves(data, a:index(xx, yy, zz), c_savannaleaves)
for yyy=math.max(minp.y, yy-2), yy do
add_leaves(data, a:index(xx, yyy, zz), c_savannaleaves)
end
end
end
end
end
if x<=maxp.x and x>=minp.x and y<=maxp.y and y>=minp.y and z<=maxp.z and z>=minp.z then
local vi = a:index(x, y, z)
data[vi] = c_savannatree
end
end
function add_pinetree(data, a, x, y, z, minp, maxp, pr, snow)
if snow == nil then snow = c_snow end
local th = pr:next(9, 13)
for yy=math.max(minp.y, y), math.min(maxp.y, y+th) do
local vi = a:index(x, yy, z)
data[vi] = c_pinetree
end
local maxy = y+th
for xx=math.max(minp.x, x-3), math.min(maxp.x, x+3) do
for yy=math.max(minp.y, maxy-1), math.min(maxp.y, maxy-1) do
for zz=math.max(minp.z, z-3), math.min(maxp.z, z+3) do
if pr:next(1, 100) < 80 then
add_leaves(data, a:index(xx, yy, zz), c_pineleaves, snow)
add_leaves(data, a:index(xx, yy+1, zz), snow)
end
end
end
end
for xx=math.max(minp.x, x-2), math.min(maxp.x, x+2) do
for yy=math.max(minp.y, maxy), math.min(maxp.y, maxy) do
for zz=math.max(minp.z, z-2), math.min(maxp.z, z+2) do
if pr:next(1, 100) < 85 then
add_leaves(data, a:index(xx, yy, zz), c_pineleaves, snow)
add_leaves(data, a:index(xx, yy+1, zz), snow)
end
end
end
end
for xx=math.max(minp.x, x-1), math.min(maxp.x, x+1) do
for yy=math.max(minp.y, maxy+1), math.min(maxp.y, maxy+1) do
for zz=math.max(minp.z, z-1), math.min(maxp.z, z+1) do
if pr:next(1, 100) < 90 then
add_leaves(data, a:index(xx, yy, zz), c_pineleaves, snow)
add_leaves(data, a:index(xx, yy+1, zz), snow)
end
end
end
end
if maxy+1<=maxp.y and maxy+1>=minp.y then
add_leaves(data, a:index(x, maxy+1, z), c_pineleaves, snow)
add_leaves(data, a:index(x, maxy+2, z), snow)
end
local my = 0
for i=1,20 do
local xi = pr:next(x-3, x+2)
local yy = pr:next(maxy-6, maxy-5)
local zi = pr:next(z-3, z+2)
if yy > my then
my = yy
end
for xx=math.max(minp.x, xi), math.min(maxp.x, xi+1) do
for zz=math.max(minp.z, zi), math.min(maxp.z, zi+1) do
if minp.y<=yy and maxp.y>=yy then
add_leaves(data, a:index(xx, yy, zz), c_pineleaves, snow)
add_leaves(data, a:index(xx, yy+1, zz), snow)
end
end
end
end
for xx=math.max(minp.x, x-2), math.min(maxp.x, x+2) do
for yy=math.max(minp.y, my+1), math.min(maxp.y, my+1) do
for zz=math.max(minp.z, z-2), math.min(maxp.z, z+2) do
if pr:next(1, 100) < 85 then
add_leaves(data, a:index(xx, yy, zz), c_pineleaves, snow)
add_leaves(data, a:index(xx, yy+1, zz), snow)
end
end
end
end
for xx=math.max(minp.x, x-1), math.min(maxp.x, x+1) do
for yy=math.max(minp.y, my+2), math.min(maxp.y, my+2) do
for zz=math.max(minp.z, z-1), math.min(maxp.z, z+1) do
if pr:next(1, 100) < 90 then
add_leaves(data, a:index(xx, yy, zz), c_pineleaves, snow)
add_leaves(data, a:index(xx, yy+1, zz), snow)
end
end
end
end
end

View File

@ -1,519 +0,0 @@
VILLAGE_CHECK_RADIUS = 2
VILLAGE_CHECK_COUNT = 1
VILLAGE_CHANCE = 28
VILLAGE_MIN_SIZE = 20
VILLAGE_MAX_SIZE = 40
FIRST_ROADSIZE = 3
BIG_ROAD_CHANCE = 0
-- Enable that for really big villages (there are also really slow to generate)
--[[VILLAGE_CHECK_RADIUS = 3
VILLAGE_CHECK_COUNT = 3
VILLAGE_CHANCE = 28
VILLAGE_MIN_SIZE = 100
VILLAGE_MAX_SIZE = 150
FIRST_ROADSIZE = 5
BIG_ROAD_CHANCE = 50]]
local function is_village_block(minp)
local x, z = math.floor(minp.x/80), math.floor(minp.z/80)
local vcc = VILLAGE_CHECK_COUNT
return (x%vcc == 0) and (z%vcc == 0)
end
function village_at_point(minp, noise1)
if not is_village_block(minp) then return 0,0,0,0 end
local vcr, vcc = VILLAGE_CHECK_RADIUS, VILLAGE_CHECK_COUNT
for xi = -vcr, vcr, vcc do
for zi = -vcr, 0, vcc do
if xi~=0 or zi~=0 then
local mp = {x=minp.x+80*xi, z=minp.z+80*zi}
local pi = PseudoRandom(get_bseed(mp))
local s = pi:next(1, 400)
local x = pi:next(mp.x, mp.x+79)
local z = pi:next(mp.z, mp.z+79)
if s<=VILLAGE_CHANCE and noise1:get2d({x=x, y=z})>=-0.3 then return 0,0,0,0 end
end
end
end
local pr = PseudoRandom(get_bseed(minp))
if pr:next(1,400)>VILLAGE_CHANCE then return 0,0,0,0 end
local x = pr:next(minp.x, minp.x+79)
local z = pr:next(minp.z, minp.z+79)
if noise1:get2d({x=x, y=z})<-0.3 then return 0,0,0,0 end
local size = pr:next(VILLAGE_MIN_SIZE, VILLAGE_MAX_SIZE)
local height = pr:next(5, 20)
print("A village spawned at: x="..x..", z="..z)
return x,z,size,height
end
--local function dist_center2(ax, bsizex, az, bsizez)
-- return math.max((ax+bsizex)*(ax+bsizex),ax*ax)+math.max((az+bsizez)*(az+bsizez),az*az)
--end
local function inside_village2(bx, sx, bz, sz, vx, vz, vs, vnoise)
return inside_village(bx, bz, vx, vz, vs, vnoise) and inside_village(bx+sx, bz, vx, vz, vs, vnoise) and inside_village(bx, bz+sz, vx, vz, vs, vnoise) and inside_village(bx+sx, bz+sz, vx, vz, vs, vnoise)
end
local function choose_building(l, pr)
--::choose::
local btype
while true do
local p = pr:next(1, 3000)
for b, i in ipairs(buildings) do
if i.max_weight >= p then
btype = b
break
end
end
if buildings[btype].pervillage ~= nil then
local n = 0
for j=1, #l do
if l[j].btype == btype then
n = n + 1
end
end
--if n >= buildings[btype].pervillage then
-- goto choose
--end
if n < buildings[btype].pervillage then
return btype
end
else
return btype
end
end
--return btype
end
local function choose_building_rot(l, pr, orient)
local btype = choose_building(l, pr)
local rotation
if buildings[btype].no_rotate then
rotation = 0
else
if buildings[btype].orients == nil then
buildings[btype].orients = {0,1,2,3}
end
rotation = (orient+buildings[btype].orients[pr:next(1, #buildings[btype].orients)])%4
end
local bsizex = buildings[btype].sizex
local bsizez = buildings[btype].sizez
if rotation%2 == 1 then
bsizex, bsizez = bsizez, bsizex
end
return btype, rotation, bsizex, bsizez
end
local function placeable(bx, bz, bsizex, bsizez, l, exclude_roads)
for _, a in ipairs(l) do
if (a.btype ~= "road" or not exclude_roads) and math.abs(bx+bsizex/2-a.x-a.bsizex/2)<=(bsizex+a.bsizex)/2 and math.abs(bz+bsizez/2-a.z-a.bsizez/2)<=(bsizez+a.bsizez)/2 then return false end
end
return true
end
local function road_in_building(rx, rz, rdx, rdz, roadsize, l)
if rdx == 0 then
return not placeable(rx-roadsize+1, rz, 2*roadsize-2, 0, l, true)
else
return not placeable(rx, rz-roadsize+1, 0, 2*roadsize-2, l, true)
end
end
local function when(a, b, c)
if a then return b else return c end
end
local function generate_road(vx, vz, vs, vh, l, pr, roadsize, rx, rz, rdx, rdz, vnoise)
local calls_to_do = {}
local rxx = rx
local rzz = rz
local mx, m2x, mz, m2z, mmx, mmz
mx, m2x, mz, m2z = rx, rx, rz, rz
local orient1, orient2
local bsizex = 0
local bsizez = 0
local tries = 0
local btype
local rotation = 0
if rdx == 0 then
orient1 = 0
orient2 = 2
else
orient1 = 3
orient2 = 1
end
while inside_village(rx, rz, vx, vz, vs, vnoise) and not road_in_building(rx, rz, rdx, rdz, roadsize, l) do
-- protect this area
--local chunk = landrush.get_chunk({x=rx, y=0, z=rz})
--landrush.claims[chunk] = {owner="NPCs",shared={},claimtype="landclaim"}
--landrush.save_claims()
if roadsize > 1 and pr:next(1, 4) == 1 then
--generate_road(vx, vz, vs, vh, l, pr, roadsize-1, rx, rz, math.abs(rdz), math.abs(rdx))
calls_to_do[#calls_to_do+1] = {rx=rx+(roadsize - 1)*rdx, rz=rz+(roadsize - 1)*rdz, rdx=math.abs(rdz), rdz=math.abs(rdx)}
m2x = rx + (roadsize - 1)*rdx
m2z = rz + (roadsize - 1)*rdz
rx = rx + (2*roadsize - 1)*rdx
rz = rz + (2*roadsize - 1)*rdz
end
--else
--::loop::
local exitloop = false
local bx
local bz
while true do
if not inside_village(rx, rz, vx, vz, vs, vnoise) or road_in_building(rx, rz, rdx, rdz, roadsize, l) then
exitloop = true
break
end
btype, rotation, bsizex, bsizez = choose_building_rot(l, pr, orient1)
bx = rx + math.abs(rdz)*(roadsize+1) - when(rdx==-1, bsizex-1, 0)
bz = rz + math.abs(rdx)*(roadsize+1) - when(rdz==-1, bsizez-1, 0)
if placeable(bx, bz, bsizex, bsizez, l) and inside_village2(bx, bsizex, bz, bsizez, vx, vz, vs, vnoise) then
break
end
if tries > 5 then
rx = rx + rdx
rz = rz + rdz
tries = 0
else
tries = tries + 1
end
--goto loop
end
if exitloop then break end
rx = rx + (bsizex+1)*rdx
rz = rz + (bsizez+1)*rdz
mx = rx - 2*rdx
mz = rz - 2*rdz
l[#l+1] = {x=bx, y=vh, z=bz, btype=btype, bsizex=bsizex, bsizez=bsizez, brotate = rotation}
--end
end
rx = rxx
rz = rzz
while inside_village(rx, rz, vx, vz, vs, vnoise) and not road_in_building(rx, rz, rdx, rdz, roadsize, l) do
if roadsize > 1 and pr:next(1, 4) == 1 then
--generate_road(vx, vz, vs, vh, l, pr, roadsize-1, rx, rz, -math.abs(rdz), -math.abs(rdx))
calls_to_do[#calls_to_do+1] = {rx=rx+(roadsize - 1)*rdx, rz=rz+(roadsize - 1)*rdz, rdx=-math.abs(rdz), rdz=-math.abs(rdx)}
m2x = rx + (roadsize - 1)*rdx
m2z = rz + (roadsize - 1)*rdz
rx = rx + (2*roadsize - 1)*rdx
rz = rz + (2*roadsize - 1)*rdz
end
--else
--::loop::
local exitloop = false
local bx
local bz
local tries = 0
while true do
if not inside_village(rx, rz, vx, vz, vs, vnoise) or road_in_building(rx, rz, rdx, rdz, roadsize, l) then
exitloop = true
break
end
btype, rotation, bsizex, bsizez = choose_building_rot(l, pr, orient2)
bx = rx - math.abs(rdz)*(bsizex+roadsize) - when(rdx==-1, bsizex-1, 0)
bz = rz - math.abs(rdx)*(bsizez+roadsize) - when(rdz==-1, bsizez-1, 0)
if placeable(bx, bz, bsizex, bsizez, l) and inside_village2(bx, bsizex, bz, bsizez, vx, vz, vs, vnoise) then
break
end
if tries > 5 then
rx = rx + rdx
rz = rz + rdz
tries = 0
else
tries = tries + 1
end
--goto loop
end
if exitloop then break end
rx = rx + (bsizex+1)*rdx
rz = rz + (bsizez+1)*rdz
m2x = rx - 2*rdx
m2z = rz - 2*rdz
l[#l+1] = {x=bx, y=vh, z=bz, btype=btype, bsizex=bsizex, bsizez=bsizez, brotate = rotation}
--end
end
if road_in_building(rx, rz, rdx, rdz, roadsize, l) then
mmx = rx - 2*rdx
mmz = rz - 2*rdz
end
local rxmin = nil
local rxmax = nil
local rzmin = nil
local rzmax = nil
mx = mmx or rdx*math.max(rdx*mx, rdx*m2x)
mz = mmz or rdz*math.max(rdz*mz, rdz*m2z)
if rdx == 0 then
rxmin = rx - roadsize + 1
rxmax = rx + roadsize - 1
rzmin = math.min(rzz, mz)
rzmax = math.max(rzz, mz)
else
rzmin = rz - roadsize + 1
rzmax = rz + roadsize - 1
rxmin = math.min(rxx, mx)
rxmax = math.max(rxx, mx)
end
l[#l+1] = {x=rxmin, y=vh, z=rzmin, btype="road", bsizex=rxmax-rxmin+1, bsizez=rzmax-rzmin+1, brotate = 0}
for _,i in ipairs(calls_to_do) do
local new_roadsize = roadsize-1
if pr:next(1, 100) <= BIG_ROAD_CHANCE then
new_roadsize = roadsize
end
--generate_road(vx, vz, vs, vh, l, pr, new_roadsize, i.rx, i.rz, i.rdx, i.rdz, vnoise)
calls[calls.index] = {vx, vz, vs, vh, l, pr, new_roadsize, i.rx, i.rz, i.rdx, i.rdz, vnoise}
calls.index = calls.index+1
end
end
local function generate_bpos(vx, vz, vs, vh, pr, vnoise)
--[=[local l={}
local total_weight = 0
for _, i in ipairs(buildings) do
if i.weight == nil then i.weight = 1 end
total_weight = total_weight+i.weight
i.max_weight = total_weight
end
local multiplier = 3000/total_weight
for _,i in ipairs(buildings) do
i.max_weight = i.max_weight*multiplier
end
for i=1, 2000 do
bx = pr:next(vx-vs, vx+vs)
bz = pr:next(vz-vs, vz+vs)
::choose::
--[[btype = pr:next(1, #buildings)
if buildings[btype].chance ~= nil then
if pr:next(1, buildings[btype].chance) ~= 1 then
goto choose
end
end]]
p = pr:next(1, 3000)
for b, i in ipairs(buildings) do
if i.max_weight > p then
btype = b
break
end
end
if buildings[btype].pervillage ~= nil then
local n = 0
for j=1, #l do
if l[j].btype == btype then
n = n + 1
end
end
if n >= buildings[btype].pervillage then
goto choose
end
end
local rotation
if buildings[btype].no_rotate then
rotation = 0
else
rotation = pr:next(0, 3)
end
bsizex = buildings[btype].sizex
bsizez = buildings[btype].sizez
if rotation%2 == 1 then
bsizex, bsizez = bsizez, bsizex
end
if dist_center2(bx-vx, bsizex, bz-vz, bsizez)>vs*vs then goto out end
for _, a in ipairs(l) do
if math.abs(bx-a.x)<=(bsizex+a.bsizex)/2+2 and math.abs(bz-a.z)<=(bsizez+a.bsizez)/2+2 then goto out end
end
l[#l+1] = {x=bx, y=vh, z=bz, btype=btype, bsizex=bsizex, bsizez=bsizez, brotate = rotation}
::out::
end
return l]=]--
local l={}
local rx = vx-vs
local rz = vz
while inside_village(rx, rz, vx, vz, vs, vnoise) do
rx = rx - 1
end
rx = rx + 5
calls = {index = 1}
generate_road(vx, vz, vs, vh, l, pr, FIRST_ROADSIZE, rx, rz, 1, 0, vnoise)
local i = 1
while i < calls.index do
generate_road(unpack(calls[i]))
i = i+1
end
return l
--[=[while rx1 < vx+vs do
local building = choose_building(l, pr)
local rotation
if buildings[btype].no_rotate then
rotation = 0
else
rotation = pr:next(0, 3)
end
bsizex = buildings[btype].sizex
bsizez = buildings[btype].sizez
if rotation%2 == 1 then
bsizex, bsizez = bsizez, bsizex
end
local bx = rx1
rx1 = rx1+bsizex+1
local bz = rz - bsizez - 3
if dist_center2(bx-vx, bsizex, bz-vz, bsizez)>vs*vs then goto out end
l[#l+1] = {x=bx, y=vh, z=bz, btype=btype, bsizex=bsizex, bsizez=bsizez, brotate = rotation}
::out::
end
while rx2 < vx+vs do
local building = choose_building(l, pr)
local rotation
if buildings[btype].no_rotate then
rotation = 0
else
rotation = pr:next(0, 3)
end
bsizex = buildings[btype].sizex
bsizez = buildings[btype].sizez
if rotation%2 == 1 then
bsizex, bsizez = bsizez, bsizex
end
local bx = rx2
rx2 = rx2+bsizex+1
local bz = rz + 3
if dist_center2(bx-vx, bsizex, bz-vz, bsizez)>vs*vs then goto out end
l[#l+1] = {x=bx, y=vh, z=bz, btype=btype, bsizex=bsizex, bsizez=bsizez, brotate = rotation}
::out::
end
return l]=]
end
local function generate_building(pos, minp, maxp, data, a, pr, extranodes, barbarian_village)
local binfo = buildings[pos.btype]
local scm
if type(binfo.scm) == "string" then
scm = import_scm(binfo.scm)
else
scm = binfo.scm
end
scm = rotate(scm, pos.brotate)
local c_ignore = minetest.get_content_id("ignore")
local c_air = minetest.get_content_id("air")
local t
local newBuilding = false
for x = 0, pos.bsizex-1 do
for y = 0, binfo.ysize-1 do
for z = 0, pos.bsizez-1 do
local ax, ay, az = pos.x+x, pos.y+y+binfo.yoff, pos.z+z
if (ax >= minp.x and ax <= maxp.x) and (ay >= minp.y and ay <= maxp.y) and (az >= minp.z and az <= maxp.z) then
if scm[y+1] ~= nil then
if scm[y+1][x+1] ~= nil then
if scm[y+1][x+1][z+1] ~= nil then
t = scm[y+1][x+1][z+1]
if type(t) == "table" then
table.insert(extranodes, {node=t.node, meta=t.meta, pos={x=ax, y=ay, z=az},})
newBuilding = true
elseif t ~= c_ignore and t ~= c_air then
data[a:index(ax, ay, az)] = t
end
end
end
end
end
end
end
end
if newBuilding == true then
--print("New building around "..minetest.pos_to_string(pos))
local numNPCs = math.random(0,1)
--print("Spawning "..tostring(numNPCs).." NPCs")
if numNPCs > 0 then
for i=0,numNPCs,1 do
local npos = pos
npos.x = npos.x + math.random(-8,8)
npos.y = npos.y + 2
npos.z = npos.z + math.random(-8,8)
local spawnerpos = {x=npos.x, y=npos.y, z=npos.z}
spawnerpos.y = spawnerpos.y - 5
if barbarian_village == true then
local barbarian = mobs:get_random('barbarian')
minetest.log("action","Spawning "..barbarian.." at "..minetest.pos_to_string(npos))
local mob = minetest.add_entity(pos, barbarian)
if mob then
local distance_rating = ( ( get_distance({x=0,y=0,z=0},npos) ) / 15000 )
mob = mob:get_luaentity()
local newHP = mob.hp_min + math.floor( mob.hp_max * distance_rating )
mob.object:set_hp( newHP )
local metatable = { fields = { entity = barbarian, active_objects = 6 } }
table.insert(extranodes, {node={name="mobs:spawner",param1=0, param2=0}, pos=spawnerpos,mob="barbarian"})
end
else
local npc = mobs:get_random('npc')
minetest.log("action","Spawning "..npc.." at "..minetest.pos_to_string(npos))
local mob = minetest.add_entity(pos, npc)
if mob then
mob = mob:get_luaentity()
local p = mob.object:getpos()
math.randomseed( ( p.x * p.y * p.z ) )
local metatable = { fields = { entity = npc, active_objects = 6 } }
table.insert(extranodes, {node={name="mobs:spawner",param1=0, param2=0}, pos=spawnerpos, mob="npc"})
end
end
end
end
end
end
local MIN_DIST = 1
local function pos_far_buildings(x, z, l)
for _,a in ipairs(l) do
if a.x-MIN_DIST<=x and x<=a.x+a.bsizex+MIN_DIST and a.z-MIN_DIST<=z and z<=a.z+a.bsizez+MIN_DIST then
return false
end
end
return true
end
local function generate_walls(bpos, data, a, minp, maxp, vh, vx, vz, vs, vnoise)
for x = minp.x, maxp.x do
for z = minp.z, maxp.z do
local xx = (vnoise:get2d({x=x, y=z})-2)*20+(40/(vs*vs))*((x-vx)*(x-vx)+(z-vz)*(z-vz))
if xx>=40 and xx <= 44 then
bpos[#bpos+1] = {x=x, z=z, y=vh, btype="wall", bsizex=1, bsizez=1, brotate=0}
end
end
end
end
function generate_village(vx, vz, vs, vh, minp, maxp, data, a, vnoise, to_grow)
local seed = get_bseed({x=vx, z=vz})
local pr_village = PseudoRandom(seed)
local pr_vtype = PseudoRandom(seed)
local bpos = generate_bpos(vx, vz, vs, vh, pr_village, vnoise)
local hasWalls = false
local rptype = pr_vtype:next(1,10)
local barbarian_village = nil
if rptype <= 3 then
barbarian_village = true
else
barbarian_village = false
end
--generate_walls(bpos, data, a, minp, maxp, vh, vx, vz, vs, vnoise)
local pr = PseudoRandom(seed)
for _, g in ipairs(to_grow) do
if pos_far_buildings(g.x, g.z, bpos) then
mg.registered_trees[g.id].grow(data, a, g.x, g.y, g.z, minp, maxp, pr)
end
end
local extranodes = {}
for _, pos in ipairs(bpos) do
generate_building(pos, minp, maxp, data, a, pr_village, extranodes, barbarian_village)
end
return extranodes
end

View File

@ -1,89 +0,0 @@
local function numk(tbl)
local i = 0
for a, b in pairs(tbl) do
i = i + 1
end
return i
end
function import_scm(scm)
local c_ignore = minetest.get_content_id("ignore")
local f, err = io.open(minetest.get_modpath("mg").."/schems/"..scm..".we", "r")
if not f then
error("Could not open schematic '" .. scm .. ".we': " .. err)
end
value = f:read("*a")
f:close()
value = value:gsub("return%s*{", "", 1):gsub("}%s*$", "", 1)
local escaped = value:gsub("\\\\", "@@"):gsub("\\\"", "@@"):gsub("(\"[^\"]*\")", function(s) return string.rep("@", #s) end)
local startpos, startpos1, endpos = 1, 1
local nodes = {}
while true do
startpos, endpos = escaped:find("},%s*{", startpos)
if not startpos then
break
end
local current = value:sub(startpos1, startpos)
table.insert(nodes, minetest.deserialize("return " .. current))
startpos, startpos1 = endpos, endpos
end
table.insert(nodes, minetest.deserialize("return " .. value:sub(startpos1)))
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
local paramtype2 = minetest.registered_nodes[ent.name] and minetest.registered_nodes[ent.name].paramtype2
if ent.name == "mg:ignore" or not paramtype2 then
scm[ent.y][ent.x][ent.z] = c_ignore
elseif paramtype2 ~= "facedir" and paramtype2 ~= "wallmounted" and numk(ent.meta.fields) == 0 and numk(ent.meta.inventory) == 0 then
scm[ent.y][ent.x][ent.z] = minetest.get_content_id(ent.name)
else
if paramtype2 ~= "facedir" and paramtype2 ~= "wallmounted" then
scm[ent.y][ent.x][ent.z] = {node={name=ent.name, param2=ent.param2}, meta=ent.meta}
else
scm[ent.y][ent.x][ent.z] = {node={name=ent.name, param2=ent.param2}, meta=ent.meta, rotation = paramtype2}
end
end
end
local c_air = minetest.get_content_id("air")
for x = 1, maxx do
for y = 1, maxy do
for z = 1, maxz do
if scm[y] == nil then
scm[y] = {}
end
if scm[y][x] == nil then
scm[y][x] = {}
end
if scm[y][x][z] == nil then
scm[y][x][z] = c_air
end
end
end
end
return scm
end

3
mods/mg_villages/README.md Executable file
View File

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

448
mods/mg_villages/buildings.lua Executable file
View File

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

View File

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

176
mods/mg_villages/config.lua Executable file
View File

@ -0,0 +1,176 @@
-----------------------------------------------------------------------------
-- configuration values which you can adjust according to your liking
-----------------------------------------------------------------------------
-- set to false if you do not want to have any villages spawning
mg_villages.ENABLE_VILLAGES = true;
-- generate one random building for each mg_villages.INVERSE_HOUSE_DENSITY th mapchunk;
-- set to 0 in order to disable spawning of these lone buildings outside villages
mg_villages.INVERSE_HOUSE_DENSITY = 0;
-- cover some villages with artificial snow; probability: 1/mg_villages.artificial_snow_probability
mg_villages.artificial_snow_probability = 0;
-- if set to true, soil around villaes will get special soil-snow instead of plant + snow cover
mg_villages.use_soil_snow = false;
-- only place roads if there are at least that many buildings in the village
mg_villages.MINIMAL_BUILDUNGS_FOR_ROAD_PLACEMENT = 4;
-- players without the mg_villages priv can only see villages which are less than that many blocks away
-- from them when using the /vmap command
mg_villages.VILLAGE_DETECT_RANGE = 400;
-- if set to true, only players which have the mg_villages priv can use the "/visit <village nr>"
-- command which allows teleporting to the village with the given number
mg_villages.REQUIRE_PRIV_FOR_TELEPORT = true;
-- if set to true, players cannot modify spawned villages without buying the house from the village first
mg_villages.ENABLE_PROTECTION = true;
-- the first village - the one the player spawns in - will be of this type
mg_villages.FIRST_VILLAGE_TYPE = 'medieval';
-- choose the debug level you want
mg_villages.DEBUG_LEVEL = mg_villages.DEBUG_LEVEL_NONE
-- background image for the /vmap command
-- RealTest comes with a diffrent texture
if( minetest.get_modpath('grounds') and minetest.get_modpath('joiner_table')) then
mg_villages.MAP_BACKGROUND_IMAGE = "default_dirt_grass.png";
elseif( minetest.registered_nodes[ 'default:dirt_with_grass'] ) then
mg_villages.MAP_BACKGROUND_IMAGE = "default_grass.png";
else
mg_villages.MAP_BACKGROUND_IMAGE = "";
end
-- if set to true, the outer buildings in medieval villages will be fields; this is not very convincing yet
-- currently not really used; does not look as good as expected
mg_villages.medieval_subtype = false;
-----------------------------------------------------------------------------
-- decrese these values slightly if you want MORE trees around your villages;
-- increase it if you want to DECREASE the amount of trees around villages
-----------------------------------------------------------------------------
-- on average, every n.th node inside a village area may be one of these trees - and it will be a relatively dense packed forrest
mg_villages.sapling_probability = {};
mg_villages.sapling_probability[ minetest.get_content_id( 'default:sapling' ) ] = 25; -- suitable for a relatively dense forrest of normal trees
mg_villages.sapling_probability[ minetest.get_content_id( 'default:junglesapling' ) ] = 40; -- jungletrees are a bit bigger and need more space
mg_villages.sapling_probability[ minetest.get_content_id( 'default:pinesapling' ) ] = 30;
if( minetest.get_modpath( 'mg' )) then
mg_villages.sapling_probability[ minetest.get_content_id( 'mg:savannasapling' ) ] = 30;
mg_villages.sapling_probability[ minetest.get_content_id( 'mg:pinesapling' ) ] = 35;
end
mg_villages.moretrees_treelist = nil;
if( minetest.get_modpath( 'moretrees' )) then
mg_villages.moretrees_treelist = moretrees.treelist;
mg_villages.sapling_probability[ minetest.get_content_id( 'moretrees:birch_sapling_ongen' ) ] = 200;
mg_villages.sapling_probability[ minetest.get_content_id( 'moretrees:spruce_sapling_ongen' ) ] = 200;
mg_villages.sapling_probability[ minetest.get_content_id( 'moretrees:fir_sapling_ongen' ) ] = 90;
mg_villages.sapling_probability[ minetest.get_content_id( 'moretrees:jungletree_sapling_ongen' ) ] = 200;
mg_villages.sapling_probability[ minetest.get_content_id( 'moretrees:beech_sapling_ongen' ) ] = 30;
mg_villages.sapling_probability[ minetest.get_content_id( 'moretrees:apple_sapling_ongen' ) ] = 380;
mg_villages.sapling_probability[ minetest.get_content_id( 'moretrees:oak_sapling_ongen' ) ] = 380; -- ca 20x20; height: 10
mg_villages.sapling_probability[ minetest.get_content_id( 'moretrees:sequoia_sapling_ongen' ) ] = 90; -- ca 10x10
mg_villages.sapling_probability[ minetest.get_content_id( 'moretrees:palm_sapling_ongen' ) ] = 90;
mg_villages.sapling_probability[ minetest.get_content_id( 'moretrees:pine_sapling_ongen' ) ] = 200;
mg_villages.sapling_probability[ minetest.get_content_id( 'moretrees:willow_sapling_ongen' ) ] = 380;
mg_villages.sapling_probability[ minetest.get_content_id( 'moretrees:rubber_tree_sapling_ongen' ) ] = 380;
end
-----------------------------------------------------------------------------
-- no need to change this, unless you add new farming_plus fruits
-----------------------------------------------------------------------------
-- the schematics for buildings of type 'farm_tiny' grow cotton; the farming_plus fruits would be far more fitting
mg_villages.fruit_list = {'carrot','potatoe','orange','rhubarb','strawberry','tomato','cotton'};
-- is farming_plus available? If not, we can't use this
if( not( minetest.get_modpath("farming_plus"))) then
mg_villages.fruit_list = nil;
end
-----------------------------------------------------------------------------
-- players can buy plots in villages with houses on for this price;
-- set according to your liking
-----------------------------------------------------------------------------
-- how much does the player have to pay for a plot with a building?
mg_villages.prices = {
empty = "default:copper_ingot 1", -- plot to build on
-- building types which usually have inhabitants (and thus allow the player
-- who bought the building to modifiy the entire village area minus other
-- buildings)
tent = "default:copper_ingot 1",
hut = "default:copper_ingot 1",
farm_full = "default:gold_ingot 4",
farm_tiny = "default:gold_ingot 2",
lumberjack = "default:gold_ingot 2",
house = "default:gold_ingot 2",
house_large = "default:gold_ingot 4",
tavern = "default:gold_ingot 12",
trader = "default:gold_ingot 2",
-- more or less community buildings
well = "default:gold_ingot 1",
village_square = "default:goldblock 1",
secular = "default:goldblock 2", -- secular buildings, such as libraries ec.
church = "default:goldblock 10",
-- places for mobs to work at; usually without inhabitants
tower = "default:copper_ingot 1",
shed = "default:copper_ingot 2",
pit = "default:copper_ingot 3", -- claytrader pit
mill = "default:gold_ingot 10",
forge = "default:gold_ingot 10",
bakery = "default:gold_ingot 10",
shop = "default:gold_ingot 20",
sawmill = "default:gold_ingot 30",
-- decoration
wagon = "default:tree 10",
bench = "default:tree 4",
-- seperate fields
pasture = "default:copper_ingot 2",
field = "default:copper_ingot 2",
-- chateaus are expensive
chateau = "default:diamondblock 5",
}
-----------------------------------------------------------------------------
-- The values below seldom need adjustment; don't change them unless you
-- know exactly what you are doing.
-----------------------------------------------------------------------------
-- if set to false, villages will not be integrated into the terrain - which looks very bad
mg_villages.ENABLE_TERRAIN_BLEND = true;
-- if set to false, holes digged by cavegen and mudflow inside the village will not be repaired; houses will be destroyed
mg_villages.UNDO_CAVEGEN_AND_MUDFLOW = false;
-- internal variables for village generation
mg_villages.VILLAGE_CHECK_RADIUS = 2
mg_villages.VILLAGE_CHECK_COUNT = 1
--mg_villages.VILLAGE_CHANCE = 28
--mg_villages.VILLAGE_MIN_SIZE = 20
--mg_villages.VILLAGE_MAX_SIZE = 40
mg_villages.VILLAGE_CHANCE = 28
-- min and max size are only used in case of them beeing not provided by the village type (see buildings.lua)
mg_villages.VILLAGE_MIN_SIZE = 25
mg_villages.VILLAGE_MAX_SIZE = 90 --55
mg_villages.FIRST_ROADSIZE = 3
mg_villages.BIG_ROAD_CHANCE = 0
-- Enable that for really big villages (there are also really slow to generate)
--[[mg_villages.VILLAGE_CHECK_RADIUS = 3
mg_villages.VILLAGE_CHECK_COUNT = 3
mg_villages.VILLAGE_CHANCE = 28
mg_villages.VILLAGE_MIN_SIZE = 100
mg_villages.VILLAGE_MAX_SIZE = 150
mg_villages.FIRST_ROADSIZE = 5
mg_villages.BIG_ROAD_CHANCE = 50]]

23
mods/mg_villages/depends.txt Executable file
View File

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

237
mods/mg_villages/fill_chest.lua Executable file
View File

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

73
mods/mg_villages/init.lua Executable file
View File

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

View File

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

193
mods/mg_villages/map_of_world.lua Executable file
View File

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

1079
mods/mg_villages/mapgen.lua Executable file

File diff suppressed because it is too large Load Diff

90
mods/mg_villages/name_gen.lua Executable file
View File

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

101
mods/mg_villages/nodes.lua Executable file
View File

@ -0,0 +1,101 @@
minetest.register_node("mg_villages:road", {
description = "village road",
tiles = {"default_gravel.png", "default_dirt.png"},
is_ground_content = false, -- will not be removed by the cave generator
groups = {crumbly=2}, -- does not fall
sounds = default.node_sound_dirt_defaults({
footstep = {name="default_gravel_footstep", gain=0.5},
dug = {name="default_gravel_footstep", gain=1.0},
}),
})
mg_villages.road_node = minetest.get_content_id( 'mg_villages:road' );
-- do not drop snow on roads
if( moresnow ) then
moresnow.snow_cover[ mg_villages.road_node ] = moresnow.c_air;
end
minetest.register_node("mg_villages:soil", {
description = "Soil found on a field",
tiles = {"default_dirt.png^farming_soil_wet.png", "default_dirt.png"},
drop = "default:dirt",
is_ground_content = true,
groups = {crumbly=3, not_in_creative_inventory=1, grassland = 1},
sounds = default.node_sound_dirt_defaults(),
})
minetest.register_node("mg_villages:desert_sand_soil", {
description = "Desert Sand",
tiles = {"default_desert_sand.png^farming_soil_wet.png", "default_desert_sand.png"},
is_ground_content = true,
drop = "default:desert_sand",
groups = {crumbly=3, not_in_creative_inventory = 1, sand=1, desert = 1},
sounds = default.node_sound_sand_defaults(),
})
-- This torch is not hot. It will not melt snow and cause no floodings in villages.
minetest.register_node("mg_villages:torch", {
description = "Torch",
drawtype = "torchlike",
--tiles = {"default_torch_on_floor.png", "default_torch_on_ceiling.png", "default_torch.png"},
tiles = {
{name="default_torch_on_floor_animated.png", animation={type="vertical_frames", aspect_w=16, aspect_h=16, length=3.0}},
{name="default_torch_on_ceiling_animated.png", animation={type="vertical_frames", aspect_w=16, aspect_h=16, length=3.0}},
{name="default_torch_animated.png", animation={type="vertical_frames", aspect_w=16, aspect_h=16, length=3.0}}
},
inventory_image = "default_torch_on_floor.png",
wield_image = "default_torch_on_floor.png",
paramtype = "light",
paramtype2 = "wallmounted",
sunlight_propagates = true,
is_ground_content = false,
walkable = false,
light_source = LIGHT_MAX-1,
selection_box = {
type = "wallmounted",
wall_top = {-0.1, 0.5-0.6, -0.1, 0.1, 0.5, 0.1},
wall_bottom = {-0.1, -0.5, -0.1, 0.1, -0.5+0.6, 0.1},
wall_side = {-0.5, -0.3, -0.1, -0.5+0.3, 0.3, 0.1},
},
groups = {choppy=2,dig_immediate=3,flammable=1,attached_node=1},
legacy_wallmounted = true,
sounds = default.node_sound_defaults(),
drop = "default:torch",
})
minetest.register_node("mg_villages:plotmarker", {
description = "Plot marker",
drawtype = "nodebox",
tiles = {"default_stone_brick.png"},
paramtype = "light",
paramtype2 = "facedir",
node_box = {
type = "fixed",
fixed = {
{-0.5+2/16, -0.5, -0.5+2/16, 0.5-2/16, -0.5+3/16, 0.5-2/16},
},
},
groups = {cracky=3,stone=2},
on_rightclick = function( pos, node, clicker, itemstack, pointed_thing)
return mg_villages.plotmarker_formspec( pos, nil, {}, clicker )
end,
on_receive_fields = function(pos, formname, fields, sender)
return mg_villages.plotmarker_formspec( pos, formname, fields, sender );
end,
-- protect against digging
can_dig = function( pos, player )
local meta = minetest.get_meta( pos );
if( meta and meta:get_string( 'village_id' )~='' and meta:get_int( 'plot_nr' ) and meta:get_int( 'plot_nr' )>0) then
return false;
end
return true;
end
})

204
mods/mg_villages/protection.lua Executable file
View File

@ -0,0 +1,204 @@
-- get the id of the village pos lies in (or nil if outside of villages)
mg_villages.get_town_id_at_pos = function( pos )
for id, v in pairs( mg_villages.all_villages ) do
local size = v.vs * 3;
if( ( math.abs( pos.x - v.vx ) < size )
and ( math.abs( pos.z - v.vz ) < size )
and ( pos.y - v.vh < 40 and v.vh - pos.y < 10 )) then
local village_noise = minetest.get_perlin(7635, 3, 0.5, 16);
if( mg_villages.inside_village_area( pos.x, pos.z, v, village_noise)) then
local node = minetest.get_node( pos );
if( node
and node.name
and minetest.registered_nodes[ node.name ]
and minetest.registered_nodes[ node.name ].groups
and minetest.registered_nodes[ node.name ].groups.leaves ) then
return nil;
else
return id;
end
end
end
end
return nil;
end
local old_is_protected = minetest.is_protected
minetest.is_protected = function(pos, name)
if( not( mg_villages.ENABLE_PROTECTION )) then
return old_is_protected( pos, name );
end
local village_id = mg_villages.get_town_id_at_pos( pos );
if( village_id ) then
local is_houseowner = false;
for nr, p in ipairs( mg_villages.all_villages[ village_id ].to_add_data.bpos ) do
-- we have located the right plot; the player can build here if he owns this particular plot
if( p.x <= pos.x and (p.x + p.bsizex) >= pos.x
and p.z <= pos.z and (p.z + p.bsizez) >= pos.z) then
if( p.owner and p.owner == name ) then
return false;
-- the player cannot modify other plots, even though he may be house owner of another house and be allowed to modify common ground
else
return true;
end
-- if the player just owns another plot in the village, check if it's one where villagers may live
elseif( p.owner and p.owner == name ) then
local btype = mg_villages.all_villages[ village_id ].to_add_data.bpos[ nr ].btype;
if( btype ~= 'road'
and mg_villages.BUILDINGS[btype]
and mg_villages.BUILDINGS[btype].inh
and mg_villages.BUILDINGS[btype].inh > 0 ) then
is_houseowner = true;
end
end
end
-- players who own a house in town where villagers may live (not only work!)
-- are allowed to modify common ground
if( is_houseowner ) then
return false;
end
return true;
end
return old_is_protected(pos, name);
end
minetest.register_on_protection_violation( function(pos, name)
if( not( mg_villages.ENABLE_PROTECTION )) then
return;
end
local found = mg_villages.get_town_id_at_pos( pos );
if( not( found ) or not( mg_villages.all_villages[ found ])) then
minetest.chat_send_player( name, 'Error: This area does not belong to a village.');
return;
end
minetest.chat_send_player( name, "You are inside of the area of the village "..
tostring( mg_villages.all_villages[ found ].name )..
". The inhabitants do not allow you any modifications.");
end );
mg_villages.plotmarker_formspec = function( pos, formname, fields, player )
-- if( not( mg_villages.ENABLE_PROTECTION )) then
-- return;
-- end
local meta = minetest.get_meta( pos );
if( not( meta )) then
return;
end
local village_id = meta:get_string('village_id');
local plot_nr = meta:get_int( 'plot_nr');
local pname = player:get_player_name();
if( not( village_id ) or not( mg_villages.all_villages ) or not( mg_villages.all_villages[ village_id ] )
or not( plot_nr ) or not( mg_villages.all_villages[ village_id ].to_add_data.bpos[ plot_nr ] )) then
minetest.chat_send_player( pname, 'Error. This plot marker is not configured correctly.'..minetest.serialize({village_id,plot_nr }));
return;
end
local owner = mg_villages.all_villages[ village_id ].to_add_data.bpos[ plot_nr ].owner;
local btype = mg_villages.all_villages[ village_id ].to_add_data.bpos[ plot_nr ].btype;
local price = "default:gold_ingot 2";
if( btype ~= 'road'
and mg_villages.BUILDINGS[btype]
and mg_villages.BUILDINGS[btype].price ) then
price = mg_villages.BUILDINGS[btype].price;
elseif( btype ~= 'road'
and mg_villages.BUILDINGS[btype]
and mg_villages.BUILDINGS[btype].typ
and mg_villages.prices[ mg_villages.BUILDINGS[btype].typ ] )then
price = mg_villages.prices[ mg_villages.BUILDINGS[btype].typ ];
end
--determine prcie depending on building type
local price_stack= ItemStack( price );
local plot_descr = 'Plot No. '..tostring( plot_nr ).. ' with '..tostring( mg_villages.BUILDINGS[btype].scm);
--minetest.chat_send_player( player:get_player_name(),'DATA FOR '..tostring(plot_nr)..': '..minetest.serialize( mg_villages.all_villages[ village_id ].to_add_data.bpos[ plot_nr ] ));
local formspec = "size[8,3]"..
"label[1.0,0.5;Plot No.: "..tostring( plot_nr ).."]"..
"label[2.5,0.5;Building:]"..
"label[3.5,0.5;"..tostring( mg_villages.BUILDINGS[btype].scm ).."]"..
"field[20,20;0.1,0.1;pos2str;Pos;"..minetest.pos_to_string( pos ).."]";
if( owner == pname and fields['abandom'] ) then
formspec = formspec.."label[0,2;You have abandomed this plot.]";
mg_villages.all_villages[ village_id ].to_add_data.bpos[ plot_nr ].owner = nil;
meta:set_string('infotext', plot_descr );
mg_villages.save_data();
elseif( (not(owner) or owner=='') and fields['buy'] ) then
-- check if the price can be paid
local inv = player:get_inventory();
if( inv and inv:contains_item( 'main', price_stack )) then
formspec = formspec.."label[0,0;Congratulations! You have bought this plot.]";
mg_villages.all_villages[ village_id ].to_add_data.bpos[ plot_nr ].owner = pname;
meta:set_string('infotext', plot_descr..' (owned by '..tostring( pname )..')');
-- save the data so that it survives server restart
mg_villages.save_data();
-- substract the price from the players inventory
inv:remove_item( 'main', price_stack );
else
formspec = formspec.."label[0,0;Sorry. You are not able to pay the price.]";
end
end
-- update the owner information
owner = mg_villages.all_villages[ village_id ].to_add_data.bpos[ plot_nr ].owner;
if( owner == pname ) then
formspec = formspec.."label[1,1;This is your plot. You have bought it.]"..
"button_exit[2,2.5;2.0,0.5;abandom;Abandom plot]"..
"button_exit[4,2.5;1.5,0.5;abort;Exit]";
elseif( not( owner ) or owner=="" ) then
formspec = formspec.."label[1,1;You can buy this plot for]"..
"label[3.8,1;"..tostring( price_stack:get_count() ).." x ]"..
"item_image[4.3,0.8;1,1;"..( price_stack:get_name() ).."]"..
"button_exit[2,2.5;1.5,0.5;buy;Buy plot]"..
"button_exit[4,2.5;1.5,0.5;abort;Exit]";
else
formspec = formspec.."label[1,1;"..tostring( owner ).." owns this plot.]"..
"button_exit[3,2.5;1.5,0.5;abort;Exit]";
end
if( btype ~= 'road'
and mg_villages.BUILDINGS[btype]
and mg_villages.BUILDINGS[btype].inh
and mg_villages.BUILDINGS[btype].inh > 0 ) then
if( owner==pname ) then
formspec = formspec.."label[1,1.5;You are allowed to modify the common village area.]";
else
formspec = formspec.."label[1,1.5;Owners of this plot count as village inhabitants.]";
end
end
minetest.show_formspec( pname, "mg_villages:plotmarker", formspec );
end
mg_villages.form_input_handler = function( player, formname, fields)
if( not( mg_villages.ENABLE_PROTECTION )) then
return false;
end
if( (formname == "mg_villages:plotmarker") and fields.pos2str and not( fields.abort )) then
local pos = minetest.string_to_pos( fields.pos2str );
mg_villages.plotmarker_formspec( pos, formname, fields, player );
return true;
end
return false;
end
minetest.register_on_player_receive_fields( mg_villages.form_input_handler )

1012
mods/mg_villages/replacements.lua Executable file

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
mods/mg_villages/schems/c_bar.mts Executable file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

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