From 0da4a93f95284d6c876c71b2e7a1a8b0e6662e94 Mon Sep 17 00:00:00 2001 From: Sokomine Date: Tue, 25 Feb 2014 21:41:29 +0100 Subject: improved building spawner chest --- handle_schematics.lua | 339 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 285 insertions(+), 54 deletions(-) (limited to 'handle_schematics.lua') 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", -- cgit v1.2.3