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