Add mg_villages dependency

master
Solebull 2018-12-14 15:58:41 +01:00
parent 0325ddf021
commit 2a826c2dc4
235 changed files with 8036 additions and 0 deletions

View File

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

View File

@ -0,0 +1,254 @@
-- helper function for mg_villages.analyze_building_for_mobs_update_paths
-- "res" is the raw data as provided by analyze_file
-- "node_name" is the name of the node we are looking for
mg_villages.analyze_building_for_mobs_search_nodes = function( res, node_name, check_place_for_standing )
local node_positions = {};
-- find out if the building contains any of the nodes we are looking for
local found = -1;
for i,n in ipairs( res.nodenames ) do
if( n == node_name ) then
found = i;
end
end
-- that node does not exist in the building
if( found == -1 ) then
return {};
end
for z = 1, res.size.z do
for y = 1, res.size.y do
for x = 1, res.size.x do
if( res.scm_data_cache[y]
and res.scm_data_cache[y][x]
and res.scm_data_cache[y][x][z]
and res.scm_data_cache[y][x][z][1] == found ) then
if( (check_place_for_standing==false)
) then
-- or ( mob_world_interaction.can_stand_in_node_type( TODO )
-- and mob_world_interaction.can_stand_in_node_type( TODO ))) then
table.insert( node_positions, {x=x, y=y, z=z, p2=res.scm_data_cache[y][x][z][2]});
end
end
end
end
end
return node_positions;
end
-- changes path_info and adds paths from beds and workplaces to front of building
-- path_info[ short_file_name ] contains the paths from the beds
-- path_info[ short_file_name.."|WORKPLACE" ] contains the paths from the workplaces
-- creates building_data.all_entrances
-- creates building_data.workplace_list
mg_villages.analyze_building_for_mobs_update_paths = function( file_name, building_data, path_info )
local short_file_name = string.sub(file_name, mg_villages.file_name_offset, 256);
building_data.short_file_name = short_file_name;
if( not( minetest.get_modpath( "mob_world_interaction" ))) then
return building_data;
end
-- identify front doors and paths to them from the beds
-- TODO: provide a more general list with beds, work places etc.
if( building_data.bed_list
and #building_data.bed_list > 0 ) then
if(not( path_info[ short_file_name ])) then
print("BEDS in "..tostring( short_file_name )..":");
path_info[ short_file_name ] = mob_world_interaction.find_all_front_doors( building_data, building_data.bed_list );
end
-- we are looking for the places in front of the front doors; not the front doors themshelves
building_data.all_entrances = {};
for i,e in ipairs( path_info[ short_file_name ] ) do
-- the last entry in the list for the first bed is what we are looking for
-- (provided there actually is a path)
if( e[1] and #e[1]>0 ) then
table.insert( building_data.all_entrances, e[1][ #e[1] ]);
end
end
end
-- some buildings (i.e. a tavern, school, shop, church, ...) contain places where a mob working
-- there will most likely be standing, awaiting his guests/doing his job. Such places can be
-- manually marked by placing mg_villages:mob_workplace_marker
-- this is diffrent information from the normal bed list
local store_as = short_file_name.."|WORKPLACE";
if(not( path_info[ store_as ] )) then
local workplace_list = mg_villages.analyze_building_for_mobs_search_nodes( building_data, "mg_villages:mob_workplace_marker", false );
if( workplace_list and #workplace_list>0) then
-- store it for later use
building_data.workplace_list = workplace_list;
print("WORKPLACE: "..tostring( building_data.short_file_name )..": "..minetest.serialize( workplace_list ));
path_info[ store_as ] = mob_world_interaction.find_all_front_doors( building_data, workplace_list );
-- if no entrances are known yet, then store them now; the entrances associated with
-- beds are considered to be more important. This here is only a fallback if no beds
-- exist in the house.
if( not( building_data.all_entrances )) then
-- we are looking for the places in front of the front doors; not the front doors themshelves
building_data.all_entrances = {};
for i,e in ipairs( path_info[ store_as ] ) do
-- might just be the place outside the house instead of a door
if( e[1] and #e[1]>0 ) then
table.insert( building_data.all_entrances, e[1][ #e[1] ]);
end
end
end
-- else
-- print("NO workplace found in "..tostring(building_data.short_file_name ));
end
end
--[[
TODO: check if 2 nodes above the target node are air or walkable;
TODO: exceptions to that: bench (only 1 above needs to be walkable)
TODO: other exceptions: furnace, chests, washing place: place in front is wanted - not on top
TODO: search for:
local pos_list = mg_villages.analyze_building_for_mobs_search_nodes( building_data, "farming:soil_wet", true );
farming:soil farming:soil_wet
cottages:straw_ground
default:chest cottages:shelf cottages:chest_storage cottages:chest_private cottages:chest_work
cottages:bench (cottages:table?)
cottages:washing
default:furnace
cottages:barrel (and variants); cottages:tub
default:ladder
default:fence_wood cottages:gate_closed cottages:gate_open
any door...
any hatch...
--]]
-- some debug information
if( mg_villages.DEBUG_LEVEL and mg_villages.DEBUG_LEVEL == mg_villages.DEBUG_LEVEL_TIMING ) then
local str2 = " in "..short_file_name.." ["..building_data.typ.."]";
if( not( path_info[ short_file_name ] )
and not( path_info[ store_as ] )) then
str2 = "nothing of intrest (no bed, no workplace)"..str2;
elseif( path_info[ short_file_name ]
and (#path_info[ short_file_name ]<1
or #path_info[ short_file_name ][1]<1
or #path_info[ short_file_name ][1][1]<1 )) then
str2 = "BROKEN paths for beds"..str2;
elseif( path_info[ store_as ]
and (#path_info[ store_as ]<1
or #path_info[ store_as ][1]<1
or #path_info[ store_as ][1][1]<1 )) then
str2 = "BROKEN paths for workplaces"..str2;
else
if( path_info[ store_as ] ) then
str2 = tostring( #path_info[ store_as ][1]-1 )..
" workplaces"..str2;
else
str2 = "no workplaces"..str2;
end
if( path_info[ short_file_name ] ) then
str2 = tostring( #path_info[ short_file_name ][1]-1 )..
" beds and "..str2;
else
str2 = "no beds and "..str2;
end
end
print( str2 );
end
return building_data;
end
-- Calls mg_villages.analyze_building_for_mobs_update_paths and evaluates the output:
-- * determines the position of front doors (building_data.front_door_list)
-- * position of beds (building_data.bed_list)
-- * places where mobs can stand when they got up from their bed or want
-- to go to bed (building_data.stand_next_to_bed_list)
-- * amount of usable beds in the house (building_data.bed_count)
-- * position of workplaces where a currently working mob may want to
-- stand (i.e. behind a shop's counter, next to a machine, in front
-- of the class/congregation, ..) (building_data.workplace_list)
-- Returns: Updated building_data with the values mentionned above set.
-- building_data Information about the building as gained from registration
-- and from handle_schematics.analze_file(..)
-- file_name with complete path to the schematic
-- path_info Data structure where path_info (paths from doors to beds etc.)
-- is cached.
mg_villages.analyze_building_for_mobs = function( building_data, file_name, path_info )
-- identify front doors, calculate paths from beds/workplaces to front of house
building_data = mg_villages.analyze_building_for_mobs_update_paths( file_name, building_data, path_info );
-- building_data.bed_list and building_data.workspace_list are calculated withhin
-- the above function - provided they are not part of path_info yet;
-- the information stored in path_info is the relevant one for mob movement/pathfinding
-- store the front doors in extra list
building_data.front_door_list = {};
-- gain the list of beds from path_info data
building_data.bed_list = {};
-- mobs are seldom able to stand directly on or even next to the bed when getting up
building_data.stand_next_to_bed_list = {};
-- have any beds been found?
if( building_data.short_file_name
and path_info[ building_data.short_file_name ] ) then
local paths = path_info[ building_data.short_file_name];
if( paths and paths[1] ) then
-- iterate over all bed-to-first-front-door-paths (we want to identify beds)
for i,p in ipairs( paths[1] ) do
-- the last entry has a diffrent meaning
if( p and p[1] and i<#paths[1]) then
-- param2 is the 5th parameter
building_data.bed_list[i] = {p[1][1],p[1][2],p[1][3],p[1][5]};
-- also store where the mob may stand
if( p[2] ) then
building_data.stand_next_to_bed_list[i] = p[2];
end
end
end
-- iterate over all paths and take a look at the first bed only (we want to
-- get the doors now, not the beds)
for i,p in ipairs( paths ) do
-- paths[i]: paths from all beds to front door i
-- paths[i][1]: path from first bed to front door i
if( p and p[1] ) then
-- the place in front of the door is the last entry
local d = p[1][ #p[1] ];
building_data.front_door_list[i] = {d[1],d[2],d[3]};
end
end
end
end
-- make sure this refers to the same data as building_data.bed_list
building_data.bed_count = #building_data.bed_list;
-- gain the list of workplaces from the path_info data
building_data.workplace_list = {};
-- have any workplaces been found?
if( building_data.short_file_name
and path_info[ building_data.short_file_name.."|WORKPLACE" ] ) then
local paths = path_info[ building_data.short_file_name.."|WORKPLACE"];
if( paths and paths[1] ) then
for i,p in ipairs( paths[1] ) do
if( p and p[1] and i<#paths[1]) then
building_data.workplace_list[i] = {p[1][1],p[1][2],p[1][3],p[1][4]};
end
end
-- no front doors found through beds? then take a look if the workplaces found doors
if( #building_data.front_door_list < 1 ) then
for i,p in ipairs( paths ) do
if( p and p[1] ) then
local d = p[1][ #p[1] ];
building_data.front_door_list[i] = {d[1],d[2],d[3]};
end
end
end
end
end
return building_data;
end

View File

@ -0,0 +1,425 @@
-- 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 ].
-- guests Negative value, i.e. -2: 2 of the beds will belong to the family working here; the rest will be guests.
-- For building type "chateau", guest names the number of servants/housemaids instead of guests.
mg_villages.all_buildings_list = {}
local buildings = {
-- the houses the mod came with
{yoff= 0, scm="house_1_0", 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='lamp', 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_1_0", typ='house', weight={nore=1, single=2 }, inh=2},
{yoff= 0, scm="house_with_garden_1_0", typ='house', weight={nore=1, single=2 }, inh=3},
{yoff= 0, scm="church_1_0", pervillage=1, typ='church', weight={nore=1 }, inh=-1},
{yoff= 0, scm="tower_1_0", typ='tower', weight={nore=1/7, single=1 }, inh=-1},
{yoff= 0, scm="forge_1_0", pervillage=2, typ='forge', weight={nore=1, single=1/3 }, inh=-1},
{yoff= 0, scm="library_1_0", pervillage=2, typ='library', weight={nore=1 }, inh=-1},
{yoff= 0, scm="inn_1_0", pervillage=4, typ='inn', weight={nore=1/2, single=1/3 }, inh=-1, guests=-2}, -- has room for 4 guests
{yoff= 0, scm="pub_1_0", pervillage=2, typ='tavern', weight={nore=1/3, single=1/3 }, 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={1}, 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, guests=-2}, -- +5 guests
{yoff= 0, scm="logcabinpub2", orients={1}, weight={logcabin=1/6, single=1}, pervillage=1, typ='tavern', axis=1, inh=2, guests=-3}, -- +8 guests
{yoff= 0, scm="logcabinpub3", orients={1}, weight={logcabin=1/6, single=1}, pervillage=1, typ='tavern', axis=1, inh=2, guests=-4}, -- +12 guest
-- grass huts (requiring cottages, dryplants, cavestuff/undergrowth, plantlife)
{yoff= 0, scm="grasshut1_1_90", weight={grasshut=1, single=1}, nomirror=1, typ='hut'},
{yoff= 0, scm="grasshut2_1_90", weight={grasshut=1, single=1}, nomirror=1, typ='townhall'}, -- community hut for meetings
{yoff= 0, scm="grasshut3_1_90", weight={grasshut=1, single=1}, nomirror=1, typ='hut'},
{yoff= 0, scm="grasshut4_1_90", weight={grasshut=1, single=1}, nomirror=1, typ='hut'},
{yoff= 0, scm="grasshut5_1_90", weight={grasshut=1, single=1}, nomirror=1, typ='hut'},
{yoff= 0, scm="grasshut6_1_90", weight={ single=1}, nomirror=1, typ='hut'},
{yoff= 0, scm="grasshutcenter_1_90", pervillage=1, weight={grasshut=2}, nomirror=1, 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={1}, 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={3}, 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, guests=-3}, -- 19 beds: 10 guest, 3 worker, 6 family
{scm="taverne_2", yoff= 0, orients={0}, farming_plus=0, avoid='', typ='tavern', weight={medieval=1/2, single=1/3}, pervillage=1, inh=2}, -- no guests
{scm="taverne_3", yoff= 0, orients={0}, farming_plus=0, avoid='', typ='tavern', weight={medieval=1/2, single=1/3}, pervillage=1, inh=2}, -- no guests
{scm="taverne_4", yoff= 0, orients={0}, farming_plus=0, avoid='', typ='tavern', weight={medieval=1/2, single=1/3}, pervillage=1, inh=1}, -- no guests
{scm="well_1", yoff= 0, orients={0}, farming_plus=0, avoid='well', typ='well', weight={medieval=1/12, single=1/2}, pervillage=4},
{scm="well_2", yoff= 0, orients={0}, farming_plus=0, avoid='well', typ='well', weight={medieval=1/12, single=1/2}, pervillage=4},
{scm="well_3", yoff= 0, orients={0}, farming_plus=0, avoid='well', typ='well', weight={medieval=1/12, single=1/2}, pervillage=4},
{scm="well_4", yoff= 0, orients={0}, farming_plus=0, avoid='well', typ='well', weight={medieval=1/12, single=1/2}, pervillage=4},
{scm="well_5", yoff= 0, orients={0}, farming_plus=0, avoid='well', typ='well', weight={medieval=1/12, single=1/2}, pervillage=4},
{scm="well_6", yoff= 0, orients={0}, farming_plus=0, avoid='well', typ='well', weight={medieval=1/12, single=1/2}, pervillage=4},
{scm="well_7", yoff= -1, orients={0}, farming_plus=0, avoid='well', typ='well', weight={medieval=1/12, single=1/2}, pervillage=4},
{scm="well_8", yoff= 0, orients={0}, farming_plus=0, avoid='well', typ='well', weight={medieval=1/12, single=1/2}, pervillage=4},
{scm="allmende_3_90", yoff=-2, orients={0}, farming_plus=0, avoid='', typ='allmende', weight={medieval=3,taoki=3,nore=3,logcabin=1,grasshut=1}, pervillage=1},
{scm="tree_place_1", yoff= 1, orients={0}, farming_plus=0, avoid='', typ='village_square', weight={medieval=1/12}, pervillage=1},
{scm="tree_place_2", yoff= 1, orients={0}, farming_plus=0, avoid='', typ='village_square', weight={medieval=1/12}, pervillage=1},
{scm="tree_place_3", yoff= 1, orients={0}, farming_plus=0, avoid='', typ='village_square', weight={medieval=1/12}, pervillage=1},
{scm="tree_place_4", yoff= 1, orients={0}, farming_plus=0, avoid='', typ='village_square', weight={medieval=1/12}, pervillage=1},
{scm="tree_place_5", yoff= 1, orients={0}, farming_plus=0, avoid='', typ='village_square', weight={medieval=1/12}, pervillage=1},
{scm="tree_place_6", yoff= 1, orients={0}, farming_plus=0, avoid='', typ='village_square', weight={medieval=1/12}, pervillage=1},
{scm="tree_place_7", yoff= 1, orients={0}, farming_plus=0, avoid='', typ='village_square', weight={medieval=1/12}, pervillage=1},
{scm="tree_place_8", yoff= 1, orients={0}, farming_plus=0, avoid='', typ='village_square', weight={medieval=1/12}, pervillage=1},
{scm="tree_place_9", yoff= 1, orients={0}, farming_plus=0, avoid='', typ='village_square', weight={medieval=1/12}, pervillage=1},
{scm="tree_place_10", yoff= 1, orients={0}, farming_plus=0, avoid='', typ='village_square', weight={medieval=1/12}, pervillage=1},
{scm="wagon_1", yoff= 0, orients={0,1,2,3}, farming_plus=0, avoid='', typ='wagon', weight={medieval=1/12,tent=1/3}, axis=1},
{scm="wagon_2", yoff= 0, orients={0,1,2,3}, farming_plus=0, avoid='', typ='wagon', weight={medieval=1/12,tent=1/3}, axis=1},
{scm="wagon_3", yoff= 0, orients={0,1,2,3}, farming_plus=0, avoid='', typ='wagon', weight={medieval=1/12,tent=1/3}, axis=1},
{scm="wagon_4", yoff= 0, orients={0,1,2,3}, farming_plus=0, avoid='', typ='wagon', weight={medieval=1/12,tent=1/3}, axis=1},
{scm="wagon_5", yoff= 0, orients={0,1,2,3}, farming_plus=0, avoid='', typ='wagon', weight={medieval=1/12,tent=1/3}, axis=1},
{scm="wagon_6", yoff= 0, orients={0,1,2,3}, farming_plus=0, avoid='', typ='wagon', weight={medieval=1/12,tent=1/3}, axis=1},
{scm="wagon_7", yoff= 0, orients={0,1,2,3}, farming_plus=0, avoid='', typ='wagon', weight={medieval=1/12,tent=1/3}, axis=1},
{scm="wagon_8", yoff= 0, orients={0,1,2,3}, farming_plus=0, avoid='', typ='wagon', weight={medieval=1/12,tent=1/3}, axis=1},
{scm="wagon_9", yoff= 0, orients={0,1,2,3}, farming_plus=0, avoid='', typ='wagon', weight={medieval=1/12,tent=1/3}, axis=1},
{scm="wagon_10", yoff= 0, orients={0,1,2,3}, farming_plus=0, avoid='', typ='wagon', weight={medieval=1/12,tent=1/3}, axis=1},
{scm="wagon_11", yoff= 0, orients={0,1,2,3}, farming_plus=0, avoid='', typ='wagon', weight={medieval=1/12,tent=1/3}, axis=1},
{scm="wagon_12", yoff= 0, orients={0,1,2,3}, farming_plus=0, avoid='', typ='wagon', weight={medieval=1/12,tent=1/3}, axis=1},
{scm="bench_1", yoff= 0, orients={0,1,2}, farming_plus=0, avoid='', typ='bench', weight={medieval=1/12}, nomirror=1},
{scm="bench_2", yoff= 0, orients={0,1,2}, farming_plus=0, avoid='', typ='bench', weight={medieval=1/12}, nomirror=1},
{scm="bench_3", yoff= 0, orients={0,1,2}, farming_plus=0, avoid='', typ='bench', weight={medieval=1/12}, nomirror=1},
{scm="bench_4", yoff= 0, orients={0,1,2}, farming_plus=0, avoid='', typ='bench', weight={medieval=1/12}, nomirror=1},
{scm="shed_1", yoff= 0, orients={0,1,2}, farming_plus=0, avoid='', typ='shed', weight={medieval=1/10}},
{scm="shed_2", yoff= 0, orients={0,1,2}, farming_plus=0, avoid='', typ='shed', weight={medieval=1/10}},
{scm="shed_3", yoff= 0, orients={0,1,2}, farming_plus=0, avoid='', typ='shed', weight={medieval=1/10}},
{scm="shed_5", yoff= 0, orients={0,1,2}, farming_plus=0, avoid='', typ='shed', weight={medieval=1/10}},
{scm="shed_6", yoff= 0, orients={0,1,2}, farming_plus=0, avoid='', typ='shed', weight={medieval=1/10}},
{scm="shed_7", yoff= 0, orients={0,1,2}, farming_plus=0, avoid='', typ='shed', weight={medieval=1/10}},
{scm="shed_8", yoff= 0, orients={0,1,2}, farming_plus=0, avoid='', typ='shed', weight={medieval=1/10}},
{scm="shed_9", yoff= 0, orients={0,1,2}, farming_plus=0, avoid='', typ='shed', weight={medieval=1/10}},
{scm="shed_10", yoff= 0, orients={0,1,2}, farming_plus=0, avoid='', typ='shed', weight={medieval=1/10}},
{scm="shed_11", yoff= 0, orients={0,1,2}, farming_plus=0, avoid='', typ='shed', weight={medieval=1/10}},
{scm="shed_12", yoff= 0, orients={0,1,2}, farming_plus=0, avoid='', typ='stable', 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='horsestable', 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={0}, avoid='', typ='inn', weight={lumberjack=1, single=1}, axis=1, inh=16, guests=-1}, -- all but one of the 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={1}, 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={1}, avoid='', typ='trader', weight={claytrader=3, single=3}, inh=2},
{scm="trader_clay_4", yoff= 1, orients={1}, 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=-1, orients={0,1,2,3}, avoid='', typ='pit', weight={claytrader=1}},
{scm="clay_pit_3", yoff=-6, 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='park', weight={taoki=1 }, axis=1},
{scm="default_town_tower", yoff= 1, orients={1}, farming_plus=0, avoid='', typ='tower', weight={taoki=1/6, single=1}, axis=1, inh=-1},
{scm="default_town_well", yoff= -6, orients={1}, farming_plus=0, avoid='', typ='well', weight={taoki=1/4 }, axis=1},
{scm="default_town_fountain", yoff= 1, orients={1}, farming_plus=0, avoid='', typ='fountain',weight={taoki=1/4 }, axis=1},
-- the hotel seems to be only the middle section of the building; it's build for another spawning algorithm
-- {scm="default_town_hotel", yoff= -1, orients={1}, farming_plus=0, avoid='', typ='house', weight={taoki=1/5}},
{scm="tent_tiny_1", yoff=0, orients={3}, farming_plus=0, avoid='', typ='tent', weight={tent=1, single=1}, inh=1},
{scm="tent_tiny_2", yoff=0, orients={3}, farming_plus=0, avoid='', typ='tent', weight={tent=1, single=1}, inh=1},
{scm="tent_big_1", yoff=0, orients={1}, farming_plus=0, avoid='', typ='shop', 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={1}, 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='shed', weight={tent=1/2, single=1}, inh=3},
{scm="tent_medium_3", yoff=0, orients={1}, farming_plus=0, avoid='', typ='tent', weight={tent=1/2, single=1}, inh=3},
{scm="tent_medium_4", yoff=0, orients={1}, 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='pub', weight={tent=1/5}},
{scm="tent_open_2", yoff=0, orients={3}, farming_plus=0, avoid='', typ='shed', weight={tent=1/5}},
{scm="tent_open_3", yoff=0, orients={3}, farming_plus=0, avoid='', typ='shop', weight={tent=1/5}},
{scm="tent_open_big_1", yoff=0, orients={3}, farming_plus=0, avoid='', typ='pub', weight={tent=1/5}},
{scm="tent_open_big_2", yoff=0, orients={3}, farming_plus=0, avoid='', typ='church', weight={tent=1/5}},
{scm="tent_open_big_3", yoff=0, orients={3}, farming_plus=0, avoid='', typ='townhall', weight={tent=5}, pervillage=1},
{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, guests=-6}, -- 6 family members of the landlord's family; rest are servants
{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=4,
weight={nore=1,taoki=1,medieval=1,charachoal=1,lumberjack=1,claytrader=1,logcabin=1,canadian=1,grasshut=1,tent=1}},
{scm="empty_2", yoff=0, typ='empty', inh=1, pervillage=4,
weight={nore=1,taoki=1,medieval=1,charachoal=1,lumberjack=1,claytrader=1,logcabin=1,canadian=1,grasshut=1,tent=1}},
{scm="empty_3", yoff=0, typ='empty', inh=1, pervillage=4,
weight={nore=1,taoki=1,medieval=1,charachoal=1,lumberjack=1,claytrader=1,logcabin=1,canadian=1,grasshut=1,tent=1}},
{scm="empty_4", yoff=0, typ='empty', inh=1, pervillage=4,
weight={nore=1,taoki=1,medieval=1,charachoal=1,lumberjack=1,claytrader=1,logcabin=1,canadian=1,grasshut=1,tent=1}},
{scm="empty_5", yoff=0, typ='empty', inh=1, pervillage=4,
weight={nore=1,taoki=1,medieval=1,charachoal=1,lumberjack=1,claytrader=1,logcabin=1,canadian=1,grasshut=1,tent=1}},
{scm="house_medieval_fancy_1_90", yoff= 0, orients={0}, farming_plus=0, avoid='', typ='farm_full', weight={medieval=1/4, single=1 }, inh=6},
{scm="cow_shed_1_270", yoff= 0, orients={0}, farming_plus=0, avoid='', typ='stable', weight={medieval=1/4, single=1 }, inh=-1},
{scm="shed_with_forge_v2_1_0", yoff= 0, orients={0}, farming_plus=0, avoid='', typ='forge', weight={medieval=1,single=1/2}, inh=1},
{scm="empty_16x32_2_90", typ='empty', inh=1, pervillage=4,
weight={nore=2,taoki=2,medieval=2,charachoal=2,lumberjack=2,claytrader=2,logcabin=2,canadian=2,grasshut=2,tent=2}},
{scm="empty_32x32_2_90", typ='empty', inh=1, pervillage=4,
weight={nore=2,taoki=2,medieval=2,charachoal=2,lumberjack=2,claytrader=2,logcabin=2,canadian=2,grasshut=2,tent=2}},
-- some new grasshut variants
{scm="grasshut7_1_90", weight={grasshut=1, single=1}, nomirror=1, typ='hut'},
{scm="grasshut8_1_90", weight={grasshut=1, single=1}, nomirror=1, typ='hut'},
{scm="grasshut9_1_90", weight={grasshut=1, single=1}, nomirror=1, typ='hut'},
{scm="grasshut_pub_1_90", weight={grasshut=1/4, single=1}, nomirror=1, typ='pub'},
{scm="grasshut_hotel_1_90", weight={grasshut=1/4, single=1}, nomirror=1, typ='inn'},
{scm="grasshut_shop_1_90", weight={grasshut=1, single=1}, nomirror=1, typ='shop'},
{scm="grasshutwell_8_90", weight={grasshut=1, single=1}, nomirror=1, typ='well'},
}
-- 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
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
-- read the size of the building;
-- convert to .mts for later usage if necessary
-- true: no entry in the build_chest (we did that manually already)
local res = handle_schematics.analyze_file( file_name, building_data.we_origin, building_data.mts_path .. building_data.scm, building_data, true );
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
building_data = res;
-- identify front doors, calculate paths from beds/workplaces to front of house
building_data = mg_villages.analyze_building_for_mobs( building_data, file_name, mg_villages.path_info);
-- 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' );
-- information about beds, positions to stand next to the beds, paths to the doors, and doors
--mg_villages.path_info = {};
--mg_villages.path_info = save_restore.restore_data( 'mg_villages_path_info.data' );
mg_villages.file_name_offset = string.len( minetest.get_modpath( "mg_villages" ))-11+1;
-- 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 = {}}
-- save the path data; wait a bit so that all mods may have registered their buildings
-- Note: uncomment the following line if you have added a larger amount of new buildings; then, after
-- WORLDNAME/mg_villages_path_info.data has been written, copy that file over to your
-- mods/mg_villages/ folder and replace the old one. Add
-- mg_villages.path_info = ..
-- at the start of the table so that a dofile can execute it.
--minetest.after( 10, save_restore.save_data, 'mg_villages_path_info.data', mg_villages.path_info );

View File

@ -0,0 +1,279 @@
minetest.register_privilege("mg_villages", { description = "Allows to teleport to villages via /vist <nr>", give_to_singleplayer = false});
-- store per player which list of villages was offered
mg_villages.tmp_player_village_list = {};
-- list all plots of a village:
-- plot_nr, type of building, #inhabitants, occupation, name
mg_villages.list_plots_formspec = function( player, formname, fields )
if( not( player ) or fields.quit or not( fields.village_id) or not( mg_villages.all_villages[ fields.village_id ])) then
return
end
local pname = player:get_player_name();
-- analyze the road network (this has not been done from the beginning..)
mg_villages.get_road_list( fields.village_id, false );
-- allow to click through the villages using prev/next buttons
local liste = mg_villages.tmp_player_village_list[ pname ];
local prev_next_button = "button[8.5,11.6;1,0.5;back_to_villagelist;Back]";
if( liste and #liste>1 and liste[1]~=fields.village_id ) then
prev_next_button = prev_next_button..'button[9.5,11.6;1,0.5;prev;Prev]';
end
if( liste and #liste>1 and liste[#liste]~=fields.village_id ) then
prev_next_button = prev_next_button..'button[10.5,11.6;1,0.5;next;Next]';
end
local formspec = 'size[12,12]'..
'field[20,20;0.1,0.1;village_id;VillageID;'..minetest.formspec_escape( fields.village_id ).."]"..
'button_exit[4.0,1.0;2,0.5;quit;Exit]'..
'button[9.5,1.0;3,0.5;back_to_villagelist;Back to village list]'..
prev_next_button..
'tablecolumns[' ..
'text,align=right;'.. -- plot nr
'text,align=center;'.. -- type of building
'text,align=center;'.. -- amount of inhabitants
'text,align=center;'.. -- occupation of first inhabitant
'text,align=center;'.. -- name of first inhabitat
'text,align=center]'.. -- comment
'table[0.1,2.0;11.4,8.8;'..formname..';'..
'PlotNr,Type of building,'..minetest.formspec_escape('#Inhab.')..
',Job,Owner,Comment,';
local bpos_list = mg_villages.all_villages[ fields.village_id ].to_add_data.bpos;
for plot_nr,bpos in ipairs( bpos_list ) do
formspec = formspec..plot_nr..',';
if( bpos.btype and bpos.btype ~= "road" and mg_villages.BUILDINGS[ bpos.btype ]) then
formspec = formspec..mg_villages.BUILDINGS[ bpos.btype ].typ..',';
else
formspec = formspec..tostring( bpos.btype )..',';
end
if( not( bpos.beds ) or #bpos.beds<1 ) then
if( bpos.worker and bpos.worker.lives_at and bpos_list[ bpos.worker.lives_at ]
and bpos_list[ bpos.worker.lives_at ].beds
and bpos_list[ bpos.worker.lives_at ].beds[1]) then
local btype2 = mg_villages.BUILDINGS[ bpos_list[ bpos.worker.lives_at ].btype];
local worker_plot = bpos_list[ bpos.worker.lives_at ];
formspec = formspec..'-,'..
( worker_plot.beds[1].title or '-')..','..
( worker_plot.beds[1].first_name or '-')..','..
"lives in the "..tostring( btype2.typ ).." on plot "..tostring( bpos.worker.lives_at )..',';
elseif( bpos.belongs_to and bpos_list[ bpos.belongs_to ]) then
formspec = formspec..'-,-,-,';
local owner_plot = bpos_list[ bpos.belongs_to ];
if( owner_plot and owner_plot.beds and owner_plot.beds[1] ) then
formspec = formspec.."owned by "..
( owner_plot.beds[1].title or '?')..' '..
( owner_plot.beds[1].first_name or '?')..
minetest.formspec_escape(" [plot "..tostring( bpos.belongs_to )..']')..',';
else
formspec = formspec.."owned by "..minetest.formspec_escape(" [plot "..tostring( bpos.belongs_to )..']')..',';
end
elseif( bpos.btype == "road" ) then
if( not( bpos.parent_road_plot )) then
formspec = formspec..'-,-,-,road stump,';
elseif( bpos.parent_road_plot==0 ) then
formspec = formspec..'-,-,-,main road,';
else
formspec = formspec..'-,-,-,road nr. '..tostring( bpos.road_nr)..
minetest.formspec_escape(', sideroad of ');
if( bpos_list[ bpos.parent_road_plot ].parent_road_plot == 0 ) then
formspec = formspec..'the main road,';
else
formspec = formspec..'road nr. '..
tostring( bpos_list[ bpos.parent_road_plot ].road_nr)..',';
end
end
else
formspec = formspec..'-,-,-,-,';
end
else
formspec = formspec..tostring( #bpos.beds )..','..
( bpos.beds[1].title or '-')..','..
( bpos.beds[1].first_name or '-')..',';
if( bpos.beds[1].works_at
and bpos.beds[1].works_at ~= plot_nr
and bpos_list[ bpos.beds[1].works_at ]) then
local btype2 = mg_villages.BUILDINGS[ bpos_list[ bpos.beds[1].works_at].btype];
formspec = formspec.."works at the "..tostring( btype2.typ ).." on plot "..tostring(bpos.beds[1].works_at)..",";
else
formspec = formspec.."-,";
end
end
end
formspec = formspec..';1]';
minetest.show_formspec( pname, formname, formspec );
end
-- list all villages withhin a certain range of the player's position:
-- village_nr, distance from player, name of village, population,
-- type (i.e. "medieval"), x, y, z, diameter, #buildings, village/single house
-- this function is only used for the chat command "/villages" 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.0;2,0.5;quit;Exit]'..
'tablecolumns[' ..
'text,align=right;'.. -- village number
'text,align=right;'.. -- distance from player
'text,align=center;'.. -- name of village
'text,align=center;'.. -- inhabitants
'text,align=center;'.. -- typ of village
'text,align=right;'.. -- x
'text,align=right;'.. -- y
'text,align=right;'.. -- z
'text,align=right;'.. -- size
'text,align=center;'.. -- #houses where inhabitants may live or work
'text,align=right]'..
'table[0.1,2.0;11.4,8.8;'..formname..';'..
'Nr,Dist,Name of village,Population,Type of village,_X_,_H_,_Z_,Size,'..minetest.formspec_escape('#Buildings')..',,';
mg_villages.tmp_player_village_list[ pname ] = {};
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
-- count the inhabitants
if( not( v.population )) then
v.population = 0;
for _,pos in ipairs( v.to_add_data.bpos ) do
if( pos and pos.beds ) then
v.population = v.population + #pos.beds;
end
end
end
local show_population = v.population;
if( show_population == 0 ) then
show_population = "-";
end
formspec = formspec..
v.nr..','..
tostring( math.floor( dist ))..','..
tostring( v.name or 'unknown' )..','..
show_population..','..
v.village_type..','..
tostring( v.vx )..','..
tostring( v.vh )..','..
tostring( v.vz )..','..
tostring( v.vs )..','..
tostring( v.anz_buildings )..','..
tostring( is_full_village )..',';
-- store which list we have shown to this particular player
table.insert( mg_villages.tmp_player_village_list[ pname ], k );
end
end
formspec = formspec..';1]';
-- 'tabheader[0.1,2.6;spalte;Nr,Dist,Name of village,Population,Type of village,_X_,_H_,_Z_,Size,'..minetest.formspec_escape('#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_villages:formspec_list_villages", {});
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
});
minetest.register_chatcommand( 'village_mob_repopulate', {
description = "Discards old mob data and assigns beds and workplaces anew. Mobs get new names.",
params = "<village number>",
privs = {},
func = function(name, param)
if( not( minetest.check_player_privs( name, {protection_bypass=true}))) then
minetest.chat_send_player( name, "You need the 'protection_bypass' priv in order to delete all the old mob data of a village and to recalculate it anew.");
return;
end
if( not( param ) or param == "" ) then
minetest.chat_send_player( name, "Which village do you want to repopulate? 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, "Deleting information about workplaces and beds. Recalculating. Assigning new data for village no. "..tostring( v.nr )..", called "..( tostring( v.name or 'unknown'))..".");
-- move the player to the center of the village he just changed
local player = minetest.get_player_by_name( name );
player:moveto( { x=v.vx, y=(v.vh+1), z=v.vz }, false);
local village_id = tostring( v.vx )..':'..tostring( v.vz );
-- actually do the reassigning
mg_villages.inhabitants.assign_mobs( v, village_id, true);
-- save the modified data
save_restore.save_data( 'mg_all_villages.data', mg_villages.all_villages );
-- adjust beds and workplaces
mg_villages.inhabitants.prepare_metadata( v, village_id, nil, nil );
return;
end
end
-- no village found
minetest.chat_send_player( name, "There is no village with the number "..tostring( param ).." (yet?).");
end
});

206
mods/mg_villages/config.lua Normal file
View File

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

View File

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

View File

@ -0,0 +1,203 @@
-- provide a list of roads; function mg_villages.identify_parent_roads got lost somehow
mg_villages.extra_show_road_list = function( village_id )
if( not( village_id ) or not( mg_villages.all_villages[ village_id ] )) then
return;
end
local str = "List of roads:\n";
local bpos_list = mg_villages.all_villages[ village_id ].village.to_add_data.bpos;
-- find out which road branches off from which other road
mg_villages.identify_parent_roads( bpos_list );
for i,pos in ipairs( bpos_list ) do
if( pos.btype and pos.btype=="road" ) then
str = str.." Plot "..tostring(i)..": road nr. "..tostring(pos.road_nr)..
" branching off from road on plot nr "..tostring(pos.parent_road)..
"\n data: "..minetest.serialize( pos ).."\n";
end
end
minetest.chat_send_player(pname, str );
end
-- DEPRECATED
-- search the trader (who is supposed to be at the given position) and
-- spawn a new one in case he went missing
mg_villages.plotmarker_search_trader = function( trader, height )
local obj_list = minetest.get_objects_inside_radius({x=trader.x, y=height, z=trader.z}, 10 );
for i,obj in ipairs( obj_list ) do
local e = obj:get_luaentity();
if( e and e.object ) then
local p = e.object:getpos();
if( p and p.x and math.abs(p.x-trader.x)<1.5
and p.z and math.abs(p.z-trader.z)<1.5
and e.name and e.name=="mobf_trader:trader"
and e.trader_typ and e.trader_typ==trader.typ) then
-- minetest.chat_send_player( "singleplayer", "FOUND trader "..tostring( e.trader_typ)); --TODO
end
end
end
end
-- TODO: deprecated; but may be useful in a new form for mobs that live in the house
mg_villages.plotmarker_list_traders = function( plot, formspec )
if( not( plot ) or not( plot.traders )) then
return formspec;
end
if( #plot.traders > 1 ) then
formspec = formspec.."label[0.3,7.0;Some traders live here. One works as a "..tostring(plot.traders[1].typ)..".]";
for i=2,#plot.traders do
formspec = formspec.."label[0.3,"..(6.0+i)..";Another trader works as a "..tostring(plot.traders[i].typ)..".]";
end
elseif( plot.traders[1] and plot.traders[1].typ) then
formspec = formspec..
"label[0.3,7.0;A trader lives here. He works as a "..tostring( plot.traders[1].typ )..".]";
else
formspec = formspec..
"label[0.3,7.0;No trader currently works at this place.]";
end
-- add buttons for visiting (teleport to trader), calling (teleporting trader to plot) and firing the trader
for i,trader in ipairs(plot.traders) do
local trader_entity = mg_villages.plotmarker_search_trader( trader, village.vh );
formspec = formspec..
"button[6.0,"..(6.0+i)..";1.2,0.5;visit_trader_"..i..";visit]"..
"button[7.4,"..(6.0+i)..";1.2,0.5;call_trader_"..i..";call]"..
"button[8.8,"..(6.0+i)..";1.2,0.5;fire_trader_"..i..";fire]";
if( fields[ "visit_trader_"..i ] ) then
player:moveto( {x=trader.x, y=(village.vh+1), z=trader.z} );
minetest.chat_send_player( pname, "You are visiting the "..tostring( trader.typ )..
" trader, who is supposed to be somewhere here. He might also be on a floor above you.");
return formspec;
end
if( fields[ "visit_call_"..i ] ) then
-- TODO: spawning: mob_basics.spawn_mob( {x=v.x, y=v.y, z=v.z}, v.typ, nil, nil, nil, nil, true );
end
-- TODO: fire mob
end
formspec = formspec.."button[3.75,"..(7.0+math.max(1,#plot.traders))..";3.5,0.5;hire_trader;Hire a new random trader]";
-- TODO: hire mob
return formspec;
end
-- provide debug information about mobs, let mobf_traders work around to some degree etc
mg_villages.mob_spanwer_on_rightclick = function( pos, node, clicker, itemstack, pointed_thing)
if( not( clicker )) 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 bed_nr = meta:get_int( "bed_nr" );
-- direction for the mob to look at
local yaw = meta:get_int( "yaw" );
local mob_info = mg_villages.inhabitants.get_mob_data( village_id, plot_nr, bed_nr );
local str = "Found: ";
local mob_pos = nil;
local mob = nil;
if( mob_info.mob_id and mob_basics) then
mob = mob_basics.find_mob_by_id( mob_info.mob_id, "trader" );
if( mob ) then
mob_pos = mob.object:getpos();
if( mob_pos and mob_pos.x == pos.x and mob_pos.z == pos.z ) then
str = str.." yes, waiting right here. ";
mob.trader_does = "stand";
-- TODO: detect "in his bed"
elseif( mob.trader_does == "sleep" and mob.trader_uses and mob.trader_uses.x ) then
str = str.." yes, sleeping in bed at "..minetest.pos_to_string( mob.trader_uses )..". ";
else
str = str.." yes, at "..minetest.pos_to_string( mob_pos)..". Teleporting here.";
mob.trader_does = "stand";
mob_world_interaction.stand_at( mob, pos, yaw );
end
else
str = str.." - not found -. ";
end
end
local res = mg_villages.get_plot_and_building_data( village_id, plot_nr );
if( not( res ) or not( res.bpos ) or not( mob_info.mob_id ) or not( mob ) or not( mob_world_interaction) or not( movement)) then
minetest.chat_send_player( clicker:get_player_name(), str.."Mob data: "..minetest.serialize(mob_info));
return;
end
-- use door_nr 1;
local path = nil;
if( mob and mob.trader_does == "sleep" ) then
path = mg_villages.get_path_from_bed_to_outside( village_id, plot_nr, bed_nr, 1 );
-- get out of the bed, walk to the middle of the front of the house
if( path and #path>0 ) then
mob_world_interaction.stand_at( mob, path[1], yaw );
-- last step: go back to the mob spawner that belongs to the mob
table.insert( path, pos );
str = str.." The mob plans to get up from his bed and stand in front of his house.\n";
else
str = str.." FAILED to get a path from bed to outside.\n";
end
else
-- go to bed and sleep
path = mg_villages.get_path_from_outside_to_bed( village_id, plot_nr, bed_nr, 1 );
str = str.." The mob plans to go to his bed and start sleeping.\n";
-- local target_plot_nr = 9; -- just for testing..
-- path = mg_villages.get_path_from_pos_to_plot_via_roads( village_id, pos, target_plot_nr );
-- str = str.." The mob plans to go to plot nr. "..tostring(target_plot_nr).."\n";
end
local move_obj = movement.getControl(mob);
move_obj:walk_path( path, 1, {find_path == true});
minetest.chat_send_player( clicker:get_player_name(), str.."Mob data: "..minetest.serialize(mob_info));
end
-- check if all mobs have beds and paths from beds to mob spawners in front of the house can be calculated;
-- deprecated since pathfinding is now done in the blueprints
mg_villages.debug_inhabitants = function( village, plot_nr)
if( not( minetest.get_modpath( "mob_world_interaction"))) then
return;
end
-- TODO: only for testing
local bpos = village.to_add_data.bpos[ plot_nr ];
if( bpos and bpos.beds ) then
for i,bed in ipairs( bpos.beds ) do
-- find a place next to the bed where the mob can stand
local p_next_to_bed = mob_world_interaction.find_place_next_to( bed, 0, {x=0,y=0,z=0});
if( not( p_next_to_bed ) or p_next_to_bed.iteration==99 ) then
minetest.chat_send_player("singleplayer", "Bed Nr. "..tostring(i).." at "..minetest.pos_to_string( bed )..": FAILED to find a place to stand.");
else
-- position in front of the building, with the building stretching equally to the right and left
-- get a diffrent one for each mob
local p_in_front = handle_schematics.get_pos_in_front_of_house( bpos, i );
local path = mob_world_interaction.find_path( p_next_to_bed, p_in_front, { collisionbox = {1,0,3,4,2}});
local str = "";
if( path ) then
str = str.."Bed Nr. "..tostring(i).." at "..minetest.pos_to_string( bed )..", standing at "..minetest.pos_to_string( p_next_to_bed )..": "..tostring( table.getn( path )).." Steps to outside.";
local front_door_pos = nil;
for j,p in ipairs( path ) do
local n = minetest.get_node( p );
if( n and n.name and mob_world_interaction.door_type[ n.name ]=="door_a_b") then
front_door_pos = p;
end
end
if( front_door_pos ) then
str = str.." Front door found at: "..minetest.pos_to_string( front_door_pos );
end
else
str = str.." FAILED to find a path from bed "..minetest.pos_to_string(bed )..", standing at "..minetest.pos_to_string( p_next_to_bed )..", to front of house.";
end
minetest.chat_send_player("singleplayer", str );
end
end
end
end

File diff suppressed because it is too large Load Diff

88
mods/mg_villages/init.lua Normal file
View File

@ -0,0 +1,88 @@
-- 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")
-- fill mg_villages.all_buildings_list with precalculated paths
dofile(mg_villages.modpath.."/mg_villages_path_info.data");
-- 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")
-- calls path calculation and stores front doors etc.; only called in mg_villages.add_building
dofile(mg_villages.modpath.."/analyze_building_for_mobs.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")
-- determine type of work, name, age, bed position etc. for villagers (none included!)
dofile(mg_villages.modpath.."/inhabitants.lua")
-- provides some extra functionality for development of mob mods etc.;
-- contains some deprecated functions
dofile(mg_villages.modpath.."/extras_for_development.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")
-- allows to buy/sell/restore/.. plots and their buildings
dofile(mg_villages.modpath.."/plotmarker_formspec.lua")
-- create and show a map of the world
dofile(mg_villages.modpath.."/map_of_world.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")
-- reconstruct the connection of the roads inside a village
dofile(mg_villages.modpath.."/roads.lua")

View File

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

View File

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

1320
mods/mg_villages/mapgen.lua Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

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

184
mods/mg_villages/nodes.lua Normal file
View File

@ -0,0 +1,184 @@
-- slightly lower than a normal nodes for better look
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_gravel_defaults,
-- sounds = default.node_sound_dirt_defaults({
-- footstep = {name="default_gravel_footstep", gain=0.5},
-- dug = {name="default_gravel_footstep", gain=1.0},
-- }),
paramtype = "light",
paramtype2 = "facedir",
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = { { -0.5, -0.5, -0.5, 0.5, 0.5-2/16, 0.5}, },
},
})
mg_villages.road_node = minetest.get_content_id( 'mg_villages:road' );
-- do not drop snow on roads
if( minetest.get_modpath("moresnow")) then
moresnow.snow_cover[ mg_villages.road_node ] = moresnow.c_air;
end
-- special soil that does not need abms/lbms or water
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, soil=3, wet=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, soil=3, wet=1},
sounds = default.node_sound_sand_defaults,
})
-- this non-snow-melting-torch is only needed if you use the old snow mod
if( mg_villages.USE_DEFAULT_3D_TORCHES == false ) then
-- 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,
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",
is_ground_content = false,
})
end
-- get information about a plot, the building, its inhabitants; allow to buy the plot etc.
minetest.register_node("mg_villages:plotmarker", {
description = "Plot marker",
drawtype = "nodebox",
tiles = {"default_stone_brick.png"},
is_ground_content = false,
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
})
-- place this node where a mob that works in your building ought to stand
minetest.register_node("mg_villages:mob_workplace_marker", {
description = "Place where a mob ought to work",
drawtype = "nodebox",
tiles = {"character.png"},
paramtype = "light",
paramtype2 = "facedir",
walkable = false,
is_ground_content = false,
node_box = {
type = "fixed",
fixed = {
{-0.5, -0.5, -0.5, 0.5, -0.5+1/16, 0.5},
},
},
groups = {crumbly=3},
})
-- helper node for villager/mob mods that want to spawn npc
minetest.register_node("mg_villages:mob_spawner", {
description = "Mob spawner",
tiles = {"wool_cyan.png^beds_bed_fancy.png","wool_blue.png^doors_door_wood.png"},
is_ground_content = false,
groups = {not_in_creative_inventory = 1 }, -- cannot be digged by players
on_rightclick = function( pos, node, clicker, itemstack, pointed_thing)
return mg_villages.mob_spanwer_on_rightclick( pos, node, clicker, itemstack, pointed_thing);
end
})
-- default to safe lava - prevent fire
if( not( mg_villages.use_normal_unsafe_lava )) then
local lava = minetest.registered_nodes[ "default:lava_source"];
if( lava ) then
-- a deep copy for the table would be more helpful...but, well, ...
local new_def = minetest.deserialize( minetest.serialize( lava ));
-- this lava does not cause fire to spread
new_def.name = nil;
new_def.groups.lava = nil;
new_def.groups.hot = nil;
new_def.groups.igniter = nil;
new_def.groups.lava_tamed = 3;
new_def.description = "Lava Source (tame)";
new_def.liquid_alternative_flowing = "mg_villages:lava_flowing_tamed";
new_def.liquid_alternative_source = "mg_villages:lava_source_tamed";
-- we create a NEW type of lava for this
minetest.register_node( "mg_villages:lava_source_tamed", new_def );
end
-- take care of the flowing variant as well
lava = minetest.registered_nodes[ "default:lava_flowing"];
if( lava ) then
-- a deep copy for the table would be more helpful...but, well, ...
local new_def = minetest.deserialize( minetest.serialize( lava ));
-- this lava does not cause fire to spread
new_def.name = nil;
new_def.groups.lava = nil;
new_def.groups.hot = nil;
new_def.groups.igniter = nil;
new_def.groups.lava_tamed = 3;
new_def.description = "Flowing Lava (tame)";
new_def.liquid_alternative_flowing = "mg_villages:lava_flowing_tamed";
new_def.liquid_alternative_source = "mg_villages:lava_source_tamed";
-- and a NEW type of flowing lava...
minetest.register_node( "mg_villages:lava_flowing_tamed", new_def );
end
end

View File

@ -0,0 +1,545 @@
-- used for buying plots, restoring buildings, getting information about mobs etc.
mg_villages.plotmarker_formspec = function( pos, formname, fields, player )
-- if( not( mg_villages.ENABLE_PROTECTION )) then
-- return;
-- end
if( not( pos )) 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 )
or not( mg_villages.all_villages[ village_id ].to_add_data.bpos )
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 village = mg_villages.all_villages[ village_id ];
local plot = mg_villages.all_villages[ village_id ].to_add_data.bpos[ plot_nr ];
local owner_name = plot.owner;
if( not( owner_name ) or owner_name == "" ) then
if( plot.btype=="road" ) then
owner_name = "- the village community -";
else
owner_name = "- for sale -";
end
end
-- missing data
if( not( plot.btype ) or not( mg_villages.BUILDINGS[ plot.btype ] )
or not( mg_villages.BUILDINGS[ plot.btype ].mts_path )
or not( mg_villages.BUILDINGS[ plot.btype ].scm )) then
minetest.chat_send_player( pname, 'Error. Unknown building. btype: '..tostring( plot.btype ));
return;
end
local building_name = mg_villages.BUILDINGS[ plot.btype ].mts_path..mg_villages.BUILDINGS[ plot.btype ].scm;
-- show coordinates of the village center to the player
local village_pos = minetest.pos_to_string( {x=village.vx, y=village.vh, z=village.vz});
-- distance from village center
local distance = math.floor( math.sqrt( (village.vx - pos.x ) * (village.vx - pos.x )
+ (village.vh - pos.y ) * (village.vh - pos.y )
+ (village.vz - pos.z ) * (village.vz - pos.z ) ));
-- show a list of who lives (or works) here at this plot
if( fields and fields.inhabitants ) then
minetest.show_formspec( pname, "mg_villages:plot_mob_list",
mg_villages.inhabitants.print_house_info( village.to_add_data.bpos, plot_nr, village_id, pname ));
--mg_villages.debug_inhabitants( village, plot_nr);
return;
end
local mirror_str = "";
if( plot.mirror ) then
mirror_str = minetest.formspec_escape(" (mirrored)");
end
-- create the header
local formspec = "size[13,10]"..
"label[3.3,0.0;Plot No.: "..tostring( plot_nr )..", with "..tostring( mg_villages.BUILDINGS[ plot.btype ].scm ).."]"..
"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 "- name unknown -").."]"
.."label[7.3,0.8;located at "..(village_pos).."]"..
"label[0.3,1.2;Owned by:]" .."label[3.3,1.2;"..(owner_name).."]"..
"label[3.3,1.6;Click on a menu entry to select it:]"..
"field[20,20;0.1,0.1;pos2str;Pos;"..minetest.pos_to_string( pos ).."]";
build_chest.show_size_data( building_name );
-- deprecated; adds buttons for registered mobf_traders
--formspec = mg_villages.plotmarker_list_traders( plot, formspec );
local replace_row = -1;
-- the player selected a material which ought to be replaced
if( fields.build_chest_replacements ) then
local event = minetest.explode_table_event( fields.build_chest_replacements );
if( event and event.row and event.row > 0 ) then
replace_row = event.row;
fields.show_materials = "show_materials";
end
-- the player provided the name of the material for the replacement of the currently selected
elseif( fields.store_replacement and fields.store_repalcement ~= ""
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, village_id );
fields.show_materials = "show_materials";
-- group selections for easily changing several nodes at once
elseif( fields.wood_selection ) then
build_chest.replacements_apply_for_group( pos, meta, 'wood', fields.wood_selection, fields.set_wood, village_id );
fields.set_wood = nil;
fields.show_materials = "show_materials";
elseif( fields.farming_selection ) then
build_chest.replacements_apply_for_group( pos, meta, 'farming', fields.farming_selection, fields.set_farming, village_id );
fields.set_farming = nil;
fields.show_materials = "show_materials";
elseif( fields.roof_selection ) then
build_chest.replacements_apply_for_group( pos, meta, 'roof', fields.roof_selection, fields.set_roof, village_id );
fields.set_roof = nil;
fields.show_materials = "show_materials";
-- actually store the new group replacement
elseif( (fields.set_wood and fields.set_wood ~= "")
or (fields.set_farming and fields.set_farming ~= "" )
or (fields.set_roof and fields.set_roof ~= "" )) then
minetest.show_formspec( pname, "mg_villages:formspec_plotmarker",
handle_schematics.get_formspec_group_replacement( pos, fields, formspec ));
return;
end
-- show which materials (and replacements!) where used for the building
if( (fields.show_materials and fields.show_materials ~= "" )
or (fields.replace_row_with and fields.replace_row_with ~= "")
or (fields.replace_row_material and fields.replace_row_material ~= "")) then
formspec = formspec.."button[9.9,0.4;2,0.5;info;Back]";
if( not( minetest.check_player_privs( pname, {protection_bypass=true}))) then
-- do not allow any changes; just show the materials and their replacements
minetest.show_formspec( pname, "mg_villages:formspec_plotmarker",
formspec..build_chest.replacements_get_list_formspec( pos, nil, 0, meta, village_id, building_name, replace_row ));
else
minetest.show_formspec( pname, "mg_villages:formspec_plotmarker",
formspec..build_chest.replacements_get_list_formspec( pos, nil, 1, nil, village_id, building_name, replace_row ));
end
return;
-- place the building again
elseif( (fields.reset_building and fields.reset_building ~= "")
or (fields.remove_building and fields.remove_building ~= "")) then
formspec = formspec.."button[9.9,0.4;2,0.5;back;Back]";
if( not( minetest.check_player_privs( pname, {protection_bypass=true}))) then
minetest.show_formspec( pname, "mg_villages:formspec_plotmarker", formspec..
"label[3,3;You need the protection_bypass priv in order to use this functin.]" );
return;
end
local selected_building = build_chest.building[ building_name ];
local start_pos = {x=plot.x, y=plot.y, z=plot.z, brotate=plot.brotate};
if( selected_building.yoff ) then
start_pos.y = start_pos.y + selected_building.yoff;
end
local end_pos = {x=plot.x+plot.bsizex-1,
y=plot.y+selected_building.yoff-1+selected_building.ysize,
z=plot.z+plot.bsizez-1};
local replacements = build_chest.replacements_get_current( meta, village_id );
if( fields.remove_building and fields.remove_building ~= "" ) then
-- clear the space above ground, put dirt below ground, but keep the
-- surface intact
handle_schematics.clear_area( start_pos, end_pos, pos.y-1);
-- also clear the meta data to avoid strange effects
handle_schematics.clear_meta( start_pos, end_pos );
formspec = formspec.."label[3,3;The plot has been cleared.]";
else
-- actually place it (disregarding mirroring)
local error_msg = handle_schematics.place_building_from_file(
start_pos,
end_pos,
building_name,
replacements,
plot.o,
build_chest.building[ building_name ].axis, plot.mirror, 1, true );
formspec = formspec.."label[3,3;The building has been reset.]";
if( error_msg ) then
formspec = formspec..'label[4,3;Error: '..tostring( fields.error_msg ).."]";
end
end
minetest.show_formspec( pname, "mg_villages:formspec_plotmarker", formspec );
return;
elseif( fields.info and fields.info ~= "" ) then
local show_material_text = "Change materials used";
if( not( minetest.check_player_privs( pname, {protection_bypass=true}))) then
show_material_text = "Show materials used";
end
minetest.show_formspec( pname, "mg_villages:formspec_plotmarker",
formspec..
"button[9.9,0.4;2,0.5;back;Back]"..
"button[3,3;5,0.5;create_backup;Create backup of current stage]"..
"button[4,4;3,0.5;show_materials;"..show_material_text.."]"..
"button[4,5;3,0.5;reset_building;Reset building]"..
"button[4,6;3,0.5;remove_building;Remove building]");
return;
end
local owner = plot.owner;
local btype = plot.btype;
local original_formspec = "size[8,3]"..
"button[7.0,0.0;1.0,0.5;info;Info]"..
"button[6.0,1.0;2.0,0.5;inhabitants;Who lives here]"..
"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 )..mirror_str.."]"..
"field[20,20;0.1,0.1;pos2str;Pos;"..minetest.pos_to_string( pos ).."]";
local formspec = "";
local ifinhabit = "";
-- Get Price
local price = "default:gold_ingot 2";
if (btype ~= 'road' and mg_villages.BUILDINGS[btype]) then
local plot_descr = 'Plot No. '..tostring( plot_nr ).. ' with '..tostring( mg_villages.BUILDINGS[btype].scm)
if (mg_villages.BUILDINGS[btype].price) then
price = mg_villages.BUILDINGS[btype].price;
elseif (mg_villages.BUILDINGS[btype].typ and mg_villages.prices[ mg_villages.BUILDINGS[btype].typ ]) then
price = mg_villages.prices[ mg_villages.BUILDINGS[btype].typ ];
end
-- Get if is inhabitant house
if (mg_villages.BUILDINGS[btype].inh and mg_villages.BUILDINGS[btype].inh > 0 ) then
ifinhabit = "label[1,1.5;Owners of this plot count as village inhabitants.]";
end
end
-- Determine price depending on building type
local price_stack= ItemStack( price );
-- If nobody owns the plot
if (not(owner) or owner=='') then
formspec = original_formspec ..
"label[1,1;You can buy this plot for]"..
"label[3.8,1;"..tostring( price_stack:get_count() ).." x ]"..
"item_image[4.3,0.8;1,1;"..( price_stack:get_name() ).."]"..
ifinhabit..
"button[2,2.5;1.5,0.5;buy;Buy plot]"..
"button_exit[4,2.5;1.5,0.5;abort;Exit]";
-- On Press buy button
if (fields['buy']) then
local inv = player:get_inventory();
if not mg_villages.all_villages[village_id].ownerlist then
mg_villages.all_villages[village_id].ownerlist = {}
end
-- Check if player already has a house in the village
if mg_villages.all_villages[village_id].ownerlist[pname] then
formspec = formspec.."label[1,1.9;Sorry. You already have a plot in this village.]";
-- Check if the price can be paid
elseif( inv and inv:contains_item( 'main', price_stack )) then
formspec = original_formspec..
"label[1,1;Congratulations! You have bought this plot.]"..
"button_exit[5.75,2.5;1.5,0.5;abort;Exit]";
mg_villages.all_villages[ village_id ].to_add_data.bpos[ plot_nr ].owner = pname;
if mg_villages.all_villages[village_id].ownerlist then
mg_villages.all_villages[village_id].ownerlist[pname] = true;
else
mg_villages.all_villages[village_id].ownerlist[pname] = true;
end
meta:set_string('infotext', 'Plot No. '..tostring( plot_nr ).. ' with '..tostring( mg_villages.BUILDINGS[btype].scm)..' (owned by '..tostring( pname )..')');
-- save the data so that it survives server restart
mg_villages.save_data();
-- substract the price from the players inventory
inv:remove_item( 'main', price_stack );
else
formspec = formspec.."label[1,1.9;Sorry. You are not able to pay the price.]";
end
end
-- If player is the owner of the plot
elseif (owner==pname) then
-- Check if inhabitant house
if(btype ~= 'road'
and mg_villages.BUILDINGS[btype]
and mg_villages.BUILDINGS[btype].inh
and mg_villages.BUILDINGS[btype].inh > 0 ) then
ifinhabit = "label[1,1.5;You are allowed to modify the common village area.]";
end
formspec = original_formspec.."size[8,3]"..
"label[1,1;This is your plot. You have bought it.]"..
"button[0.75,2.5;3,0.5;add_remove;Add/Remove Players]"..
ifinhabit..
"button_exit[3.75,2.5;2.0,0.5;abandon;Abandon plot]"..
"button_exit[5.75,2.5;1.5,0.5;abort;Exit]";
-- If Player wants to abandon plot
if(fields['abandon'] ) then
formspec = original_formspec..
"label[1,1;You have abandoned this plot.]"..
"button_exit[5.75,2.5;1.5,0.5;abort;Exit]";
mg_villages.all_villages[village_id].ownerlist[pname] = nil;
mg_villages.all_villages[ village_id ].to_add_data.bpos[ plot_nr ].can_edit = {}
mg_villages.all_villages[ village_id ].to_add_data.bpos[ plot_nr ].owner = nil;
-- Return price to player
local inv = player:get_inventory();
inv:add_item( 'main', price_stack );
meta:set_string('infotext', 'Plot No. '..tostring( plot_nr ).. ' with '..tostring( mg_villages.BUILDINGS[btype].scm) );
mg_villages.save_data();
end
-- If Player wants to add/remove trusted players
if (fields['add_remove']) then
local previousTrustees = mg_villages.all_villages[ village_id ].to_add_data.bpos[ plot_nr ].can_edit
local output = "";
if previousTrustees == nil then
previousTrustees = {}
else
for _, player in ipairs(previousTrustees) do
output = output..player.."\n"
end
end
formspec = "size[8,3]"..
"field[20,20;0.1,0.1;pos2str;Pos;"..minetest.pos_to_string( pos ).."]"..
"textarea[0.3,0.2;8,2.5;ownerplayers;Trusted Players;"..output.."]"..
"button[3.25,2.5;1.5,0.5;savetrustees;Save]";
mg_villages.save_data()
end
-- Save trusted players
if (fields["savetrustees"] == "Save") then
if not mg_villages.all_villages[ village_id ].to_add_data.bpos[ plot_nr ].can_edit then
mg_villages.all_villages[ village_id ].to_add_data.bpos[ plot_nr ].can_edit = {}
end
local x = 1;
for _, player in ipairs(fields.ownerplayers:split("\n")) do
mg_villages.all_villages[ village_id ].to_add_data.bpos[ plot_nr ].can_edit[x] = player
x = x + 1
end
mg_villages.save_data();
end
-- If A different Player owns plot
else
formspec = original_formspec.."label[1,1;"..tostring( owner ).." owns this plot.]"..
"button_exit[3,2.5;1.5,0.5;abort;Exit]";
end
minetest.show_formspec( pname, "mg_villages:formspec_plotmarker", formspec );
end
mg_villages.form_input_handler = function( player, formname, fields)
-- mg_villages.print(mg_villages.DEBUG_LEVEL_NORMAL,minetest.serialize(fields));
if( not( mg_villages.ENABLE_PROTECTION )) then
return false;
end
-- teleport to a plot or mob
if( fields[ 'teleport_to' ]
and fields[ 'pos2str' ]
and player ) then
local pname = player:get_player_name();
if( minetest.check_player_privs( pname, {teleport=true})) then
local pos = minetest.string_to_pos( fields.pos2str );
-- teleport the player to the target position
player:moveto( { x=pos.x, y=(pos.y+1), z=pos.z }, false);
else
minetest.chat_sned_player( pname, "Sorry. You do not have the teleport privilege.");
end
-- do not abort; continue with showing the formspec
end
-- are we supposed to show information about a particular mob?
local mob_selected = nil;
-- show previous mob that lives on the plot
if( formname=="mg_villages:formspec_list_one_mob" and fields["prev"] and fields["bed_nr"]) then
mob_selected = tonumber(fields.bed_nr) - 1;
-- show next mob that lives on the mob
elseif( formname=="mg_villages:formspec_list_one_mob" and fields["next"] and fields["bed_nr"]) then
mob_selected = tonumber(fields.bed_nr) + 1;
-- show informaton about mob selected from list of inhabitants
elseif( not( fields['back_to_houselist'])
and fields['mg_villages:formspec_list_inhabitants']
and fields['mg_villages:formspec_list_inhabitants']~=""
and fields['village_id']
and fields['plot_nr']) then
local selection = minetest.explode_table_event( fields['mg_villages:formspec_list_inhabitants'] );
mob_selected = selection.row;
end
-- this index has to be a number and not a string
fields.plot_nr = tonumber( fields.plot_nr or "0");
local pname = player:get_player_name();
-- provide information about a particular mob
if( mob_selected
and fields.village_id
and fields.plot_nr
and mg_villages.all_villages[ fields.village_id ]
and mg_villages.all_villages[ fields.village_id ].to_add_data
and mg_villages.all_villages[ fields.village_id ].to_add_data.bpos
and mg_villages.all_villages[ fields.village_id ].to_add_data.bpos[ fields.plot_nr ])then
-- and mg_villages.all_villages[ fields.village_id ].to_add_data.bpos[ fields.plot_nr ].beds[mob_selected]) then
if( not( mg_villages.all_villages[ fields.village_id ].to_add_data.bpos[ fields.plot_nr ].beds)
or not( mg_villages.all_villages[ fields.village_id ].to_add_data.bpos[ fields.plot_nr ].beds[ mob_selected] )) then
-- allow to click at the worker
local bpos = mg_villages.all_villages[ fields.village_id ].to_add_data.bpos;
if( bpos[ fields.plot_nr ].worker
and bpos[ fields.plot_nr ].worker.lives_at
and bpos[ bpos[ fields.plot_nr ].worker.lives_at ]
and bpos[ bpos[ fields.plot_nr ].worker.lives_at ].beds
and bpos[ bpos[ fields.plot_nr ].worker.lives_at ].beds[1] ) then
fields.plot_nr = tonumber(bpos[ fields.plot_nr ].worker.lives_at);
mob_selected = 1;
-- allow to click at the owner
elseif( bpos[ fields.plot_nr ].belongs_to
and bpos[ bpos[ fields.plot_nr ].belongs_to ]
and bpos[ bpos[ fields.plot_nr ].belongs_to ].beds
and bpos[ bpos[ fields.plot_nr ].belongs_to ].beds[1] ) then
fields.plot_nr = tonumber(bpos[ fields.plot_nr ].belongs_to);
mob_selected = 1;
-- this is not a mob
else
mob_selected = nil;
end
end
if( mob_selected ) then
local village = mg_villages.all_villages[ fields.village_id ];
minetest.show_formspec( pname, "mg_villages:formspec_list_one_mob",
mg_villages.inhabitants.print_mob_info( village.to_add_data.bpos, fields.plot_nr, fields.village_id, mob_selected, pname ));
return true;
end
end
-- are we supposed to show information about a particular plot?
local plot_selected = nil;
-- show previous plot of that village
if( formname=="mg_villages:formspec_list_inhabitants" and fields["prev"] and fields["plot_nr"]) then
plot_selected = fields.plot_nr - 1;
-- show next plot of that village
elseif( formname=="mg_villages:formspec_list_inhabitants" and fields["next"] and fields["plot_nr"]) then
plot_selected = fields.plot_nr + 1;
-- back from the list of details of a mob to the list of inhabitants of the plot where it lives
elseif( fields['back_to_houselist'] ) then
plot_selected = fields.plot_nr;
-- show informaton about plot selected from list of plots in a village
elseif( not( fields['back_to_plotlist'])
and fields['mg_villages:formspec_list_plots']
and fields['mg_villages:formspec_list_plots']~=""
and fields['village_id']) then
local selection = minetest.explode_table_event( fields['mg_villages:formspec_list_plots'] );
plot_selected = selection.row-1;
end
-- provide information about the inhabitants of a particular plot
if( plot_selected
and fields.village_id
and mg_villages.all_villages[ fields.village_id ]
and mg_villages.all_villages[ fields.village_id ].to_add_data
and mg_villages.all_villages[ fields.village_id ].to_add_data.bpos
and mg_villages.all_villages[ fields.village_id ].to_add_data.bpos[ plot_selected ]) then
local village = mg_villages.all_villages[ fields.village_id ];
minetest.show_formspec( pname, "mg_villages:formspec_list_inhabitants",
mg_villages.inhabitants.print_house_info( village.to_add_data.bpos, plot_selected, fields.village_id, pname ));
return true;
end
-- are we supposed to show the plots contained in a particular village?
local village_selected = nil;
-- where are we currently in the list of villages as shown to that particular player?
local liste = mg_villages.tmp_player_village_list[ pname ];
local curr_list_pos = -1;
if( liste ) then
for i,v in ipairs( liste ) do
if( fields.village_id and v==fields.village_id ) then
curr_list_pos = i;
end
end
end
-- show previous village in list
if( formname=="mg_villages:formspec_list_plots" and fields["prev"] and curr_list_pos>1) then
village_selected = liste[ curr_list_pos - 1 ];
-- show next village
elseif( formname=="mg_villages:formspec_list_plots" and fields["next"] and curr_list_pos<#liste) then
village_selected = liste[ curr_list_pos + 1 ];
-- back from the list of inhabitants to the list of plots of a village
elseif( fields['back_to_plotlist'] and fields.village_id) then
village_selected = fields.village_id;
-- show informaton about all plots in the selected village
elseif( not( fields['back_to_villagelist'])
and fields['mg_villages:formspec_list_villages']
and fields['mg_villages:formspec_list_villages']~="" ) then
local selection = minetest.explode_table_event( fields['mg_villages:formspec_list_villages'] );
-- this is the village the player is intrested in
village_selected = mg_villages.tmp_player_village_list[ pname ][ selection.row-1 ];
end
-- the player has selected a village in the village list
if( village_selected
and mg_villages.all_villages[ village_selected ]
and mg_villages.all_villages[ village_selected ].to_add_data
and mg_villages.all_villages[ village_selected ].to_add_data.bpos ) then
fields.village_id = village_selected;
-- show the player a list of plots of the selected village
mg_villages.list_plots_formspec( player, 'mg_villages:formspec_list_plots', fields );
return true;
end
-- back from plotlist of a village to the list of nearby villages
-- mg_villages.list_villages_formspec can be found in chat_commands.lua
if( fields['back_to_villagelist']) then
mg_villages.list_villages_formspec( player, "mg_villages:formspec_list_villages", {});
return true;
end
if( (formname == "mg_villages:formspec_plotmarker") and fields.pos2str and not( fields.abort )) then
local pos = minetest.string_to_pos( fields.pos2str );
mg_villages.plotmarker_formspec( pos, formname, fields, player );
return true;
end
return false;
end
minetest.register_on_player_receive_fields( mg_villages.form_input_handler )

View File

@ -0,0 +1,163 @@
-- 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 height_diff = pos.y - v.vh;
if( height_diff < 40 and height_diff > -10 ) then
local size = v.vs * 3;
if( ( math.abs( pos.x - v.vx ) < size )
and ( math.abs( pos.z - v.vz ) < size )) then
local village_noise = minetest.get_perlin(7635, 3, 0.5, 16);
if( mg_villages.inside_village_area( pos.x, pos.z, v, village_noise)) then
local node = minetest.get_node( pos );
-- leaves can be digged in villages
if( node and node.name ) then
if( minetest.registered_nodes[ node.name ]
and minetest.registered_nodes[ node.name ].groups
and minetest.registered_nodes[ node.name ].groups.leaves ) then
return nil;
elseif( node.name=='default:snow' ) then
return nil;
-- bones can be digged in villages
elseif( node.name == 'bones:bones' ) then
return nil;
else
return id;
end
else
return id;
end
end
end
end
end
return nil;
end
-- checks if the plot marker is still present; places a new one if needed
-- p: plot data (position, size, orientation, owner, ..)
mg_villages.check_plot_marker = function( p, plot_nr, village_id )
-- roads cannot be bought
if( p.btype and p.btype=="road" ) then
return;
end
local plot_pos = { x=p.x, y=p.y, z=p.z };
if( p.o==3 ) then
plot_pos = { x=p.x, y=p.y+1, z=p.z-1 };
elseif( p.o==1 ) then
plot_pos = { x=p.x+p.bsizex-1, y=p.y+1, z=p.z+p.bsizez };
elseif ( p.o==2 ) then
plot_pos = { x=p.x+p.bsizex, y=p.y+1, z=p.z };
elseif ( p.o==0 ) then
plot_pos = { x=p.x-1, y=p.y+1, z=p.z+p.bsizez-1 };
end
-- is the plotmarker still present?
local node = minetest.get_node( plot_pos );
if( not(node) or not(node.name) or node.name ~= "mg_villages:plotmarker" ) then
-- place a new one if needed
minetest.set_node( plot_pos, {name="mg_villages:plotmarker", param2=p.o});
local meta = minetest.get_meta( plot_pos );
-- strange error happend; maybe we're more lucky next time...
if( not( meta )) then
return;
end
meta:set_string('village_id', village_id );
meta:set_int( 'plot_nr', plot_nr );
end
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
-- allow players with protection_bypass to build anyway
if( minetest.check_player_privs( name, {protection_bypass=true})) then
return false;
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
local trustedusers = p.can_edit
local trustedUser = false
if trustedusers ~= nil then
for _,trusted in ipairs(trustedusers) do
if trusted == name then
trustedUser = true
end
end
end
-- we have located the right plot; the player can build here if he owns this particular plot
if( p.x <= pos.x and (p.x + p.bsizex) >= pos.x
and p.z <= pos.z and (p.z + p.bsizez) >= pos.z) then
-- place a new plot marker if necessary
mg_villages.check_plot_marker( p, nr, village_id );
-- If player has been trusted by owner, can build
if (trustedUser) then
return false;
-- If player is owner, can build
elseif( p.owner and p.owner == name ) then
return false;
-- the allmende can be used by all
elseif( mg_villages.BUILDINGS[p.btype] and mg_villages.BUILDINGS[p.btype].typ=="allmende" ) then
return false;
-- the player cannot modify other plots, even though he may be house owner of another house and be allowed to modify common ground
else
return true;
end
-- if the player just owns another plot in the village, check if it's one where villagers may live
elseif( p.owner and p.owner == name or trustedUser) then
local btype = mg_villages.all_villages[ village_id ].to_add_data.bpos[ nr ].btype;
if( btype ~= 'road'
and mg_villages.BUILDINGS[btype]
and mg_villages.BUILDINGS[btype].inh
and mg_villages.BUILDINGS[btype].inh > 0 ) then
is_houseowner = true;
-- check the node below
local node = minetest.get_node( {x=pos.x, y=pos.y-1, z=pos.z});
-- replace the fake, inaktive village soil with real farming soil if a player diggs the plant above
if( node and node.name and node.name=="mg_villages:soil" ) then
minetest.swap_node( {x=pos.x, y=pos.y-1, z=pos.z}, {name="farming:soil_wet"});
end
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 );

View File

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

209
mods/mg_villages/roads.lua Normal file
View File

@ -0,0 +1,209 @@
-- helper function for get_path_from_pos_to_plot
mg_villages.next_step_on_road_path = function( p, this_road_xdir, following_road )
if( this_road_xdir == true ) then
if( p.x < following_road.x ) then
p.x = following_road.x;
else
p.x = following_road.x + following_road.bsizex -1;
end
else
if( p.z < following_road.z ) then
p.z = following_road.z;
else
p.z = following_road.z + following_road.bsizez -1;
end
end
return p;
end
-- pos needs to be a position either on a road or at max 1 node away from a road
mg_villages.get_path_from_pos_to_plot_via_roads = function( village_id, pos, target_plot_nr )
if( not( mg_villages.all_villages[ village_id ] )
or not( target_plot_nr )
or not( mg_villages.all_villages[ village_id ].to_add_data.bpos[ target_plot_nr ])
or not( mg_villages.all_villages[ village_id ].to_add_data.bpos[ target_plot_nr ].road_nr)) then
return {};
end
local bpos_list = mg_villages.all_villages[ village_id ].to_add_data.bpos;
-- find out which road is the one next to pos
local standing_on_road = nil;
local roads = mg_villages.get_road_list( village_id, false );
for i,road in ipairs( roads ) do
local r = bpos_list[ road ]; -- road data
-- if this is really a road, and if a parent road exists (or is 0)
if( r and r.btype == "road" and r.parent_road_plot
-- ..and pos is in the area of the road or next to it
and pos.x >= r.x-1 and pos.x <= r.x + r.bsizex + 1
and pos.z >= r.z-1 and pos.z <= r.z + r.bsizez + 1
and pos.y >= r.y-4 and pos.y <= r.y + 4 ) then
standing_on_road = i;
end
end
-- nothing found
if( not( standing_on_road )) then
return;
end
-- walk from pos up to the main road
local start_to_main_road = {};
local next_road_plot = roads[ standing_on_road ];
while( next_road_plot and bpos_list[ next_road_plot ] and bpos_list[ next_road_plot ].btype=="road" ) do
table.insert( start_to_main_road, next_road_plot );
next_road_plot = bpos_list[ next_road_plot ].parent_road_plot;
end
-- walk from the target road up to the main road - until we find a road that is
-- already part of the path from pos to the main road
local target_to_main_road = {};
local next_road_plot = roads[ bpos_list[ target_plot_nr ].road_nr ];
local match_found = -1;
while( next_road_plot and bpos_list[ next_road_plot ] and bpos_list[ next_road_plot ].btype=="road" and match_found==-1) do
-- it may not be necessary to go all the way back to the main road
for i,r in ipairs( start_to_main_road ) do
if( r == next_road_plot ) then
match_found = i;
end
end
if( match_found == -1) then
table.insert( target_to_main_road, next_road_plot );
end
next_road_plot = bpos_list[ next_road_plot ].parent_road_plot;
end
if( match_found == -1 ) then
match_found = #start_to_main_road;
end
-- we may have gone too far up and can take a turn much earlier
local start_to_target = {};
for i=1,match_found do
table.insert( start_to_target, start_to_main_road[i] );
end
-- combine the full walk through the tree-like road structure into one list of roads
for i=#target_to_main_road,1,-1 do
table.insert( start_to_target, target_to_main_road[i] );
end
-- generate a path for travelling on these roads
local path = {};
-- let the mob take the first step onto the road
local first_road = bpos_list[ start_to_target[1] ];
local p = {x=pos.x, y=first_road.y+1, z=pos.z};
p = mg_villages.next_step_on_road_path( p, not(first_road.xdir), first_road );
table.insert( path, {x=p.x, y=p.y, z=p.z} );
-- travel using all the given roads
for i=1,#start_to_target-1 do
local this_road = bpos_list[ start_to_target[i] ];
local following_road = bpos_list[ start_to_target[i+1]];
-- walk on the inside in curves instead of taking longer paths
p = mg_villages.next_step_on_road_path( p, this_road.xdir, following_road );
table.insert( path, {x=p.x, y=p.y, z=p.z} );
end
-- walk on the last road to the target plot
local last_road = bpos_list[ start_to_target[ #start_to_target ] ];
local target = {x=bpos_list[ target_plot_nr ].x + math.floor(bpos_list[ target_plot_nr ].bsizex/2),
y = p.y,
z=bpos_list[ target_plot_nr ].z + math.floor(bpos_list[ target_plot_nr ].bsizez/2),
bsizex = 2, bsizez = 2};
p = mg_villages.next_step_on_road_path( p, last_road.xdir, target);
table.insert( path, {x=p.x, y=p.y, z=p.z} );
-- take the very last step and leave the road
p = mg_villages.next_step_on_road_path( p, not(last_road.xdir), target);
-- make sure we do not walk further than one step into the plot
if( p.x < last_road.x ) then
p.x = last_road.x - 1;
elseif( p.x >= last_road.x + last_road.bsizex ) then
p.x = last_road.x + last_road.bsizex + 1;
elseif( p.z < last_road.z ) then
p.z = last_road.z - 1;
elseif( p.z >= last_road.z + last_road.bsizez ) then
p.z = last_road.z + last_road.bsizez + 1;
end
table.insert( path, {x=p.x, y=p.y, z=p.z} );
--[[
-- if you want to visualize the path with yellow wool blocks for debugging, uncomment this
local str = " path: ";
for i,p in ipairs( path ) do
minetest.set_node( p, {name="wool:yellow"});
str = str.." "..minetest.pos_to_string( p );
end
minetest.chat_send_player("singleplayer","roads to walk on: "..minetest.serialize( start_to_target)..str);
--]]
return path;
end
-- try to reconstruct the tree-like road network structure (the data was
-- not saved completely from the beginning)
mg_villages.get_road_list = function( village_id, force_check )
if( not( mg_villages.all_villages[ village_id ] )) then
return {};
end
local bpos_list = mg_villages.all_villages[ village_id ].to_add_data.bpos;
local roads = {};
for i,pos in ipairs( bpos_list ) do
if( pos.btype and pos.btype=="road" ) then
-- store the plot nr for each road nr
roads[ pos.road_nr ] = i;
-- store weather the road streches in x- or z-direction
if( pos.bsizex >= pos.bsizez) then
pos.xdir = true;
else
pos.xdir = false;
end
end
end
-- a village without roads (i.e. a single house)
if( not( roads[1])) then
return {};
end
-- the parent roads have already been identified
if( not( force_check ) and bpos_list[ roads[ 1 ]].parent_road_plot == 0 ) then
return roads;
end
-- assume that road nr. 1 is the main road (which it is due to the way villages are constructed)
bpos_list[ roads[ 1 ]].parent_road_plot = 0;
-- identify all parent roads
for i=1,#roads do
if( bpos_list[ roads[i] ].parent_road_plot ) then
mg_villages.mark_roads_that_branch_off( bpos_list, roads, roads[i] );
end
end
return roads;
end
-- changes bpos_list and sets bpos_list[ road ].parent_road_plot = plot_nr for those roads where
-- plot_nr contains the road from which road branches off
mg_villages.mark_roads_that_branch_off = function( bpos_list, roads, plot_nr )
-- see which roads branch off from this parent road
local parent_road = bpos_list[ plot_nr ];
for i,road in ipairs( roads ) do
local r = bpos_list[ road ]; -- road data
-- if the road is not yet connected to another one
if( r.parent_road_plot == nil
-- and if it is 90 degree rotated compared to the potential parent road
and( r.xdir ~= parent_road.xdir )
-- and if one end lies inside the parent road
and( (r.x >= parent_road.x and r.x <= parent_road.x + parent_road.bsizex)
or(r.x+r.bsizex >= parent_road.x and r.x+r.bsizex <= parent_road.x + parent_road.bsizex))
and( (r.z >= parent_road.z and r.z <= parent_road.z + parent_road.bsizez)
or(r.z+r.bsizez >= parent_road.z and r.z+r.bsizez <= parent_road.z + parent_road.bsizez))) then
-- store plot_nr instead of road_nr as that is more useful
bpos_list[ road ].parent_road_plot = plot_nr;
end
end
end

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

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