diff options
| -rw-r--r-- | handle_schematics.lua | 339 | ||||
| -rw-r--r-- | init.lua | 41 | 
2 files changed, 325 insertions, 55 deletions
| diff --git a/handle_schematics.lua b/handle_schematics.lua index 2cd0035..18e0bee 100644 --- a/handle_schematics.lua +++ b/handle_schematics.lua @@ -148,11 +148,12 @@ end  -- set up steel doors in a usable way;  -- set up apartments from the apartment mod;  -- placer is the player who initialized the placement of the schematic (placer will be passed on to after_place_node etc) -handle_schematics.update_nodes = function( start_pos, end_pos, on_constr, after_place_node, placer ) +handle_schematics.update_nodes = function( start_pos, end_pos, on_constr, after_place_node, placer, extra_params )  	local p={};  	local i=0;  	local v=0; +  	-- call on_construct for all the nodes that require it  	for i, v in ipairs( on_constr ) do @@ -187,34 +188,80 @@ handle_schematics.update_nodes = function( start_pos, end_pos, on_constr, after_  			meta:set_string("infotext", "Owned by "..player_name)  		end +  		-- prepare apartment rental panels  		local nodes = minetest.find_nodes_in_area( start_pos, end_pos, {'apartment:apartment'} ); -		for _, p in ipairs(nodes ) do -			local meta  = minetest.get_meta( p ); -			meta:set_string( 'original_owner', player_name ); +		if( extra_params and extra_params.apartment_type and extra_params.apartment_name ) then +			for _, p in ipairs(nodes ) do +				local meta  = minetest.get_meta( p ); +				meta:set_string( 'original_owner', player_name ); +		 +				-- lua can't count variables of this type on its own... +				local nr = 1; +				for _, _ in pairs( apartment.apartments ) do +					nr = nr+1; +				end +				--  this depends on relative position and param2 of the formspec +				local fields = { +					quit=true, store=true, +	 +					size_up    = math.abs(  end_pos.y - p.y-1), +					size_down  = math.abs(start_pos.y - p.y), +	 +					norm_right = math.abs(  end_pos.x - p.x-1), +					norm_left  = math.abs(start_pos.x - p.x), +					norm_back  = math.abs(  end_pos.z - p.z-1), +					norm_front = math.abs(start_pos.z - p.z), +	 +					category   = extra_params.apartment_type, +					-- numbering them all seems best +					descr      = extra_params.apartment_name +				};  +	 +				-- up and down are independent of rotation +				fields.size_up   = math.abs(  end_pos.y - p.y-1); +				fields.size_down = math.abs(start_pos.y - p.y); +	 +				local node = minetest.get_node( p ); +				if(     node.param2 == 0 ) then -- z gets larger +					fields.size_left  = fields.norm_left;  fields.size_right = fields.norm_right; +					fields.size_back  = fields.norm_back;  fields.size_front = fields.norm_front; +	 +				elseif( node.param2 == 1 ) then -- x gets larger +					fields.size_left  = fields.norm_back;  fields.size_right = fields.norm_front; +					fields.size_back  = fields.norm_right; fields.size_front = fields.norm_left;  -			-- lua can't count variables of this type on its own... -			local nr = 1; -			for _, _ in pairs( apartment.apartments ) do -				nr = nr+1; +				elseif( node.param2 == 2 ) then -- z gets smaller +					fields.size_left  = fields.norm_right; fields.size_right = fields.norm_left; +					fields.size_back  = fields.norm_front; fields.size_front = fields.norm_back; +	 +				elseif( node.param2 == 3 ) then -- x gets smaller +					fields.size_left  = fields.norm_front; fields.size_right = fields.norm_back;  +					fields.size_back  = fields.norm_left;  fields.size_front = fields.norm_right; +				end +	 +				-- configure and prepare the apartment +				apartment.on_receive_fields( p, nil, fields, placer);  			end -			-- TODO: this depends on relative position and param2 of the formspec -			local fields = { -				quit=true, store=true, -				size_left=2, size_right=1, size_up=2, size_down=1, size_front=1, size_back=6, -				category='apartment', -				-- numbering them all seems best -				descr='Apartment #'..tostring( nr ) }; - -			-- configure and prepare the apartment -			apartment.on_receive_fields( p, nil, fields, placer);  		end +	end +end + +-- this is lua...it doesn't contain the basic functions +handle_schematics.table_contains = function( table, value ) +	local i = 1; +	local v; +	for i, v in ipairs( table ) do +		if( v==value ) then +			return true; +		end  	end +	return false;  end -handle_schematics.place_schematic = function( pos, param2, path, mirror, replacement_function, replacement_param, placer, do_copies ) +handle_schematics.place_schematic = function( pos, param2, path, mirror, replacement_function, replacement_param, placer, do_copies, extra_params )  	local node = minetest.env:get_node( pos );  	if( not( node ) or not( node.param2 ) or node.name=="air") then @@ -225,6 +272,12 @@ handle_schematics.place_schematic = function( pos, param2, path, mirror, replace  	end  	local building_data = handle_schematics.analyze_mts_file( path ); +	if( not( building_data ) or not( building_data.size )) then +		if( placer ) then +			minetest.chat_send_player( placer:get_player_name(), 'Could not place schematic. Please check the filename.'); +		end +		return; +	end  	local position_data = handle_schematics.translate_param2_to_rotation( node.param2, mirror, pos, building_data.size, building_data.rotated, building_data.burried );  	local replacements = {}; @@ -241,13 +294,47 @@ handle_schematics.place_schematic = function( pos, param2, path, mirror, replace  		force_place = false;  	end -	table.insert( building_data.on_constr,        'default:chest' ); + +	-- it is possible that replacements require calls to on_constr/after_place_node +	-- and that the nodes that are introduced through replacements where not present in the original schematic +	local all_replacements = {}; +	for i, v in ipairs( replacements ) do +		table.insert( all_replacements, v[2] ); +	end +	if( replacement_param and replacement_param.even and replacement_param.odd ) then +		for i, v in ipairs( replacement_param.even ) do +			table.insert( all_replacements, v[2] ); +		end +		for i, v in ipairs( replacement_param.odd  ) do +			table.insert( all_replacements, v[2] ); +		end +	end +	for i, v in ipairs( all_replacements ) do +			 +		if( minetest.registered_nodes[ v ] and minetest.registered_nodes[ v ].on_construct +		  and not(handle_schematics.table_contains( building_data.on_constr, v ))) then +			table.insert( building_data.on_constr, v ); +		end +		-- some nodes need after_place_node to be called for initialization +		if( minetest.registered_nodes[ v ] and minetest.registered_nodes[ v ].after_place_node +		  and not(handle_schematics.table_contains( building_data.after_place_node, v ))) then +			table.insert( building_data.after_place_node, v ); +		end +	end + + +	-- apartments need a name if they are to be configured +	if( extra_params and not( extra_params.apartment_type )) then +		extra_params.apartment_type = 'apartment'; +	end +  	-- actually place the schematic  	if( not( do_copies ) or not( do_copies.h ) or not( do_copies.v )) then  		minetest.place_schematic( position_data.start_pos, path..'.mts', tostring(position_data.rotate), replacements, force_place );  		handle_schematics.update_nodes( position_data.start_pos, position_data.end_pos, -								building_data.on_constr, building_data.after_place_node, placer ); +								building_data.on_constr, building_data.after_place_node, placer, +								extra_params );  	else  		-- place multiple copies  		local vector = {h=-1,v=1}; @@ -275,8 +362,39 @@ handle_schematics.place_schematic = function( pos, param2, path, mirror, replace  					minetest.place_schematic( p, path..'.mts', tostring(position_data.rotate), replacements_odd,  force_place );  				end +				-- generate apartment name +				if( extra_params and extra_params.apartment_type and extra_params.apartment_house_name ) then +					local nr = i-1; +					local apartment_name = ''; + +					-- find the first free number for an apartment with this apartment_house_name +					while( nr < 99 and apartment_name == '' ) do +						nr = nr+1; +						apartment_name = extra_params.apartment_house_name..' '..tostring(j); +						if( nr < 10 ) then +							apartment_name = apartment_name..'0'..tostring(nr); +						elseif( nr<100 ) then +							apartment_name = apartment_name..tostring(nr); +						else +							apartment_name = ''; +						end + +						-- avoid duplicates +						if( apartment.apartments[ apartment_name ] ) then +							apartment_name = ''; +						end +					end +					if( apartment_name ) then +						extra_params.apartment_name = apartment_name; +					else +						extra_params.apartment_name = nil; +						extra_params.apartment_type = nil; +					end + +				end +				-- replacements_even/replacements_odd ought to affect only DECORATIVE nodes - and none that have on_construct/after_place_node!  				handle_schematics.update_nodes( p, {x=p.x+position_data.max.x, y=p.y+position_data.max.y*j, z=p.z+position_data.max.z}, -								building_data.on_constr, building_data.after_place_node, placer ); +								building_data.on_constr, building_data.after_place_node, placer, extra_params );  				if( node.param2 == 0 or node.param2 == 2 ) then   					p.x = p.x + vector.h*position_data.max.x;  @@ -322,6 +440,121 @@ handle_schematics.replacement_function_decay = function( nodenames )  	return replacements;  end + + +handle_schematics.update_apartment_spawner_formspec = function( pos ) +	 +	local meta  = minetest.get_meta( pos ); + +	if( not( meta ) or not( meta:get_string('path')) or meta:get_string('path')=='') then +		return 'size[9,7]'.. +			'label[2.0,-0.3;Apartment Spawner]'.. +			'label[0.5,0.5;Load schematic from file:]'.. +			'field[5.0,0.9;4.0,0.5;path;;apartment_4x10_0_270]'.. +			'label[0.5,1.5;Name for this apartment house:]'.. +			'field[5.0,1.9;4.0,0.5;apartment_house_name;;Enter house name]'.. +			'label[0.5,2.0;Category (i.e. house, shop):]'.. +			'field[5.0,2.4;4.0,0.5;apartment_type;;apartment]'.. +			'label[0.5,2.5;Horizontal copies (to the left):]'.. +			'field[5.0,2.9;0.5,0.5;h;;1]'.. +			'label[0.5,3.0;Vertical copies (upwards):]'.. +			'field[5.0,3.4;0.5,0.5;v;;1]'.. +			'label[0.5,3.5;Replace clay in 1st building:]'.. +			'field[5.0,3.9;4.0,0.5;replacement_1;;default:sandstonebrick]'.. +			'label[0.5,4.0;Replace clay in 2nd building:]'.. +			'field[5.0,4.4;4.0,0.5;replacement_2;;default:brick]'.. +			'button_exit[4,6.0;2,0.5;store;Spawn building]'.. +			'button_exit[1,6.0;1,0.5;abort;Abort]'; +	end +	return 'size[9,7]'.. +			'label[2.0,-0.3;Information about the spawned Apartment]'.. +			'label[0.5,0.5;The schematic was loaded from:]'.. +			'label[5.0,0.5;'..tostring( meta:get_string('path'))..']'.. +			'label[0.5,1.5;Name for this apartment house:]'.. +			'label[5.0,1.5;'..tostring( meta:get_string('apartment_house_name'))..']'.. +			'label[0.5,2.0;Category (i.e. house, shop):]'.. +			'label[5.0,2.0;'..tostring( meta:get_string('apartment_type'))..']'.. +			'label[0.5,2.5;Horizontal copies (to the left):]'.. +			'label[5.0,2.5;'..tostring( meta:get_string('h'))..']'.. +			'label[0.5,3.0;Vertical copies (upwards):]'.. +			'label[5.0,3.0;'..tostring( meta:get_string('v'))..']'.. +			'label[0.5,3.5;Replace clay in 1st building:]'.. +			'label[5.0,3.5;'..tostring( meta:get_string('replacement_1'))..']'.. +			'label[0.5,4.0;Replace clay in 2nd building:]'.. +			'label[5.0,4.0;'..tostring( meta:get_string('replacement_2'))..']'.. +			'label[0.5,4.5;This building was spawned by:]'.. +			'label[5.0,4.5;'..tostring( meta:get_string('placed_by'))..']'.. +			'button_exit[4,6.0;2,0.5;store;Remove building]'.. +			'button_exit[1,6.0;1,0.5;abort;OK]'; +end + + +handle_schematics.on_receive_fields = function(pos, formname, fields, sender) + +	local meta  = minetest.get_meta( pos ); + +	if( not( sender )) then +		return; +	end + +	pname = sender:get_player_name(); +	if( not( minetest.check_player_privs(pname, {apartment_unrent=true}))) then +		minetest.chat_send_player( pname, 'You do not have the necessary privileges.'); +		return; +	end + +	if( meta and (not( meta:get_string('path')) or meta:get_string('path')=='') and fields.store) then + +		--  check if all params have been supplied +		if(  not( fields.path ) +		  or not( fields.apartment_house_name ) or not( fields.apartment_type ) +		  or not( fields.h )                    or not( fields.v ) +		  or not( fields.replacement_1 )        or not( fields.replacement_2 )) then +			minetest.chat_send_player( pname, 'Please fill all fields with information.'); +			return; +		end + +		fields.h = tonumber( fields.h ); +		if( fields.h < 1 or fields.h > 20 ) then +			fields.h = 1; +		end +		fields.h = tostring( fields.h ); +		fields.v = tonumber( fields.v ); +		if( fields.v < 1 or fields.v > 20 ) then +			fields.v = 1; +		end +		fields.v = tostring( fields.v ); + +		meta:set_string('path',                 fields.path ); +		meta:set_string('apartment_house_name', fields.apartment_house_name ); +		meta:set_string('apartment_type',       fields.apartment_type ); +		meta:set_string('h',                    fields.h ); +		meta:set_string('v',                    fields.v ); +		meta:set_string('replacement_1',        fields.replacement_1 ); +		meta:set_string('replacement_2',        fields.replacement_2 ); +		meta:set_string('placed_by',            pname ); + +		meta:set_string('formspec',             handle_schematics.update_apartment_spawner_formspec( pos )); +		minetest.chat_send_player( pname, 'Placing building '..tostring( fields.path )); + +		local path = minetest.get_modpath("apartment")..'/schems/'..fields.path; +		local mirror = 0; +		local replacement_function = nil; +		local replacement_param    = { odd={{'default:clay',fields.replacement_1}}, +					      even={{'default:clay',fields.replacement_2}}}; + +		handle_schematics.place_schematic( pos, nil, path, mirror, +				replacement_function, replacement_param, +				sender, {h=fields.h,v=fields.v}, +				{ apartment_type = fields.apartment_type, apartment_house_name = fields.apartment_house_name}) +		return; +	end +	-- TODO +	minetest.chat_send_player( pname, 'NOT YET IMPLEMENTED.'); +end + + +  local filename = "apartment_4x10_0_270";  --local filename = "apartment_4x6_0_90"; @@ -334,54 +567,52 @@ minetest.register_node("apartment:build_chest", {  	legacy_facedir_simple = true,  	after_place_node = function(pos, placer, itemstack) - --- TODO: check if placement is allowed; read parameters (how many? what to replace? which schematic?) etc. ---		local path = minetest.get_modpath("apartment")..'/schems/apartment_4x6_0_90'; -		local path = minetest.get_modpath("apartment")..'/schems/'..filename; -		local mirror = 0; -		local replacement_function = nil; -		local replacement_param    = { odd={{'default:clay','default:sandstonebrick'}, -							{'inbox:empty','default:chest'}, -							{'travelnet:travelnet','default:furnace'}, -							{'stairs:slab_junglewood','stairs:slab_wood'}, -							{'stairs:stair_junglewood','stairs:stair_wood'}}, -					      even={{'default:clay','default:brick'}, -							{'inbox:empty','default:chest'}, -							{'travelnet:travelnet','default:furnace'}, -							{'stairs:slab_junglewood','stairs:slab_wood'}, -							{'stairs:stair_junglewood','stairs:stair_wood'}}}; - ---		replacement_param    = { odd={{'default:clay','default:desert_stone'},{'stairs:slab_junglewood','stairs:slab_sandstone'},{'stairs:stair_junglewood','stairs:stair_sandstone'}}, ---					      even={{'default:clay','default:desert_stone'},{'stairs:slab_junglewood','stairs:slab_sandstone'},{'stairs:stair_junglewood','stairs:stair_sandstone'}}}; - -		minetest.chat_send_player( placer:get_player_name(), 'Placing building '..tostring( path )); - -		minetest.chat_send_player( placer:get_player_name(), 'Placing building '..tostring( path )); -		handle_schematics.place_schematic( pos, nil, path, mirror, replacement_function, replacement_param, placer, {h=8,v=6} ) +		local meta  = minetest.get_meta( pos ); +		meta:set_string('formspec', handle_schematics.update_apartment_spawner_formspec( pos ));          end, +	on_receive_fields = function( pos, formname, fields, sender ) +		handle_schematics.on_receive_fields(pos, formname, fields, sender) +	end, +  	-- if the building chest is removed, remove the building as well - and place nodes looking like leaves and autodecaying in order   	-- to indicate where the building has been  	after_dig_node = function(pos, oldnode, oldmetadata, digger) +		local meta  = minetest.get_meta( pos ); + +		-- TODO: remove the correct building  		local path = minetest.get_modpath("apartment")..'/schems/'..filename;  		local mirror = 0;  		local replacement_function = handle_schematics.replacement_function_decay;  		local replacement_param    = nil;  		minetest.chat_send_player( digger:get_player_name(), 'Removing building '..tostring( path )); -		handle_schematics.place_schematic( pos, oldnode.param2, path, mirror, replacement_function, replacement_param, digger, {h=8,v=6} ) +		handle_schematics.place_schematic( pos, oldnode.param2, path, mirror, replacement_function, replacement_param, digger, {h=4,v=3} )  	end, --- TODO: check if digging is allowed + +	-- check if digging is allowed +	can_dig = function(pos,player)	 + +		if( not( player )) then +			return false; +		end +		local pname = player:get_player_name(); +		if( not( minetest.check_player_privs(pname, {apartment_unrent=true}))) then +			minetest.chat_send_player( pname, 'You do not have the apartment_unrent priv which is necessary to dig this node.'); +			return false; +		end +		local meta  = minetest.get_meta( pos ); +		local old_placer = meta:get_string('placed_by'); +		if( old_placer and old_placer ~= '' and old_placer ~= pname ) then +			minetest.chat_send_player( pname, 'Only '..tostring( old_placer )..' can dig this node.'); +			return false; +		end +		return true; +	end,  }) ---	local p1={x=position_data.start_pos.x, y=position_data.start_pos.y, z=position_data.start_pos.z }; ---	local p2={x=position_data.end_pos.x,   y=position_data.end_pos.y,   z=position_data.end_pos.z }; ---	minetest.set_node( {x=p1.x, y=p1.y, z=p1.z }, {name='wool:red'} ); ---	minetest.set_node( {x=p1.x, y=p1.y, z=p2.z }, {name='wool:red'} ); ---	minetest.set_node( {x=p2.x, y=p1.y, z=p1.z }, {name='wool:red'} ); ---	minetest.set_node( {x=p2.x, y=p2.y, z=p2.z }, {name='wool:red'} );  minetest.register_node( handle_schematics.AUTODECAY, {          description = "decaying building", @@ -26,6 +26,9 @@  --]]      -- Changelog: +-- 25.02.14 Buildings can now be saved. Just prefix the apartment name with save_as_ +--          start_pos and end_pos of apartments are now saved (necessary for the above mentioned save function). +--          Building spawner chest is now working.  -- 22.02.14 Added code for spawning several apartments at the same time.   -- 18.02.14 Added support for further nodes (shops, travelnet, ..).  --          Cleaned up formspec that contains apartment information. @@ -232,10 +235,40 @@ apartment.on_receive_fields = function(pos, formname, fields, player)  		apartment.rent( pos, original_owner, nil, player ); -		apartment.apartments[ fields.descr ] = { pos={x=pos.x, y=pos.y, z=pos.z}, original_owner = original_owner, owner='', category = fields.category }; +		apartment.apartments[ fields.descr ] = { pos={x=pos.x, y=pos.y, z=pos.z}, original_owner = original_owner, owner='', category = fields.category, +		                                         start_pos = apartment.apartments[ fields.descr ].start_pos, +		                                         end_pos   = apartment.apartments[ fields.descr ].end_pos  };  		apartment.save_data();  		minetest.chat_send_player( pname, 'Apartment \''..tostring( fields.descr )..'\' (category: '..tostring( fields.category )..') is ready for rental.'); + +		-- this way, schematics can be created +		if( minetest.check_player_privs(pname, {apartment_unrent=true}) +		    and string.sub( fields.descr, 1, string.len( 'save_as_' ))=='save_as_') then + +			local filename = string.sub( fields.descr, string.len( 'save_as' )+2); +			if( filename and filename ~= '' ) then +				-- param2 needs to be translated init initial rotation as well +				local node = minetest.get_node( pos ); +				if(     node.param2 == 0 ) then +					filename = filename..'_0_90'; +				elseif( node.param2 == 3 ) then +					filename = filename..'_0_180'; +				elseif( node.param2 == 1 ) then +					filename = filename..'_0_0'; +				elseif( node.param2 == 2 ) then +					filename = filename..'_0_270'; +				end +				filename = minetest.get_modpath("apartment")..'/schems/'..filename..'.mts'; +				-- really save it with probability_list and slice_prob_list both as nil +				minetest.create_schematic( apartment.apartments[ fields.descr ].start_pos, +				                           apartment.apartments[ fields.descr ].end_pos, +				                           nil, filename, nil); +				minetest.chat_send_player( pname, 'Created schematic \''..tostring( filename )..'\' for use with the apartment spawner. Saved from '.. +						           minetest.serialize(  apartment.apartments[ fields.descr ].start_pos )..' to '.. +						           minetest.serialize(  apartment.apartments[ fields.descr ].end_pos )..'.'); +			end +		end  		return;  	elseif( fields.rent and pname == original_owner ) then @@ -393,6 +426,12 @@ apartment.rent = function( pos, pname, oldmetadata, actor )  	end  	y1 = y1 - size_down;      y2 = y2 + size_up;   +	if( not( apartment.apartments[ original_descr ] )) then +		apartment.apartments[ original_descr ] = {}; +	end +	apartment.apartments[ original_descr ].start_pos   = {x=x1, y=y1, z=z1}; +	apartment.apartments[ original_descr ].end_pos     = {x=x2, y=y2, z=z2}; +  	local px = x1;  	local py = x1;  	local pz = z1; | 
