summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGabriel Pérez-Cerezo <gabriel@gpcf.eu>2020-10-11 09:59:55 +0200
committerGabriel Pérez-Cerezo <gabriel@gpcf.eu>2020-10-11 09:59:55 +0200
commit963ddfd61bf780a91fd22881eba103744f09b46c (patch)
tree47603a213695120892c375ed507f49f9cf1a59dc
parent68e8a0562d68e609e31e0a2b4eb0316c0bdeb8eb (diff)
parent6f0d99680255d3c773420c26f4604b5457b531f0 (diff)
downloadforks-modpack-963ddfd61bf780a91fd22881eba103744f09b46c.tar.gz
forks-modpack-963ddfd61bf780a91fd22881eba103744f09b46c.tar.bz2
forks-modpack-963ddfd61bf780a91fd22881eba103744f09b46c.zip
Merge commit '6f0d99680255d3c773420c26f4604b5457b531f0' as 'apartment'
-rw-r--r--apartment/README.md2
-rw-r--r--apartment/handle_schematics.lua670
-rw-r--r--apartment/init.lua966
-rw-r--r--apartment/schems/apartment_4x10_0_270.mtsbin0 -> 497 bytes
-rw-r--r--apartment/schems/apartment_4x11_0_180.mtsbin0 -> 489 bytes
-rw-r--r--apartment/schems/apartment_4x6_0_90.mtsbin0 -> 349 bytes
-rw-r--r--apartment/textures/apartment_controls_occupied.pngbin0 -> 2853 bytes
-rw-r--r--apartment/textures/apartment_controls_vacant.pngbin0 -> 2815 bytes
8 files changed, 1638 insertions, 0 deletions
diff --git a/apartment/README.md b/apartment/README.md
new file mode 100644
index 0000000..fbcf732
--- /dev/null
+++ b/apartment/README.md
@@ -0,0 +1,2 @@
+
+Still experimental. Use /giveme apartment:apartment to get a control panel.
diff --git a/apartment/handle_schematics.lua b/apartment/handle_schematics.lua
new file mode 100644
index 0000000..e72ed60
--- /dev/null
+++ b/apartment/handle_schematics.lua
@@ -0,0 +1,670 @@
+
+local handle_schematics = {}
+
+-- node name used to indicate where the building will eventually be placed
+handle_schematics.SCAFFOLDING = 'random_buildings:support';
+
+handle_schematics.AUTODECAY = 'apartment:autodecay';
+
+handle_schematics.ENABLE_SLOW_DECAY = false
+
+-- taken from https://github.com/MirceaKitsune/minetest_mods_structures/blob/master/structures_io.lua (Taokis Sructures I/O mod)
+-- gets the size of a structure file
+-- nodenames: contains all the node names that are used in the schematic
+-- on_constr: lists all the node names for which on_construct has to be called after placement of the schematic
+handle_schematics.analyze_mts_file = function( path )
+ local size = { x = 0, y = 0, z = 0, version = 0 }
+ local version = 0;
+
+ local file = io.open(path..'.mts', "r")
+ if (file == nil) then
+ return nil
+ end
+
+ -- thanks to sfan5 for this advanced code that reads the size from schematic files
+ local read_s16 = function(fi)
+ return string.byte(fi:read(1)) * 256 + string.byte(fi:read(1))
+ end
+
+ local function get_schematic_size(f)
+ -- make sure those are the first 4 characters, otherwise this might be a corrupt file
+ if f:read(4) ~= "MTSM" then
+ return nil
+ end
+ -- advance 2 more characters
+ local version = read_s16(f); --f:read(2)
+ -- the next characters here are our size, read them
+ return read_s16(f), read_s16(f), read_s16(f), version
+ end
+
+ size.x, size.y, size.z, size.version = get_schematic_size(file)
+
+ -- read the slice probability for each y value that was introduced in version 3
+ if( size.version >= 3 ) then
+ -- the probability is not very intresting for buildings so we just skip it
+ file:read( size.y );
+ end
+
+
+ -- this list is not yet used for anything
+ local nodenames = {};
+ -- this list is needed for calling on_construct after place_schematic
+ local on_constr = {};
+ -- nodes that require after_place_node to be called
+ local after_place_node = {};
+
+ -- after that: read_s16 (2 bytes) to find out how many diffrent nodenames (node_name_count) are present in the file
+ local node_name_count = read_s16( file );
+
+ for i = 1, node_name_count do
+
+ -- the length of the next name
+ local name_length = read_s16( file );
+ -- the text of the next name
+ local name_text = file:read( name_length );
+
+ table.insert( nodenames, name_text );
+ -- in order to get this information, the node has to be defined and loaded
+ if( minetest.registered_nodes[ name_text ] and minetest.registered_nodes[ name_text ].on_construct) then
+ table.insert( on_constr, name_text );
+ end
+ -- some nodes need after_place_node to be called for initialization
+ if( minetest.registered_nodes[ name_text ] and minetest.registered_nodes[ name_text ].after_place_node) then
+ table.insert( after_place_node, name_text );
+ end
+ end
+
+ file.close(file)
+
+ local rotated = 0;
+ local burried = 0;
+ local parts = path:split('_');
+ if( parts and #parts > 2 ) then
+ if( parts[#parts]=="0" or parts[#parts]=="90" or parts[#parts]=="180" or parts[#parts]=="270" ) then
+ rotated = tonumber( parts[#parts] );
+ burried = tonumber( parts[ #parts-1 ] );
+ if( not( burried ) or burried>20 or burried<0) then
+ burried = 0;
+ end
+ end
+ end
+ return { size = { x=size.x, y=size.y, z=size.z}, nodenames = nodenames, on_constr = on_constr, after_place_node = after_place_node, rotated=rotated, burried=burried };
+end
+
+
+-- depending on the orientation (param2) of the build chest, the start position of the building may have to be moved;
+-- this function makes sure that the building will always extend to the right and in front of the build chest
+handle_schematics.translate_param2_to_rotation = function( param2, mirror, start_pos, orig_max, rotated, burried )
+
+ local max = {x=orig_max.x, y=orig_max.y, z=orig_max.z};
+ -- if the schematic has been saved in a rotated way, swapping x and z may be necessary
+ if( rotated==90 or rotated==270) then
+ max.x = orig_max.z;
+ max.z = orig_max.x;
+ end
+
+ -- the building may have a cellar or something alike
+ if( burried > 0 ) then
+ start_pos.y = start_pos.y - burried;
+ end
+
+ -- make sure the building always extends forward and to the right of the player
+ local rotate = 0;
+ if( param2 == 0 ) then rotate = 270; if( mirror==1 ) then start_pos.x = start_pos.x - max.x + max.z; end -- z gets larger
+ elseif( param2 == 1 ) then rotate = 0; start_pos.z = start_pos.z - max.z; -- x gets larger
+ elseif( param2 == 2 ) then rotate = 90; start_pos.z = start_pos.z - max.x;
+ if( mirror==0 ) then start_pos.x = start_pos.x - max.z; -- z gets smaller
+ else start_pos.x = start_pos.x - max.x; end
+ elseif( param2 == 3 ) then rotate = 180; start_pos.x = start_pos.x - max.x; -- x gets smaller
+ end
+
+ if( param2 == 1 or param2 == 0) then
+ start_pos.z = start_pos.z + 1;
+ elseif( param2 == 1 or param2 == 2 ) then
+ start_pos.x = start_pos.x + 1;
+ end
+ if( param2 == 1 ) then
+ start_pos.x = start_pos.x + 1;
+ end
+
+ rotate = rotate + rotated;
+ -- make sure the rotation does not reach or exceed 360 degree
+ if( rotate >= 360 ) then
+ rotate = rotate - 360;
+ end
+ -- rotate dimensions when needed
+ if( param2==0 or param2==2) then
+ local tmp = max.x;
+ max.x = max.z;
+ max.z = tmp;
+ end
+
+ return { rotate=rotate, start_pos = {x=start_pos.x, y=start_pos.y, z=start_pos.z},
+ end_pos = {x=(start_pos.x+max.x-1), y=(start_pos.y+max.y-1), z=(start_pos.z+max.z-1) },
+ max = {x=max.x, y=max.y, z=max.z}};
+end
+
+
+
+-- call on_construct and after_place_node for nodes that require it;
+-- 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, 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
+
+ -- there are only very few nodes which need this special treatment
+ local nodes = minetest.find_nodes_in_area( start_pos, end_pos, v);
+
+ for _, p in ipairs( nodes ) do
+ minetest.registered_nodes[ v ].on_construct( p );
+ end
+ end
+
+ if( placer ) then
+ for i, v in ipairs( after_place_node ) do
+
+ -- there are only very few nodes which need this special treatment
+ local nodes = minetest.find_nodes_in_area( start_pos, end_pos, v);
+
+ for _, p in ipairs( nodes ) do
+ minetest.registered_nodes[ v ].after_place_node( p, placer, nil, nil );
+ end
+ end
+
+ local player_name = placer:get_player_name();
+
+ -- steel doors are annoying because the cannot be catched with the functions above
+ local doornodes = minetest.find_nodes_in_area( start_pos, end_pos,
+ {'doors:door_steel_b_1','doors:door_steel_b_2',
+ 'doors:door_steel_t_1','doors:door_steel_t_2'});
+ for _, p in ipairs( doornodes ) do
+ local meta = minetest.get_meta( p );
+ meta:set_string("doors_owner", player_name );
+ 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'} );
+ 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;
+
+ 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
+ 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, extra_params )
+
+ local node = minetest.env:get_node( pos );
+ if( not( node ) or not( node.param2 ) or node.name=="air") then
+ if( not( param2 )) then
+ return false;
+ end
+ node = {name="", param2 = param2 };
+ 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 = {};
+ if( replacement_function ) then
+ replacements = replacement_function( building_data.nodenames, replacement_param );
+ elseif( replacement_param and not replacement_param.even ) then
+ replacements = replacement_param;
+ end
+
+
+ local force_place = true;
+ -- when building scaffolding, do not replace anything yet
+ if( replacement_function and replacement_function == handle_schematics.replacement_function_scaffolding ) then
+ force_place = false;
+ end
+
+
+ -- 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,
+ extra_params );
+ else
+ -- place multiple copies
+ local vector = {h=-1,v=1};
+ if( node.param2 == 0 or node.param2 == 3) then --node.param2 == 1 or node.param2 == 3 ) then
+ vector.h = 1;
+ end
+
+ -- it looks best if every second house is built out of another material
+ local replacements_even = replacements;
+ local replacements_odd = replacements;
+ if( replacement_param and replacement_param.even and replacement_param.odd ) then
+ replacements_even = replacement_param.even;
+ replacements_odd = replacement_param.odd;
+ end
+
+ local p = {x=position_data.start_pos.x , y=position_data.start_pos.y, z=position_data.start_pos.z };
+ for j=1,do_copies.v do
+ p.x = position_data.start_pos.x;
+ p.z = position_data.start_pos.z;
+ for i=1,do_copies.h do -- horizontal copies
+
+
+ local key = '';
+ local val = {};
+ local p_end = {x=(p.x+position_data.max.x), y=(p.y+position_data.max.y), z=(p.z+position_data.max.z)};
+
+ for key,val in pairs( apartment.apartments ) do
+ if( val and val.pos
+ and (val.pos.x >= p.x) and (val.pos.x <= p_end.x)
+ and (val.pos.y >= p.y) and (val.pos.y <= p_end.y)
+ and (val.pos.z >= p.z) and (val.pos.z <= p_end.z)) then
+
+-- TODO: add FAIL if the apartment is still rented
+ if( placer ) then
+ minetest.chat_send_player( placer:get_player_name(), 'Removing Apartment '..tostring( key )..
+ ' (new usage for that place). Position: '..minetest.serialize( val.pos ));
+ end
+ print( 'Removing Apartment '..tostring( key )..' (new usage for that place). Position: '..minetest.serialize( val.pos ));
+ apartment.apartments[ key ] = nil;
+ end
+ end
+ -- switch replacements between houses
+ if( i%2==0 ) then
+ minetest.place_schematic( p, path..'.mts', tostring(position_data.rotate), replacements_even, force_place );
+ else
+ 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, z=p.z+position_data.max.z},
+ 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;
+ else
+ p.z = p.z + vector.h*position_data.max.z;
+ end
+ end
+ p.y = p.y + vector.v*position_data.max.y;
+ end
+
+ if( node.param2 == 0 or node.param2 == 2 ) then
+ position_data.end_pos.x = position_data.start_pos.x + vector.h*position_data.max.x*do_copies.h;
+ else
+ position_data.end_pos.z = position_data.start_pos.z + vector.h*position_data.max.z*do_copies.v;
+ end
+ position_data.end_pos.y = position_data.start_pos.y + vector.v*position_data.max.y*do_copies.v;
+ end
+ return {start_pos = position_data.start_pos, end_pos = position_data.end_pos };
+end
+
+
+
+-- replace all nodes with scaffolding ones so that the player can see where the real building will be placed
+handle_schematics.replacement_function_scaffolding = function( nodenames )
+
+ local replacements = {};
+ for _,v in ipairs( nodenames ) do
+ table.insert( replacements, { v, handle_schematics.SCAFFOLDING })
+ end
+ return replacements;
+end
+
+
+-- places nodes that look like leaves at the positions where the building was;
+-- those nodes will decay using an abm;
+-- this gradual disappearance of the building helps to understand the player what
+-- just happend (=building was removed) and where it happened
+handle_schematics.replacement_function_decay = function( nodenames )
+
+ local replacements = {};
+ for _,v in ipairs( nodenames ) do
+ if( handle_schematics.ENABLE_SLOW_DECAY ) then
+ table.insert( replacements, { v, handle_schematics.AUTODECAY })
+ else
+ table.insert( replacements, { v, 'air' })
+ end
+ end
+ 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_4x11_0_180]'..
+ '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}}};
+
+ local res = {};
+ res = 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})
+ if( res and res.start_pos ) then
+ meta:set_string('start_pos', minetest.serialize( res.start_pos ));
+ meta:set_string('end_pos', minetest.serialize( res.end_pos ));
+ end
+ return;
+ end
+ -- TODO
+ minetest.chat_send_player( pname, 'Dig this spawner in order to remove the building.');
+end
+
+
+
+minetest.register_node("apartment:build_chest", {
+ description = "Apartment spawner",
+ tiles = {"default_chest_side.png", "default_chest_top.png^door_steel.png", "default_chest_side.png",
+ "default_chest_side.png", "default_chest_side.png", "default_chest_lock.png^door_steel.png"},
+ paramtype2 = "facedir",
+ groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2},
+ legacy_facedir_simple = true,
+
+ after_place_node = function(pos, placer, itemstack)
+ 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 );
+
+ if( oldmetadata and oldmetadata.fields and oldmetadata.fields.path ) then
+
+ local replacement_function = handle_schematics.replacement_function_decay;
+ local replacement_param = nil;
+ local path = minetest.get_modpath("apartment")..'/schems/'..oldmetadata.fields.path;
+
+ minetest.chat_send_player( digger:get_player_name(), 'Removing building '..tostring( oldmetadata.fields.path ));
+ handle_schematics.place_schematic( pos, oldnode.param2, path, 0,
+ replacement_function, replacement_param, digger,
+ {h=oldmetadata.fields.h,v=oldmetadata.fields.v} )
+ end
+ end,
+
+ -- 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,
+
+})
+
+
+if handle_schematics.ENABLE_SLOW_DECAY then
+ minetest.register_node( handle_schematics.AUTODECAY, {
+ description = "decaying building",
+ drawtype = "allfaces_optional",
+ visual_scale = 1.3,
+ tiles = {"default_leaves.png"},
+ paramtype = "light",
+ waving = 1,
+ is_ground_content = false,
+ groups = {snappy=3},
+ })
+
+ minetest.register_abm({
+ nodenames = {handle_schematics.AUTODECAY},
+ -- A low interval and a high inverse chance spreads the load
+ interval = 2,
+ chance = 3,
+ action = function(p0, node, _, _)
+ minetest.remove_node( p0 );
+ end
+ })
+end
diff --git a/apartment/init.lua b/apartment/init.lua
new file mode 100644
index 0000000..9cf7c5a
--- /dev/null
+++ b/apartment/init.lua
@@ -0,0 +1,966 @@
+
+--[[
+ The apartment mod allows players to rent a place with locked objects in
+ - the ownership of the locked objects is transfered to the player who
+ rented the apartment.
+
+ Copyright (C) 2014 Sokomine
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+ Version: 1.3
+ Autor: Sokomine
+ Date: 12.02.14
+--]]
+
+-- Changelog:
+-- 15.06.14 Added abm to turn apartment:apartment into either apartment:apartment_free or apartment:apartment_occupied
+-- so that it becomes visible weather an apartment is free or not
+-- 15.05.14 Added diffrent panel for occupied apartments. Added textures created by VanessaE.
+-- 24.02.14 Buildings can now be removed again (dig the spawn chest)
+-- 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.
+-- Introduced diffrent categories so that i.e. a shop and an apartment can be rented at the same time.
+-- 16.02.14 Removed MAX_LIGHT var and set to fixed value.
+-- 16.02.14 Only descriptions and ownership of known objects are changed.
+-- When digging the panel, the descriptions are reset.
+-- 14.02.14 Improved formspecs, messages and descriptions of rented and vacant items.
+-- Players with the apartment_unrent priv can now throw other players out of apartments.
+-- Apartment names have to be uniq.
+-- Each player can only rent one apartment at a time.
+-- Added /aphome command
+
+minetest.register_privilege("apartment_unrent", { description = "allows to throw players out of apartments they have rented", give_to_singleplayer = false});
+
+apartment = {}
+
+dofile(minetest.get_modpath("apartment")..'/handle_schematics.lua');
+
+-- will contain information about all apartments of the server in the form:
+-- { apartment_descr = { pos={x=0,y=0,z=0}, original_owner='', owner=''}
+apartment.apartments = {};
+
+-- set to false if you do not like your players
+apartment.enable_aphome_command = true;
+
+-- TODO: save and restore ought to be library functions and not implemented in each individual mod!
+-- called whenever an apartment is added or removed
+apartment.save_data = function()
+
+ local data = minetest.serialize( apartment.apartments );
+ local path = minetest.get_worldpath().."/apartment.data";
+
+ local file = io.open( path, "w" );
+ if( file ) then
+ file:write( data );
+ file:close();
+ else
+ print("[Mod apartment] Error: Savefile '"..tostring( path ).."' could not be written.");
+ end
+end
+
+
+apartment.restore_data = function()
+
+ local path = minetest.get_worldpath().."/apartment.data";
+
+ local file = io.open( path, "r" );
+ if( file ) then
+ local data = file:read("*all");
+ apartment.apartments = minetest.deserialize( data );
+ file:close();
+ else
+ print("[Mod apartment] Error: Savefile '"..tostring( path ).."' not found.");
+ end
+end
+
+
+
+
+apartment.get_formspec = function( pos, placer )
+
+ local meta = minetest.get_meta(pos);
+ local original_owner = meta:get_string( 'original_owner' );
+ local owner = meta:get_string( 'owner' );
+ local descr = meta:get_string( 'descr' );
+
+ -- misconfigured
+ if( not( original_owner ) or original_owner == '' ) then
+ return 'field[text;;Panel misconfigured. Please dig and place again.] ';
+ end
+
+ -- if a name has been set
+ if( descr and descr ~= '' ) then
+
+ local size_txt = 'textarea[0.0,0.8;6.5,1.2;info;;'..minetest.formspec_escape(
+ 'It extends '..
+ (meta:get_string("size_right") or '?')..' m to the right, '..
+ (meta:get_string("size_left" ) or '?')..' m to the left, '..
+ (meta:get_string("size_up" ) or '?')..' m up, '..
+ (meta:get_string("size_down" ) or '?')..' m down,\n'..
+ (meta:get_string("size_back" ) or '?')..' m in front of you and '..
+ (meta:get_string("size_front") or '?')..' m behind you. '..
+ 'It has been built by\n'..(original_owner or '?')..
+ '. Building category: '..tostring( meta:get_string('category'))..'.')..']';
+
+ if( original_owner ~= owner and owner ~= '' ) then
+ return 'size[6.5,3]'..
+ 'label[2.0,-0.3;Apartment \''..minetest.formspec_escape( descr )..'\']'..
+ size_txt..
+ 'label[0.5,1.7;This apartment is rented by:]'..
+ 'label[3.5,1.7;'..tostring( owner )..']'..
+ 'button_exit[3,2.5;2,0.5;unrent;Move out]'..
+ 'button_exit[1,2.5;1,0.5;abort;OK]';
+ end
+ return 'size[6,3]'..
+ 'label[2.0,-0.3;Apartment \''..minetest.formspec_escape( descr )..'\']'..
+ size_txt..
+ 'label[0.3,1.8;Do you want to rent this]'..
+ 'label[3.0,1.8;apartment? It\'s free!]'..
+ 'button_exit[3,2.5;2,0.5;rent;Yes, rent it]'..
+ 'button_exit[1,2.5;1,0.5;abort;No.]';
+ end
+
+ -- defaults that fit to small appartments - change this if needed!
+ local size_up = 2;
+ local size_down = 1;
+ local size_right = 1;
+ local size_left = 2;
+ local size_front = 1;
+ local size_back = 7;
+
+ -- show configuration formspec
+ if( not( owner ) or owner=='' or owner==original_owner ) then
+ return 'size[7,7]'..
+ 'label[2.0,-0.3;Apartment Configuration]'..
+
+ 'label[0.5,0.5;Name or number for this apartment:]'..
+ 'field[5.0,0.9;2.0,0.5;descr;;'..tostring( descr )..']'..
+
+ 'label[0.5,0.8;Category (i.e. house, shop):]'..
+ 'field[5.0,1.4;2.0,0.5;category;;apartment]'..
+
+ 'label[0.5,1.7;The apartment shall extend]'..
+ 'label[3.4,1.7;this many blocks from here:]'..
+ 'label[0.5,2.1;(relative to this panel)]'..
+
+ 'label[1.3,3.5;left:]' ..'field[2.0,4.0;1.0,0.5;size_left;;' ..tostring( size_left )..']'..
+ 'label[4.6,3.5;right]' ..'field[4.0,4.0;1.0,0.5;size_right;;'..tostring( size_right )..']'..
+ 'label[2.8,5.0;front]' ..'field[3.0,5.0;1.0,0.5;size_front;;'..tostring( size_front )..']'..
+ 'label[2.8,2.1;back:]' ..'field[3.0,3.0;1.0,0.5;size_back;;' ..tostring( size_back )..']'..
+ 'label[5.8,2.1;up:]' ..'field[6.0,3.0;1.0,0.5;size_up;;' ..tostring( size_up )..']'..
+ 'label[5.8,5.0;down]' ..'field[6.0,5.0;1.0,0.5;size_down;;' ..tostring( size_down )..']'..
+
+ 'button_exit[4,6.0;2,0.5;store;Store and offer]'..
+ 'button_exit[1,6.0;1,0.5;abort;Abort]';
+ end
+end
+
+
+
+apartment.on_receive_fields = function(pos, formname, fields, player)
+
+ local meta = minetest.get_meta(pos);
+ local pname = player:get_player_name();
+ local original_owner = meta:get_string( 'original_owner' );
+ local owner = meta:get_string( 'owner' );
+ local descr = meta:get_string( 'descr' );
+ local category = meta:get_string( 'category' );
+
+ if( not( fields ) or fields.abort or not( original_owner ) or original_owner=='' or not( fields.quit )) then
+ return;
+
+ elseif( not( descr ) or descr=='' ) then
+
+ -- only the player who placed the panel can configure it
+ if( not( fields.store ) or pname ~= original_owner or pname ~= owner) then
+ if( fields.descr and fields.descr ~= '') then
+ minetest.chat_send_player( pname, 'Error: Only the owner of this panel can configure it.');
+ end
+ return;
+ end
+
+ local size_left = tonumber( fields.size_left or -1);
+ local size_right = tonumber( fields.size_right or -1);
+ local size_up = tonumber( fields.size_up or -1);
+ local size_down = tonumber( fields.size_down or -1);
+ local size_front = tonumber( fields.size_front or -1);
+ local size_back = tonumber( fields.size_back or -1);
+
+ -- have all fields been filled int?
+ if( not(fields.store)
+ or not(size_left ) or size_left < 0 or size_left > 10
+ or not(size_right ) or size_right< 0 or size_right> 10
+ or not(size_up ) or size_up < 0 or size_up > 10
+ or not(size_down ) or size_down < 0 or size_down > 10
+ or not(size_front ) or size_front< 0 or size_front> 10
+ or not(size_back ) or size_back < 0 or size_back > 10
+ or not(fields.category )
+ or not(fields.descr ) or fields.descr == '') then
+
+ minetest.chat_send_player( pname, 'Error: Not all fields have been filled in or the area is too large.');
+ return;
+ end
+
+ -- avoid duplicate names
+ if( apartment.apartments[ fields.descr ] ) then
+ minetest.chat_send_player( pname, 'Error: An apartment by that name exists already (name: '..fields.descr..').'..
+ 'Please choose a diffrent name/id.');
+ return;
+ end
+
+ meta:set_int( 'size_up', size_up );
+ meta:set_int( 'size_down', size_down );
+ meta:set_int( 'size_right', size_right );
+ meta:set_int( 'size_left', size_left );
+ meta:set_int( 'size_front', size_front );
+ meta:set_int( 'size_back', size_back );
+
+ meta:set_string( 'descr', fields.descr );
+ meta:set_string( 'category', fields.category );
+
+ meta:set_string( 'formspec', apartment.get_formspec( pos, 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,
+ 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
+ minetest.chat_send_player( pname, 'You cannot rent your own appartment. Dig the panel if you no longer want to rent it.');
+ return;
+
+ elseif( fields.rent and owner == pname ) then
+ minetest.chat_send_player( pname, 'You have already rented this apartment.');
+ return;
+
+ elseif( fields.rent and owner ~= original_owner ) then
+ minetest.chat_send_player( pname, 'Sorry, this apartment has already been rented to '..tostring( owner )..'.');
+ return;
+
+ -- actually rent the appartment
+ elseif( fields.rent ) then
+
+ if( not( apartment.apartments[ descr ] )) then
+ minetest.chat_send_player( pname, 'Error: This apartment is not registered. Please un-rent it and ask the original buildier '..
+ 'to dig and place this panel again.');
+ return;
+ end
+
+ -- make sure only one apartment can be rented at a time
+ for k,v in pairs( apartment.apartments ) do
+ if( v and v.owner and v.owner==pname
+ and v.category and category and v.category == category) then
+ minetest.chat_send_player( pname, 'Sorry. You can only rent one apartment per category at a time. You have already '..
+ 'rented apartment '..k..'.');
+ return;
+ end
+ end
+
+ if( not( apartment.rent( pos, pname, nil, player ))) then
+ minetest.chat_send_player( pname, 'Sorry. There was an internal error. Please try again later.');
+ return;
+ end
+
+ minetest.chat_send_player( pname, 'You have rented apartment \''..tostring( descr )..'\'. Enjoy your stay!');
+ meta:set_string( 'formspec', apartment.get_formspec( pos, player ));
+ return;
+
+ elseif( fields.unrent and owner ~= original_owner and owner==pname ) then
+ if( not( apartment.rent( pos, original_owner, nil, player ) )) then
+ minetest.chat_send_player( pname, 'Something went wrong when giving back the apartment.');
+ return;
+ end
+ minetest.chat_send_player( pname, 'You have ended your rent of apartment \''..tostring( descr )..'\'. It is free for others to rent again.');
+ meta:set_string( 'formspec', apartment.get_formspec( pos, player ));
+ return;
+
+ -- someone else tries to throw the current owner out
+ elseif( fields.unrent and owner ~= original_owner and owner ~= pname ) then
+ if( not( minetest.check_player_privs(pname, {apartment_unrent=true}))) then
+ minetest.chat_send_player( pname, 'You do not have the privilelge to throw other people out of apartments they have rented.');
+ return;
+ end
+ if( not( apartment.rent( pos, original_owner, nil, player ) )) then
+ minetest.chat_send_player( pname, 'Something went wrong when giving back the apartment.');
+ return;
+ end
+ minetest.chat_send_player( pname, 'Player '..owner..' has been thrown out of the apartment. It can now be rented by another player.');
+ meta:set_string( 'formspec', apartment.get_formspec( pos, player ));
+ end
+end
+
+
+-- actually rent the apartment (if possible); return true on success
+-- the "actor" field is only for such cases in which an object is needed for update functiones (i.e. travelnet)
+apartment.rent = function( pos, pname, oldmetadata, actor )
+ local node = minetest.env:get_node(pos);
+ local meta = minetest.get_meta(pos);
+ local original_owner = meta:get_string( 'original_owner' );
+ local owner = meta:get_string( 'owner' );
+ local descr = meta:get_string( 'descr' );
+
+ if( oldmetadata ) then
+ original_owner = oldmetadata.fields[ "original_owner" ];
+ owner = oldmetadata.fields[ "owner" ];
+ descr = oldmetadata.fields[ "descr" ];
+ meta = {};
+ end
+
+ if( not( node ) or not( meta ) or not( original_owner ) or not( owner ) or not( descr )) then
+ return false;
+ end
+
+ local size_up = 0;
+ local size_down = 0;
+ local size_right = 0;
+ local size_left = 0;
+ local size_front = 0;
+ local size_back = 0;
+ if( not( oldmetadata )) then
+ size_up = meta:get_int( 'size_up' );
+ size_down = meta:get_int( 'size_down' );
+ size_right = meta:get_int( 'size_right' );
+ size_left = meta:get_int( 'size_left' );
+ size_front = meta:get_int( 'size_front' );
+ size_back = meta:get_int( 'size_back' );
+ else
+ size_up = tonumber(oldmetadata.fields[ "size_up" ]);
+ size_down = tonumber(oldmetadata.fields[ "size_down" ]);
+ size_right = tonumber(oldmetadata.fields[ "size_right" ]);
+ size_left = tonumber(oldmetadata.fields[ "size_left" ]);
+ size_front = tonumber(oldmetadata.fields[ "size_front" ]);
+ size_back = tonumber(oldmetadata.fields[ "size_back" ]);
+ end
+
+ if( not( size_up ) or not( size_down ) or not( size_right ) or not( size_left ) or not( size_front ) or not( size_back )) then
+ return false;
+ end
+
+ local rented_by = 'rented by '..pname;
+ if( pname == original_owner ) then
+ rented_by = '- vacant -';
+ elseif( pname == '' ) then
+ rented_by = 'owned by '..original_owner;
+ end
+ -- else we might run into trouble if we use it in formspecs
+ local original_descr = descr;
+ descr = minetest.formspec_escape( descr );
+
+ local x1 = pos.x;
+ local y1 = pos.y;
+ local z1 = pos.z;
+ local x2 = pos.x;
+ local y2 = pos.y;
+ local z2 = pos.z;
+
+ if( oldmetadata and oldmetadata.param2 ) then
+ node.param2 = oldmetadata.param2;
+ end
+
+ if( node.param2 == 0 ) then -- z gets larger
+
+ x1 = x1 - size_left; x2 = x2 + size_right;
+ z1 = z1 - size_front; z2 = z2 + size_back;
+
+ elseif( node.param2 == 1 ) then -- x gets larger
+
+ z1 = z1 - size_right; z2 = z2 + size_left;
+ x1 = x1 - size_front; x2 = x2 + size_back;
+
+ elseif( node.param2 == 2 ) then -- z gets smaller
+
+ x1 = x1 - size_right; x2 = x2 + size_left;
+ z1 = z1 - size_back; z2 = z2 + size_front;
+
+ elseif( node.param2 == 3 ) then -- x gets smaller
+
+ z1 = z1 - size_left; z2 = z2 + size_right;
+ x1 = x1 - size_back; x2 = x2 + size_front;
+
+ 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;
+ for px = x1, x2 do
+ for py = y1, y2 do
+ for pz = z1, z2 do
+
+ local m = minetest.get_meta( {x=px, y=py, z=pz});
+ if( m ) then
+ local s = m:get_string( 'owner' );
+ -- doors are diffrent
+ if( not( s ) or s=='' ) then
+ s = m:get_string( 'doors_owner' );
+ end
+ if ( not s or s == '' )then
+ s = original_owner
+ end
+ -- change owner to the new player
+ if( s and s ~= '' and (s==original_owner or s==owner)) then
+ -- change the actual owner
+ -- set a fitting infotext
+ local itext = 'Object in Ap. '..descr..' ('..rented_by..')';
+ n = minetest.get_node( {x=px, y=py, z=pz} );
+ if( n.name == 'default:chest_locked' ) then
+ if( pname == '' ) then
+ itext = "Locked Chest (owned by "..original_owner..")";
+ else
+ itext = "Locked Chest in Ap. "..descr.." ("..rented_by..")";
+ end
+ elseif( n.name == 'doors:door_steel_b_1' or n.name == 'doors:door_steel_t_1'
+ or n.name == 'doors:door_steel_a' or n.name == 'doors:door_steel_b'
+ or n.name == 'doors:door_steel_b_2' or n.name == 'doors:door_steel_t_2' ) then
+ if( pname=='' ) then
+ itext = "Locked Door (owned by "..original_owner..")";
+ elseif( pname==original_owner ) then
+ itext = "Apartment "..descr.." (vacant)";
+ else
+ itext = "Apartment "..descr.." ("..rented_by..")";
+ end
+ -- doors use another meta text
+ m:set_string( 'doors_owner', pname );
+ elseif( n.name == "locked_sign:sign_wall_locked" ) then
+ itext = "\"\" ("..rented_by..")";
+
+ -- only change the one panel that controls this apartment - not any others in the way
+ elseif((n.name == 'apartment:apartment_free' and px==pos.x and py==pos.y and pz==pos.z)
+ or(n.name == 'apartment:apartment_occupied' and px==pos.x and py==pos.y and pz==pos.z)) then
+ if( pname==original_owner ) then
+ itext = "Rent apartment "..descr.." here by right-clicking this panel!";
+ else
+ itext = "Apartment rental control panel for apartment "..descr.." ("..rented_by..")";
+ end
+
+ elseif( n.name == "technic:iron_locked_chest" ) then
+ if( pname=='' ) then
+ itext = "Iron Locked Chest (owned by "..original_owner..")";
+ else
+ itext = "Iron Locked Chest in Ap. "..descr.." (" ..rented_by..")";
+ end
+ elseif( n.name == "technic:copper_locked_chest" ) then
+ if( pname=='' ) then
+ itext = "Copper Locked Chest (owned by "..original_owner..")";
+ else
+ itext = "Copper Locked Chest in Ap. "..descr.." ("..rented_by..")";
+ end
+ elseif( n.name == "technic:gold_locked_chest" ) then
+ if( pname=='' ) then
+ itext = "Gold Locked Chest (owned by "..original_owner..")";
+ else
+ itext = "Gold Locked Chest in Ap. "..descr.." (" ..rented_by..")";
+ end
+
+ elseif( n.name == "inbox:empty" ) then
+ if( pname=='' ) then
+ itext = original_owner.."'s Mailbox";
+ else
+ itext = pname.."'s Mailbox";
+ end
+ elseif( n.name == "locks:shared_locked_chest") then
+ itext = "Shared locked chest ("..rented_by..")";
+ elseif( n.name == "locks:shared_locked_furnace"
+ or n.name == "locks:shared_locked_furnace_active") then
+ itext = "Shared locked furnace ("..rented_by..")";
+ elseif( n.name == "locks:shared_locked_sign_wall") then
+ itext = "Shared locked sign ("..rented_by..")";
+ elseif( n.name == "locks:door"
+ or n.name == "locks:door_top_1"
+ or n.name == "locks:door_top_2"
+ or n.name == "locks:door_bottom_1"
+ or n.name == "locks:door_bottom_2") then
+ itext = "Shared locked door ("..rented_by..")";
+
+ elseif( n.name == "chests_0gb_us:shared" ) then
+ itext = "Shared Chest ("..rented_by..")";
+ elseif( n.name == "chests_0gb_us:secret" ) then
+ itext = "Secret Chest ("..rented_by..")";
+ elseif( n.name == "chests_0gb_us:dropbox") then
+ itext = "Dropbox ("..rented_by..")";
+
+ elseif( n.name == "itemframes:frame" ) then
+ itext = "Item frame ("..rented_by..")";
+ elseif( n.name == "itemframes:pedestral" ) then
+ itext = "Pedestral frame ("..rented_by..")";
+
+
+ -- money mod - shop and barter shop; admin shops do not change ownership
+ elseif( n.name == "money:shop" ) then
+ if( m:get_string('infotext')=="Untuned Shop"
+ or m:get_string('infotext')=="Detuned Shop"
+ or not( m:get_string('shopname' ))
+ or m:get_string('infotext')=="Untuned Shop (owned by "..(m:get_string('owner') or "")..")") then
+ itext = "Untuned Shop ("..rented_by..")";
+ else
+ itext = "Shop \""..m:get_string('shopname').."\" ("..rented_by..")";
+ end
+ elseif( n.name == "money:barter_shop" ) then
+ if( m:get_string('infotext')=="Untuned Barter Shop"
+ or m:get_string('infotext')=="Detuned Barter Shop"
+ or not( m:get_string('shopname' ))
+ or m:get_string('infotext')=="Untuned Barter Shop (owned by "..(m:get_string('owner') or "")..")") then
+ itext = "Untuned Barter Shop ("..rented_by..")";
+ else
+ itext = "Barter Shop \""..m:get_string('shopname').."\" ("..rented_by..")";
+ end
+
+ elseif( n.name == "currency:safe") then
+ itext = "Safe ("..rented_by..")";
+ elseif( n.name == "currency:shop") then
+ itext = "Exchange shop ("..rented_by..")";
+
+ elseif( n.name == "bitchange:bank" ) then
+ itext = "Bank ("..rented_by..")";
+ elseif( n.name == "bitchange:moneychanger" ) then
+ itext = "Moneychanger ("..rented_by..")";
+ elseif( n.name == "bitchange:warehouse" ) then
+ itext = "Warehouse ("..rented_by..")";
+ elseif (n.name == "smartshop:shop") then
+ itext = "Shop " .. rented_by
+ m:set_int("creative", 0)
+ m:set_int("type",1)
+ elseif( n.name == "bitchange:shop" ) then
+ if( m:get_string('title') and m:get_string('title') ~= '' ) then
+ itext = "Exchange shop \""..( m:get_string('title')).."\" ("..rented_by..")";
+ else
+ itext = "Exchange shop ("..rented_by..")";
+ end
+
+ elseif( n.name == "vendor:vendor"
+ or n.name == "vendor:depositor") then
+ if( pname == '' ) then
+ m:set_string( 'owner', original_owner );
+ else
+ m:set_string( 'owner', pname );
+ end
+ vendor.refresh( {x=px, y=py, z=pz}, nil);
+ -- everything has been set alrady
+ itext = '';
+
+ -- un-configure the travelnet
+ elseif( n.name == "travelnet:travelnet"
+ or n.name == "travelnet:elevator"
+ or n.name == "locked_travelnet:elevator"
+ or n.name == "locked_travelnet:travelnet" ) then
+
+ local oldmetadata = { fields = {
+ owner = m:get_string('owner'),
+ station_name = m:get_string('station_name'),
+ station_network = m:get_string('station_network') }};
+
+ -- the old box has to be removed from the network
+ travelnet.remove_box( {x=px, y=py, z=pz}, nil, oldmetadata, actor );
+ if( pname == '' ) then
+ m:set_string( 'owner', original_owner );
+ else
+ m:set_string( 'owner', pname );
+ end
+ -- prepare the box with new input formspec etc.
+ minetest.registered_nodes[ n.name ].after_place_node({x=px, y=py, z=pz}, actor, nil);
+ itext = '';
+
+
+ else
+ itext = '';
+ end
+ -- only set ownership of nodes the mod knows how to handle
+ if( itext ) then
+ m:set_string( "infotext", itext );
+ if( pname == '' ) then
+ m:set_string( 'owner', original_owner );
+ else
+ m:set_string( 'owner', pname );
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+
+ -- here, we need the original descr again
+ if( apartment.apartments[ original_descr ] ) then
+ if( original_owner == pname ) then
+ apartment.apartments[ original_descr ].owner = '';
+ else
+ apartment.apartments[ original_descr ].owner = pname;
+ end
+ apartment.save_data();
+ end
+
+ if( not( oldmetadata) ) then
+ if( (owner == '' or original_owner==pname)
+ and (node.name ~= 'apartment:apartment_free')) then
+ minetest.swap_node( pos, {name='apartment:apartment_free', param2 = node.param2} );
+ elseif( (node.name ~= 'apartment:apartment_occupied')
+ and (original_owner ~= pname)) then
+ minetest.swap_node( pos, {name='apartment:apartment_occupied', param2 = node.param2} );
+ end
+ end
+ return true;
+end
+
+
+
+apartment.on_construct = function(pos)
+ local meta = minetest.env:get_meta(pos);
+ meta:set_string('infotext', 'Apartment Management Panel (unconfigured)');
+ meta:set_string('original_owner', '' );
+ meta:set_string('owner', '' );
+ meta:set_string('descr', '' );
+ meta:set_int( 'size_up', 0 );
+ meta:set_int( 'size_down', 0 );
+ meta:set_int( 'size_right', 0 );
+ meta:set_int( 'size_left', 0 );
+ meta:set_int( 'size_front', 0 );
+ meta:set_int( 'size_back', 0 );
+end
+
+
+apartment.after_place_node = function(pos, placer)
+ local meta = minetest.get_meta(pos);
+ local pname = (placer:get_player_name() or "");
+ meta:set_string("original_owner", pname );
+ meta:set_string("owner", pname );
+ meta:set_string('infotext', 'Apartment Management Panel (owned by '..pname..')' );
+
+ meta:set_string("formspec", apartment.get_formspec( pos, placer ));
+end
+
+
+apartment.can_dig = function(pos,player)
+
+ local meta = minetest.get_meta(pos);
+ local owner = meta:get_string('owner');
+ local original_owner = meta:get_string( 'original_owner' );
+ local pname = player:get_player_name();
+
+ if( not( original_owner ) or original_owner == '' ) then
+ return true;
+ end
+
+ if( original_owner ~= pname ) then
+ minetest.chat_send_player( pname, 'Sorry. Only the original owner of this apartment control panel can dig it.');
+ return false;
+ end
+
+ if( original_owner ~= owner ) then
+ minetest.chat_send_player( pname, 'The apartment is currently rented to '..tostring( owner )..'. Please end that first.');
+ return false;
+ end
+
+ return true;
+end
+
+
+apartment.after_dig_node = function(pos, oldnode, oldmetadata, digger)
+
+ if( not( oldmetadata ) or oldmetadata=="nil" or not(oldmetadata.fields)) then
+ minetest.chat_send_player( digger:get_player_name(), "Error: Could not find information about the apartment panel that is to be removed.");
+ return;
+ end
+
+ local descr = oldmetadata.fields[ "descr" ];
+ if( apartment.apartments[ descr ] ) then
+
+ -- actually remove the apartment
+ oldmetadata.param2 = oldnode.param2;
+ apartment.rent( pos, '', oldmetadata, digger );
+ apartment.apartments[ descr ] = nil;
+ apartment.save_data();
+ minetest.chat_send_player( digger:get_player_name(), "Removed apartment "..descr.." successfully.");
+ end
+end
+
+
+
+
+minetest.register_node("apartment:apartment_free", {
+ drawtype = "nodebox",
+ description = "apartment management panel",
+--- tiles = {"default_chest_top.png^door_steel.png"},
+ tiles = {"default_steel_block.png","default_steel_block.png","default_steel_block.png","default_steel_block.png",
+ "default_steel_block.png","apartment_controls_vacant.png","default_steel_block.png"},
+ paramtype = "light",
+ paramtype2 = "facedir",
+ light_source = 14,
+ groups = {cracky=2},
+ node_box = {
+ type = "fixed",
+ fixed = {
+ { -0.5+(1/16), -0.5+(1/16), 0.5, 0.5-(1/16), 0.5-(1/16), 0.30},
+
+ }
+ },
+ selection_box = {
+ type = "fixed",
+ fixed = {
+ { -0.5+(1/16), -0.5+(1/16), 0.5, 0.5-(1/16), 0.5-(1/16), 0.30},
+ }
+ },
+
+ on_construct = function(pos)
+ return apartment.on_construct( pos );
+ end,
+
+ after_place_node = function(pos, placer)
+ return apartment.after_place_node( pos, placer );
+ end,
+
+ on_receive_fields = function( pos, formname, fields, player )
+ return apartment.on_receive_fields(pos, formname, fields, player);
+ end,
+
+ can_dig = function(pos,player)
+ return apartment.can_dig( pos, player );
+ end,
+
+ after_dig_node = function(pos, oldnode, oldmetadata, digger)
+ return apartment.after_dig_node( pos, oldnode, oldmetadata, digger );
+ end,
+
+})
+
+
+-- this one is not in the creative inventory
+minetest.register_node("apartment:apartment_occupied", {
+ drawtype = "nodebox",
+ description = "apartment management panel",
+--- tiles = {"default_chest_top.png^door_steel.png"},
+ tiles = {"default_steel_block.png","default_steel_block.png","default_steel_block.png","default_steel_block.png",
+ "default_steel_block.png","apartment_controls_occupied.png","default_steel_block.png"},
+ paramtype = "light",
+ paramtype2 = "facedir",
+ light_source = 14,
+ groups = {cracky=2, not_in_creative_inventory=1 },
+ node_box = {
+ type = "fixed",
+ fixed = {
+ { -0.5+(1/16), -0.5+(1/16), 0.5, 0.5-(1/16), 0.5-(1/16), 0.30},
+
+ }
+ },
+ selection_box = {
+ type = "fixed",
+ fixed = {
+ { -0.5+(1/16), -0.5+(1/16), 0.5, 0.5-(1/16), 0.5-(1/16), 0.30},
+ }
+ },
+
+ on_construct = function(pos)
+ return apartment.on_construct( pos );
+ end,
+
+ after_place_node = function(pos, placer)
+ return apartment.after_place_node( pos, placer );
+ end,
+
+ on_receive_fields = function( pos, formname, fields, player )
+ return apartment.on_receive_fields(pos, formname, fields, player);
+ end,
+
+ can_dig = function(pos,player)
+ return apartment.can_dig( pos, player );
+ end,
+
+ after_dig_node = function(pos, oldnode, oldmetadata, digger)
+ return apartment.after_dig_node( pos, oldnode, oldmetadata, digger );
+ end,
+
+})
+
+
+if( apartment.enable_aphome_command ) then
+ minetest.register_chatcommand("aphome", {
+ params = "<category>",
+ description = "Teleports you back to the apartment you rented.",
+ privs = {},
+ func = function(name, param)
+
+ if( not( name )) then
+ return;
+ end
+ local category;
+ if (not param or param == "") then
+ category = 'apartment'
+ else
+ category = param
+ end
+ local player = minetest.env:get_player_by_name(name);
+
+ for k,v in pairs( apartment.apartments ) do
+ -- found the apartment the player rented
+ if( v and v.owner and v.owner==name and v.category == category) then
+ player:moveto( v.pos, false);
+ minetest.chat_send_player(name, "Welcome back to your apartment "..k..".");
+ return;
+ end
+ end
+
+ minetest.chat_send_player(name, "Please rent a "..category.." first.");
+ end
+ })
+end
+
+
+
+-- old version of the node - will transform into _free or _occupied
+minetest.register_node("apartment:apartment", {
+ drawtype = "nodebox",
+ description = "apartment management panel (transition state)",
+--- tiles = {"default_chest_top.png^door_steel.png"},
+ tiles = {"default_steel_block.png","default_steel_block.png","default_steel_block.png","default_steel_block.png",
+ "default_steel_block.png","apartment_controls_vacant.png","default_steel_block.png"},
+ paramtype = "light",
+ paramtype2 = "facedir",
+ light_source = 14,
+ groups = {cracky=2},
+ node_box = {
+ type = "fixed",
+ fixed = {
+ { -0.5+(1/16), -0.5+(1/16), 0.5, 0.5-(1/16), 0.5-(1/16), 0.30},
+
+ }
+ },
+ selection_box = {
+ type = "fixed",
+ fixed = {
+ { -0.5+(1/16), -0.5+(1/16), 0.5, 0.5-(1/16), 0.5-(1/16), 0.30},
+ }
+ },
+})
+
+minetest.register_abm({
+ nodenames = {"apartment:apartment"},
+ interval = 60,
+ chance = 1,
+ action = function(pos, node)
+
+ local node = minetest.get_node( pos );
+ local meta = minetest.get_meta( pos );
+ local owner = meta:get_string( 'owner' );
+ local original_owner = meta:get_string( 'original_owner' );
+
+ if( owner == '' or original_owner==owner ) then
+ minetest.swap_node( pos, {name='apartment:apartment_free', param2 = node.param2} );
+ else
+ minetest.swap_node( pos, {name='apartment:apartment_occupied', param2 = node.param2} );
+ end
+ end
+})
+
+minetest.register_abm({
+ -- handle duplicates
+ nodenames= {"apartment:apartment_free" },
+ interval = 1,
+ chance = 1,
+ action = function(pos,node)
+ local meta = minetest.get_meta( pos );
+ local name = meta:get_string('descr');
+-- minetest.chat_send_all(name)
+ if apartment.apartments[name] and apartment.apartments[name].pos and ( apartment.apartments[name].pos.x ~= pos.x
+ or apartment.apartments[name].pos.y ~= pos.y or apartment.apartments[name].pos.z ~= pos.z ) then
+ -- duplicate name
+ old = apartment.apartments[name]
+ local number = name:match('%d+$')
+ if number then
+ n = name:sub(1,-tostring(number):len()-1)..tostring(number+1)
+ else
+ n = name..' 1'
+ end
+-- minetest.chat_send_all(n)
+ meta:set_string('descr', n)
+ meta:set_string('formspec', apartment.get_formspec(pos, ""))
+ if not apartment.apartments[ n ] then
+ apartment.apartments[ n ] = { pos={x=pos.x, y=pos.y, z=pos.z}, original_owner = old.original_owner, owner='', category = old.category,
+ start_pos = old.start_pos,
+ end_pos = old.end_pos };
+ end
+ end
+ end
+})
+
+-- give each player an apartment upon joining the server --
+local apartment_give_player = minetest.setting_getbool("apartment_give_newplayer") or true;
+if apartment_give_player then
+ minetest.register_on_newplayer(function(player)
+ for k,v in pairs( apartment.apartments ) do
+ if (v.owner == '' and v.category == 'apartment') then
+
+ local meta = minetest.get_meta( v.pos );
+ local node = minetest.get_node( v.pos );
+ if node.name == "ignore" then -- deal with unloaded nodes.
+ minetest.get_voxel_manip():read_from_map(v.pos, v.pos)
+ node = minetest.get_node(v.pos)
+ end
+ if (node.name == 'apartment:apartment_free' and apartment.rent( v.pos, player:get_player_name(), nil, player )) then
+ player:moveto( v.pos, false);
+ meta:set_string( 'formspec', apartment.get_formspec( v.pos, player ));
+ minetest.chat_send_player(player:get_player_name(),"Welcome to your new apartment. You can return here by saying '/aphome'")
+ break
+ elseif node.name == 'apartment:apartment_occupied' then -- Possible case of database corruption...
+ apartment.apartments[k] = nil
+ end
+ end
+ end
+ end)
+end
+-- upon server start, read the savefile
+apartment.restore_data();
diff --git a/apartment/schems/apartment_4x10_0_270.mts b/apartment/schems/apartment_4x10_0_270.mts
new file mode 100644
index 0000000..3009a77
--- /dev/null
+++ b/apartment/schems/apartment_4x10_0_270.mts
Binary files differ
diff --git a/apartment/schems/apartment_4x11_0_180.mts b/apartment/schems/apartment_4x11_0_180.mts
new file mode 100644
index 0000000..07a0779
--- /dev/null
+++ b/apartment/schems/apartment_4x11_0_180.mts
Binary files differ
diff --git a/apartment/schems/apartment_4x6_0_90.mts b/apartment/schems/apartment_4x6_0_90.mts
new file mode 100644
index 0000000..2b28664
--- /dev/null
+++ b/apartment/schems/apartment_4x6_0_90.mts
Binary files differ
diff --git a/apartment/textures/apartment_controls_occupied.png b/apartment/textures/apartment_controls_occupied.png
new file mode 100644
index 0000000..432d0cc
--- /dev/null
+++ b/apartment/textures/apartment_controls_occupied.png
Binary files differ
diff --git a/apartment/textures/apartment_controls_vacant.png b/apartment/textures/apartment_controls_vacant.png
new file mode 100644
index 0000000..9f781fc
--- /dev/null
+++ b/apartment/textures/apartment_controls_vacant.png
Binary files differ