k
2
apartment/README.md
Normal file
@ -0,0 +1,2 @@
|
||||
|
||||
Still experimental. Use /giveme apartment:apartment to get a control panel.
|
687
apartment/handle_schematics.lua
Normal file
@ -0,0 +1,687 @@
|
||||
|
||||
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_top.png", "default_chest_top.png^apartment_controls_vacant.png", "default_chest_top.png",
|
||||
"default_chest_top.png", "default_chest_top.png", "default_chest_top.png^apartment_controls_vacant.png"},
|
||||
paramtype2 = "facedir",
|
||||
not_in_creative_inventory = 1,
|
||||
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
|
||||
|
||||
minetest.register_craft({
|
||||
type = "shaped",
|
||||
output = "apartment:build_chest 0",
|
||||
recipe = {
|
||||
{""}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "apartment:build_chest",
|
||||
recipe = {
|
||||
{"", "" },
|
||||
{"", ""},
|
||||
{"", ""}
|
||||
}
|
||||
})
|
898
apartment/init.lua
Normal file
@ -0,0 +1,898 @@
|
||||
|
||||
--[[
|
||||
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.4;This apartment is rented by:]'..
|
||||
'label[3.5,1.4;'..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.5,1.4;Do you want to rent this]'..
|
||||
'label[2.8,1.4;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.3;The apartment shall extend]'..
|
||||
'label[3.3,1.3;this many blocks from here:]'..
|
||||
'label[0.5,1.5;(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
|
||||
-- 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_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 == "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,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,
|
||||
|
||||
})
|
||||
|
||||
|
||||
-- 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 = "",
|
||||
description = "Teleports you back to the apartment you rented.",
|
||||
privs = {},
|
||||
func = function(name, param)
|
||||
|
||||
if( not( name )) then
|
||||
return;
|
||||
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 ) 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 an apartment 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,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},
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
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
|
||||
})
|
||||
|
||||
|
||||
-- upon server start, read the savefile
|
||||
apartment.restore_data();
|
5
apartment/mod.conf
Normal file
@ -0,0 +1,5 @@
|
||||
description = Rent an apartment with your own chests and furnaces close to spawn
|
||||
author = Sokomine
|
||||
title = Apartment
|
||||
release = 311
|
||||
name = apartment
|
BIN
apartment/schems/apartment_4x10_0_270.mts
Normal file
BIN
apartment/schems/apartment_4x11_0_180.mts
Normal file
BIN
apartment/schems/apartment_4x6_0_90.mts
Normal file
BIN
apartment/textures/apartment_controls_occupied.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
apartment/textures/apartment_controls_vacant.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
17
areas/.github/workflows/luacheck.yml
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
name: luacheck
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: apt
|
||||
run: sudo apt-get install -y luarocks
|
||||
- name: luacheck install
|
||||
run: luarocks install --local luacheck
|
||||
- name: luacheck run
|
||||
run: $HOME/.luarocks/bin/luacheck ./
|
2
areas/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*~
|
||||
|
24
areas/.luacheckrc
Normal file
@ -0,0 +1,24 @@
|
||||
unused_args = false
|
||||
|
||||
read_globals = {
|
||||
"DIR_DELIM",
|
||||
"core",
|
||||
"dump",
|
||||
"vector", "nodeupdate",
|
||||
"VoxelManip", "VoxelArea",
|
||||
"PseudoRandom", "ItemStack",
|
||||
"AreaStore",
|
||||
"default",
|
||||
"factions",
|
||||
table = { fields = { "copy", "getn", "indexof" } }
|
||||
}
|
||||
|
||||
globals = {
|
||||
"minetest",
|
||||
-- mod namespace
|
||||
"areas"
|
||||
}
|
||||
|
||||
files["legacy.lua"] = {
|
||||
ignore = {"512"}
|
||||
}
|
502
areas/LICENSE.txt
Normal file
@ -0,0 +1,502 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
not price. Our General Public Licenses are designed to make sure that
|
||||
you have the freedom to distribute copies of free software (and charge
|
||||
for this service if you wish); that you receive source code or can get
|
||||
it if you want it; that you can change the software and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for
|
||||
you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link other code with the library, you must provide
|
||||
complete object files to the recipients, so that they can relink them
|
||||
with the library after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
modified by someone else and passed on, the recipients should know
|
||||
that what they have is not the original version, so that the original
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
effectively restrict the users of a free program by obtaining a
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser General Public License (also called "this License").
|
||||
Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, complete source code means
|
||||
all the source code for all modules it contains, plus any associated
|
||||
interface definition files, plus the scripts used to control compilation
|
||||
and installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete source code as you receive it, in any medium, provided that
|
||||
you conspicuously and appropriately publish on each copy an
|
||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||
all the notices that refer to this License and to the absence of any
|
||||
warranty; and distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy,
|
||||
and you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion
|
||||
of it, thus forming a work based on the Library, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Library, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you accompany
|
||||
it with the complete corresponding machine-readable source code, which
|
||||
must be distributed under the terms of Sections 1 and 2 above on a
|
||||
medium customarily used for software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy
|
||||
from a designated place, then offering equivalent access to copy the
|
||||
source code from the same place satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also combine or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the materials to be distributed need not include anything that is
|
||||
normally distributed (in either source or binary form) with the major
|
||||
components (compiler, kernel, and so on) of the operating system on
|
||||
which the executable runs, unless that component itself accompanies
|
||||
the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library is void, and will automatically terminate your
|
||||
rights under this License. However, parties who have received copies,
|
||||
or rights, from you under this License will not have their licenses
|
||||
terminated so long as such parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties with
|
||||
this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any
|
||||
particular circumstance, the balance of the section is intended to apply,
|
||||
and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library under this License may add
|
||||
an explicit geographical distribution limitation excluding those countries,
|
||||
so that distribution is permitted only in or among countries not thus
|
||||
excluded. In such case, this License incorporates the limitation as if
|
||||
written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Lesser General Public License from time to time.
|
||||
Such new versions will be similar in spirit to the present version,
|
||||
but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library
|
||||
specifies a version number of this License which applies to it and
|
||||
"any later version", you have the option of following the terms and
|
||||
conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
write to the author to ask for permission. For software which is
|
||||
copyrighted by the Free Software Foundation, write to the Free
|
||||
Software Foundation; we sometimes make exceptions for this. Our
|
||||
decision will be guided by the two goals of preserving the free status
|
||||
of all derivatives of our free software and of promoting the sharing
|
||||
and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
everyone can redistribute and change. You can do so by permitting
|
||||
redistribution under these terms (or, alternatively, under the terms of the
|
||||
ordinary General Public License).
|
||||
|
||||
To apply these terms, attach the following notices to the library. It is
|
||||
safest to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least the
|
||||
"copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the library's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1990
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
127
areas/README.md
Normal file
@ -0,0 +1,127 @@
|
||||
Areas mod for Minetest
|
||||
======================
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
Minetest 5.0.0+ is recommended, but 0.4.16+ should work as well.
|
||||
Minetest 5.0.0+
|
||||
|
||||
Optional support for following mods:
|
||||
|
||||
* [playerfactions](https://git.leagueh.xyz/katp32/playerfactions/) by [katp32](https://git.leagueh.xyz/katp32) & [Kalio_42](https://git.leagueh.xyz/Kalio_42)
|
||||
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
Open the tab `Settings -> All Settings -> Mods -> areas` to get a list of all
|
||||
possible settings.
|
||||
|
||||
For server owners: Check `settingtypes.txt` and modify your `minetest.conf`
|
||||
according to the wanted setting changes.
|
||||
|
||||
|
||||
|
||||
Tutorial
|
||||
--------
|
||||
|
||||
1) Specify the corner positions of the area you would like to protect.
|
||||
Use one of the following commands:
|
||||
|
||||
* `/area_pos set` and punch the two corner nodes to set them.
|
||||
* `/area_pos set1/set2` and punch only the first or second corner node to
|
||||
set them one at a time.
|
||||
* `/area_pos1/2` to set one of the positions to your current position.
|
||||
* `/area_pos1/2 X Y Z` to set one of the positions to the specified
|
||||
coordinates.
|
||||
|
||||
2) Protect the selected area by running one of the following commands:
|
||||
|
||||
* `/set_owner <OwnerName> <AreaName>` -- If you have the `areas` privilege.
|
||||
* `/protect <AreaName>` -- If you have the `areas` privilege or the server
|
||||
administrator has enabled area self-protection.
|
||||
|
||||
The area name is used only for informational purposes and has no functional
|
||||
importance.
|
||||
|
||||
For example: `/set_owner SomePlayer Mese city`
|
||||
|
||||
3) You now own an area. You may now add sub-owners to it if you want to (see command `/add_owner`). Before using the `/add_owner` command you have to
|
||||
select the corners of the sub-area as you did in step 1.
|
||||
|
||||
If your markers are still around your original area and you want to grant
|
||||
access to your entire area you will not have to re-set them. Use `/select_area` to place the markers at the corners of an existing area if you've reset your
|
||||
markers and want to grant access to a full area.
|
||||
|
||||
The `/add_owner` command expects three arguments:
|
||||
1. The ID number of the parent area (the area that you want to add a
|
||||
sub-area to).
|
||||
2. The name of the player that will own the sub-area.
|
||||
3. The name of the sub-area. (can contain spaces)
|
||||
|
||||
For example: `/add_owner 123 BobTheBuilder Diamond lighthouse`
|
||||
|
||||
|
||||
Commands
|
||||
--------
|
||||
|
||||
* `/protect <AreaName>` -- Protects an area for yourself. (if
|
||||
self-protection is enabled)
|
||||
|
||||
* `/set_owner <OwnerName> <AreaName>` -- Protects an area for a specified
|
||||
player. (requires the `areas` privilege)
|
||||
|
||||
* `/add_owner <ParentID> <OwnerName> <ChildName>` -- Grants another player
|
||||
control over part (or all) of an area.
|
||||
|
||||
* `/rename_area <ID> <NewName>` -- Renames an existing area.
|
||||
|
||||
* `/list_areas` -- Lists all of the areas that you own, or all areas if you
|
||||
have the `areas` privilege.
|
||||
|
||||
* `/find_areas <Regex>` -- Finds areas using a Lua regular expresion.
|
||||
For example, to find castles:
|
||||
|
||||
/find_areas [Cc]astle
|
||||
|
||||
* `/remove_area <ID>` -- Removes an area that you own. Any sub-areas of that
|
||||
area are made sub-areas of the removed area's parent, if it exists.
|
||||
If the removed area has no parent it's sub-areas will have no parent.
|
||||
|
||||
* `/recursive_remove_areas <ID>` -- Removes an area and all sub-areas of it.
|
||||
|
||||
* `/change_owner <ID> <NewOwner>` -- Change the owner of an area.
|
||||
|
||||
* `/area_info` -- Returns information about area configuration and usage.
|
||||
|
||||
* `/select_area <ID>` -- Sets the area positions to those of an existing
|
||||
area.
|
||||
|
||||
* `/area_pos {set,set1,set2,get}` -- Sets the area positions by punching
|
||||
nodes or shows the current area positions.
|
||||
|
||||
* `/area_pos1 [X,Y,Z|X Y Z]` -- Sets area position one to your position or
|
||||
the one supplied.
|
||||
|
||||
* `/area_pos2 [X,Y,Z|X Y Z]` -- Sets area position two to your position or
|
||||
the one supplied.
|
||||
|
||||
* `/areas_cleanup` -- Removes all ownerless areas.
|
||||
Useful for cleaning after user deletion, for example using /remove_player.
|
||||
|
||||
* `/area_open <ID>` -- Toggle open/closed the specified area for everyone.
|
||||
|
||||
* `/area_faction_open <ID> <faction>` -- Toggle open/closed the specified
|
||||
area for members of the faction. Factions are created and managed by
|
||||
playerfactions mod.
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
Copyright (C) 2013-2017 ShadowNinja
|
||||
|
||||
Copyright (C) 2015-2020 various contributors
|
||||
|
||||
Licensed under the GNU LGPL version 2.1 or later.
|
||||
See LICENSE.txt and http://www.gnu.org/licenses/lgpl-2.1.txt
|
174
areas/api.lua
Normal file
@ -0,0 +1,174 @@
|
||||
local hudHandlers = {}
|
||||
|
||||
areas.registered_on_adds = {}
|
||||
areas.registered_on_removes = {}
|
||||
areas.registered_on_moves = {}
|
||||
|
||||
function areas:registerOnAdd(func)
|
||||
table.insert(areas.registered_on_adds, func)
|
||||
end
|
||||
|
||||
function areas:registerOnRemove(func)
|
||||
table.insert(areas.registered_on_removes, func)
|
||||
end
|
||||
|
||||
function areas:registerOnMove(func)
|
||||
table.insert(areas.registered_on_moves, func)
|
||||
end
|
||||
|
||||
--- Adds a function as a HUD handler, it will be able to add items to the Areas HUD element.
|
||||
function areas:registerHudHandler(handler)
|
||||
table.insert(hudHandlers, handler)
|
||||
end
|
||||
|
||||
function areas:getExternalHudEntries(pos)
|
||||
local areas = {}
|
||||
for _, func in pairs(hudHandlers) do
|
||||
func(pos, areas)
|
||||
end
|
||||
return areas
|
||||
end
|
||||
|
||||
--- Returns a list of areas that include the provided position.
|
||||
function areas:getAreasAtPos(pos)
|
||||
local res = {}
|
||||
|
||||
if self.store then
|
||||
local a = self.store:get_areas_for_pos(pos, false, true)
|
||||
for store_id, store_area in pairs(a) do
|
||||
local id = tonumber(store_area.data)
|
||||
res[id] = self.areas[id]
|
||||
end
|
||||
else
|
||||
local px, py, pz = pos.x, pos.y, pos.z
|
||||
for id, area in pairs(self.areas) do
|
||||
local ap1, ap2 = area.pos1, area.pos2
|
||||
if
|
||||
(px >= ap1.x and px <= ap2.x) and
|
||||
(py >= ap1.y and py <= ap2.y) and
|
||||
(pz >= ap1.z and pz <= ap2.z) then
|
||||
res[id] = area
|
||||
end
|
||||
end
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
--- Returns areas that intersect with the passed area.
|
||||
function areas:getAreasIntersectingArea(pos1, pos2)
|
||||
local res = {}
|
||||
if self.store then
|
||||
local a = self.store:get_areas_in_area(pos1, pos2,
|
||||
true, false, true)
|
||||
for store_id, store_area in pairs(a) do
|
||||
local id = tonumber(store_area.data)
|
||||
res[id] = self.areas[id]
|
||||
end
|
||||
else
|
||||
self:sortPos(pos1, pos2)
|
||||
local p1x, p1y, p1z = pos1.x, pos1.y, pos1.z
|
||||
local p2x, p2y, p2z = pos2.x, pos2.y, pos2.z
|
||||
for id, area in pairs(self.areas) do
|
||||
local ap1, ap2 = area.pos1, area.pos2
|
||||
if
|
||||
(ap1.x <= p2x and ap2.x >= p1x) and
|
||||
(ap1.y <= p2y and ap2.y >= p1y) and
|
||||
(ap1.z <= p2z and ap2.z >= p1z) then
|
||||
-- Found an intersecting area.
|
||||
res[id] = area
|
||||
end
|
||||
end
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
-- Checks if the area is unprotected or owned by you
|
||||
function areas:canInteract(pos, name)
|
||||
if minetest.check_player_privs(name, self.adminPrivs) then
|
||||
return true
|
||||
end
|
||||
local owned = false
|
||||
for _, area in pairs(self:getAreasAtPos(pos)) do
|
||||
if area.owner == name or area.open then
|
||||
return true
|
||||
elseif areas.factions_available and area.faction_open then
|
||||
if (factions.version or 0) < 2 then
|
||||
local faction_name = factions.get_player_faction(name)
|
||||
if faction_name then
|
||||
for _, fname in ipairs(area.faction_open or {}) do
|
||||
if faction_name == fname then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
for _, fname in ipairs(area.faction_open or {}) do
|
||||
if factions.player_is_in_faction(fname, name) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
owned = true
|
||||
end
|
||||
return not owned
|
||||
end
|
||||
|
||||
-- Returns a table (list) of all players that own an area
|
||||
function areas:getNodeOwners(pos)
|
||||
local owners = {}
|
||||
for _, area in pairs(self:getAreasAtPos(pos)) do
|
||||
table.insert(owners, area.owner)
|
||||
end
|
||||
return owners
|
||||
end
|
||||
|
||||
--- Checks if the area intersects with an area that the player can't interact in.
|
||||
-- Note that this fails and returns false when the specified area is fully
|
||||
-- owned by the player, but with multiple protection zones, none of which
|
||||
-- cover the entire checked area.
|
||||
-- @param name (optional) Player name. If not specified checks for any intersecting areas.
|
||||
-- @param allow_open Whether open areas should be counted as if they didn't exist.
|
||||
-- @return Boolean indicating whether the player can interact in that area.
|
||||
-- @return Un-owned intersecting area ID, if found.
|
||||
function areas:canInteractInArea(pos1, pos2, name, allow_open)
|
||||
if name and minetest.check_player_privs(name, self.adminPrivs) then
|
||||
return true
|
||||
end
|
||||
self:sortPos(pos1, pos2)
|
||||
|
||||
-- Intersecting non-owned area ID, if found.
|
||||
local blocking_area = nil
|
||||
|
||||
local areas = self:getAreasIntersectingArea(pos1, pos2)
|
||||
for id, area in pairs(areas) do
|
||||
-- First check for a fully enclosing owned area.
|
||||
-- A little optimization: isAreaOwner isn't necessary
|
||||
-- here since we're iterating over all relevant areas.
|
||||
if area.owner == name and
|
||||
self:isSubarea(pos1, pos2, id) then
|
||||
return true
|
||||
end
|
||||
|
||||
-- Then check for intersecting non-owned (blocking) areas.
|
||||
-- We don't bother with this check if we've already found a
|
||||
-- blocking area, as the check is somewhat expensive.
|
||||
-- The area blocks if the area is closed or open areas aren't
|
||||
-- acceptable to the caller, and the area isn't owned.
|
||||
-- Note: We can't return directly here, because there might be
|
||||
-- an exclosing owned area that we haven't gotten to yet.
|
||||
if not blocking_area and
|
||||
(not allow_open or not area.open) and
|
||||
(not name or not self:isAreaOwner(id, name)) then
|
||||
blocking_area = id
|
||||
end
|
||||
end
|
||||
|
||||
if blocking_area then
|
||||
return false, blocking_area
|
||||
end
|
||||
|
||||
-- There are no intersecting areas or they are only partially
|
||||
-- intersecting areas and they are all owned by the player.
|
||||
return true
|
||||
end
|
48
areas/api.md
Normal file
@ -0,0 +1,48 @@
|
||||
Areas mod API
|
||||
===
|
||||
|
||||
API list
|
||||
---
|
||||
|
||||
* `areas:registerHudHandler(handler)` - Registers a handler to add items to the Areas HUD. See [HUD](#hud).
|
||||
* `areas:registerOnAdd(func(id, area))`
|
||||
* `areas:registerOnRemove(func(id))`
|
||||
* `areas:registerOnMove(func(id, area, pos1, pos2))`
|
||||
|
||||
|
||||
HUD
|
||||
---
|
||||
|
||||
If you are making a protection mod or a similar mod that adds invisible regions
|
||||
to the world, and you would like then to show up in the areas HUD element, you
|
||||
can register a callback to show your areas.
|
||||
|
||||
HUD handler specification:
|
||||
|
||||
* `handler(pos, list)`
|
||||
* `pos` - The position to check.
|
||||
* `list` - The list of area HUD elements, this should be modified in-place.
|
||||
|
||||
The area list item is a table containing a list of tables with the following fields:
|
||||
|
||||
* `id` - An identifier for the area. This should be a unique string in the format `mod:id`.
|
||||
* `name` - The name of the area.
|
||||
* `owner` - The player name of the region owner, if any.
|
||||
|
||||
All of the fields are optional but at least one of them must be set.
|
||||
|
||||
### Example
|
||||
|
||||
local function areas_hud_handler(pos, areas)
|
||||
local val = find_my_protection(pos)
|
||||
|
||||
if val then
|
||||
table.insert(areas, {
|
||||
id = "mod:"..val.id,
|
||||
name = val.name,
|
||||
owner = val.owner,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
areas:registerHudHandler(areas_hud_handler)
|
462
areas/chatcommands.lua
Normal file
@ -0,0 +1,462 @@
|
||||
local S = minetest.get_translator("areas")
|
||||
|
||||
minetest.register_chatcommand("protect", {
|
||||
params = S("<AreaName>"),
|
||||
description = S("Protect your own area"),
|
||||
privs = {[areas.config.self_protection_privilege]=true},
|
||||
func = function(name, param)
|
||||
if param == "" then
|
||||
return false, S("Invalid usage, see /help @1.", "protect")
|
||||
end
|
||||
local pos1, pos2 = areas:getPos(name)
|
||||
if not (pos1 and pos2) then
|
||||
return false, S("You need to select an area first.")
|
||||
end
|
||||
|
||||
minetest.log("action", "/protect invoked, owner="..name..
|
||||
" AreaName="..param..
|
||||
" StartPos="..minetest.pos_to_string(pos1)..
|
||||
" EndPos=" ..minetest.pos_to_string(pos2))
|
||||
|
||||
local canAdd, errMsg = areas:canPlayerAddArea(pos1, pos2, name)
|
||||
if not canAdd then
|
||||
return false, S("You can't protect that area: @1", errMsg)
|
||||
end
|
||||
|
||||
local id = areas:add(name, param, pos1, pos2, nil)
|
||||
areas:save()
|
||||
|
||||
return true, S("Area protected. ID: @1", id)
|
||||
end
|
||||
})
|
||||
|
||||
|
||||
minetest.register_chatcommand("set_owner", {
|
||||
params = S("<PlayerName>").." "..S("<AreaName>"),
|
||||
description = S("Protect an area between two positions and give"
|
||||
.." a player access to it without setting the parent of the"
|
||||
.." area to any existing area"),
|
||||
privs = areas.adminPrivs,
|
||||
func = function(name, param)
|
||||
local ownerName, areaName = param:match('^(%S+)%s(.+)$')
|
||||
|
||||
if not ownerName then
|
||||
return false, S("Invalid usage, see /help @1.", "set_owner")
|
||||
end
|
||||
|
||||
local pos1, pos2 = areas:getPos(name)
|
||||
if not (pos1 and pos2) then
|
||||
return false, S("You need to select an area first.")
|
||||
end
|
||||
|
||||
if not areas:player_exists(ownerName) then
|
||||
return false, S("The player \"@1\" does not exist.", ownerName)
|
||||
end
|
||||
|
||||
minetest.log("action", name.." runs /set_owner. Owner = "..ownerName..
|
||||
" AreaName = "..areaName..
|
||||
" StartPos = "..minetest.pos_to_string(pos1)..
|
||||
" EndPos = " ..minetest.pos_to_string(pos2))
|
||||
|
||||
local id = areas:add(ownerName, areaName, pos1, pos2, nil)
|
||||
areas:save()
|
||||
|
||||
minetest.chat_send_player(ownerName,
|
||||
S("You have been granted control over area #@1. "..
|
||||
"Type /list_areas to show your areas.", id))
|
||||
return true, S("Area protected. ID: @1", id)
|
||||
end
|
||||
})
|
||||
|
||||
|
||||
minetest.register_chatcommand("add_owner", {
|
||||
params = S("<ParentID>").." "..S("<PlayerName>").." "..S("<AreaName>"),
|
||||
description = S("Give a player access to a sub-area beetween two"
|
||||
.." positions that have already been protected,"
|
||||
.." Use set_owner if you don't want the parent to be set."),
|
||||
func = function(name, param)
|
||||
local pid, ownerName, areaName = param:match('^(%d+) ([^ ]+) (.+)$')
|
||||
|
||||
if not pid then
|
||||
minetest.chat_send_player(name, S("Invalid usage, see /help @1.", "add_owner"))
|
||||
return
|
||||
end
|
||||
|
||||
local pos1, pos2 = areas:getPos(name)
|
||||
if not (pos1 and pos2) then
|
||||
return false, S("You need to select an area first.")
|
||||
end
|
||||
|
||||
if not areas:player_exists(ownerName) then
|
||||
return false, S("The player \"@1\" does not exist.", ownerName)
|
||||
end
|
||||
|
||||
minetest.log("action", name.." runs /add_owner. Owner = "..ownerName..
|
||||
" AreaName = "..areaName.." ParentID = "..pid..
|
||||
" StartPos = "..pos1.x..","..pos1.y..","..pos1.z..
|
||||
" EndPos = " ..pos2.x..","..pos2.y..","..pos2.z)
|
||||
|
||||
-- Check if this new area is inside an area owned by the player
|
||||
pid = tonumber(pid)
|
||||
if (not areas:isAreaOwner(pid, name)) or
|
||||
(not areas:isSubarea(pos1, pos2, pid)) then
|
||||
return false, S("You can't protect that area.")
|
||||
end
|
||||
|
||||
local id = areas:add(ownerName, areaName, pos1, pos2, pid)
|
||||
areas:save()
|
||||
|
||||
minetest.chat_send_player(ownerName,
|
||||
S("You have been granted control over area #@1. "..
|
||||
"Type /list_areas to show your areas.", id))
|
||||
return true, S("Area protected. ID: @1", id)
|
||||
end
|
||||
})
|
||||
|
||||
|
||||
minetest.register_chatcommand("rename_area", {
|
||||
params = S("<ID>").." "..S("<newName>"),
|
||||
description = S("Rename an area that you own"),
|
||||
func = function(name, param)
|
||||
local id, newName = param:match("^(%d+)%s(.+)$")
|
||||
if not id then
|
||||
return false, S("Invalid usage, see /help @1.", "rename_area")
|
||||
end
|
||||
|
||||
id = tonumber(id)
|
||||
if not id then
|
||||
return false, S("That area doesn't exist.")
|
||||
end
|
||||
|
||||
if not areas:isAreaOwner(id, name) then
|
||||
return true, S("You don't own that area.")
|
||||
end
|
||||
|
||||
areas.areas[id].name = newName
|
||||
areas:save()
|
||||
return true, S("Area renamed.")
|
||||
end
|
||||
})
|
||||
|
||||
|
||||
minetest.register_chatcommand("find_areas", {
|
||||
params = "<regexp>",
|
||||
description = S("Find areas using a Lua regular expression"),
|
||||
privs = areas.adminPrivs,
|
||||
func = function(name, param)
|
||||
if param == "" then
|
||||
return false, S("A regular expression is required.")
|
||||
end
|
||||
|
||||
-- Check expression for validity
|
||||
local function testRegExp()
|
||||
("Test [1]: Player (0,0,0) (0,0,0)"):find(param)
|
||||
end
|
||||
if not pcall(testRegExp) then
|
||||
return false, S("Invalid regular expression.")
|
||||
end
|
||||
|
||||
local matches = {}
|
||||
for id, area in pairs(areas.areas) do
|
||||
local str = areas:toString(id)
|
||||
if str:find(param) then
|
||||
table.insert(matches, str)
|
||||
end
|
||||
end
|
||||
if #matches > 0 then
|
||||
return true, table.concat(matches, "\n")
|
||||
else
|
||||
return true, S("No matches found.")
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
|
||||
minetest.register_chatcommand("list_areas", {
|
||||
description = S("List your areas, or all areas if you are an admin."),
|
||||
func = function(name, param)
|
||||
local admin = minetest.check_player_privs(name, areas.adminPrivs)
|
||||
local areaStrings = {}
|
||||
for id, area in pairs(areas.areas) do
|
||||
if admin or areas:isAreaOwner(id, name) then
|
||||
table.insert(areaStrings, areas:toString(id))
|
||||
end
|
||||
end
|
||||
if #areaStrings == 0 then
|
||||
return true, S("No visible areas.")
|
||||
end
|
||||
return true, table.concat(areaStrings, "\n")
|
||||
end
|
||||
})
|
||||
|
||||
|
||||
minetest.register_chatcommand("recursive_remove_areas", {
|
||||
params = S("<ID>"),
|
||||
description = S("Recursively remove areas using an ID"),
|
||||
func = function(name, param)
|
||||
local id = tonumber(param)
|
||||
if not id then
|
||||
return false, S("Invalid usage, see"
|
||||
.." /help @1.", "recursive_remove_areas")
|
||||
end
|
||||
|
||||
if not areas:isAreaOwner(id, name) then
|
||||
return false, S("Area @1 does not exist or is"
|
||||
.." not owned by you.", id)
|
||||
end
|
||||
|
||||
areas:remove(id, true)
|
||||
areas:save()
|
||||
return true, S("Removed area @1 and it's sub areas.", id)
|
||||
end
|
||||
})
|
||||
|
||||
|
||||
minetest.register_chatcommand("remove_area", {
|
||||
params = S("<ID>"),
|
||||
description = S("Remove an area using an ID"),
|
||||
func = function(name, param)
|
||||
local id = tonumber(param)
|
||||
if not id then
|
||||
return false, S("Invalid usage, see /help @1.", "remove_area")
|
||||
end
|
||||
|
||||
if not areas:isAreaOwner(id, name) then
|
||||
return false, S("Area @1 does not exist or"
|
||||
.." is not owned by you.", id)
|
||||
end
|
||||
|
||||
areas:remove(id)
|
||||
areas:save()
|
||||
return true, S("Removed area @1", id)
|
||||
end
|
||||
})
|
||||
|
||||
|
||||
minetest.register_chatcommand("change_owner", {
|
||||
params = S("<ID>").." "..S("<NewOwner>"),
|
||||
description = S("Change the owner of an area using its ID"),
|
||||
func = function(name, param)
|
||||
local id, newOwner = param:match("^(%d+)%s(%S+)$")
|
||||
if not id then
|
||||
return false, S("Invalid usage, see"
|
||||
.." /help @1.", "change_owner")
|
||||
end
|
||||
|
||||
if not areas:player_exists(newOwner) then
|
||||
return false, S("The player \"@1\" does not exist.", newOwner)
|
||||
end
|
||||
|
||||
id = tonumber(id)
|
||||
if not areas:isAreaOwner(id, name) then
|
||||
return false, S("Area @1 does not exist"
|
||||
.." or is not owned by you.", id)
|
||||
end
|
||||
areas.areas[id].owner = newOwner
|
||||
areas:save()
|
||||
minetest.chat_send_player(newOwner,
|
||||
S("@1 has given you control over the area \"@2\" (ID @3).",
|
||||
name, areas.areas[id].name, id))
|
||||
return true, S("Owner changed.")
|
||||
end
|
||||
})
|
||||
|
||||
|
||||
minetest.register_chatcommand("area_open", {
|
||||
params = S("<ID>"),
|
||||
description = S("Toggle an area open (anyone can interact) or closed"),
|
||||
func = function(name, param)
|
||||
local id = tonumber(param)
|
||||
if not id then
|
||||
return false, S("Invalid usage, see /help @1.", "area_open")
|
||||
end
|
||||
|
||||
if not areas:isAreaOwner(id, name) then
|
||||
return false, S("Area @1 does not exist"
|
||||
.." or is not owned by you.", id)
|
||||
end
|
||||
local open = not areas.areas[id].open
|
||||
-- Save false as nil to avoid inflating the DB.
|
||||
areas.areas[id].open = open or nil
|
||||
areas:save()
|
||||
return true, open and S("Area opened.") or S("Area closed.")
|
||||
end
|
||||
})
|
||||
|
||||
|
||||
if areas.factions_available then
|
||||
minetest.register_chatcommand("area_faction_open", {
|
||||
params = S("<ID> [faction_name]"),
|
||||
description = S("Toggle an area open/closed for members in your faction."),
|
||||
func = function(name, param)
|
||||
local params = param:split(" ")
|
||||
|
||||
local id = tonumber(params[1])
|
||||
local faction_name = params[2]
|
||||
|
||||
if not id or not faction_name then
|
||||
return false, S("Invalid usage, see /help @1.", "area_faction_open")
|
||||
end
|
||||
|
||||
if not areas:isAreaOwner(id, name) then
|
||||
return false, S("Area @1 does not exist"
|
||||
.." or is not owned by you.", id)
|
||||
end
|
||||
|
||||
if not factions.get_owner(faction_name) then
|
||||
return false, S("Faction doesn't exists")
|
||||
end
|
||||
local fnames = areas.areas[id].faction_open or {}
|
||||
local pos = table.indexof(fnames, faction_name)
|
||||
if pos < 0 then
|
||||
-- Add new faction to the list
|
||||
table.insert(fnames, faction_name)
|
||||
else
|
||||
table.remove(fnames, pos)
|
||||
end
|
||||
if #fnames == 0 then
|
||||
-- Save {} as nil to avoid inflating the DB.
|
||||
fnames = nil
|
||||
end
|
||||
areas.areas[id].faction_open = fnames
|
||||
areas:save()
|
||||
return true, fnames and S("Area is open for members of: @1", table.concat(fnames, ", "))
|
||||
or S("Area closed for faction members.")
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
minetest.register_chatcommand("move_area", {
|
||||
params = S("<ID>"),
|
||||
description = S("Move (or resize) an area to the current positions."),
|
||||
privs = areas.adminPrivs,
|
||||
func = function(name, param)
|
||||
local id = tonumber(param)
|
||||
if not id then
|
||||
return false, S("Invalid usage, see /help @1.", "move_area")
|
||||
end
|
||||
|
||||
local area = areas.areas[id]
|
||||
if not area then
|
||||
return false, S("Area does not exist.")
|
||||
end
|
||||
|
||||
local pos1, pos2 = areas:getPos(name)
|
||||
if not pos1 then
|
||||
return false, S("You need to select an area first.")
|
||||
end
|
||||
|
||||
areas:move(id, area, pos1, pos2)
|
||||
areas:save()
|
||||
|
||||
return true, S("Area successfully moved.")
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
minetest.register_chatcommand("area_info", {
|
||||
description = S("Get information about area configuration and usage."),
|
||||
func = function(name, param)
|
||||
local lines = {}
|
||||
local privs = minetest.get_player_privs(name)
|
||||
|
||||
-- Short (and fast to access) names
|
||||
local cfg = areas.config
|
||||
local self_prot = cfg.self_protection
|
||||
local prot_priv = cfg.self_protection_privilege
|
||||
local limit = cfg.self_protection_max_areas
|
||||
local limit_high = cfg.self_protection_max_areas_high
|
||||
local size_limit = cfg.self_protection_max_size
|
||||
local size_limit_high = cfg.self_protection_max_size_high
|
||||
|
||||
local has_high_limit = privs.areas_high_limit
|
||||
local has_prot_priv = not prot_priv or privs[prot_priv]
|
||||
local can_prot = privs.areas or (self_prot and has_prot_priv)
|
||||
local max_count = can_prot and
|
||||
(has_high_limit and limit_high or limit) or 0
|
||||
local max_size = has_high_limit and
|
||||
size_limit_high or size_limit
|
||||
|
||||
-- Self protection information
|
||||
local self_prot_line = self_prot and S("Self protection is enabled.") or
|
||||
S("Self protection is disabled.")
|
||||
table.insert(lines, self_prot_line)
|
||||
-- Privilege information
|
||||
local priv_line = has_prot_priv and
|
||||
S("You have the necessary privilege (\"@1\").", prot_priv) or
|
||||
S("You don't have the necessary privilege (\"@1\").", prot_priv)
|
||||
table.insert(lines, priv_line)
|
||||
if privs.areas then
|
||||
table.insert(lines, S("You are an area"..
|
||||
" administrator (\"areas\" privilege)."))
|
||||
elseif has_high_limit then
|
||||
table.insert(lines,
|
||||
S("You have extended area protection"..
|
||||
" limits (\"areas_high_limit\" privilege)."))
|
||||
end
|
||||
|
||||
-- Area count
|
||||
local area_num = 0
|
||||
for id, area in pairs(areas.areas) do
|
||||
if area.owner == name then
|
||||
area_num = area_num + 1
|
||||
end
|
||||
end
|
||||
table.insert(lines, S("You have @1 areas.", area_num))
|
||||
|
||||
-- Area limit
|
||||
local area_limit_line = privs.areas and
|
||||
S("Limit: no area count limit") or
|
||||
S("Limit: @1 areas", max_count)
|
||||
table.insert(lines, area_limit_line)
|
||||
|
||||
-- Area size limits
|
||||
local function size_info(str, size)
|
||||
table.insert(lines, S("@1 spanning up to @2x@3x@4.",
|
||||
str, size.x, size.y, size.z))
|
||||
end
|
||||
local function priv_limit_info(lpriv, lmax_count, lmax_size)
|
||||
size_info(S("Players with the \"@1\" privilege"..
|
||||
" can protect up to @2 areas", lpriv, lmax_count),
|
||||
lmax_size)
|
||||
end
|
||||
if self_prot then
|
||||
if privs.areas then
|
||||
priv_limit_info(prot_priv,
|
||||
limit, size_limit)
|
||||
priv_limit_info("areas_high_limit",
|
||||
limit_high, size_limit_high)
|
||||
elseif has_prot_priv then
|
||||
size_info(S("You can protect areas"), max_size)
|
||||
end
|
||||
end
|
||||
|
||||
return true, table.concat(lines, "\n")
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
minetest.register_chatcommand("areas_cleanup", {
|
||||
description = S("Removes all ownerless areas"),
|
||||
privs = areas.adminPrivs,
|
||||
func = function()
|
||||
local total, count = 0, 0
|
||||
|
||||
local aareas = areas.areas
|
||||
for id, _ in pairs(aareas) do
|
||||
local owner = aareas[id].owner
|
||||
|
||||
if not areas:player_exists(owner) then
|
||||
areas:remove(id)
|
||||
count = count + 1
|
||||
end
|
||||
|
||||
total = total + 1
|
||||
end
|
||||
areas:save()
|
||||
|
||||
return true, "Total areas: " .. total .. ", Removed " ..
|
||||
count .. " areas. New count: " .. (total - count)
|
||||
end
|
||||
})
|
89
areas/hud.lua
Normal file
@ -0,0 +1,89 @@
|
||||
-- This is inspired by the landrush mod by Bremaweb
|
||||
local S = minetest.get_translator("areas")
|
||||
areas.hud = {}
|
||||
areas.hud.refresh = 0
|
||||
|
||||
minetest.register_globalstep(function(dtime)
|
||||
areas.hud.refresh = areas.hud.refresh + dtime
|
||||
if areas.hud.refresh > areas.config["tick"] then
|
||||
areas.hud.refresh = 0
|
||||
else
|
||||
return
|
||||
end
|
||||
|
||||
for _, player in pairs(minetest.get_connected_players()) do
|
||||
local name = player:get_player_name()
|
||||
local pos = vector.round(player:get_pos())
|
||||
pos = vector.apply(pos, function(p)
|
||||
return math.max(math.min(p, 2147483), -2147483)
|
||||
end)
|
||||
local areaStrings = {}
|
||||
|
||||
for id, area in pairs(areas:getAreasAtPos(pos)) do
|
||||
local faction_info
|
||||
if area.faction_open and areas.factions_available then
|
||||
-- Gather and clean up disbanded factions
|
||||
local changed = false
|
||||
for i, fac_name in ipairs(area.faction_open) do
|
||||
if not factions.get_owner(fac_name) then
|
||||
table.remove(area.faction_open, i)
|
||||
changed = true
|
||||
end
|
||||
end
|
||||
if #area.faction_open == 0 then
|
||||
-- Prevent DB clutter, remove value
|
||||
area.faction_open = nil
|
||||
else
|
||||
faction_info = table.concat(area.faction_open, ", ")
|
||||
end
|
||||
|
||||
if changed then
|
||||
areas:save()
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(areaStrings, ("%s [%u] (%s%s%s)")
|
||||
:format(area.name, id, area.owner,
|
||||
area.open and S(":open") or "",
|
||||
faction_info and ": "..faction_info or ""))
|
||||
end
|
||||
|
||||
for i, area in pairs(areas:getExternalHudEntries(pos)) do
|
||||
local str = ""
|
||||
if area.name then str = area.name .. " " end
|
||||
if area.id then str = str.."["..area.id.."] " end
|
||||
if area.owner then str = str.."("..area.owner..")" end
|
||||
table.insert(areaStrings, str)
|
||||
end
|
||||
|
||||
local areaString = S("Areas:")
|
||||
if #areaStrings > 0 then
|
||||
areaString = areaString.."\n"..
|
||||
table.concat(areaStrings, "\n")
|
||||
end
|
||||
local hud = areas.hud[name]
|
||||
if not hud then
|
||||
hud = {}
|
||||
areas.hud[name] = hud
|
||||
hud.areasId = player:hud_add({
|
||||
hud_elem_type = "text",
|
||||
name = "Areas",
|
||||
number = 0xFFFFFF,
|
||||
position = {x=0, y=1},
|
||||
offset = {x=8, y=-8},
|
||||
text = areaString,
|
||||
scale = {x=200, y=60},
|
||||
alignment = {x=1, y=-1},
|
||||
})
|
||||
hud.oldAreas = areaString
|
||||
return
|
||||
elseif hud.oldAreas ~= areaString then
|
||||
player:hud_change(hud.areasId, "text", areaString)
|
||||
hud.oldAreas = areaString
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
minetest.register_on_leaveplayer(function(player)
|
||||
areas.hud[player:get_player_name()] = nil
|
||||
end)
|
44
areas/init.lua
Normal file
@ -0,0 +1,44 @@
|
||||
-- Areas mod by ShadowNinja
|
||||
-- Based on node_ownership
|
||||
-- License: LGPLv2+
|
||||
|
||||
areas = {}
|
||||
|
||||
areas.factions_available = minetest.get_modpath("playerfactions") and true
|
||||
|
||||
areas.adminPrivs = {areas=true}
|
||||
areas.startTime = os.clock()
|
||||
|
||||
areas.modpath = minetest.get_modpath("areas")
|
||||
dofile(areas.modpath.."/settings.lua")
|
||||
dofile(areas.modpath.."/api.lua")
|
||||
dofile(areas.modpath.."/internal.lua")
|
||||
dofile(areas.modpath.."/chatcommands.lua")
|
||||
dofile(areas.modpath.."/pos.lua")
|
||||
dofile(areas.modpath.."/interact.lua")
|
||||
dofile(areas.modpath.."/legacy.lua")
|
||||
dofile(areas.modpath.."/hud.lua")
|
||||
|
||||
areas:load()
|
||||
|
||||
local S = minetest.get_translator("areas")
|
||||
|
||||
minetest.register_privilege("areas", {
|
||||
description = S("Can administer areas."),
|
||||
give_to_singleplayer = false
|
||||
})
|
||||
minetest.register_privilege("areas_high_limit", {
|
||||
description = S("Can protect more, bigger areas."),
|
||||
give_to_singleplayer = false
|
||||
})
|
||||
|
||||
if not minetest.registered_privileges[areas.config.self_protection_privilege] then
|
||||
minetest.register_privilege(areas.config.self_protection_privilege, {
|
||||
description = S("Can protect areas."),
|
||||
})
|
||||
end
|
||||
|
||||
if minetest.settings:get_bool("log_mods") then
|
||||
local diffTime = os.clock() - areas.startTime
|
||||
minetest.log("action", "areas loaded in "..diffTime.."s.")
|
||||
end
|
19
areas/interact.lua
Normal file
@ -0,0 +1,19 @@
|
||||
local S = minetest.get_translator("areas")
|
||||
|
||||
local old_is_protected = minetest.is_protected
|
||||
function minetest.is_protected(pos, name)
|
||||
if not areas:canInteract(pos, name) then
|
||||
return true
|
||||
end
|
||||
return old_is_protected(pos, name)
|
||||
end
|
||||
|
||||
minetest.register_on_protection_violation(function(pos, name)
|
||||
if not areas:canInteract(pos, name) then
|
||||
local owners = areas:getNodeOwners(pos)
|
||||
minetest.chat_send_player(name,
|
||||
S("@1 is protected by @2.",
|
||||
minetest.pos_to_string(pos),
|
||||
table.concat(owners, ", ")))
|
||||
end
|
||||
end)
|
314
areas/internal.lua
Normal file
@ -0,0 +1,314 @@
|
||||
local S = minetest.get_translator("areas")
|
||||
|
||||
function areas:player_exists(name)
|
||||
return minetest.get_auth_handler().get_auth(name) ~= nil
|
||||
end
|
||||
|
||||
local safe_file_write = minetest.safe_file_write
|
||||
if safe_file_write == nil then
|
||||
function safe_file_write(path, content)
|
||||
local file, err = io.open(path, "w")
|
||||
if err then
|
||||
return err
|
||||
end
|
||||
file:write(content)
|
||||
file:close()
|
||||
end
|
||||
end
|
||||
|
||||
-- Save the areas table to a file
|
||||
function areas:save()
|
||||
local datastr = minetest.write_json(self.areas)
|
||||
if not datastr then
|
||||
minetest.log("error", "[areas] Failed to serialize area data!")
|
||||
return
|
||||
end
|
||||
return safe_file_write(self.config.filename, datastr)
|
||||
end
|
||||
|
||||
-- Load the areas table from the save file
|
||||
function areas:load()
|
||||
local file, err = io.open(self.config.filename, "r")
|
||||
if err then
|
||||
self.areas = self.areas or {}
|
||||
return err
|
||||
end
|
||||
local data = file:read("*a")
|
||||
if data:sub(1, 1) == "[" then
|
||||
self.areas, err = minetest.parse_json(data)
|
||||
else
|
||||
self.areas, err = minetest.deserialize(data)
|
||||
end
|
||||
if type(self.areas) ~= "table" then
|
||||
self.areas = {}
|
||||
end
|
||||
if err and #data > 10 then
|
||||
minetest.log("error", "[areas] Failed to load area data: " ..
|
||||
tostring(err))
|
||||
end
|
||||
file:close()
|
||||
self:populateStore()
|
||||
end
|
||||
|
||||
--- Checks an AreaStore ID.
|
||||
-- Deletes the AreaStore (falling back to the iterative method)
|
||||
-- and prints an error message if the ID is invalid.
|
||||
-- @return Whether the ID was valid.
|
||||
function areas:checkAreaStoreId(sid)
|
||||
if not sid then
|
||||
minetest.log("error", "AreaStore failed to find an ID for an "
|
||||
.."area! Falling back to iterative area checking.")
|
||||
self.store = nil
|
||||
self.store_ids = nil
|
||||
end
|
||||
return sid and true or false
|
||||
end
|
||||
|
||||
-- Populates the AreaStore after loading, if needed.
|
||||
function areas:populateStore()
|
||||
if not rawget(_G, "AreaStore") then
|
||||
return
|
||||
end
|
||||
local store = AreaStore()
|
||||
local store_ids = {}
|
||||
for id, area in pairs(areas.areas) do
|
||||
local sid = store:insert_area(area.pos1,
|
||||
area.pos2, tostring(id))
|
||||
if not self:checkAreaStoreId(sid) then
|
||||
return
|
||||
end
|
||||
store_ids[id] = sid
|
||||
end
|
||||
self.store = store
|
||||
self.store_ids = store_ids
|
||||
end
|
||||
|
||||
-- Finds the first usable index in a table
|
||||
-- Eg: {[1]=false,[4]=true} -> 2
|
||||
local function findFirstUnusedIndex(t)
|
||||
local i = 0
|
||||
repeat i = i + 1
|
||||
until t[i] == nil
|
||||
return i
|
||||
end
|
||||
|
||||
--- Add a area.
|
||||
-- @return The new area's ID.
|
||||
function areas:add(owner, name, pos1, pos2, parent)
|
||||
local id = findFirstUnusedIndex(self.areas)
|
||||
self.areas[id] = {
|
||||
name = name,
|
||||
pos1 = pos1,
|
||||
pos2 = pos2,
|
||||
owner = owner,
|
||||
parent = parent
|
||||
}
|
||||
|
||||
for i=1, #areas.registered_on_adds do
|
||||
areas.registered_on_adds[i](id, self.areas[id])
|
||||
end
|
||||
|
||||
-- Add to AreaStore
|
||||
if self.store then
|
||||
local sid = self.store:insert_area(pos1, pos2, tostring(id))
|
||||
if self:checkAreaStoreId(sid) then
|
||||
self.store_ids[id] = sid
|
||||
end
|
||||
end
|
||||
return id
|
||||
end
|
||||
|
||||
--- Remove a area, and optionally it's children recursively.
|
||||
-- If a area is deleted non-recursively the children will
|
||||
-- have the removed area's parent as their new parent.
|
||||
function areas:remove(id, recurse)
|
||||
if recurse then
|
||||
-- Recursively find child entries and remove them
|
||||
local cids = self:getChildren(id)
|
||||
for _, cid in pairs(cids) do
|
||||
self:remove(cid, true)
|
||||
end
|
||||
else
|
||||
-- Update parents
|
||||
local parent = self.areas[id].parent
|
||||
local children = self:getChildren(id)
|
||||
for _, cid in pairs(children) do
|
||||
-- The subarea parent will be niled out if the
|
||||
-- removed area does not have a parent
|
||||
self.areas[cid].parent = parent
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
for i=1, #areas.registered_on_removes do
|
||||
areas.registered_on_removes[i](id)
|
||||
end
|
||||
|
||||
-- Remove main entry
|
||||
self.areas[id] = nil
|
||||
|
||||
-- Remove from AreaStore
|
||||
if self.store then
|
||||
self.store:remove_area(self.store_ids[id])
|
||||
self.store_ids[id] = nil
|
||||
end
|
||||
end
|
||||
|
||||
--- Move an area.
|
||||
function areas:move(id, area, pos1, pos2)
|
||||
area.pos1 = pos1
|
||||
area.pos2 = pos2
|
||||
|
||||
for i=1, #areas.registered_on_moves do
|
||||
areas.registered_on_moves[i](id, area, pos1, pos2)
|
||||
end
|
||||
|
||||
if self.store then
|
||||
self.store:remove_area(areas.store_ids[id])
|
||||
local sid = self.store:insert_area(pos1, pos2, tostring(id))
|
||||
if self:checkAreaStoreId(sid) then
|
||||
self.store_ids[id] = sid
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Checks if a area between two points is entirely contained by another area.
|
||||
-- Positions must be sorted.
|
||||
function areas:isSubarea(pos1, pos2, id)
|
||||
local area = self.areas[id]
|
||||
if not area then
|
||||
return false
|
||||
end
|
||||
local ap1, ap2 = area.pos1, area.pos2
|
||||
local ap1x, ap1y, ap1z = ap1.x, ap1.y, ap1.z
|
||||
local ap2x, ap2y, ap2z = ap2.x, ap2.y, ap2.z
|
||||
local p1x, p1y, p1z = pos1.x, pos1.y, pos1.z
|
||||
local p2x, p2y, p2z = pos2.x, pos2.y, pos2.z
|
||||
if
|
||||
(p1x >= ap1x and p1x <= ap2x) and
|
||||
(p2x >= ap1x and p2x <= ap2x) and
|
||||
(p1y >= ap1y and p1y <= ap2y) and
|
||||
(p2y >= ap1y and p2y <= ap2y) and
|
||||
(p1z >= ap1z and p1z <= ap2z) and
|
||||
(p2z >= ap1z and p2z <= ap2z) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
-- Returns a table (list) of children of an area given it's identifier
|
||||
function areas:getChildren(id)
|
||||
local children = {}
|
||||
for cid, area in pairs(self.areas) do
|
||||
if area.parent and area.parent == id then
|
||||
table.insert(children, cid)
|
||||
end
|
||||
end
|
||||
return children
|
||||
end
|
||||
|
||||
-- Checks if the user has sufficient privileges.
|
||||
-- If the player is not a administrator it also checks
|
||||
-- if the area intersects other areas that they do not own.
|
||||
-- Also checks the size of the area and if the user already
|
||||
-- has more than max_areas.
|
||||
function areas:canPlayerAddArea(pos1, pos2, name)
|
||||
local privs = minetest.get_player_privs(name)
|
||||
if privs.areas then
|
||||
return true
|
||||
end
|
||||
|
||||
-- Check self protection privilege, if it is enabled,
|
||||
-- and if the area is too big.
|
||||
if not self.config.self_protection or
|
||||
not privs[areas.config.self_protection_privilege] then
|
||||
return false, S("Self protection is disabled or you do not have"
|
||||
.." the necessary privilege.")
|
||||
end
|
||||
|
||||
local max_size = privs.areas_high_limit and
|
||||
self.config.self_protection_max_size_high or
|
||||
self.config.self_protection_max_size
|
||||
if
|
||||
(pos2.x - pos1.x) > max_size.x or
|
||||
(pos2.y - pos1.y) > max_size.y or
|
||||
(pos2.z - pos1.z) > max_size.z then
|
||||
return false, S("Area is too big.")
|
||||
end
|
||||
|
||||
-- Check number of areas the user has and make sure it not above the max
|
||||
local count = 0
|
||||
for _, area in pairs(self.areas) do
|
||||
if area.owner == name then
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
local max_areas = privs.areas_high_limit and
|
||||
self.config.self_protection_max_areas_high or
|
||||
self.config.self_protection_max_areas
|
||||
if count >= max_areas then
|
||||
return false, S("You have reached the maximum amount of"
|
||||
.." areas that you are allowed to protect.")
|
||||
end
|
||||
|
||||
-- Check intersecting areas
|
||||
local can, id = self:canInteractInArea(pos1, pos2, name)
|
||||
if not can then
|
||||
local area = self.areas[id]
|
||||
return false, S("The area intersects with @1 [@2] (@3).",
|
||||
area.name, id, area.owner)
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- Given a id returns a string in the format:
|
||||
-- "name [id]: owner (x1, y1, z1) (x2, y2, z2) -> children"
|
||||
function areas:toString(id)
|
||||
local area = self.areas[id]
|
||||
local message = ("%s [%d]: %s %s %s"):format(
|
||||
area.name, id, area.owner,
|
||||
minetest.pos_to_string(area.pos1),
|
||||
minetest.pos_to_string(area.pos2))
|
||||
|
||||
local children = areas:getChildren(id)
|
||||
if #children > 0 then
|
||||
message = message.." -> "..table.concat(children, ", ")
|
||||
end
|
||||
return message
|
||||
end
|
||||
|
||||
-- Re-order areas in table by their identifiers
|
||||
function areas:sort()
|
||||
local sa = {}
|
||||
for k, area in pairs(self.areas) do
|
||||
if not area.parent then
|
||||
table.insert(sa, area)
|
||||
local newid = #sa
|
||||
for _, subarea in pairs(self.areas) do
|
||||
if subarea.parent == k then
|
||||
subarea.parent = newid
|
||||
table.insert(sa, subarea)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
self.areas = sa
|
||||
end
|
||||
|
||||
-- Checks if a player owns an area or a parent of it
|
||||
function areas:isAreaOwner(id, name)
|
||||
local cur = self.areas[id]
|
||||
if cur and minetest.check_player_privs(name, self.adminPrivs) then
|
||||
return true
|
||||
end
|
||||
while cur do
|
||||
if cur.owner == name then
|
||||
return true
|
||||
elseif cur.parent then
|
||||
cur = self.areas[cur.parent]
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
108
areas/legacy.lua
Normal file
@ -0,0 +1,108 @@
|
||||
-- This file contains functions to convert from
|
||||
-- the old areas format and other compatability code.
|
||||
local S = minetest.get_translator("areas")
|
||||
|
||||
minetest.register_chatcommand("legacy_load_areas", {
|
||||
params = S("<version>"),
|
||||
description = S("Loads, converts, and saves the areas from"
|
||||
.." a legacy save file."),
|
||||
privs = {areas=true, server=true},
|
||||
func = function(name, param)
|
||||
minetest.chat_send_player(name, S("Converting areas…"))
|
||||
local version = tonumber(param)
|
||||
if version == 0 then
|
||||
local err = areas:node_ownership_load()
|
||||
if err then
|
||||
minetest.chat_send_player(name, S("Error loading legacy file: @1", err))
|
||||
return
|
||||
end
|
||||
else
|
||||
minetest.chat_send_player(name, S("Invalid version number. (0 allowed)"))
|
||||
return
|
||||
end
|
||||
minetest.chat_send_player(name, S("Legacy file loaded."))
|
||||
|
||||
for k, area in pairs(areas.areas) do
|
||||
-- New position format
|
||||
area.pos1 = {x=area.x1, y=area.y1, z=area.z1}
|
||||
area.pos2 = {x=area.x2, y=area.y2, z=area.z2}
|
||||
|
||||
area.x1, area.y1, area.z1,
|
||||
area.x2, area.y2, area.z2 =
|
||||
nil, nil, nil, nil, nil, nil
|
||||
|
||||
-- Area positions sorting
|
||||
areas:sortPos(area.pos1, area.pos2)
|
||||
|
||||
-- Add name
|
||||
area.name = S("unnamed")
|
||||
|
||||
-- Remove ID
|
||||
area.id = nil
|
||||
end
|
||||
minetest.chat_send_player(name, S("Table format updated."))
|
||||
|
||||
areas:save()
|
||||
minetest.chat_send_player(name, S("Converted areas saved. Done."))
|
||||
end
|
||||
})
|
||||
|
||||
function areas:node_ownership_load()
|
||||
local filename = minetest.get_worldpath().."/owners.tbl"
|
||||
local tables, err
|
||||
tables, err = loadfile(filename)
|
||||
if err then
|
||||
return err
|
||||
end
|
||||
|
||||
tables = tables()
|
||||
for idx = 1, #tables do
|
||||
local tolinkv, tolinki = {}, {}
|
||||
for i, v in pairs(tables[idx]) do
|
||||
if type(v) == "table" and tables[v[1]] then
|
||||
table.insert(tolinkv, {i, tables[v[1]]})
|
||||
end
|
||||
if type(i) == "table" and tables[i[1]] then
|
||||
table.insert(tolinki, {i, tables[i[1]]})
|
||||
end
|
||||
end
|
||||
-- link values, first due to possible changes of indices
|
||||
for _, v in ipairs(tolinkv) do
|
||||
tables[idx][v[1]] = v[2]
|
||||
end
|
||||
-- link indices
|
||||
for _, v in ipairs(tolinki) do
|
||||
tables[idx][v[2]], tables[idx][v[1]] = tables[idx][v[1]], nil
|
||||
end
|
||||
end
|
||||
self.areas = tables[1]
|
||||
end
|
||||
|
||||
-- Returns the name of the first player that owns an area
|
||||
function areas.getNodeOwnerName(pos)
|
||||
for id, area in pairs(areas:getAreasAtPos(pos)) do
|
||||
return area.owner
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- Checks if a node is owned by you
|
||||
function areas.isNodeOwner(pos, name)
|
||||
if minetest.check_player_privs(name, areas.adminPrivs) then
|
||||
return true
|
||||
end
|
||||
for id, area in pairs(areas:getAreasAtPos(pos)) do
|
||||
if name == area.owner then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- Checks if an area has an owner
|
||||
function areas.hasOwner(pos)
|
||||
for id, area in pairs(areas:getAreasAtPos(pos)) do
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
135
areas/locale/areas.fr.tr
Normal file
@ -0,0 +1,135 @@
|
||||
# textdomain: areas
|
||||
|
||||
|
||||
|
||||
### chatcommands.lua ###
|
||||
|
||||
<AreaName>=<NomZone>
|
||||
<ID> [faction_name]=<ID> [nom_de_faction]
|
||||
<NewOwner>=<NouveauPropriétaire>
|
||||
<ParentID>=<IDZonePrincipale>
|
||||
<PlayerName>=<NomJoueur>
|
||||
<newName>=<NouveauNom>
|
||||
@1 has given you control over the area "@2" (ID @3).=@1 vous a donné le contrôle de la zone "@2" (ID @3).
|
||||
@1 spanning up to @2x@3x@4.=@1 s’étendant jusqu’à @2x@3x@4.
|
||||
A regular expression is required.=Une expression régulière est requise.
|
||||
Area @1 does not exist or is not owned by you.=La zone @1 n’existe pas ou ne vous appartient pas.
|
||||
Area closed for faction members.=Zone fermée aux membres de la faction.
|
||||
Area closed.=Zone fermée.
|
||||
Area does not exist.=La zone n’existe pas.
|
||||
Area is open for members of: @1=Zone ouverte aux membres de ces factions : @1
|
||||
Area opened.=Zone ouverte.
|
||||
Area protected. ID: @1=Zone protégée. ID : @1
|
||||
Area renamed.=Zone renommée.
|
||||
Area successfully moved.=Zone déplacée avec succès.
|
||||
Change the owner of an area using its ID=Change le propriétaire d’une zone en utilisant son ID.
|
||||
Faction doesn't exists=La faction n'existe pas
|
||||
Find areas using a Lua regular expression=Trouve les zones en utilisant une expression régulière Lua.
|
||||
Get information about area configuration and usage.=Obtient des informations sur la configuration des zones et l’utilisation des zones.
|
||||
|
||||
Give a player access to a sub-area beetween two positions that have already been protected, Use set_owner if you don't want the parent to be set.=Donne au joueur accès aux sous-zones entre deux positions qui ont déjà été protégées ; utilisez set_owner si vous ne voulez pas que la zone pricipale soit définie.
|
||||
|
||||
Invalid regular expression.=Expression régulière invalide.
|
||||
Limit: @1 areas=Limite: @1 zones.
|
||||
Limit: no area count limit=Limite: pas de limite de nombre de zones.
|
||||
List your areas, or all areas if you are an admin.=Liste vos zones, ou toutes les zones si vous êtes administrateur.
|
||||
Move (or resize) an area to the current positions.=Déplace (ou redimensionne) une zone aux positions actuelles.
|
||||
No matches found.=Aucun résultat.
|
||||
No visible areas.=Pas de zone visible.
|
||||
Owner changed.=Propriétaire changé.
|
||||
Players with the "@1" privilege can protect up to @2 areas=Les joueurs avec le privilège "@1" peuvent protéger jusqu’à @2 zones
|
||||
|
||||
Protect an area between two positions and give a player access to it without setting the parent of the area to any existing area=Protège une zone entre deux positions et donne à un joueur accès à cette zone sans définir la zone principale de cette zone ni aucune zone existante.
|
||||
|
||||
Protect your own area=Protège votre zone.
|
||||
Recursively remove areas using an ID=Supprime les zones récursivement en utilisant un ID.
|
||||
Remove an area using an ID=Supprime une zone en utilisant son ID.
|
||||
Removed area @1=Zone @1 supprimée.
|
||||
Removed area @1 and it's sub areas.=Zone @1 et ses sous-zones supprimées.
|
||||
Removes all ownerless areas=Supprime toutes les zones sans propriétaire
|
||||
Rename an area that you own=Renomme une zone qui vous appartient.
|
||||
Self protection is disabled.=L’autoprotection est désactivée.
|
||||
Self protection is enabled.=L’autoprotection est activée.
|
||||
That area doesn't exist.=La zone n’existe pas.
|
||||
The player "@1" does not exist.=Le joueur "@1" n’existe pas.
|
||||
Toggle an area open (anyone can interact) or closed=Bascule entre zone ouverte (tout le monde peut intéragir) ou fermée.
|
||||
Toggle an area open/closed for members in your faction.=Bascule entre zone ouverte/fermée pour les membres de votre faction.
|
||||
You are an area administrator ("areas" privilege).=Vous êtes un administrateur de zone (privilège "areas").
|
||||
You can protect areas=Vous pouvez protéger des zones.
|
||||
You can't protect that area.=Vous ne pouvez pas protéger cette zone.
|
||||
You can't protect that area: @1=Vous ne pouvez pas protéger cette zone : @1.
|
||||
You don't have the necessary privilege ("@1").=Vous n’avez pas le privilège nécessaire ("@1").
|
||||
You don't own that area.=Vous ne possédez pas cette zone.
|
||||
You have @1 areas.=Vous avez @1 zones.
|
||||
|
||||
You have been granted control over area #@1. Type /list_areas to show your areas.=Vous avez reçu l’autorisation de contrôler la zone #@1.
|
||||
|
||||
You have extended area protection limits ("areas_high_limit" privilege).=Votre limite de protection de zones est étendue (privilège "areas_high_limit").
|
||||
|
||||
You have the necessary privilege ("@1").=Vous avez le privilège nécessaire ("@1").
|
||||
You need to select an area first.=Vous devez sélectionner une zone d’abord.
|
||||
|
||||
### chatcommands.lua ###
|
||||
### pos.lua ###
|
||||
|
||||
<ID>=<ID>
|
||||
Invalid usage, see /help @1.=Utilisation incorrecte, voir /help @1.
|
||||
|
||||
### hud.lua ###
|
||||
|
||||
:open= : ouverte
|
||||
Areas:=Zones :
|
||||
|
||||
### init.lua ###
|
||||
|
||||
Can administer areas.=Permet d’administrer des zones.
|
||||
Can protect areas.=Permet de protéger des zones.
|
||||
Can protect more, bigger areas.=Permet de protéger plus, et de plus grandes zones.
|
||||
|
||||
### interact.lua ###
|
||||
|
||||
@1 is protected by @2.=@1 est protégée par @2.
|
||||
|
||||
### internal.lua ###
|
||||
|
||||
Area is too big.=La zone est trop grande.
|
||||
|
||||
Self protection is disabled or you do not have the necessary privilege.=L’autoprotection est désactivée ou vous n’avez pas le privilège nécessaire.
|
||||
|
||||
The area intersects with @1 [@2] (@3).=La zone a une intersection avec @1 [@2] (@3).
|
||||
|
||||
You have reached the maximum amount of areas that you are allowed to protect.=Vous avez atteint le nombre maximum de zones que vous êtes autorisé à protéger.
|
||||
|
||||
|
||||
### legacy.lua ###
|
||||
|
||||
<version>=<version>
|
||||
Converted areas saved. Done.=Zones converties sauvegardées. Fait.
|
||||
Converting areas…=Conversion des zones…
|
||||
Error loading legacy file: @1=Erreur lors du chargement du fichier : @1
|
||||
Invalid version number. (0 allowed)=Numéro de version invalide. (0 autorisé)
|
||||
Legacy file loaded.=Fichier obsolète chargé.
|
||||
|
||||
Loads, converts, and saves the areas from a legacy save file.=Charge, fait la conversion et sauvegarde les zones depuis un fichier de sauvegarde obsolète.
|
||||
|
||||
Table format updated.=Format de tableau mis à jour.
|
||||
unnamed=Non nommé
|
||||
|
||||
### pos.lua ###
|
||||
|
||||
<not set>=<no définie>
|
||||
Area @1 selected.=Zone @1 sélectionnée.
|
||||
Area position @1 set to @2=Position @1 de la zone définie à @2.
|
||||
Position @1 set to @2=Position @1 définie à @2.
|
||||
Position @1: =Position @1 :
|
||||
Select an area by ID.=Sélectionnez une zone par son ID.
|
||||
Select position @1 by punching a node.=Sélectionnez une position en frappant un bloc.
|
||||
Select positions by punching two nodes.=Sélectionnez une position en frappant deux blocs.
|
||||
|
||||
Set area protection region position @1 to your location or the one specified=Définit la position @1 de la région de protection de zone à votre position ou à celle spécifiée.
|
||||
|
||||
Set area protection region, position 1, or position 2 by punching nodes, or display the region=Définit la région de protection de zone, la position 1, ou la position 2 en frappant des blocs, ou en affichant la région.
|
||||
|
||||
The area @1 does not exist.=La zone @1 n’existe pas.
|
||||
Unable to get position.=Impossible d’obtenir la position.
|
||||
Unknown subcommand: @1=Sous-commande inconnue : @1
|
135
areas/locale/areas.it.tr
Normal file
@ -0,0 +1,135 @@
|
||||
# textdomain: areas
|
||||
|
||||
|
||||
|
||||
### chatcommands.lua ###
|
||||
|
||||
<AreaName>=<NomeArea>
|
||||
<ID> [faction_name]= <ID> [nome_fazione]
|
||||
<NewOwner>=<NuovoProprietario>
|
||||
<ParentID>=<IDparent>
|
||||
<PlayerName>=<NomeGiocatore>
|
||||
<newName>=<nuovoNome>
|
||||
@1 has given you control over the area "@2" (ID @3).=@1 ti ha dato il controllo sull'area "@2" (ID @3).
|
||||
@1 spanning up to @2x@3x@4.=@1 si estende fino a @2x@3@4.
|
||||
A regular expression is required.=È necessaria una espressione regolare.
|
||||
Area @1 does not exist or is not owned by you.=L'area @1 non esiste o non è di tua proprietà.
|
||||
Area closed for faction members.=Area chiusa per i membri della fazione.
|
||||
Area closed.=Area chiusa.
|
||||
Area does not exist.=L'area non esiste.
|
||||
Area is open for members of: @1=L'area è aperta ai membri di: @1
|
||||
Area opened.=Area aperta.
|
||||
Area protected. ID: @1=Area protetta. ID: @1
|
||||
Area renamed.=Area rinominata.
|
||||
Area successfully moved.=Area spostata con successo.
|
||||
Change the owner of an area using its ID=Cambia il proprietario di un'area usando il suo ID
|
||||
Faction doesn't exists=La fazione non esiste
|
||||
Find areas using a Lua regular expression=Trova aree usando una espressione regolare Lua
|
||||
Get information about area configuration and usage.=Ottieni informazioni sulla configurazione e l'uso delle aree.
|
||||
|
||||
Give a player access to a sub-area beetween two positions that have already been protected, Use set_owner if you don't want the parent to be set.=Dai a un giocatore l'accesso a una sotto-area tra due posizioni che sono già state protette, usa set_owner se non vuoi che sia impostato il parent.
|
||||
|
||||
Invalid regular expression.=Espressione regolare non valida.
|
||||
Limit: @1 areas=Limite: @1 aree
|
||||
Limit: no area count limit=Limite: nessun limite al numero delle aree
|
||||
List your areas, or all areas if you are an admin.=Elenca le tue aree, o tutte le aree se sei un amministratore.
|
||||
Move (or resize) an area to the current positions.=Sposta (o ridimensiona) un'area alle posizioni attuali.
|
||||
No matches found.=Nessuna corrispondenza trovata.
|
||||
No visible areas.=Nessuna area visibile.
|
||||
Owner changed.=Proprietario cambiato.
|
||||
Players with the "@1" privilege can protect up to @2 areas=I giocatori col privilegio "@1" possono proteggere fino a @2 aree
|
||||
|
||||
Protect an area between two positions and give a player access to it without setting the parent of the area to any existing area=Proteggi un'area tra due posizioni e danne l'accesso a un giocatore senza impostare il parent dell'area a qualsiasi area esistente
|
||||
|
||||
Protect your own area=Proteggi la tua area
|
||||
Recursively remove areas using an ID=Elimina ricorsivamente delle aree usando un ID
|
||||
Remove an area using an ID=Elimina un'area usando un ID
|
||||
Removed area @1=Eliminata l'area @1
|
||||
Removed area @1 and it's sub areas.=Eliminata l'area @1 e le sue sotto-aree.
|
||||
Removes all ownerless areas=
|
||||
Rename an area that you own=Rinomina un'area che ti appartiene
|
||||
Self protection is disabled.=L'auto-protezione è disattivata.
|
||||
Self protection is enabled.=L'auto-protezione è attivata.
|
||||
That area doesn't exist.=Quell'area non esiste.
|
||||
The player "@1" does not exist.=Il giocatore "@1" non esiste.
|
||||
Toggle an area open (anyone can interact) or closed=Apri o chiudi un'area (chiunque può interagirvi)
|
||||
Toggle an area open/closed for members in your faction.=Apri o chiudi un'area per i membri della tua fazione.
|
||||
You are an area administrator ("areas" privilege).=Sei un amministratore di aree (privilegio "areas")
|
||||
You can protect areas=Puoi proteggere aree
|
||||
You can't protect that area.=Non puoi proteggere quell'area.
|
||||
You can't protect that area: @1=Non puoi proteggere quell'area: @1
|
||||
You don't have the necessary privilege ("@1").=Non hai il privilegio necessario ("@1")
|
||||
You don't own that area.=Non possiedi quell'area.
|
||||
You have @1 areas.=Hai @1 aree.
|
||||
|
||||
You have been granted control over area #@1. Type /list_areas to show your areas.=Ti è stato concesso il controllo sull'area #@1. Digita /list_areas per mostrare le tue aree.
|
||||
|
||||
You have extended area protection limits ("areas_high_limit" privilege).=Hai limiti di protezione aree estesi (privilegio "areas_high_limit")
|
||||
|
||||
You have the necessary privilege ("@1").=Hai il privilegio necessario ("@1")
|
||||
You need to select an area first.=Prima devi selezionare un'area.
|
||||
|
||||
### chatcommands.lua ###
|
||||
### pos.lua ###
|
||||
|
||||
<ID>=<ID>
|
||||
Invalid usage, see /help @1.=Utilizzo non valido, si veda /help @1.
|
||||
|
||||
### hud.lua ###
|
||||
|
||||
:open=:aperta
|
||||
Areas:=Aree:
|
||||
|
||||
### init.lua ###
|
||||
|
||||
Can administer areas.=
|
||||
Can protect areas.=
|
||||
Can protect more, bigger areas.=
|
||||
|
||||
### interact.lua ###
|
||||
|
||||
@1 is protected by @2.=@1 è protetta da @2.
|
||||
|
||||
### internal.lua ###
|
||||
|
||||
Area is too big.=L'area è troppo grande.
|
||||
|
||||
Self protection is disabled or you do not have the necessary privilege.=L'auto-protezione è disattivata o non possiedi il privilegio necessario.
|
||||
|
||||
The area intersects with @1 [@2] (@3).=L'area interseca con @1 [@2] (@3).
|
||||
|
||||
You have reached the maximum amount of areas that you are allowed to protect.=Hai raggiunto il numero massimo di aree che ti è consentito proteggere.
|
||||
|
||||
|
||||
### legacy.lua ###
|
||||
|
||||
<version>=<versione>
|
||||
Converted areas saved. Done.=Aree convertite salvate. Fatto.
|
||||
Converting areas…=Conversione delle aree...
|
||||
Error loading legacy file: @1=Errore nel caricamento del file precedente: @1
|
||||
Invalid version number. (0 allowed)=Numero di versione non valido. (0 permesso)
|
||||
Legacy file loaded.=File precedente caricato.
|
||||
|
||||
Loads, converts, and saves the areas from a legacy save file.=Carica, converte e salva le aree da un file di salvataggio precedente.
|
||||
|
||||
Table format updated.=Aggiornato il formato della tabella.
|
||||
unnamed=innominato
|
||||
|
||||
### pos.lua ###
|
||||
|
||||
<not set>=<non impostato>
|
||||
Area @1 selected.=Area @1 selezionata.
|
||||
Area position @1 set to @2=Posizione @1 dell'area impostata a @2
|
||||
Position @1 set to @2=Posizione @1 impostata a @2
|
||||
Position @1: =Posizione @1:
|
||||
Select an area by ID.=Scegli un'area tramite l'ID.
|
||||
Select position @1 by punching a node.=Seleziona la posizione @1 colpendo un nodo.
|
||||
Select positions by punching two nodes.=Seleziona le posizioni colpendo due nodi.
|
||||
|
||||
Set area protection region position @1 to your location or the one specified=Imposta la protezione area della posizione @1 della regione alla tua posizione o quella specificata
|
||||
|
||||
Set area protection region, position 1, or position 2 by punching nodes, or display the region=Imposta la protezione area della regione, posizione 1, o posizione 2, colpendo due nodi, o mostra la regione
|
||||
|
||||
The area @1 does not exist.=L'area @1 non esiste.
|
||||
Unable to get position.=Impossibile ottenere la posizione.
|
||||
Unknown subcommand: @1=Sotto-comando sconosciuto: @1
|
135
areas/locale/template.txt
Normal file
@ -0,0 +1,135 @@
|
||||
# textdomain: areas
|
||||
|
||||
|
||||
|
||||
### chatcommands.lua ###
|
||||
|
||||
<AreaName>=
|
||||
<ID> [faction_name]=
|
||||
<NewOwner>=
|
||||
<ParentID>=
|
||||
<PlayerName>=
|
||||
<newName>=
|
||||
@1 has given you control over the area "@2" (ID @3).=
|
||||
@1 spanning up to @2x@3x@4.=
|
||||
A regular expression is required.=
|
||||
Area @1 does not exist or is not owned by you.=
|
||||
Area closed for faction members.=
|
||||
Area closed.=
|
||||
Area does not exist.=
|
||||
Area is open for members of: @1=
|
||||
Area opened.=
|
||||
Area protected. ID: @1=
|
||||
Area renamed.=
|
||||
Area successfully moved.=
|
||||
Change the owner of an area using its ID=
|
||||
Faction doesn't exists=
|
||||
Find areas using a Lua regular expression=
|
||||
Get information about area configuration and usage.=
|
||||
|
||||
Give a player access to a sub-area beetween two positions that have already been protected, Use set_owner if you don't want the parent to be set.=
|
||||
|
||||
Invalid regular expression.=
|
||||
Limit: @1 areas=
|
||||
Limit: no area count limit=
|
||||
List your areas, or all areas if you are an admin.=
|
||||
Move (or resize) an area to the current positions.=
|
||||
No matches found.=
|
||||
No visible areas.=
|
||||
Owner changed.=
|
||||
Players with the "@1" privilege can protect up to @2 areas=
|
||||
|
||||
Protect an area between two positions and give a player access to it without setting the parent of the area to any existing area=
|
||||
|
||||
Protect your own area=
|
||||
Recursively remove areas using an ID=
|
||||
Remove an area using an ID=
|
||||
Removed area @1=
|
||||
Removed area @1 and it's sub areas.=
|
||||
Removes all ownerless areas=
|
||||
Rename an area that you own=
|
||||
Self protection is disabled.=
|
||||
Self protection is enabled.=
|
||||
That area doesn't exist.=
|
||||
The player "@1" does not exist.=
|
||||
Toggle an area open (anyone can interact) or closed=
|
||||
Toggle an area open/closed for members in your faction.=
|
||||
You are an area administrator ("areas" privilege).=
|
||||
You can protect areas=
|
||||
You can't protect that area.=
|
||||
You can't protect that area: @1=
|
||||
You don't have the necessary privilege ("@1").=
|
||||
You don't own that area.=
|
||||
You have @1 areas.=
|
||||
|
||||
You have been granted control over area #@1. Type /list_areas to show your areas.=
|
||||
|
||||
You have extended area protection limits ("areas_high_limit" privilege).=
|
||||
|
||||
You have the necessary privilege ("@1").=
|
||||
You need to select an area first.=
|
||||
|
||||
### chatcommands.lua ###
|
||||
### pos.lua ###
|
||||
|
||||
<ID>=
|
||||
Invalid usage, see /help @1.=
|
||||
|
||||
### hud.lua ###
|
||||
|
||||
:open=
|
||||
Areas:=
|
||||
|
||||
### init.lua ###
|
||||
|
||||
Can administer areas.=
|
||||
Can protect areas.=
|
||||
Can protect more, bigger areas.=
|
||||
|
||||
### interact.lua ###
|
||||
|
||||
@1 is protected by @2.=
|
||||
|
||||
### internal.lua ###
|
||||
|
||||
Area is too big.=
|
||||
|
||||
Self protection is disabled or you do not have the necessary privilege.=
|
||||
|
||||
The area intersects with @1 [@2] (@3).=
|
||||
|
||||
You have reached the maximum amount of areas that you are allowed to protect.=
|
||||
|
||||
|
||||
### legacy.lua ###
|
||||
|
||||
<version>=
|
||||
Converted areas saved. Done.=
|
||||
Converting areas…=
|
||||
Error loading legacy file: @1=
|
||||
Invalid version number. (0 allowed)=
|
||||
Legacy file loaded.=
|
||||
|
||||
Loads, converts, and saves the areas from a legacy save file.=
|
||||
|
||||
Table format updated.=
|
||||
unnamed=
|
||||
|
||||
### pos.lua ###
|
||||
|
||||
<not set>=
|
||||
Area @1 selected.=
|
||||
Area position @1 set to @2=
|
||||
Position @1 set to @2=
|
||||
Position @1: =
|
||||
Select an area by ID.=
|
||||
Select position @1 by punching a node.=
|
||||
Select positions by punching two nodes.=
|
||||
|
||||
Set area protection region position @1 to your location or the one specified=
|
||||
|
||||
Set area protection region, position 1, or position 2 by punching nodes, or display the region=
|
||||
|
||||
The area @1 does not exist.=
|
||||
Unable to get position.=
|
||||
Unknown subcommand: @1=
|
6
areas/mod.conf
Normal file
@ -0,0 +1,6 @@
|
||||
name = areas
|
||||
optional_depends = playerfactions
|
||||
release = 5030
|
||||
title = Areas
|
||||
author = ShadowNinja
|
||||
description = Areas is a advanced area protection mod based on node_ownership.
|
265
areas/pos.lua
Normal file
@ -0,0 +1,265 @@
|
||||
local S = minetest.get_translator("areas")
|
||||
|
||||
-- I could depend on WorldEdit for this, but you need to have the 'worldedit'
|
||||
-- permission to use those commands and you don't have
|
||||
-- /area_pos{1,2} [X Y Z|X,Y,Z].
|
||||
-- Since this is mostly copied from WorldEdit it is mostly
|
||||
-- licensed under the AGPL. (select_area is a exception)
|
||||
|
||||
areas.marker1 = {}
|
||||
areas.marker2 = {}
|
||||
areas.set_pos = {}
|
||||
areas.pos1 = {}
|
||||
areas.pos2 = {}
|
||||
|
||||
local LIMIT = 30992 -- this is due to MAPBLOCK_SIZE=16!
|
||||
|
||||
local function posLimit(pos)
|
||||
return {
|
||||
x = math.max(math.min(pos.x, LIMIT), -LIMIT),
|
||||
y = math.max(math.min(pos.y, LIMIT), -LIMIT),
|
||||
z = math.max(math.min(pos.z, LIMIT), -LIMIT)
|
||||
}
|
||||
end
|
||||
|
||||
minetest.register_chatcommand("select_area", {
|
||||
params = S("<ID>"),
|
||||
description = S("Select an area by ID."),
|
||||
func = function(name, param)
|
||||
local id = tonumber(param)
|
||||
if not id then
|
||||
return false, S("Invalid usage, see /help @1.", "select_area")
|
||||
end
|
||||
if not areas.areas[id] then
|
||||
return false, S("The area @1 does not exist.", id)
|
||||
end
|
||||
|
||||
areas:setPos1(name, areas.areas[id].pos1)
|
||||
areas:setPos2(name, areas.areas[id].pos2)
|
||||
return true, S("Area @1 selected.", id)
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_chatcommand("area_pos1", {
|
||||
params = "[X Y Z|X,Y,Z]",
|
||||
description = S("Set area protection region position @1 to your"
|
||||
.." location or the one specified", "1"),
|
||||
privs = {},
|
||||
func = function(name, param)
|
||||
local pos
|
||||
local found, _, x, y, z = param:find(
|
||||
"^(-?%d+)[, ](-?%d+)[, ](-?%d+)$")
|
||||
if found then
|
||||
pos = {x=tonumber(x), y=tonumber(y), z=tonumber(z)}
|
||||
elseif param == "" then
|
||||
local player = minetest.get_player_by_name(name)
|
||||
if player then
|
||||
pos = player:get_pos()
|
||||
else
|
||||
return false, S("Unable to get position.")
|
||||
end
|
||||
else
|
||||
return false, S("Invalid usage, see /help @1.", "area_pos1")
|
||||
end
|
||||
pos = posLimit(vector.round(pos))
|
||||
areas:setPos1(name, pos)
|
||||
return true, S("Area position @1 set to @2", "1",
|
||||
minetest.pos_to_string(pos))
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_chatcommand("area_pos2", {
|
||||
params = "[X Y Z|X,Y,Z]",
|
||||
description = S("Set area protection region position @1 to your"
|
||||
.." location or the one specified", "2"),
|
||||
func = function(name, param)
|
||||
local pos
|
||||
local found, _, x, y, z = param:find(
|
||||
"^(-?%d+)[, ](-?%d+)[, ](-?%d+)$")
|
||||
if found then
|
||||
pos = {x=tonumber(x), y=tonumber(y), z=tonumber(z)}
|
||||
elseif param == "" then
|
||||
local player = minetest.get_player_by_name(name)
|
||||
if player then
|
||||
pos = player:get_pos()
|
||||
else
|
||||
return false, S("Unable to get position.")
|
||||
end
|
||||
else
|
||||
return false, S("Invalid usage, see /help @1.", "area_pos2")
|
||||
end
|
||||
pos = posLimit(vector.round(pos))
|
||||
areas:setPos2(name, pos)
|
||||
return true, S("Area position @1 set to @2", "2",
|
||||
minetest.pos_to_string(pos))
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
minetest.register_chatcommand("area_pos", {
|
||||
params = "set/set1/set2/get",
|
||||
description = S("Set area protection region, position 1, or position 2"
|
||||
.." by punching nodes, or display the region"),
|
||||
func = function(name, param)
|
||||
if param == "set" then -- Set both area positions
|
||||
areas.set_pos[name] = "pos1"
|
||||
return true, S("Select positions by punching two nodes.")
|
||||
elseif param == "set1" then -- Set area position 1
|
||||
areas.set_pos[name] = "pos1only"
|
||||
return true, S("Select position @1 by punching a node.", "1")
|
||||
elseif param == "set2" then -- Set area position 2
|
||||
areas.set_pos[name] = "pos2"
|
||||
return true, S("Select position @1 by punching a node.", "2")
|
||||
elseif param == "get" then -- Display current area positions
|
||||
local pos1str, pos2str = S("Position @1: ", "1"), S("Position @1: ", "2")
|
||||
if areas.pos1[name] then
|
||||
pos1str = pos1str..minetest.pos_to_string(areas.pos1[name])
|
||||
else
|
||||
pos1str = pos1str..S("<not set>")
|
||||
end
|
||||
if areas.pos2[name] then
|
||||
pos2str = pos2str..minetest.pos_to_string(areas.pos2[name])
|
||||
else
|
||||
pos2str = pos2str..S("<not set>")
|
||||
end
|
||||
return true, pos1str.."\n"..pos2str
|
||||
else
|
||||
return false, S("Unknown subcommand: @1", param)
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
function areas:getPos(playerName)
|
||||
local pos1, pos2 = areas.pos1[playerName], areas.pos2[playerName]
|
||||
if not (pos1 and pos2) then
|
||||
return nil
|
||||
end
|
||||
-- Copy positions so that the area table doesn't contain multiple
|
||||
-- references to the same position.
|
||||
pos1, pos2 = vector.new(pos1), vector.new(pos2)
|
||||
return areas:sortPos(pos1, pos2)
|
||||
end
|
||||
|
||||
function areas:setPos1(playerName, pos)
|
||||
areas.pos1[playerName] = posLimit(pos)
|
||||
areas.markPos1(playerName)
|
||||
end
|
||||
|
||||
function areas:setPos2(playerName, pos)
|
||||
areas.pos2[playerName] = posLimit(pos)
|
||||
areas.markPos2(playerName)
|
||||
end
|
||||
|
||||
|
||||
minetest.register_on_punchnode(function(pos, node, puncher)
|
||||
local name = puncher:get_player_name()
|
||||
-- Currently setting position
|
||||
if name ~= "" and areas.set_pos[name] then
|
||||
if areas.set_pos[name] == "pos1" then
|
||||
areas.pos1[name] = pos
|
||||
areas.markPos1(name)
|
||||
areas.set_pos[name] = "pos2"
|
||||
minetest.chat_send_player(name,
|
||||
S("Position @1 set to @2", "1",
|
||||
minetest.pos_to_string(pos)))
|
||||
elseif areas.set_pos[name] == "pos1only" then
|
||||
areas.pos1[name] = pos
|
||||
areas.markPos1(name)
|
||||
areas.set_pos[name] = nil
|
||||
minetest.chat_send_player(name,
|
||||
S("Position @1 set to @2", "1",
|
||||
minetest.pos_to_string(pos)))
|
||||
elseif areas.set_pos[name] == "pos2" then
|
||||
areas.pos2[name] = pos
|
||||
areas.markPos2(name)
|
||||
areas.set_pos[name] = nil
|
||||
minetest.chat_send_player(name,
|
||||
S("Position @1 set to @2", "2",
|
||||
minetest.pos_to_string(pos)))
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- Modifies positions `pos1` and `pos2` so that each component of `pos1`
|
||||
-- is less than or equal to its corresponding component of `pos2`,
|
||||
-- returning the two positions.
|
||||
function areas:sortPos(pos1, pos2)
|
||||
if pos1.x > pos2.x then
|
||||
pos2.x, pos1.x = pos1.x, pos2.x
|
||||
end
|
||||
if pos1.y > pos2.y then
|
||||
pos2.y, pos1.y = pos1.y, pos2.y
|
||||
end
|
||||
if pos1.z > pos2.z then
|
||||
pos2.z, pos1.z = pos1.z, pos2.z
|
||||
end
|
||||
return pos1, pos2
|
||||
end
|
||||
|
||||
-- Marks area position 1
|
||||
areas.markPos1 = function(name)
|
||||
local pos = areas.pos1[name]
|
||||
if areas.marker1[name] ~= nil then -- Marker already exists
|
||||
areas.marker1[name]:remove() -- Remove marker
|
||||
areas.marker1[name] = nil
|
||||
end
|
||||
if pos ~= nil then -- Add marker
|
||||
areas.marker1[name] = minetest.add_entity(pos, "areas:pos1")
|
||||
areas.marker1[name]:get_luaentity().active = true
|
||||
end
|
||||
end
|
||||
|
||||
-- Marks area position 2
|
||||
areas.markPos2 = function(name)
|
||||
local pos = areas.pos2[name]
|
||||
if areas.marker2[name] ~= nil then -- Marker already exists
|
||||
areas.marker2[name]:remove() -- Remove marker
|
||||
areas.marker2[name] = nil
|
||||
end
|
||||
if pos ~= nil then -- Add marker
|
||||
areas.marker2[name] = minetest.add_entity(pos, "areas:pos2")
|
||||
areas.marker2[name]:get_luaentity().active = true
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_entity("areas:pos1", {
|
||||
initial_properties = {
|
||||
visual = "cube",
|
||||
visual_size = {x=1.1, y=1.1},
|
||||
textures = {"areas_pos1.png", "areas_pos1.png",
|
||||
"areas_pos1.png", "areas_pos1.png",
|
||||
"areas_pos1.png", "areas_pos1.png"},
|
||||
collisionbox = {-0.55, -0.55, -0.55, 0.55, 0.55, 0.55},
|
||||
},
|
||||
on_step = function(self, dtime)
|
||||
if self.active == nil then
|
||||
self.object:remove()
|
||||
end
|
||||
end,
|
||||
on_punch = function(self, hitter)
|
||||
self.object:remove()
|
||||
local name = hitter:get_player_name()
|
||||
areas.marker1[name] = nil
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_entity("areas:pos2", {
|
||||
initial_properties = {
|
||||
visual = "cube",
|
||||
visual_size = {x=1.1, y=1.1},
|
||||
textures = {"areas_pos2.png", "areas_pos2.png",
|
||||
"areas_pos2.png", "areas_pos2.png",
|
||||
"areas_pos2.png", "areas_pos2.png"},
|
||||
collisionbox = {-0.55, -0.55, -0.55, 0.55, 0.55, 0.55},
|
||||
},
|
||||
on_step = function(self, dtime)
|
||||
if self.active == nil then
|
||||
self.object:remove()
|
||||
end
|
||||
end,
|
||||
on_punch = function(self, hitter)
|
||||
self.object:remove()
|
||||
local name = hitter:get_player_name()
|
||||
areas.marker2[name] = nil
|
||||
end,
|
||||
})
|
45
areas/settings.lua
Normal file
@ -0,0 +1,45 @@
|
||||
local world_path = minetest.get_worldpath()
|
||||
|
||||
areas.config = {}
|
||||
|
||||
local function setting(name, tp, default)
|
||||
local full_name = "areas." .. name
|
||||
local value
|
||||
if tp == "bool" then
|
||||
value = minetest.settings:get_bool(full_name)
|
||||
default = value == nil and minetest.is_yes(default)
|
||||
elseif tp == "string" then
|
||||
value = minetest.settings:get(full_name)
|
||||
elseif tp == "v3f" then
|
||||
value = minetest.setting_get_pos(full_name)
|
||||
default = value == nil and minetest.string_to_pos(default)
|
||||
elseif tp == "float" or tp == "int" then
|
||||
value = tonumber(minetest.settings:get(full_name))
|
||||
local v, other = default:match("^(%S+) (.+)")
|
||||
default = value == nil and tonumber(other and v or default)
|
||||
else
|
||||
error("Cannot parse setting type " .. tp)
|
||||
end
|
||||
|
||||
if value == nil then
|
||||
value = default
|
||||
assert(default ~= nil, "Cannot parse default for " .. full_name)
|
||||
end
|
||||
--print("add", name, default, value)
|
||||
areas.config[name] = value
|
||||
end
|
||||
|
||||
local file = io.open(areas.modpath .. "/settingtypes.txt", "r")
|
||||
for line in file:lines() do
|
||||
local name, tp, value = line:match("^areas%.(%S+) %(.*%) (%S+) (.*)")
|
||||
if value then
|
||||
setting(name, tp, value)
|
||||
end
|
||||
end
|
||||
file:close()
|
||||
|
||||
--------------
|
||||
-- Settings --
|
||||
--------------
|
||||
|
||||
setting("filename", "string", world_path.."/areas.dat")
|
37
areas/settingtypes.txt
Normal file
@ -0,0 +1,37 @@
|
||||
# This file is parsed in "settings.lua". Check regex first.
|
||||
|
||||
# Static paths do not work well with settings
|
||||
#areas.filename (Configuration file path) string (world_path)/areas.dat
|
||||
|
||||
# Allow players with a privilege create their own areas using /protect
|
||||
# within the specified size and amount limits.
|
||||
areas.self_protection (Self protection) bool false
|
||||
|
||||
# Self protection: Privilege required to protect an area
|
||||
areas.self_protection_privilege (Self protection: Required privs) string interact
|
||||
|
||||
# Refresh delay for the name displays in the HUD in seconds
|
||||
areas.tick (HUD update delay) float 0.5 0 100
|
||||
|
||||
# Enable the legacy owner_defs metatable mode. Untested and possibly unstable
|
||||
areas.legacy_table (Legacy owner_defs metatable) bool false
|
||||
|
||||
[Self protection (normal)]
|
||||
|
||||
# Self protection (normal): Maximal size of the protectable area
|
||||
# Only enter positive whole numbers for the coordinate values or you'll mess up stuff.
|
||||
areas.self_protection_max_size (Maximal area size) v3f (64, 128, 64)
|
||||
|
||||
# Self protection (normal): Maximal amount of protected areas per player
|
||||
areas.self_protection_max_areas (Maximal area count) int 4
|
||||
|
||||
[Self protection (high)]
|
||||
|
||||
# Self protection (normal): Maximal size of the protectable area
|
||||
# This setting applies for plyaers with the privilege 'areas_high_limit'
|
||||
areas.self_protection_max_size_high (Maximal area size) v3f (512, 512, 512)
|
||||
|
||||
# Self protection (normal): Maximal amount of protected areas per player
|
||||
# Only enter positive whole numbers for the coordinate values or you'll mess up stuff.
|
||||
# This setting applies for plyaers with the privilege 'areas_high_limit'
|
||||
areas.self_protection_max_areas_high (Maximal area count) float 32
|
BIN
areas/textures/areas_pos1.png
Normal file
After Width: | Height: | Size: 142 B |
BIN
areas/textures/areas_pos2.png
Normal file
After Width: | Height: | Size: 157 B |
11
locked_travelnet/README.md
Normal file
@ -0,0 +1,11 @@
|
||||
|
||||
Shared locked travelnet
|
||||
|
||||
A travelnet for PvP-server. Please don't use on servers dedicated to building!
|
||||
This is really only good if there is a very special need for private travelnets.
|
||||
|
||||
Liscence: GPLv3
|
||||
|
||||
Version: 1.0
|
||||
|
||||
Autor: Sokomine
|
2
locked_travelnet/depends.txt
Normal file
@ -0,0 +1,2 @@
|
||||
locks
|
||||
travelnet
|
30
locked_travelnet/init.lua
Normal file
@ -0,0 +1,30 @@
|
||||
|
||||
|
||||
--[[
|
||||
Locked version of my travelnet that allow players to choose a destination out of a list
|
||||
Copyright (C) 2013 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.0
|
||||
|
||||
Changelog:
|
||||
--]]
|
||||
|
||||
|
||||
dofile(minetest.get_modpath("locked_travelnet").."/locked_travelnet.lua");
|
||||
-- does not work very satisfying yet
|
||||
--dofile(minetest.get_modpath("locked_travelnet").."/locked_elevator.lua");
|
||||
-- not yet useful; right-click opens config menu + door...
|
||||
--dofile(minetest.get_modpath("locked_travelnet").."/locked_doors.lua");
|
225
locked_travelnet/locked_doors.lua
Normal file
@ -0,0 +1,225 @@
|
||||
-- Doors that are especially useful for travelnet elevators but can also be used in other situations.
|
||||
-- All doors (not only these here) in front of a travelnet or elevator are opened automaticly when a player arrives
|
||||
-- and are closed when a player departs from the travelnet or elevator.
|
||||
-- Autor: Sokomine
|
||||
|
||||
locked_travelnet_doors_transform = function( pos, node, puncher, transform_into )
|
||||
|
||||
if( not( locks:lock_allow_use( pos, puncher ))) then
|
||||
minetest.chat_send_player( puncher:get_player_name(), "This door is locked. It can only be opened by its owner or people with a key that fits.");
|
||||
return;
|
||||
end
|
||||
|
||||
local olddata = locks:get_lockdata( pos );
|
||||
|
||||
minetest.env:add_node(pos, {name = transform_into, param2 = node.param2})
|
||||
locks:set_lockdata( pos, olddata );
|
||||
end
|
||||
|
||||
|
||||
|
||||
minetest.register_node("locked_travelnet:elevator_door_steel_open", {
|
||||
description = "elevator door (open)",
|
||||
drawtype = "nodebox",
|
||||
-- top, bottom, side1, side2, inner, outer
|
||||
tiles = {"default_stone.png"},
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
is_ground_content = true,
|
||||
groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2,not_in_creative_inventory=1},
|
||||
-- larger than one node but slightly smaller than a half node so that wallmounted torches pose no problem
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-0.90, -0.5, 0.4, -0.49, 1.5, 0.5},
|
||||
{ 0.49, -0.5, 0.4, 0.9, 1.5, 0.5},
|
||||
},
|
||||
},
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-0.9, -0.5, 0.4, 0.9, 1.5, 0.5},
|
||||
},
|
||||
},
|
||||
drop = "locked_travelnet:elevator_door_steel_closed",
|
||||
on_rightclick = function(pos, node, puncher)
|
||||
|
||||
locked_travelnet_doors_transform( pos, node, puncher, "locked_travelnet:elevator_door_steel_closed" );
|
||||
end,
|
||||
|
||||
on_construct = function(pos)
|
||||
locks:lock_init( pos,
|
||||
"size[8,2]"..
|
||||
"field[0.3,0.6;6,0.7;locks_sent_lock_command;Locked door. Type /help for help:;]"..
|
||||
"button_exit[6.3,1.2;1.7,0.7;locks_sent_input;Proceed]" );
|
||||
end,
|
||||
|
||||
after_place_node = function(pos, placer, itemstack)
|
||||
locks:lock_set_owner( pos, placer, "Shared locked door" );
|
||||
end,
|
||||
|
||||
on_receive_fields = function(pos, formname, fields, sender)
|
||||
locks:lock_handle_input( pos, formname, fields, sender );
|
||||
end,
|
||||
|
||||
can_dig = function(pos,player)
|
||||
return locks:lock_allow_dig( pos, player );
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_node("locked_travelnet:elevator_door_steel_closed", {
|
||||
description = "elevator door (closed)",
|
||||
drawtype = "nodebox",
|
||||
-- top, bottom, side1, side2, inner, outer
|
||||
tiles = {"default_stone.png"},
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
is_ground_content = true,
|
||||
groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2},
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-0.5, -0.5, 0.4, -0.01, 1.5, 0.5},
|
||||
{ 0.01, -0.5, 0.4, 0.5, 1.5, 0.5},
|
||||
},
|
||||
},
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-0.5, -0.5, 0.4, 0.5, 1.5, 0.5},
|
||||
},
|
||||
},
|
||||
on_rightclick = function(pos, node, puncher)
|
||||
locked_travelnet_doors_transform( pos, node, puncher, "locked_travelnet:elevator_door_steel_open" );
|
||||
end,
|
||||
on_construct = function(pos)
|
||||
locks:lock_init( pos,
|
||||
"size[8,2]"..
|
||||
"field[0.3,0.6;6,0.7;locks_sent_lock_command;Locked door. Type /help for help:;]"..
|
||||
"button_exit[6.3,1.2;1.7,0.7;locks_sent_input;Proceed]" );
|
||||
end,
|
||||
|
||||
after_place_node = function(pos, placer, itemstack)
|
||||
locks:lock_set_owner( pos, placer, "Shared locked door" );
|
||||
end,
|
||||
|
||||
on_receive_fields = function(pos, formname, fields, sender)
|
||||
locks:lock_handle_input( pos, formname, fields, sender );
|
||||
end,
|
||||
|
||||
can_dig = function(pos,player)
|
||||
return locks:lock_allow_dig( pos, player );
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
minetest.register_node("locked_travelnet:elevator_door_glass_open", {
|
||||
description = "elevator door (open)",
|
||||
drawtype = "nodebox",
|
||||
-- top, bottom, side1, side2, inner, outer
|
||||
tiles = {"travelnet_elevator_door_glass.png"},
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
is_ground_content = true,
|
||||
groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2,not_in_creative_inventory=1},
|
||||
-- larger than one node but slightly smaller than a half node so that wallmounted torches pose no problem
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-0.99, -0.5, 0.4, -0.49, 1.5, 0.5},
|
||||
{ 0.49, -0.5, 0.4, 0.99, 1.5, 0.5},
|
||||
},
|
||||
},
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-0.9, -0.5, 0.4, 0.9, 1.5, 0.5},
|
||||
},
|
||||
},
|
||||
drop = "locked_travelnet:elevator_door_glass_closed",
|
||||
on_rightclick = function(pos, node, puncher)
|
||||
locked_travelnet_doors_transform( pos, node, puncher, "locked_travelnet:elevator_door_glass_closed" );
|
||||
end,
|
||||
on_construct = function(pos)
|
||||
locks:lock_init( pos,
|
||||
"size[8,2]"..
|
||||
"field[0.3,0.6;6,0.7;locks_sent_lock_command;Locked door. Type /help for help:;]"..
|
||||
"button_exit[6.3,1.2;1.7,0.7;locks_sent_input;Proceed]" );
|
||||
end,
|
||||
|
||||
after_place_node = function(pos, placer, itemstack)
|
||||
locks:lock_set_owner( pos, placer, "Shared locked door" );
|
||||
end,
|
||||
|
||||
on_receive_fields = function(pos, formname, fields, sender)
|
||||
locks:lock_handle_input( pos, formname, fields, sender );
|
||||
end,
|
||||
|
||||
can_dig = function(pos,player)
|
||||
return locks:lock_allow_dig( pos, player );
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_node("locked_travelnet:elevator_door_glass_closed", {
|
||||
description = "elevator door (closed)",
|
||||
drawtype = "nodebox",
|
||||
-- top, bottom, side1, side2, inner, outer
|
||||
tiles = {"travelnet_elevator_door_glass.png"},
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
is_ground_content = true,
|
||||
groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2},
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-0.5, -0.5, 0.4, -0.01, 1.5, 0.5},
|
||||
{ 0.01, -0.5, 0.4, 0.5, 1.5, 0.5},
|
||||
},
|
||||
},
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-0.5, -0.5, 0.4, 0.5, 1.5, 0.5},
|
||||
},
|
||||
},
|
||||
on_rightclick = function(pos, node, puncher)
|
||||
locked_travelnet_doors_transform( pos, node, puncher, "locked_travelnet:elevator_door_glass_open" );
|
||||
end,
|
||||
on_construct = function(pos)
|
||||
locks:lock_init( pos,
|
||||
"size[8,2]"..
|
||||
"field[0.3,0.6;6,0.7;locks_sent_lock_command;Locked door. Type /help for help:;]"..
|
||||
"button_exit[6.3,1.2;1.7,0.7;locks_sent_input;Proceed]" );
|
||||
end,
|
||||
|
||||
after_place_node = function(pos, placer, itemstack)
|
||||
locks:lock_set_owner( pos, placer, "Shared locked door" );
|
||||
end,
|
||||
|
||||
on_receive_fields = function(pos, formname, fields, sender)
|
||||
locks:lock_handle_input( pos, formname, fields, sender );
|
||||
end,
|
||||
|
||||
can_dig = function(pos,player)
|
||||
return locks:lock_allow_dig( pos, player );
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "locked_travelnet:elevator_door_glass_closed",
|
||||
recipe = {{'travelnet:elevator_door_glass_closed','locks:lock'},
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "locked_travelnet:elevator_door_steel_closed",
|
||||
recipe = {
|
||||
{'travelnet:elevator_door_steel_closed','locks:lock'},
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
print( "[Mod] locked_travelnet: loading locked_travelnet:elevator_door_xxx_closed (steel and glass)");
|
||||
|
183
locked_travelnet/locked_elevator.lua
Normal file
@ -0,0 +1,183 @@
|
||||
-- This version of the travelnet box allows to move up or down only.
|
||||
-- The network name is determined automaticly from the position (x/z coordinates).
|
||||
-- Autor: Sokomine
|
||||
|
||||
minetest.register_node("locked_travelnet:elevator", {
|
||||
description = "Shared locked elevator",
|
||||
|
||||
drawtype = "nodebox",
|
||||
sunlight_propagates = true,
|
||||
paramtype = 'light',
|
||||
paramtype2 = "facedir",
|
||||
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = { -0.5, -0.5, -0.5, 0.5, 1.5, 0.5 }
|
||||
},
|
||||
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
|
||||
{ 0.48, -0.5,-0.5, 0.5, 0.5, 0.5},
|
||||
{-0.5 , -0.5, 0.48, 0.48, 0.5, 0.5},
|
||||
{-0.5, -0.5,-0.5 ,-0.48, 0.5, 0.5},
|
||||
|
||||
--groundplate to stand on
|
||||
{ -0.5,-0.5,-0.5,0.5,-0.48, 0.5},
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
tiles = {
|
||||
|
||||
"travelnet_elevator_inside_floor.png", -- view from top
|
||||
"default_stone.png", -- view from bottom
|
||||
"travelnet_elevator_inside_bottom.png", -- left side
|
||||
"travelnet_elevator_inside_bottom.png", -- right side
|
||||
"travelnet_elevator_inside_bottom.png", -- front view
|
||||
"travelnet_elevator_inside_bottom.png", -- backward view
|
||||
},
|
||||
inventory_image = "travelnet_elevator_inv.png",
|
||||
wield_image = "travelnet_elevator_wield.png",
|
||||
|
||||
groups = {}, --cracky=1,choppy=1,snappy=1},
|
||||
|
||||
|
||||
light_source = 10,
|
||||
|
||||
on_construct = function(pos)
|
||||
local meta = minetest.env:get_meta(pos)
|
||||
--- prepare the lock of the travelnet
|
||||
locks:lock_init( pos,
|
||||
"size[12,10]"..
|
||||
"field[0.3,5.6;6,0.7;station_name;Name of this station:;]"..
|
||||
-- "button_exit[6.3,6.2;1.7,0.7;station_set;Store]"..
|
||||
"button_exit[8.0,0.0;2.2,0.7;station_dig;Remove station]"..
|
||||
"field[0.3,3.0;6,0.7;locks_sent_lock_command;Locked travelnet. Type /help for help:;]"..
|
||||
"button_exit[6.3,3.2;1.7,0.7;locks_sent_input;Store]" );
|
||||
end,
|
||||
|
||||
after_place_node = function(pos, placer, itemstack)
|
||||
local meta = minetest.env:get_meta(pos);
|
||||
meta:set_string("infotext", "Elevator (unconfigured)");
|
||||
meta:set_string("station_name", "");
|
||||
meta:set_string("station_network","");
|
||||
meta:set_string("owner", placer:get_player_name() );
|
||||
-- request initinal data
|
||||
|
||||
local p = {x=pos.x, y=pos.y+1, z=pos.z}
|
||||
local p2 = minetest.dir_to_facedir(placer:get_look_dir())
|
||||
minetest.env:add_node(p, {name="locked_travelnet:elevator_top", paramtype2="facedir", param2=p2})
|
||||
|
||||
locks:lock_set_owner( pos, placer, "Shared locked elevator" );
|
||||
end,
|
||||
|
||||
on_receive_fields = function(pos, formname, fields, sender)
|
||||
|
||||
-- abort if no input has been sent
|
||||
if( fields.quit ) then
|
||||
return;
|
||||
end
|
||||
|
||||
-- if the user already has the right to use this and did input text
|
||||
if( (not(fields.locks_sent_lock_command)
|
||||
or fields.locks_sent_lock_command=="")
|
||||
and locks:lock_allow_use( pos, sender )) then
|
||||
|
||||
travelnet.on_receive_fields( pos, formname, fields, sender );
|
||||
|
||||
-- a command for the lock?
|
||||
else
|
||||
locks:lock_handle_input( pos, formname, fields, sender );
|
||||
end
|
||||
end,
|
||||
|
||||
on_punch = function(pos, node, puncher)
|
||||
travelnet.update_formspec(pos, puncher:get_player_name())
|
||||
end,
|
||||
|
||||
can_dig = function( pos, player )
|
||||
if( not(locks:lock_allow_dig( pos, player ))) then
|
||||
return false;
|
||||
end
|
||||
return travelnet.can_dig( pos, player, 'elevator' )
|
||||
end,
|
||||
|
||||
after_dig_node = function(pos, oldnode, oldmetadata, digger)
|
||||
travelnet.remove_box( pos, oldnode, oldmetadata, digger )
|
||||
end,
|
||||
|
||||
-- taken from VanessaEs homedecor fridge
|
||||
on_place = function(itemstack, placer, pointed_thing)
|
||||
local pos = pointed_thing.above;
|
||||
local node = minetest.env:get_node({x=pos.x, y=pos.y+1, z=pos.z});
|
||||
-- leftover elevator_top nodes can be removed by placing a new elevator underneath
|
||||
if( node ~= nil and node.name ~= "air" and node.name ~= 'locked_travelnet:elevator_top') then
|
||||
minetest.chat_send_player( placer:get_player_name(), 'Not enough vertical space to place the travelnet box!' )
|
||||
return;
|
||||
end
|
||||
return minetest.item_place(itemstack, placer, pointed_thing);
|
||||
end,
|
||||
|
||||
on_destruct = function(pos)
|
||||
local p = {x=pos.x, y=pos.y+1, z=pos.z}
|
||||
minetest.env:remove_node(p)
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_node("locked_travelnet:elevator_top", {
|
||||
description = "Elevator Top",
|
||||
|
||||
drawtype = "nodebox",
|
||||
sunlight_propagates = true,
|
||||
paramtype = 'light',
|
||||
paramtype2 = "facedir",
|
||||
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = { 0, 0, 0, 0, 0, 0 }
|
||||
-- fixed = { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 }
|
||||
},
|
||||
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
|
||||
{ 0.48, -0.5,-0.5, 0.5, 0.5, 0.5},
|
||||
{-0.5 , -0.5, 0.48, 0.48, 0.5, 0.5},
|
||||
{-0.5, -0.5,-0.5 ,-0.48, 0.5, 0.5},
|
||||
|
||||
--top ceiling
|
||||
{ -0.5, 0.48,-0.5,0.5, 0.5, 0.5},
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
tiles = {
|
||||
|
||||
"default_stone.png", -- view from top
|
||||
"travelnet_elevator_inside_ceiling.png", -- view from bottom
|
||||
"travelnet_elevator_inside_top_control.png", -- left side
|
||||
"travelnet_elevator_inside_top.png", -- right side
|
||||
"travelnet_elevator_inside_top.png", -- front view
|
||||
"travelnet_elevator_inside_top.png", -- backward view
|
||||
},
|
||||
inventory_image = "travelnet_elevator_inv.png",
|
||||
wield_image = "travelnet_elevator_wield.png",
|
||||
|
||||
light_source = 10,
|
||||
|
||||
groups = {}, --cracky=1,choppy=1,snappy=1,not_in_creative_inventory=1},
|
||||
})
|
||||
|
||||
|
||||
|
||||
minetest.register_craft({
|
||||
output = 'locked_travelnet:elevator',
|
||||
recipe = {
|
||||
{ 'travelnet:elevator', 'locks:lock' },
|
||||
},
|
||||
})
|
||||
|
||||
print( "[Mod] locked_travelnet: loading locked_travelnet:elevator");
|
128
locked_travelnet/locked_travelnet.lua
Normal file
@ -0,0 +1,128 @@
|
||||
-- contains the node definition for a general travelnet that can be used by anyone
|
||||
-- further travelnets can only be installed by the owner or by people with the travelnet_attach priv
|
||||
-- digging of such a travelnet is limited to the owner and to people with the travelnet_remove priv (useful for admins to clean up)
|
||||
-- (this can be overrided in config.lua)
|
||||
-- Autor: Sokomine
|
||||
minetest.register_node("locked_travelnet:travelnet", {
|
||||
|
||||
description = "Shared locked travelnet box",
|
||||
|
||||
drawtype = "nodebox",
|
||||
sunlight_propagates = true,
|
||||
paramtype = 'light',
|
||||
paramtype2 = "facedir",
|
||||
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = { -0.5, -0.5, -0.5, 0.5, 1.5, 0.5 }
|
||||
},
|
||||
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
|
||||
{ 0.45, -0.5,-0.5, 0.5, 1.45, 0.5},
|
||||
{-0.5 , -0.5, 0.45, 0.45, 1.45, 0.5},
|
||||
{-0.5, -0.5,-0.5 ,-0.45, 1.45, 0.5},
|
||||
|
||||
--groundplate to stand on
|
||||
{ -0.5,-0.5,-0.5,0.5,-0.45, 0.5},
|
||||
--roof
|
||||
{ -0.5, 1.45,-0.5,0.5, 1.5, 0.5},
|
||||
|
||||
},
|
||||
},
|
||||
|
||||
tiles = {
|
||||
"default_clay.png", -- view from top
|
||||
"default_clay.png", -- view from bottom
|
||||
"locked_travelnet_travelnet_side_lock.png", -- left side
|
||||
"locked_travelnet_travelnet_side_lock.png", -- right side
|
||||
"locked_travelnet_travelnet_back_lock.png", -- front view
|
||||
"locked_travelnet_travelnet_front_lock.png", -- backward view
|
||||
},
|
||||
inventory_image = "locked_travelnet_lock_inv.png",
|
||||
|
||||
groups = {}, --cracky=1,choppy=1,snappy=1},
|
||||
|
||||
light_source = 10,
|
||||
|
||||
on_construct = function(pos)
|
||||
local meta = minetest.env:get_meta(pos)
|
||||
--- prepare the lock of the travelnet
|
||||
locks:lock_init( pos,
|
||||
"size[12,10]"..
|
||||
"field[0.3,5.6;6,0.7;station_name;Name of this station:;]"..
|
||||
"field[0.3,6.6;6,0.7;station_network;Assign to Network:;]"..
|
||||
"field[0.3,7.6;6,0.7;owner_name;(optional) owned by:;]"..
|
||||
"button_exit[8.0,0.0;2.2,0.7;station_dig;Remove station]"..
|
||||
-- "button_exit[6.3,6.2;1.7,0.7;station_set;Store]"..
|
||||
"field[0.3,3.0;6,0.7;locks_sent_lock_command;Locked travelnet. Type /help for help:;]"..
|
||||
"button_exit[6.3,3.2;1.7,0.7;locks_sent_input;Store]" );
|
||||
end,
|
||||
|
||||
after_place_node = function(pos, placer, itemstack)
|
||||
local meta = minetest.env:get_meta(pos);
|
||||
meta:set_string("infotext", "Travelnet-box (unconfigured)");
|
||||
meta:set_string("station_name", "");
|
||||
meta:set_string("station_network","");
|
||||
meta:set_string("owner", placer:get_player_name() );
|
||||
-- request initinal data
|
||||
locks:lock_set_owner( pos, placer, "Shared locked travelnet" );
|
||||
end,
|
||||
|
||||
on_receive_fields = function(pos, formname, fields, sender)
|
||||
|
||||
-- if the user already has the right to use this and did input text
|
||||
if( (not(fields.locks_sent_lock_command)
|
||||
or fields.locks_sent_lock_command=="")
|
||||
and locks:lock_allow_use( pos, sender )) then
|
||||
|
||||
travelnet.on_receive_fields( pos, formname, fields, sender );
|
||||
|
||||
-- a command for the lock?
|
||||
else
|
||||
locks:lock_handle_input( pos, formname, fields, sender );
|
||||
end
|
||||
end,
|
||||
|
||||
on_punch = function(pos, node, puncher)
|
||||
travelnet.update_formspec(pos, puncher:get_player_name())
|
||||
end,
|
||||
|
||||
can_dig = function( pos, player )
|
||||
|
||||
if( not(locks:lock_allow_dig( pos, player ))) then
|
||||
return false;
|
||||
end
|
||||
|
||||
return travelnet.can_dig( pos, player, 'travelnet box' )
|
||||
end,
|
||||
|
||||
after_dig_node = function(pos, oldnode, oldmetadata, digger)
|
||||
travelnet.remove_box( pos, oldnode, oldmetadata, digger )
|
||||
end,
|
||||
|
||||
-- taken from VanessaEs homedecor fridge
|
||||
on_place = function(itemstack, placer, pointed_thing)
|
||||
|
||||
local pos = pointed_thing.above;
|
||||
if( minetest.env:get_node({x=pos.x, y=pos.y+1, z=pos.z}).name ~= "air" ) then
|
||||
|
||||
minetest.chat_send_player( placer:get_player_name(), 'Not enough vertical space to place the travelnet box!' )
|
||||
return;
|
||||
end
|
||||
return minetest.item_place(itemstack, placer, pointed_thing);
|
||||
end,
|
||||
|
||||
})
|
||||
|
||||
|
||||
minetest.register_craft({
|
||||
output = 'locked_travelnet:travelnet',
|
||||
recipe = {
|
||||
{ 'travelnet:travelnet', 'locks:lock' },
|
||||
},
|
||||
})
|
||||
|
||||
print( "[Mod] locked_travelnet: loading locked_travelnet:travelnet");
|
5
locked_travelnet/mod.conf
Normal file
@ -0,0 +1,5 @@
|
||||
description = Protect travelnet stations with locks and/or passwords
|
||||
author = Sokomine
|
||||
title = Locked Travelnet
|
||||
release = 1159
|
||||
name = locked_travelnet
|
BIN
locked_travelnet/textures/locked_travelnet_lock_inv.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 323 B |
After Width: | Height: | Size: 277 B |
After Width: | Height: | Size: 553 B |
36
locks/README.md
Normal file
@ -0,0 +1,36 @@
|
||||
This Mod for Minetest adds objects that can be locked and shared.
|
||||
|
||||
It is written so that other objects/mods can easily use the functions provided here.
|
||||
|
||||
Comes with modified chest, sign and xyz' xdoor2 as sample objects.
|
||||
New: Furnaces added.
|
||||
For the (unmodified) xdoors2, see http://minetest.net/forum/viewtopic.php?id=2757. Chest and sign take their textures out of default.
|
||||
The textures (lock, key and keychain) have been provided by Addi. Please consult textures/licence.txt.
|
||||
The code of the lock mod has been written by Sokomine.
|
||||
|
||||
A player may open/use a shared locked object if he/she is/has...
|
||||
- the owner
|
||||
- diglocks priv (may dig up shared locked objects)
|
||||
- openlocks priv (object may only be used - i.e. take something out of a chest, open a door - not digged up!)
|
||||
- has been added by the owner with the /add playername command
|
||||
- member of a playergroup that has been added with /add :playergroupname command (provided the playergroups mod is installed)
|
||||
- typed in the correct password that the owner did set with /set thisisthepassword
|
||||
|
||||
Adds lock, key and keychain.
|
||||
- lock: craftitem to create shared locked objects (chests, signs, doors)
|
||||
- key: needed to open your own shared locked objects (a keychain is ok as well)
|
||||
- keychain: needed to open the shared locked objects of other players
|
||||
The tools do not have to be wielded. They just have to be in the players' inventory.
|
||||
Keys and keychains are not specific to a selected lock. They fit all locks - provided the player may open/use the object.
|
||||
Thus it is sufficient to carry around only one keychain to open all shared locked objects the player has access to.
|
||||
|
||||
If you do not want any of the objects chest, sign and/or door, just remove the corresponding lines from the init.lua:
|
||||
dofile(minetest.get_modpath("locks").."/shared_locked_chest.lua");
|
||||
dofile(minetest.get_modpath("locks").."/shared_locked_sign_wall.lua");
|
||||
dofile(minetest.get_modpath("locks").."/shared_locked_xdoors2.lua");
|
||||
dofile(minetest.get_modpath("locks").."/shared_locked_furnace.lua");
|
||||
|
||||
I hope this mod will be helpful.
|
||||
|
||||
Sokomine
|
||||
|
2
locks/depends.txt
Normal file
@ -0,0 +1,2 @@
|
||||
default
|
||||
pipeworks?
|
803
locks/init.lua
Normal file
@ -0,0 +1,803 @@
|
||||
--[[
|
||||
Shared locked objects (Mod for MineTest)
|
||||
Allows to restrict usage of blocks to a certain player or a group of
|
||||
players.
|
||||
Copyright (C) 2013 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 2.10
|
||||
|
||||
-- Changelog:
|
||||
-- 30.07.2018 * Merged PR from adrido from 25.02.2016: Added new Locks config Formspec.
|
||||
-- 30.07.2018 * Removed deprecated minetest.env usage.
|
||||
-- 30.07.2018 * Front side of chest does not get pipeworks image anymore.
|
||||
-- Instead it always shows the locks texture as overlay.
|
||||
-- 30.07.2018 * Fixed bug with pipeworks.
|
||||
-- * Repaired password.
|
||||
-- * Converted back to unix file format.
|
||||
-- 08.05.2014 * Changed animation of shared locked furnace (removed pipeworks overlay on front, changed to new animation type)
|
||||
-- 10.01.2013 * Added command to toggle for pipeworks output
|
||||
-- * Added pipeworks support for chests and furnace.
|
||||
-- 17.12.2013 * aborting input with ESC is possible again
|
||||
-- 01.09.2013 * fixed bug in input sanitization
|
||||
-- 31.08.2013 * changed receipe for key to avoid crafting conflickt with screwdriver
|
||||
-- 10.07.2013 * removed a potential bug (now uses string:gmatch)
|
||||
-- * added shared locked furnaces
|
||||
|
||||
|
||||
|
||||
locks = {};
|
||||
|
||||
minetest.register_privilege("openlocks", { description = "allows to open/use all locked objects", give_to_singleplayer = false});
|
||||
minetest.register_privilege("diglocks", { description = "allows to open/use and dig up all locked objects", give_to_singleplayer = false});
|
||||
|
||||
|
||||
locks.config_button = [[
|
||||
image_button[%s,%s;1,1;locks_lock16.png;locks_config;Config
|
||||
Locks]
|
||||
tooltip[locks_config;Configure the players or set the password to grant access to other players.]
|
||||
]]
|
||||
|
||||
function locks.get_config_button(x,y)
|
||||
return locks.config_button:format((x or 0), (y or 0))
|
||||
end
|
||||
|
||||
locks.authorize_button = [[
|
||||
image_button[%s,%s;1,1;locks_key16.png;locks_authorize;Autho-
|
||||
rize]
|
||||
tooltip[locks_authorize;Opens a password prompt to grant you access to this object.]
|
||||
]]
|
||||
function locks.get_authorize_button(x,y)
|
||||
return locks.authorize_button:format((x or 1), (y or 0))
|
||||
end
|
||||
|
||||
locks.password_prompt = [[
|
||||
size[6,3;]
|
||||
pwdfield[0.5,1;5.5,0;password;Enter password:]
|
||||
tooltip[password;Opens a password prompt to grant you access to this object.]
|
||||
|
||||
box[0.1,1.5;5.5,0.05;#FFFFFF]
|
||||
button[1.5,2;3,1;proceed;Proceed]
|
||||
]]
|
||||
function locks.prompt_password(playername, formname)
|
||||
local fs = locks.password_prompt;
|
||||
minetest.show_formspec(playername, formname, fs);
|
||||
end
|
||||
|
||||
function locks.access_denied(playername, formname, ask_for_pw)
|
||||
local fs = [[
|
||||
size[6,3;]
|
||||
label[0.5,1;Access denied]
|
||||
|
||||
box[0.1,1.5;5.5,0.05;#FFFFFF]
|
||||
button_exit[4,2;2,1;cancel;Cancel]
|
||||
]];
|
||||
if ask_for_pw == true then
|
||||
fs = fs.."button[0,2;3.5,1;enter_password;Enter Password]";
|
||||
end
|
||||
minetest.show_formspec(playername, formname, fs);
|
||||
end
|
||||
|
||||
locks.access_granted_formspec = [[
|
||||
size[6,3;]
|
||||
label[0.5,1;Access granted]
|
||||
|
||||
box[0.1,1.5;5.5,0.05;#FFFFFF]
|
||||
button_exit[1.5,2;3,1;proceed;Proceed]
|
||||
]]
|
||||
function locks.access_granted(playername, formname)
|
||||
minetest.show_formspec(playername, formname, locks.access_granted_formspec);
|
||||
end
|
||||
|
||||
locks.config_formspec = [[
|
||||
size[12,9;]
|
||||
|
||||
image[0,0;0.7,0.7;locks_lock32.png] label[0.7,0;Locks configuration panel]
|
||||
box[0.1,0.6;11.5,0.05;#FFFFFF]
|
||||
|
||||
label[0.1,0.7;Owner: %q]
|
||||
|
||||
checkbox[0.1,1;pipeworks;Enable Pipeworks;%s]
|
||||
tooltip[pipeworks;Tubes from pipeworks may be used to extract items out of/add items to this shared locked object.]
|
||||
|
||||
textarea[0.4,2;6.5,4;allowed_users;Allowed Players:;%s]
|
||||
label[6.5,2;Insert the Playernames here,
|
||||
that should have access to this Object.
|
||||
One Player per line]
|
||||
tooltip[allowed_users;Insert the Playernames here,
|
||||
that should have access to this Object.
|
||||
One Player per line]
|
||||
|
||||
field[0.4,6.5;4.5,0;password;Password:;%s] button[4.5,6.2;2,0;save_pw;Set PW]
|
||||
tooltip[password;Every player with this password can access this object]
|
||||
label[6.5,5.5;Specify a password here.
|
||||
Every Player with this password can access this object.
|
||||
Set an empty Password to remove the Password]
|
||||
|
||||
box[0.1,8.5;11.5,0.05;#FFFFFF]
|
||||
button_exit[4,9;2,0;ok;OK] button_exit[6,9;2,0;cancel;Cancel]
|
||||
]]
|
||||
locks.uniform_background = "";
|
||||
|
||||
if default and default.gui_bg then
|
||||
locks.uniform_background = locks.uniform_background..default.gui_bg;
|
||||
end
|
||||
|
||||
if default and default.gui_bg_img then
|
||||
locks.uniform_background = locks.uniform_background..default.gui_bg_img;
|
||||
end
|
||||
|
||||
if default and default.gui_slots then
|
||||
locks.uniform_background = locks.uniform_background..default.gui_slots;
|
||||
end
|
||||
|
||||
locks.config_formspec = locks.config_formspec..locks.uniform_background
|
||||
|
||||
|
||||
locks.pipeworks_enabled = false;
|
||||
|
||||
if( minetest.get_modpath("pipeworks") ~= nil ) then
|
||||
locks.pipeworks_enabled = true;
|
||||
end
|
||||
|
||||
-- initializes a lock (that is: prepare the metadata so that it can store data)
|
||||
-- default_formspec is the formspec that will be used on right click; the input field for the commands has to exist
|
||||
-- Call this in on_construct in register_node. Excample:
|
||||
-- on_construct = function(pos)
|
||||
-- locks:lock_init( pos, "" );
|
||||
-- end;
|
||||
|
||||
function locks:lock_init( pos, default_formspec )
|
||||
|
||||
if( pos == nil ) then
|
||||
print( "Error: [locks] lock_init: pos is nil");
|
||||
return;
|
||||
end
|
||||
|
||||
local meta = minetest.get_meta(pos);
|
||||
if( meta == nil ) then
|
||||
print( "Error: [locks] lock_init: unable to get meta data");
|
||||
return;
|
||||
end
|
||||
|
||||
-- this will be changed after the node is placed
|
||||
meta:set_string("infotext", "Locked object");
|
||||
-- prepare the field for the owner
|
||||
meta:set_string("owner", "");
|
||||
-- this is the list of players/groups that may unlock the lock even if they are not the owner
|
||||
meta:set_string("allowed_users","");
|
||||
-- objects can be unlocked by passwords as well (if it is set)
|
||||
meta:set_string("password","");
|
||||
meta:mark_as_private("password")
|
||||
-- the last player who entered the right password (to save space this is not a list)
|
||||
meta:set_string("pw_user","");
|
||||
-- this formspec is presented on right-click for every user
|
||||
meta:set_string("formspec", default_formspec..
|
||||
locks.get_authorize_button(6,0)..
|
||||
locks.get_config_button(7,0));
|
||||
-- by default, do not send output to pipework tubes
|
||||
meta:set_int( "allow_pipeworks", 0 );
|
||||
end
|
||||
|
||||
|
||||
-- returns the information stored in the metadata strings (like owner etc.)
|
||||
function locks:get_lockdata( pos )
|
||||
if( pos == nil ) then
|
||||
return;
|
||||
end
|
||||
|
||||
local meta = minetest.get_meta(pos);
|
||||
if( meta == nil) then
|
||||
return;
|
||||
end
|
||||
|
||||
return{ infotext = (meta:get_string( "infotext" ) or ""),
|
||||
owner = (meta:get_string( "owner" ) or ""),
|
||||
allowed_users = (meta:get_string( "allowed_users" ) or ""),
|
||||
password = (meta:get_string( "password" ) or ""),
|
||||
pw_user = (meta:get_string( "w_user" ) or ""),
|
||||
formspec = (meta:get_string( "formspec" ) or "")
|
||||
};
|
||||
end
|
||||
|
||||
|
||||
-- sets all the metadata the look needs (used e.g. in doors)
|
||||
function locks:set_lockdata( pos, data )
|
||||
if( pos == nil ) then
|
||||
return;
|
||||
end
|
||||
|
||||
local meta = minetest.get_meta(pos);
|
||||
if( meta == nil) then
|
||||
return;
|
||||
end
|
||||
|
||||
meta:set_string("infotext", (data.infotext or ""));
|
||||
meta:set_string("owner", (data.owner or ""));
|
||||
meta:set_string("allowed_users",(data.allowed_users or ""));
|
||||
meta:set_string("password", (data.password or ""));
|
||||
meta:mark_as_private("password")
|
||||
meta:set_string("pw_user", (data.pw_user or ""));
|
||||
meta:set_string("formspec", (data.formspec or ""));
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
-- Set the owner of the locked object.
|
||||
-- Call this in after_place_node in register_node. Example:
|
||||
-- after_place_node = function(pos, placer)
|
||||
-- locks:lock_set_owner( pos, placer, "Shared locked object" );
|
||||
-- end,
|
||||
function locks:lock_set_owner( pos, player_or_name, description )
|
||||
|
||||
if( pos == nil or player_or_name == nil ) then
|
||||
print( "Error: [locks] Missing/wrong parameters to lock_set_owner");
|
||||
return false;
|
||||
end
|
||||
|
||||
local meta = minetest.get_meta(pos);
|
||||
if( meta == nil ) then
|
||||
print( "Error: [locks] lock_set_owner: unable to get meta data");
|
||||
return;
|
||||
end
|
||||
|
||||
-- accepts a name or a player object
|
||||
if( type( player_or_name )~="string") then
|
||||
player_or_name = player_or_name:get_player_name();
|
||||
end
|
||||
|
||||
meta:set_string("owner", player_or_name or "");
|
||||
-- add the name of the owner to the description
|
||||
meta:set_string("infotext", ( description or "Shared lockecd object" ).." (owned by "..meta:get_string("owner")..")");
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- The locked object can only be digged by the owner OR by people with the diglocks priv
|
||||
-- Call this in can_dig in register_node. Example:
|
||||
-- can_dig = function(pos,player)
|
||||
-- return locks:lock_allow_dig( pos, player );
|
||||
-- end
|
||||
function locks:lock_allow_dig( pos, player )
|
||||
|
||||
if( pos == nil or player == nil ) then
|
||||
print( "Error: [locks] Missing/wrong parameters to lock_allow_dig");
|
||||
return false;
|
||||
end
|
||||
|
||||
local meta = minetest.get_meta(pos);
|
||||
local lock_owner = meta:get_string("owner");
|
||||
|
||||
-- locks who lost their owner can be opened/digged by anyone
|
||||
if( meta == nil or lock_owner == nil or lock_owner == "") then
|
||||
return true;
|
||||
end
|
||||
|
||||
-- the owner can dig up his own locked objects
|
||||
if( player:get_player_name() == meta:get_string("owner")) then
|
||||
return true;
|
||||
end
|
||||
|
||||
-- players with diglocks priv can dig up locked objects as well
|
||||
if( minetest.check_player_privs(player:get_player_name(), {diglocks=true})) then
|
||||
return true;
|
||||
end
|
||||
|
||||
return false; -- fallback
|
||||
end
|
||||
|
||||
|
||||
-- The locked object can only be used (i.e. opened, stuff taken out, changed, ... - depends on object) if this
|
||||
-- function returns true. Call it wherever appropriate (usually in on_punch in register_node). Example:
|
||||
-- on_punch = function(pos,player)
|
||||
-- if( !locks:lock_allow_use( pos, player ) then
|
||||
-- print( "Sorry, you have no access here.");
|
||||
-- else
|
||||
-- do_what_this_object_is_good_for( pos, puncher );
|
||||
-- end
|
||||
-- end
|
||||
|
||||
function locks:lock_allow_use( pos, player )
|
||||
|
||||
if( pos == nil or player == nil ) then
|
||||
print( "Error: [locks] Missing/wrong parameters to lock_allow_use");
|
||||
return false;
|
||||
end
|
||||
|
||||
local name = player:get_player_name();
|
||||
local meta = minetest.get_meta(pos);
|
||||
|
||||
-- pipeworks sends a special username
|
||||
if( player.is_fake_player) then
|
||||
if( locks:lock_allow_dig( pos, player ) and meta:get_int( 'allow_pipeworks' ) == 1 ) then
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
end
|
||||
end
|
||||
|
||||
-- the player has to have a key or a keychain to open his own shared locked objects
|
||||
if( name == meta:get_string("owner")) then
|
||||
|
||||
if( not( player:get_inventory():contains_item("main","locks:keychain 1"))
|
||||
and not( player:get_inventory():contains_item("main","locks:key 1"))) then
|
||||
minetest.chat_send_player( name, "You do not have a key or a keychain. Without that you can't use your shared locked objects!");
|
||||
return false;
|
||||
end
|
||||
|
||||
-- the player has to have a keychain to open shared locked objects of other players
|
||||
else
|
||||
|
||||
if( not( player:get_inventory():contains_item("main","locks:keychain 1"))) then
|
||||
minetest.chat_send_player(name, "You do not have a keychain. Without that you can't open shared locked objects of other players!");
|
||||
return false;
|
||||
end
|
||||
end
|
||||
|
||||
-- if the user would even be allowed to dig this node up, using the node is allowed as well
|
||||
if( locks:lock_allow_dig( pos, player )) then
|
||||
return true;
|
||||
end
|
||||
|
||||
|
||||
if( meta == nil ) then
|
||||
minetest.chat_send_player( name, "Error: Could not access metadata of this shared locked object.");
|
||||
return false;
|
||||
end
|
||||
|
||||
-- players with openlocks priv can open locked objects
|
||||
if( minetest.check_player_privs(name, {openlocks=true})) then
|
||||
return true;
|
||||
end
|
||||
|
||||
-- the player might be specificly allowed to use this object through allowed_users
|
||||
local liste = meta:get_string("allowed_users"):split( "," );
|
||||
for i in ipairs( liste ) do
|
||||
|
||||
if( liste[i] == name ) then
|
||||
return true;
|
||||
end
|
||||
|
||||
-- the player might member of a playergroup that is allowed to use this object
|
||||
if( liste[i]:sub(1,1) == ":"
|
||||
and playergroups ~= nil
|
||||
and playergroups:is_group_member( meta:get_string("owner"), liste[i]:sub(2), name )) then
|
||||
return true;
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
-- the player may have entered the right password
|
||||
if( name == meta:get_string("pw_user")) then
|
||||
return true;
|
||||
end
|
||||
|
||||
-- the lock may have a password set. If this is the case then ask the user for it
|
||||
if( meta:get_string( "password" ) and meta:get_string( "password" ) ~= "" ) then
|
||||
minetest.chat_send_player(name, "Access denied. Right-click and enter password first!");
|
||||
return false;
|
||||
end
|
||||
|
||||
return false; -- fallback
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- Method for the lock to get password and configuration data
|
||||
-- Call in on_receive_fields in register_node. Example:
|
||||
-- on_receive_fields = function(pos, formname, fields, sender)
|
||||
-- locks:lock_handle_input( pos, formname, fields, sender );
|
||||
-- end,
|
||||
function locks:lock_handle_input( pos, formname, fields, player )
|
||||
|
||||
if( pos == nil or player == nil ) then
|
||||
print( "Error: [locks] Missing/wrong parameters to lock_handle_input");
|
||||
return false;
|
||||
end
|
||||
|
||||
local meta = minetest.get_meta(pos);
|
||||
if( meta == nil ) then
|
||||
print( "Error: [locks] lock_handle_input: unable to get meta data");
|
||||
return;
|
||||
end
|
||||
|
||||
local name = player:get_player_name();
|
||||
local owner = meta:get_string("owner");
|
||||
|
||||
--first check for locks_config_button
|
||||
if fields.locks_config then
|
||||
-- else the player could set a new pw and gain access anyway...
|
||||
if( owner and owner ~= "" and owner ~= name ) then
|
||||
minetest.chat_send_player(name, "Only the owner can change the configuration.");
|
||||
return;
|
||||
end
|
||||
local allow_pipeworks = "false";
|
||||
if meta:get_int( 'allow_pipeworks' ) == 1 then
|
||||
allow_pipeworks = "true";
|
||||
end
|
||||
local data = locks:get_lockdata( pos );
|
||||
local fs = locks.config_formspec:format(data.owner,
|
||||
allow_pipeworks,
|
||||
data.allowed_users:gsub(",","\n"),
|
||||
meta:get_string("password"));
|
||||
minetest.show_formspec(player:get_player_name(), "locks_config:"..minetest.pos_to_string(pos), fs);
|
||||
return true; -- we could full handle the input. No need to continue. so we return true
|
||||
elseif fields.locks_authorize then
|
||||
local data = locks:get_lockdata( pos );
|
||||
|
||||
if name == data.owner then
|
||||
minetest.chat_send_player(name, "You are the owner of this object. Its not required to enter a password.",false)
|
||||
elseif minetest.string_to_privs(meta:get_string("allowed_users"))[name] then
|
||||
minetest.chat_send_player(name, "You are already authorized in the whitelist. Its not required to enter a password.",false)
|
||||
else
|
||||
|
||||
local fs = locks.password_prompt;
|
||||
minetest.show_formspec(name, "locks_authorize:"..minetest.pos_to_string(pos), fs);
|
||||
end
|
||||
return true;
|
||||
end
|
||||
|
||||
|
||||
-- is this input the lock is supposed to handle?
|
||||
if( ( not( fields.locks_sent_lock_command )
|
||||
or fields.locks_sent_lock_command == "" )
|
||||
and (fields.quit and (fields.quit==true or fields.quit=='true'))) then
|
||||
-- or not( fields.locks_sent_input )
|
||||
return;
|
||||
end
|
||||
|
||||
if( fields.locks_sent_lock_command == "/help" ) then
|
||||
|
||||
if( name == meta:get_string( "owner" )) then
|
||||
minetest.chat_send_player(name, "The following commands are available to you, the owner of this object, only:\n"..
|
||||
" /help Shows this help text.\n"..
|
||||
" /add <name> Player <name> can now unlock this object with any key.\n"..
|
||||
" /del <name> Player <name> can no longer use this object.\n"..
|
||||
" /list Shows a list of players who can use this object.\n"..
|
||||
" /set <password> Sets a password. Everyone who types that in can use the object.\n"..
|
||||
" /pipeworks Toggles permission for pipeworks to take inventory out of the shared locked object.\n");
|
||||
|
||||
else if( locks:lock_allow_use( pos, player )) then
|
||||
minetest.chat_send_player(name, "This locked object is owned by "..tostring( meta:get_string( "owner" ))..".\n"..
|
||||
"You do have access to it.\n");
|
||||
|
||||
else if( meta:get_string( "password" ) ~= "" ) then
|
||||
minetest.chat_send_player(name, "This locked object is owned by "..tostring( meta:get_string( "owner" ))..".\n"..
|
||||
"Enter the correct password to gain access.\n");
|
||||
|
||||
else
|
||||
minetest.chat_send_player(name, "This locked object is owned by "..tostring( meta:get_string( "owner" ))..".\n"..
|
||||
"There is no password set. You can only gain access if the owner grants it to you.");
|
||||
|
||||
end end end -- lua is not the most intuitive language here....
|
||||
return;
|
||||
end -- of /help
|
||||
|
||||
-- sanitize player input
|
||||
if( fields.locks_sent_lock_command:match("[^%a%d%s_%- /%:]")) then
|
||||
minetest.chat_send_player(name, "Input contains unsupported characters. Allowed: a-z, A-Z, 0-9, _, -, :.");
|
||||
return;
|
||||
end
|
||||
|
||||
if( #fields.locks_sent_lock_command > 60) then
|
||||
minetest.chat_send_player(name, "Input too long. Only up to 80 characters supported.");
|
||||
return;
|
||||
end
|
||||
|
||||
|
||||
local password = meta:get_string("password");
|
||||
-- other players can only try to input the correct password
|
||||
if( name ~= meta:get_string( "owner" )
|
||||
or (password and password ~= "" and password==fields.locks_sent_lock_command)
|
||||
or (name==meta:get_string("pw_user"))) then
|
||||
|
||||
-- no need to bother with trying other PWs if none is set...
|
||||
if( meta:get_string("password")=="" ) then
|
||||
minetest.chat_send_player(name, "There is no password set. Access denied.");
|
||||
return;
|
||||
end
|
||||
|
||||
-- the player may have entered the right password already
|
||||
if( name == meta:get_string("pw_user")) then
|
||||
-- nothing to do - the player entered the right pw alredy
|
||||
minetest.chat_send_player(name, "You have entered the right password already. Access granted.");
|
||||
return;
|
||||
end
|
||||
|
||||
if( fields.locks_sent_lock_command ~= meta:get_string("password")) then
|
||||
minetest.chat_send_player(name, "Wrong password. Access denied.");
|
||||
return;
|
||||
end
|
||||
|
||||
-- store the last user (this one) who entered the right pw
|
||||
meta:set_string( "pw_user", name );
|
||||
|
||||
minetest.chat_send_player(name, "Password confirmed. Access granted.");
|
||||
return;
|
||||
end
|
||||
|
||||
local txt = "";
|
||||
|
||||
|
||||
if( fields.locks_sent_lock_command == "/list" ) then
|
||||
|
||||
if( meta:get_string("allowed_users")=="" ) then
|
||||
txt = "No other users are allowed to use this object (except those with global privs like moderators/admins).";
|
||||
else
|
||||
txt = "You granted the following users/groups of users access to this object:\n";
|
||||
local liste = meta:get_string("allowed_users"):split( "," );
|
||||
for i in ipairs( liste ) do
|
||||
txt = txt.." "..tostring(liste[i]);
|
||||
end
|
||||
end
|
||||
|
||||
if( meta:get_string( "password" ) == "" ) then
|
||||
txt = txt.."\nThere is no password set. That means no one can get access through a password.";
|
||||
else
|
||||
txt = txt.."\nThe password for this lock is: \""..tostring( meta:get_string( "password" ).."\"");
|
||||
end
|
||||
|
||||
if( not( minetest.get_modpath("pipeworks") )) then
|
||||
txt = txt.."\nThe pipeworks mod is not installed. Install it if you wish support for tubes.";
|
||||
elseif( meta:get_int( "allow_pipeworks" ) == 1 ) then
|
||||
txt = txt.."\nTubes from pipeworks may be used to extract items out of/add items to this shared locked object.";
|
||||
else
|
||||
txt = txt.."\nInput from tubes is accepted, but output to them is denied (default).";
|
||||
end
|
||||
|
||||
minetest.chat_send_player(name, txt );
|
||||
return;
|
||||
end -- of /list
|
||||
|
||||
|
||||
-- toggle tube output on/off
|
||||
if( fields.locks_sent_lock_command == "/pipeworks" ) then
|
||||
|
||||
if( meta:get_int('allow_pipeworks') == 1 ) then
|
||||
meta:set_int('allow_pipeworks', 0 );
|
||||
minetest.chat_send_player( name, 'Output to pipework tubes is now DISABLED (input is still acceped).');
|
||||
return;
|
||||
else
|
||||
meta:set_int('allow_pipeworks', 1 );
|
||||
minetest.chat_send_player( name, 'Output to pipework tubes is now ENABLED. Connected tubes may insert and remove items.');
|
||||
return;
|
||||
end
|
||||
end
|
||||
|
||||
-- -- all other commands take exactly one parameter
|
||||
local help = fields.locks_sent_lock_command:split( " " );
|
||||
|
||||
print( tostring( help[1] ));
|
||||
print( tostring( help[2] ));
|
||||
|
||||
|
||||
-- set/change a password
|
||||
if( help[1]=="/set" ) then
|
||||
|
||||
-- if empty password then delete it
|
||||
if( help[2]==nil ) then
|
||||
help[2] = "";
|
||||
end
|
||||
|
||||
minetest.chat_send_player(name, "Old password: \""..tostring( meta:get_string( "password" ))..
|
||||
"\"\n Changed to new password: \""..tostring( help[2]).."\".");
|
||||
|
||||
|
||||
meta:set_string( "password", help[2]);
|
||||
meta:mark_as_private("password")
|
||||
-- reset the list of users who typed the right password
|
||||
meta:set_string("pw_users","");
|
||||
|
||||
if( help[2]=="") then
|
||||
minetest.chat_send_player(name, "The password is empty and thus will be disabled.");
|
||||
end
|
||||
return;
|
||||
end
|
||||
|
||||
if( help[2]==nil or help[2]=="") then
|
||||
minetest.chat_send_player(name, "Error: Missing parameter (player name) for command \""..tostring( help[1] ).."\"." );
|
||||
return;
|
||||
end
|
||||
|
||||
-- for add and del: check if the player is already in the list
|
||||
|
||||
local found = false;
|
||||
local anz = 0;
|
||||
local liste = meta:get_string("allowed_users"):split( "," );
|
||||
for i in ipairs( liste ) do
|
||||
|
||||
anz = anz + 1; -- count players
|
||||
if( tostring( liste[i] ) == help[2] ) then
|
||||
found = true;
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if( help[1]=="/add" and found==true ) then
|
||||
minetest.chat_send_player(name, "Player \""..tostring( help[2] ).."\" is already allowed to use this locked object. Nothing to do.");
|
||||
return;
|
||||
end
|
||||
|
||||
if( help[1]=="/del" and found==false) then
|
||||
minetest.chat_send_player(name, "Player \""..tostring( help[2] ).."\" is not amongst the players allowed to use this locked object. Nothing to do.");
|
||||
return;
|
||||
end
|
||||
|
||||
|
||||
if( help[1]=="/add" ) then
|
||||
|
||||
if( anz >= 6 ) then
|
||||
minetest.chat_send_player(name, "Sorry, no more players can be added. To save space, only up to 6 players can be added. For more players please use groups!");
|
||||
return;
|
||||
end
|
||||
|
||||
if( name == help[2] ) then
|
||||
minetest.chat_send_player(name, "You are already owner of this object.");
|
||||
return;
|
||||
end
|
||||
|
||||
-- the player might try to add a playergroup
|
||||
if( help[2]:sub(1,1) == ":" ) then
|
||||
|
||||
if( not( playergroups )) then
|
||||
minetest.chat_send_player(name, "Sorry, this server does not support playergroups.");
|
||||
return;
|
||||
end
|
||||
|
||||
if( #help[2]<2 ) then
|
||||
minetest.chat_send_player(name, "Please specify the name of the playergroup you want to add!");
|
||||
return;
|
||||
end
|
||||
|
||||
if( not( playergroups:is_playergroup(meta:get_string("owner"), help[2]:sub(2) ))) then
|
||||
minetest.chat_send_player(name, "You do not have a playergroup named \""..tostring( help[2]:sub(2)).."\".");
|
||||
return;
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
-- check if the player exists
|
||||
local privs = minetest.get_player_privs( help[2] );
|
||||
if( not( privs ) or not( privs.interact )) then
|
||||
minetest.chat_send_player(name, "Player \""..help[2].."\" not found or has no interact privs.");
|
||||
return;
|
||||
end
|
||||
end
|
||||
|
||||
meta:set_string( "allowed_users", meta:get_string("allowed_users")..","..help[2] );
|
||||
|
||||
if( help[2]:sub(1,1) == ":" ) then
|
||||
minetest.chat_send_player(name, "All members of your playergroup "..tostring(help[2]:sub(2)).." may now use/access this locked object.");
|
||||
else
|
||||
minetest.chat_send_player(name, help[2].." may now use/access this locked object.");
|
||||
end
|
||||
return;
|
||||
end
|
||||
|
||||
|
||||
if( help[1]=="/del" ) then
|
||||
|
||||
userlist = meta:get_string("allowed_users"):split( ","..help[2] );
|
||||
meta:set_string( "allowed_users", ( userlist[1] or "" )..(userlist[2] or "" ));
|
||||
|
||||
minetest.chat_send_player(name, "Access for player \""..tostring(help[2]).."\" has been revoked.");
|
||||
return;
|
||||
end
|
||||
|
||||
minetest.chat_send_player(name, "Error: Command \""..tostring(help[1]).."\" not understood.");
|
||||
end
|
||||
|
||||
|
||||
--this is required to handle the locks control panel
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
local playername = player:get_player_name();
|
||||
if formname:find("locks_config:") then -- search if formname contains locks
|
||||
--minetest.chat_send_player(playername,dump(fields));
|
||||
local pos = minetest.string_to_pos(formname:gsub("locks_config:",""))
|
||||
if fields.ok then
|
||||
local data = locks:get_lockdata( pos )
|
||||
data.allowed_users = fields.allowed_users:gsub("\n",",");
|
||||
--data.password = fields.password;
|
||||
locks:set_lockdata( pos, data )
|
||||
--print("Player "..player:get_player_name().." submitted fields "..dump(fields))
|
||||
elseif fields.save_pw then
|
||||
local data = locks:get_lockdata( pos )
|
||||
data.password = fields.password;
|
||||
locks:set_lockdata( pos, data );
|
||||
elseif fields.pipeworks then
|
||||
local meta = minetest.get_meta(pos);
|
||||
if fields.pipeworks == "true" then
|
||||
meta:set_int( 'allow_pipeworks', 1 );
|
||||
else
|
||||
meta:set_int( 'allow_pipeworks', 0 );
|
||||
end
|
||||
end
|
||||
return true; --everything handled good :)
|
||||
|
||||
elseif formname:find("locks_authorize:") then
|
||||
if fields.password and fields.password ~="" then
|
||||
local pos = minetest.string_to_pos(formname:gsub("locks_authorize:",""))
|
||||
local meta = minetest.get_meta(pos);
|
||||
if meta:get_string("password")==fields.password then
|
||||
locks.access_granted(playername, formname);
|
||||
meta:set_string("pw_user", playername)
|
||||
else
|
||||
locks.access_denied(playername, formname, true);
|
||||
|
||||
end
|
||||
elseif fields.enter_password then --if the user clicks the "Enter Password" button in the "access denied" formspec
|
||||
locks.prompt_password(playername, formname);
|
||||
end
|
||||
|
||||
return true --evrything is great :)
|
||||
end
|
||||
return false;
|
||||
end)
|
||||
|
||||
-- craftitem; that can be used to craft shared locked objects
|
||||
minetest.register_craftitem("locks:lock", {
|
||||
description = "Lock to lock and share objects",
|
||||
image = "locks_lock16.png",
|
||||
});
|
||||
|
||||
|
||||
minetest.register_craft({
|
||||
output = "locks:lock 2",
|
||||
recipe = {
|
||||
{'default:steel_ingot', 'default:steel_ingot','default:steel_ingot'},
|
||||
{'default:steel_ingot', '', 'default:steel_ingot'},
|
||||
{'', 'default:steel_ingot',''},
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
-- a key allowes to open your own shared locked objects
|
||||
minetest.register_craftitem("locks:key", {
|
||||
description = "Key to open your own shared locked objects",
|
||||
image = "locks_key32.png",
|
||||
});
|
||||
|
||||
minetest.register_craft({
|
||||
output = "locks:key",
|
||||
recipe = {
|
||||
{'', 'default:stick', ''},
|
||||
{'', 'default:steel_ingot',''},
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
-- in order to open shared locked objects of other players, a keychain is needed (plus the owner has to admit it via /add playername or through /set password)
|
||||
minetest.register_craftitem("locks:keychain", {
|
||||
description = "Keychain to open shared locked objects of others",
|
||||
image = "locks_keychain32.png",
|
||||
});
|
||||
|
||||
minetest.register_craft({
|
||||
output = "locks:keychain",
|
||||
recipe = {
|
||||
{'', 'default:steel_ingot', '' },
|
||||
{'locks:key', 'locks:key', 'locks:key'},
|
||||
}
|
||||
});
|
||||
|
||||
dofile(minetest.get_modpath("locks").."/shared_locked_chest.lua");
|
||||
dofile(minetest.get_modpath("locks").."/shared_locked_sign_wall.lua");
|
||||
dofile(minetest.get_modpath("locks").."/shared_locked_xdoors2.lua");
|
||||
dofile(minetest.get_modpath("locks").."/shared_locked_furnace.lua");
|
19
locks/liscence.txt
Normal file
@ -0,0 +1,19 @@
|
||||
License of locks mod for Minetest-c55
|
||||
-------------------------------------
|
||||
|
||||
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser 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 Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
5
locks/mod.conf
Normal file
@ -0,0 +1,5 @@
|
||||
description = Shared chests, doors, furnaces and signs - with keys
|
||||
author = Sokomine
|
||||
title = Locks
|
||||
release = 1160
|
||||
name = locks
|
147
locks/shared_locked_chest.lua
Normal file
@ -0,0 +1,147 @@
|
||||
-- 25.02.16 Added new Locks config Buttons.
|
||||
-- 09.01.13 Added support for pipeworks.
|
||||
|
||||
|
||||
locks.chest_add = {};
|
||||
locks.chest_add.tiles = {"default_chest_top.png", "default_chest_top.png", "default_chest_side.png",
|
||||
"default_chest_side.png", "default_chest_side.png", "default_chest_front.png^locks_lock16.png"};
|
||||
locks.chest_add.groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2};
|
||||
locks.chest_add.tube = {};
|
||||
|
||||
-- additional/changed definitions for pipeworks;
|
||||
-- taken from pipeworks/compat.lua
|
||||
if( locks.pipeworks_enabled ) then
|
||||
locks.chest_add.tiles = {
|
||||
"default_chest_top.png^pipeworks_tube_connection_wooden.png",
|
||||
"default_chest_top.png^pipeworks_tube_connection_wooden.png",
|
||||
"default_chest_side.png^pipeworks_tube_connection_wooden.png",
|
||||
"default_chest_side.png^pipeworks_tube_connection_wooden.png",
|
||||
"default_chest_side.png^pipeworks_tube_connection_wooden.png",
|
||||
"default_chest_front.png^locks_lock16.png"};
|
||||
locks.chest_add.groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2,
|
||||
tubedevice = 1, tubedevice_receiver = 1 };
|
||||
locks.chest_add.tube = {
|
||||
insert_object = function(pos, node, stack, direction)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
return inv:add_item("main", stack)
|
||||
end,
|
||||
can_insert = function(pos, node, stack, direction)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
return inv:room_for_item("main", stack)
|
||||
end,
|
||||
input_inventory = "main",
|
||||
connect_sides = {left=1, right=1, back=1, front=1, bottom=1, top=1}
|
||||
};
|
||||
end
|
||||
|
||||
|
||||
minetest.register_node("locks:shared_locked_chest", {
|
||||
description = "Shared locked chest",
|
||||
tiles = locks.chest_add.tiles,
|
||||
paramtype2 = "facedir",
|
||||
groups = locks.chest_add.groups,
|
||||
tube = locks.chest_add.tube,
|
||||
legacy_facedir_simple = true,
|
||||
|
||||
on_construct = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
-- prepare the lock of the chest
|
||||
locks:lock_init( pos,
|
||||
"size[8,10]"..
|
||||
---- "field[0.5,0.2;8,1.0;locks_sent_lock_command;Locked chest. Type password, command or /help for help:;]"..
|
||||
---- "button_exit[3,0.8;2,1.0;locks_sent_input;Proceed]"..
|
||||
-- "list[current_name;main;0,0;8,4;]"..
|
||||
-- "list[current_player;main;0,5;8,4;]"..
|
||||
-- "field[0.3,9.6;6,0.7;locks_sent_lock_command;Locked chest. Type /help for help:;]"..
|
||||
-- "background[-0.5,-0.65;9,11.2;bg_shared_locked_chest.jpg]"..
|
||||
-- "button_exit[6.3,9.2;1.7,0.7;locks_sent_input;Proceed]" );
|
||||
---- "size[8,9]"..
|
||||
---- "list[current_name;main;0,0;8,4;]"..
|
||||
---- "list[current_player;main;0,5;8,4;]");
|
||||
locks.uniform_background ..
|
||||
"list[current_name;main;0,1;8,4;]"..
|
||||
"list[current_player;main;0,5.85;8,1;]" ..
|
||||
"list[current_player;main;0,7.08;8,3;8]" ..
|
||||
"listring[current_name;main]" ..
|
||||
"listring[current_player;main]" ..
|
||||
default.get_hotbar_bg(0,5.85) );
|
||||
|
||||
local inv = meta:get_inventory()
|
||||
inv:set_size("main", 8*4)
|
||||
end,
|
||||
|
||||
after_place_node = function(pos, placer)
|
||||
|
||||
if( locks.pipeworks_enabled ) then
|
||||
pipeworks.scan_for_tube_objects( pos );
|
||||
end
|
||||
|
||||
locks:lock_set_owner( pos, placer, "Shared locked chest" );
|
||||
end,
|
||||
|
||||
|
||||
can_dig = function(pos,player)
|
||||
|
||||
if( not(locks:lock_allow_dig( pos, player ))) then
|
||||
return false;
|
||||
end
|
||||
local meta = minetest.get_meta(pos);
|
||||
local inv = meta:get_inventory()
|
||||
return inv:is_empty("main")
|
||||
end,
|
||||
|
||||
on_receive_fields = function(pos, formname, fields, sender)
|
||||
locks:lock_handle_input( pos, formname, fields, sender );
|
||||
end,
|
||||
|
||||
|
||||
|
||||
allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
|
||||
if( not( locks:lock_allow_use( pos, player ))) then
|
||||
return 0;
|
||||
end
|
||||
return count;
|
||||
end,
|
||||
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
|
||||
if( not( locks:lock_allow_use( pos, player ))) then
|
||||
return 0;
|
||||
end
|
||||
return stack:get_count()
|
||||
end,
|
||||
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
|
||||
if( not( locks:lock_allow_use( pos, player ))) then
|
||||
return 0;
|
||||
end
|
||||
return stack:get_count()
|
||||
end,
|
||||
on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
|
||||
minetest.log("action", player:get_player_name()..
|
||||
" moves stuff in locked shared chest at "..minetest.pos_to_string(pos))
|
||||
end,
|
||||
on_metadata_inventory_put = function(pos, listname, index, stack, player)
|
||||
minetest.log("action", player:get_player_name()..
|
||||
" moves stuff to locked shared chest at "..minetest.pos_to_string(pos))
|
||||
end,
|
||||
on_metadata_inventory_take = function(pos, listname, index, stack, player)
|
||||
minetest.log("action", player:get_player_name()..
|
||||
" takes stuff from locked shared chest at "..minetest.pos_to_string(pos))
|
||||
end,
|
||||
|
||||
|
||||
after_dig_node = function( pos )
|
||||
if( locks.pipeworks_enabled ) then
|
||||
pipeworks.scan_for_tube_objects(pos)
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = 'locks:shared_locked_chest',
|
||||
recipe = {
|
||||
{ 'default:chest', 'locks:lock', '' },
|
||||
},
|
||||
})
|
||||
|
||||
print( "[Mod] locks: loading locks:shared_locked_chest");
|
437
locks/shared_locked_furnace.lua
Normal file
@ -0,0 +1,437 @@
|
||||
|
||||
-- this is a (slightly!) modified copy of minetest_game/mods/default/nodes.lua,
|
||||
-- containing only the furnace and adopted slightly for my locks mod
|
||||
|
||||
|
||||
-- 25.02.16 Added new Locks config Buttons.
|
||||
-- 09.01.13 Added support for pipeworks.
|
||||
|
||||
|
||||
locks.furnace_add = {};
|
||||
locks.furnace_add.tiles_normal = {"default_furnace_top.png", "default_furnace_bottom.png", "default_furnace_side.png",
|
||||
"default_furnace_side.png", "default_furnace_side.png", "default_furnace_front.png"};
|
||||
locks.furnace_add.tiles_active = {"default_furnace_top.png", "default_furnace_bottom.png", "default_furnace_side.png",
|
||||
"default_furnace_side.png", "default_furnace_side.png", --"default_furnace_front_active.png"};
|
||||
{
|
||||
image = "default_furnace_front_active.png",
|
||||
backface_culling = false,
|
||||
animation = {
|
||||
type = "vertical_frames",
|
||||
aspect_w = 16,
|
||||
aspect_h = 16,
|
||||
length = 1.5
|
||||
},
|
||||
}};
|
||||
locks.furnace_add.groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2};
|
||||
locks.furnace_add.tube = {};
|
||||
|
||||
-- additional/changed definitions for pipeworks;
|
||||
-- taken from pipeworks/compat.lua
|
||||
if( locks.pipeworks_enabled ) then
|
||||
|
||||
locks.furnace_add.tiles_normal = {
|
||||
"default_furnace_top.png^pipeworks_tube_connection_stony.png",
|
||||
"default_furnace_bottom.png^pipeworks_tube_connection_stony.png",
|
||||
"default_furnace_side.png^pipeworks_tube_connection_stony.png",
|
||||
"default_furnace_side.png^pipeworks_tube_connection_stony.png",
|
||||
"default_furnace_side.png^pipeworks_tube_connection_stony.png",
|
||||
"default_furnace_front.png" };
|
||||
|
||||
|
||||
locks.furnace_add.tiles_active = {
|
||||
"default_furnace_top.png^pipeworks_tube_connection_stony.png",
|
||||
"default_furnace_bottom.png^pipeworks_tube_connection_stony.png",
|
||||
"default_furnace_side.png^pipeworks_tube_connection_stony.png",
|
||||
"default_furnace_side.png^pipeworks_tube_connection_stony.png",
|
||||
"default_furnace_side.png^pipeworks_tube_connection_stony.png",
|
||||
{
|
||||
image = "default_furnace_front_active.png",
|
||||
backface_culling = false,
|
||||
animation = {
|
||||
type = "vertical_frames",
|
||||
aspect_w = 16,
|
||||
aspect_h = 16,
|
||||
length = 1.5
|
||||
},
|
||||
}};
|
||||
-- "default_furnace_front_active.png" };
|
||||
|
||||
|
||||
locks.furnace_add.groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2,
|
||||
tubedevice = 1, tubedevice_receiver = 1 };
|
||||
locks.furnace_add.tube = {
|
||||
insert_object = function(pos, node, stack, direction)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
if direction.y == 1 then
|
||||
return inv:add_item("fuel",stack)
|
||||
else
|
||||
return inv:add_item("src",stack)
|
||||
end
|
||||
end,
|
||||
can_insert = function(pos, node, stack, direction)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
if direction.y == 1 then
|
||||
return inv:room_for_item("fuel", stack)
|
||||
else
|
||||
return inv:room_for_item("src", stack)
|
||||
end
|
||||
end,
|
||||
input_inventory = "dst",
|
||||
connect_sides = {left=1, right=1, back=1, front=1, bottom=1, top=1}
|
||||
};
|
||||
end
|
||||
|
||||
|
||||
function locks.get_furnace_active_formspec(pos, percent)
|
||||
local formspec =
|
||||
"size[8,9]"..
|
||||
"image[2,1.5;1,1;default_furnace_fire_bg.png^[lowpart:"..
|
||||
(100-percent)..":default_furnace_fire_fg.png]"..
|
||||
"list[current_name;fuel;2,2.5;1,1;]"..
|
||||
"list[current_name;src;2,0.5;1,1;]"..
|
||||
"list[current_name;dst;5,1;2,2;]"..
|
||||
"list[current_player;main;0,5;8,4;]"..
|
||||
locks.uniform_background..
|
||||
locks.get_authorize_button(6,0)..
|
||||
locks.get_config_button(7,0)
|
||||
|
||||
return formspec
|
||||
end
|
||||
|
||||
locks.furnace_inactive_formspec =
|
||||
"size[8,9]"..
|
||||
"image[2,1.5;1,1;default_furnace_fire_bg.png]"..
|
||||
"list[current_name;fuel;2,2.5;1,1;]"..
|
||||
"list[current_name;src;2,0.5;1,1;]"..
|
||||
"list[current_name;dst;5,1;2,2;]"..
|
||||
"list[current_player;main;0,5;8,4;]"..
|
||||
locks.uniform_background..
|
||||
locks.get_authorize_button(6,0)..
|
||||
locks.get_config_button(7,0)
|
||||
|
||||
minetest.register_node("locks:shared_locked_furnace", {
|
||||
description = "Shared locked furnace",
|
||||
paramtype2 = "facedir",
|
||||
groups = {cracky=2},
|
||||
legacy_facedir_simple = true,
|
||||
|
||||
tiles = locks.furnace_add.tiles_normal,
|
||||
groups = locks.furnace_add.groups,
|
||||
tube = locks.furnace_add.tube,
|
||||
|
||||
-- sounds = default.node_sound_stone_defaults(),
|
||||
on_construct = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
locks:lock_init( pos, locks.furnace_inactive_formspec)
|
||||
meta:set_string("infotext", "Shared locked furnace")
|
||||
local inv = meta:get_inventory()
|
||||
inv:set_size("fuel", 1)
|
||||
inv:set_size("src", 1)
|
||||
inv:set_size("dst", 4)
|
||||
end,
|
||||
|
||||
after_place_node = function(pos, placer)
|
||||
if( locks.pipeworks_enabled ) then
|
||||
pipeworks.scan_for_tube_objects(pos)
|
||||
end
|
||||
locks:lock_set_owner( pos, placer, "Shared locked furnace" );
|
||||
end,
|
||||
after_dig_node = function(pos)
|
||||
if( locks.pipeworks_enabled ) then
|
||||
pipeworks.scan_for_tube_objects(pos)
|
||||
end
|
||||
end,
|
||||
|
||||
can_dig = function(pos,player)
|
||||
if( not(locks:lock_allow_dig( pos, player ))) then
|
||||
return false;
|
||||
end
|
||||
local meta = minetest.get_meta(pos);
|
||||
local inv = meta:get_inventory()
|
||||
if not inv:is_empty("fuel") then
|
||||
return false
|
||||
elseif not inv:is_empty("dst") then
|
||||
return false
|
||||
elseif not inv:is_empty("src") then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end,
|
||||
on_receive_fields = function(pos, formname, fields, sender)
|
||||
locks:lock_handle_input( pos, formname, fields, sender );
|
||||
end,
|
||||
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
|
||||
if( not( locks:lock_allow_use( pos, player ))) then
|
||||
return 0;
|
||||
end
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
if listname == "fuel" then
|
||||
if minetest.get_craft_result({method="fuel",width=1,items={stack}}).time ~= 0 then
|
||||
if inv:is_empty("src") then
|
||||
meta:set_string("infotext","Furnace is empty")
|
||||
end
|
||||
return stack:get_count()
|
||||
else
|
||||
return 0
|
||||
end
|
||||
elseif listname == "src" then
|
||||
return stack:get_count()
|
||||
elseif listname == "dst" then
|
||||
return 0
|
||||
end
|
||||
end,
|
||||
allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
|
||||
if( not( locks:lock_allow_use( pos, player ))) then
|
||||
return 0;
|
||||
end
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
local stack = inv:get_stack(from_list, from_index)
|
||||
if to_list == "fuel" then
|
||||
if minetest.get_craft_result({method="fuel",width=1,items={stack}}).time ~= 0 then
|
||||
if inv:is_empty("src") then
|
||||
meta:set_string("infotext","Furnace is empty")
|
||||
end
|
||||
return count
|
||||
else
|
||||
return 0
|
||||
end
|
||||
elseif to_list == "src" then
|
||||
return count
|
||||
elseif to_list == "dst" then
|
||||
return 0
|
||||
end
|
||||
end,
|
||||
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
|
||||
if( not( locks:lock_allow_use( pos, player ))) then
|
||||
return 0;
|
||||
end
|
||||
return stack:get_count()
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_node("locks:shared_locked_furnace_active", {
|
||||
description = "Furnace",
|
||||
paramtype2 = "facedir",
|
||||
light_source = 8,
|
||||
drop = "locks:shared_locked_furnace",
|
||||
groups = {cracky=2, not_in_creative_inventory=1},
|
||||
legacy_facedir_simple = true,
|
||||
|
||||
tiles = locks.furnace_add.tiles_active,
|
||||
groups = locks.furnace_add.groups,
|
||||
tube = locks.furnace_add.tube,
|
||||
|
||||
-- sounds = default.node_sound_stone_defaults(),
|
||||
on_construct = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
locks:lock_init( pos, locks.furnace_inactive_formspec)
|
||||
meta:set_string("infotext", "Shared locked furnace");
|
||||
local inv = meta:get_inventory()
|
||||
inv:set_size("fuel", 1)
|
||||
inv:set_size("src", 1)
|
||||
inv:set_size("dst", 4)
|
||||
end,
|
||||
can_dig = function(pos,player)
|
||||
if( not(locks:lock_allow_dig( pos, player ))) then
|
||||
return false;
|
||||
end
|
||||
local meta = minetest.get_meta(pos);
|
||||
local inv = meta:get_inventory()
|
||||
if not inv:is_empty("fuel") then
|
||||
return false
|
||||
elseif not inv:is_empty("dst") then
|
||||
return false
|
||||
elseif not inv:is_empty("src") then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end,
|
||||
|
||||
after_place_node = function(pos, placer)
|
||||
if( locks.pipeworks_enabled ) then
|
||||
pipeworks.scan_for_tube_objects(pos)
|
||||
end
|
||||
locks:lock_set_owner( pos, placer, "Shared locked furnace (active)" );
|
||||
end,
|
||||
after_dig_node = function(pos)
|
||||
if( locks.pipeworks_enabled ) then
|
||||
pipeworks.scan_for_tube_objects(pos)
|
||||
end
|
||||
end,
|
||||
|
||||
on_receive_fields = function(pos, formname, fields, sender)
|
||||
locks:lock_handle_input( pos, formname, fields, sender );
|
||||
end,
|
||||
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
|
||||
if( not( locks:lock_allow_use( pos, player ))) then
|
||||
return 0;
|
||||
end
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
if listname == "fuel" then
|
||||
if minetest.get_craft_result({method="fuel",width=1,items={stack}}).time ~= 0 then
|
||||
if inv:is_empty("src") then
|
||||
meta:set_string("infotext","Shared locked furnace (empty)")
|
||||
end
|
||||
return stack:get_count()
|
||||
else
|
||||
return 0
|
||||
end
|
||||
elseif listname == "src" then
|
||||
return stack:get_count()
|
||||
elseif listname == "dst" then
|
||||
return 0
|
||||
end
|
||||
end,
|
||||
allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
|
||||
if( not( locks:lock_allow_use( pos, player ))) then
|
||||
return 0;
|
||||
end
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
local stack = inv:get_stack(from_list, from_index)
|
||||
if to_list == "fuel" then
|
||||
if minetest.get_craft_result({method="fuel",width=1,items={stack}}).time ~= 0 then
|
||||
if inv:is_empty("src") then
|
||||
meta:set_string("infotext","Shared locked furnace (empty)")
|
||||
end
|
||||
return count
|
||||
else
|
||||
return 0
|
||||
end
|
||||
elseif to_list == "src" then
|
||||
return count
|
||||
elseif to_list == "dst" then
|
||||
return 0
|
||||
end
|
||||
end,
|
||||
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
|
||||
if( not( locks:lock_allow_use( pos, player ))) then
|
||||
return 0;
|
||||
end
|
||||
return stack:get_count()
|
||||
end,
|
||||
})
|
||||
|
||||
-- better make this a function specific to this mod to avoid trouble with the same function in default
|
||||
locks.hacky_swap_node = function(pos,name)
|
||||
local node = minetest.get_node(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local meta0 = meta:to_table()
|
||||
if node.name == name then
|
||||
return
|
||||
end
|
||||
node.name = name
|
||||
local meta0 = meta:to_table()
|
||||
minetest.set_node(pos,node)
|
||||
meta = minetest.get_meta(pos)
|
||||
meta:from_table(meta0)
|
||||
end
|
||||
|
||||
minetest.register_abm({
|
||||
nodenames = {"locks:shared_locked_furnace","locks:shared_locked_furnace_active"},
|
||||
interval = 1.0,
|
||||
chance = 1,
|
||||
action = function(pos, node, active_object_count, active_object_count_wider)
|
||||
local meta = minetest.get_meta(pos)
|
||||
for i, name in ipairs({
|
||||
"fuel_totaltime",
|
||||
"fuel_time",
|
||||
"src_totaltime",
|
||||
"src_time"
|
||||
}) do
|
||||
if meta:get_string(name) == "" then
|
||||
meta:set_float(name, 0.0)
|
||||
end
|
||||
end
|
||||
|
||||
local inv = meta:get_inventory()
|
||||
|
||||
local srclist = inv:get_list("src")
|
||||
local cooked = nil
|
||||
local aftercooked
|
||||
|
||||
if srclist then
|
||||
cooked, aftercooked = minetest.get_craft_result({method = "cooking", width = 1, items = srclist})
|
||||
end
|
||||
|
||||
local was_active = false
|
||||
|
||||
if meta:get_float("fuel_time") < meta:get_float("fuel_totaltime") then
|
||||
was_active = true
|
||||
meta:set_float("fuel_time", meta:get_float("fuel_time") + 1)
|
||||
meta:set_float("src_time", meta:get_float("src_time") + 1)
|
||||
if cooked and cooked.item and meta:get_float("src_time") >= cooked.time then
|
||||
-- check if there's room for output in "dst" list
|
||||
if inv:room_for_item("dst",cooked.item) then
|
||||
-- Put result in "dst" list
|
||||
inv:add_item("dst", cooked.item)
|
||||
-- take stuff from "src" list
|
||||
inv:set_stack("src", 1, aftercooked.items[1])
|
||||
else
|
||||
print("Could not insert '"..cooked.item:to_string().."'")
|
||||
end
|
||||
meta:set_string("src_time", 0)
|
||||
end
|
||||
end
|
||||
|
||||
if meta:get_float("fuel_time") < meta:get_float("fuel_totaltime") then
|
||||
local percent = math.floor(meta:get_float("fuel_time") /
|
||||
meta:get_float("fuel_totaltime") * 100)
|
||||
meta:set_string("infotext","Shared locked furnace active: "..percent.."%")
|
||||
locks.hacky_swap_node(pos,"locks:shared_locked_furnace_active")
|
||||
meta:set_string("formspec",locks.get_furnace_active_formspec(pos, percent))
|
||||
return
|
||||
end
|
||||
|
||||
local fuel = nil
|
||||
local afterfuel
|
||||
local cooked = nil
|
||||
local fuellist = inv:get_list("fuel")
|
||||
local srclist = inv:get_list("src")
|
||||
|
||||
if srclist then
|
||||
cooked = minetest.get_craft_result({method = "cooking", width = 1, items = srclist})
|
||||
end
|
||||
if fuellist then
|
||||
fuel, afterfuel = minetest.get_craft_result({method = "fuel", width = 1, items = fuellist})
|
||||
end
|
||||
|
||||
if not( fuel) or fuel.time <= 0 then
|
||||
meta:set_string("infotext","Shared locked furnace out of fuel")
|
||||
locks.hacky_swap_node(pos,"locks:shared_locked_furnace")
|
||||
meta:set_string("formspec", locks.furnace_inactive_formspec)
|
||||
return
|
||||
end
|
||||
|
||||
if cooked.item:is_empty() then
|
||||
if was_active then
|
||||
meta:set_string("infotext","Shared locked furnace is empty")
|
||||
locks.hacky_swap_node(pos,"locks:shared_locked_furnace")
|
||||
meta:set_string("formspec", locks.furnace_inactive_formspec)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
meta:set_string("fuel_totaltime", fuel.time)
|
||||
meta:set_string("fuel_time", 0)
|
||||
|
||||
inv:set_stack("fuel", 1, afterfuel.items[1])
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
minetest.register_craft({
|
||||
output = 'locks:shared_locked_furnace',
|
||||
recipe = {
|
||||
{ 'default:furnace', 'locks:lock', '' },
|
||||
},
|
||||
})
|
||||
|
||||
print( "[Mod] locks: loading locks:shared_locked_furnace");
|
||||
|
||||
|
78
locks/shared_locked_sign_wall.lua
Normal file
@ -0,0 +1,78 @@
|
||||
|
||||
-- 25.02.16 Added new Locks config Buttons.
|
||||
-- allow aborting with ESC in newer Versions of MT again
|
||||
|
||||
-- a sign
|
||||
minetest.register_node("locks:shared_locked_sign_wall", {
|
||||
description = "Shared locked sign",
|
||||
drawtype = "signlike",
|
||||
tiles = {"locks_lock16.png^default_sign_wood.png"},
|
||||
inventory_image = "locks_lock16.png^default_sign_wood.png",
|
||||
wield_image = "locks_lock16.png^default_sign_wood.png",
|
||||
paramtype = "light",
|
||||
paramtype2 = "wallmounted",
|
||||
sunlight_propagates = true,
|
||||
walkable = false,
|
||||
selection_box = {
|
||||
type = "wallmounted",
|
||||
--wall_top = <default>
|
||||
--wall_bottom = <default>
|
||||
--wall_side = <default>
|
||||
},
|
||||
groups = {choppy=2,dig_immediate=2},
|
||||
legacy_wallmounted = true,
|
||||
|
||||
|
||||
on_construct = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
-- prepare the lock of the sign
|
||||
locks:lock_init( pos,
|
||||
"size[8,4]"..
|
||||
"field[0.3,0.6;6,0.7;text;Text:;${text}]"..
|
||||
-- "field[0.3,3.6;6,0.7;locks_sent_lock_command;Locked sign. Type /help for help:;]"..
|
||||
-- "background[-0.5,-0.5;9,5;bg_shared_locked_sign.jpg]" );
|
||||
"button_exit[6.3,3.2;1.7,0.7;locks_sent_input;Proceed]"..
|
||||
locks.uniform_background );
|
||||
end,
|
||||
|
||||
after_place_node = function(pos, placer)
|
||||
locks:lock_set_owner( pos, placer, "Shared locked sign" );
|
||||
end,
|
||||
|
||||
|
||||
can_dig = function(pos,player)
|
||||
return locks:lock_allow_dig( pos, player );
|
||||
end,
|
||||
|
||||
on_receive_fields = function(pos, formname, fields, sender)
|
||||
|
||||
-- if locks can not handle the input
|
||||
if not locks:lock_handle_input( pos, formname, fields, sender ) then
|
||||
--then handle compatibility stuff or insert text
|
||||
if( fields.text
|
||||
and ( not(fields.locks_sent_lock_command) --compatibility
|
||||
or fields.locks_sent_lock_command=="") --compatibility
|
||||
and locks:lock_allow_use( pos, sender )) then
|
||||
|
||||
--print("Sign at "..minetest.pos_to_string(pos).." got "..dump(fields))
|
||||
local meta = minetest.get_meta(pos)
|
||||
fields.text = fields.text or "";
|
||||
print((sender:get_player_name() or "").." wrote \""..fields.text..
|
||||
"\" to sign at "..minetest.pos_to_string(pos));
|
||||
meta:set_string("text", fields.text);
|
||||
meta:set_string("infotext", '"'..fields.text..'"'.." ["..sender:get_player_name().."]");
|
||||
end
|
||||
end
|
||||
|
||||
end,
|
||||
});
|
||||
|
||||
|
||||
minetest.register_craft({
|
||||
output = 'locks:shared_locked_sign_wall',
|
||||
recipe = {
|
||||
{ 'default:sign_wall', 'locks:lock', '' },
|
||||
},
|
||||
})
|
||||
|
||||
print( "[Mod] locks: loading locks:shared_locked_sign_wall");
|
222
locks/shared_locked_xdoors2.lua
Normal file
@ -0,0 +1,222 @@
|
||||
-- xDoors² mod by xyz
|
||||
-- modified by Sokomine to allow locked doors that can only be opened/closed/dig up by the player who placed them
|
||||
-- a little bit modified by addi to allow someone with the priv "opendoors" to open/close/dig all locked doors.
|
||||
-- Sokomine: modified again so that it uses the new locks-mod
|
||||
-- 25.02.16 Added new Locks config Buttons.
|
||||
|
||||
local door_bottom = {-0.5, -0.5, -0.5, 0.5, 0.5, -0.4}
|
||||
local door_top = {
|
||||
{-0.5, -0.5, -0.5, -0.3, 0.5, -0.4},
|
||||
{0.3, -0.5, -0.5, 0.5, 0.5, -0.4},
|
||||
{-0.3, 0.3, -0.5, 0.3, 0.5, -0.4},
|
||||
{-0.3, -0.5, -0.5, 0.3, -0.4, -0.4},
|
||||
{-0.05, -0.4, -0.5, 0.05, 0.3, -0.4},
|
||||
{-0.3, -0.1, -0.5, -0.05, 0, -0.4},
|
||||
{0.05, -0.1, -0.5, 0.3, 0, -0.4}
|
||||
}
|
||||
|
||||
local is_top = function(name)
|
||||
return name:sub(12, 12) == "t"
|
||||
end
|
||||
|
||||
|
||||
local xdoors2_transform = function(pos, node, puncher)
|
||||
|
||||
if( not( locks:lock_allow_use( pos, puncher ))) then
|
||||
minetest.chat_send_player( puncher:get_player_name(), "This door is locked. It can only be opened by its owner or people with a key that fits.");
|
||||
return;
|
||||
end
|
||||
|
||||
if is_top(node.name) then
|
||||
pos = {x = pos.x, y = pos.y - 1, z = pos.z}
|
||||
end
|
||||
local t = 3 - node.name:sub(-1)
|
||||
local p2 = 0
|
||||
if t == 2 then
|
||||
p2 = (node.param2 + 1) % 4
|
||||
minetest.sound_play("doors_door_open", { pos = pos, gain = 0.3, max_hear_distance = 10})
|
||||
else
|
||||
p2 = (node.param2 + 3) % 4
|
||||
minetest.sound_play("doors_door_close", { pos = pos, gain = 0.3, max_hear_distance = 10})
|
||||
end
|
||||
|
||||
local olddata = locks:get_lockdata( pos );
|
||||
minetest.add_node(pos, {name = "locks:door_bottom_"..t, param2 = p2})
|
||||
minetest.add_node({x = pos.x, y = pos.y + 1, z = pos.z}, {name = "locks:door_top_"..t, param2 = p2})
|
||||
|
||||
-- remember who owns the door, what passwords are set etc.
|
||||
locks:set_lockdata( pos, olddata );
|
||||
locks:set_lockdata( {x = pos.x, y = pos.y + 1, z = pos.z}, olddata );
|
||||
end
|
||||
|
||||
|
||||
local xdoors2_destruct = function(pos, oldnode)
|
||||
if is_top(oldnode.name) then
|
||||
pos = {x = pos.x, y = pos.y - 1, z = pos.z}
|
||||
end
|
||||
minetest.remove_node(pos)
|
||||
minetest.remove_node({x = pos.x, y = pos.y + 1, z = pos.z})
|
||||
end
|
||||
|
||||
for i = 1, 2 do
|
||||
minetest.register_node("locks:door_top_"..i, {
|
||||
tiles = {"xdoors2_side.png", "xdoors2_side.png", "xdoors2_top.png", "xdoors2_bottom.png", "xdoors2_top_"..(3 - i)..".png", "xdoors2_top_"..i..".png"},
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
drawtype = "nodebox",
|
||||
drop = "locks:door",
|
||||
groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2},
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = door_top
|
||||
},
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = door_bottom
|
||||
},
|
||||
on_punch = xdoors2_transform,
|
||||
after_dig_node = xdoors2_destruct,
|
||||
|
||||
on_construct = function(pos)
|
||||
locks:lock_init( pos,
|
||||
"size[8,2]"..
|
||||
locks.uniform_background..
|
||||
"button_exit[6.3,1.2;1.7,1;locks_sent_input;Proceed]" );
|
||||
end,
|
||||
|
||||
on_receive_fields = function(pos, formname, fields, sender)
|
||||
locks:lock_handle_input( pos, formname, fields, sender );
|
||||
end,
|
||||
|
||||
can_dig = function(pos,player)
|
||||
return locks:lock_allow_dig( pos, player );
|
||||
end
|
||||
})
|
||||
minetest.register_node("locks:door_bottom_"..i, {
|
||||
tiles = {"xdoors2_side.png", "xdoors2_side.png", "xdoors2_top.png", "xdoors2_bottom.png", "locks_xdoors2_bottom_"..(3 - i)..".png", "locks_xdoors2_bottom_"..i..".png"},
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
drawtype = "nodebox",
|
||||
drop = "locks:door",
|
||||
groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2},
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = door_bottom
|
||||
},
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = door_bottom
|
||||
},
|
||||
on_punch = xdoors2_transform,
|
||||
after_dig_node = xdoors2_destruct,
|
||||
|
||||
on_construct = function(pos)
|
||||
locks:lock_init( pos,
|
||||
"size[8,2]"..
|
||||
locks.uniform_background..
|
||||
"button_exit[6.3,1.2;1.7,1;locks_sent_input;Proceed]" );
|
||||
end,
|
||||
|
||||
on_receive_fields = function(pos, formname, fields, sender)
|
||||
locks:lock_handle_input( pos, formname, fields, sender );
|
||||
end,
|
||||
|
||||
can_dig = function(pos,player)
|
||||
return locks:lock_allow_dig( pos, player );
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
local delta = {
|
||||
{x = -1, z = 0},
|
||||
{x = 0, z = 1},
|
||||
{x = 1, z = 0},
|
||||
{x = 0, z = -1}
|
||||
}
|
||||
|
||||
minetest.register_node("locks:door", {
|
||||
description = "Shared locked Wooden Door",
|
||||
node_placement_prediction = "",
|
||||
inventory_image = 'locks_xdoors2_door.png',
|
||||
wield_image = 'xdoors2_door.png',
|
||||
stack_max = 1,
|
||||
sunlight_propogates = true,
|
||||
on_place = function(itemstack, placer, pointed_thing)
|
||||
local above = pointed_thing.above
|
||||
local above1 = {x = above.x, y = above.y + 1, z = above.z};
|
||||
if (minetest.is_protected(above, placer:get_player_name())
|
||||
or minetest.is_protected(above1, placer:get_player_name())) then
|
||||
minetest.chat_send_player(placer:get_player_name(), "This area is protected!")
|
||||
return itemstack
|
||||
else
|
||||
-- there should be 2 empty nodes
|
||||
if minetest.get_node(above1).name ~= "air" then
|
||||
return itemstack
|
||||
end
|
||||
|
||||
local fdir = 0
|
||||
local placer_pos = placer:getpos()
|
||||
if placer_pos then
|
||||
dir = {
|
||||
x = above.x - placer_pos.x,
|
||||
y = above.y - placer_pos.y,
|
||||
z = above.z - placer_pos.z
|
||||
}
|
||||
fdir = minetest.dir_to_facedir(dir)
|
||||
end
|
||||
|
||||
local t = 1
|
||||
local another_door = minetest.get_node({x = above.x + delta[fdir + 1].x, y = above.y, z = above.z + delta[fdir + 1].z})
|
||||
if (another_door.name:sub(-1) == "1" and another_door.param2 == fdir)
|
||||
or (another_door.name:sub(-1) == "2" and another_door.param2 == (fdir + 1) % 4) then
|
||||
t = 2
|
||||
end
|
||||
|
||||
minetest.add_node(above, {name = "locks:door_bottom_"..t, param2 = fdir})
|
||||
minetest.add_node({x = above.x, y = above.y + 1, z = above.z}, {name = "locks:door_top_"..t, param2 = fdir})
|
||||
|
||||
-- store who owns the door; the other data can be default for now
|
||||
locks:lock_set_owner( above, placer:get_player_name() or "", "Shared locked door");
|
||||
locks:lock_set_owner( {x = above.x, y = above.y + 1, z = above.z}, placer:get_player_name() or "", "Shared locked door");
|
||||
|
||||
return ItemStack("")
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
|
||||
-- if xdoors2 is installed
|
||||
if( minetest.get_modpath("xdoors2") ~= nil ) then
|
||||
minetest.register_craft({
|
||||
output = 'locks:door',
|
||||
recipe = {
|
||||
{ 'xdoors2:door', 'locks:lock', '' },
|
||||
},
|
||||
});
|
||||
|
||||
-- if the normal doors are installed
|
||||
else if( minetest.get_modpath("doors") ~= nil ) then
|
||||
|
||||
minetest.register_craft({
|
||||
output = 'locks:door',
|
||||
recipe = {
|
||||
{ 'doors:door_wood', 'locks:lock', '' },
|
||||
},
|
||||
})
|
||||
|
||||
-- fallback if no doors can be found
|
||||
else
|
||||
minetest.register_craft({
|
||||
output = 'locks:door',
|
||||
recipe = {
|
||||
{ 'default:wood', 'default:wood', '' },
|
||||
{ 'default:wood', 'default:wood', 'locks:lock' },
|
||||
{ 'default:wood', 'default:wood', '' },
|
||||
},
|
||||
});
|
||||
end
|
||||
end -- of previous else
|
||||
|
||||
|
||||
|
||||
print( "[Mod] locks: loading locks:door");
|
BIN
locks/textures/bg_shared_locked_chest.jpg
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
locks/textures/bg_shared_locked_furnace.jpg
Normal file
After Width: | Height: | Size: 84 KiB |
BIN
locks/textures/bg_shared_locked_sign.jpg
Normal file
After Width: | Height: | Size: 70 KiB |
88
locks/textures/licence.txt
Normal file
@ -0,0 +1,88 @@
|
||||
Licence for the following grafiks:
|
||||
lock
|
||||
key
|
||||
keychain
|
||||
|
||||
author: addi
|
||||
|
||||
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
|
||||
http://creativecommons.org/licenses/by-sa/3.0/
|
||||
|
||||
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
You are free:
|
||||
|
||||
to Share — to copy, distribute and transmit the work
|
||||
to Remix — to adapt the work
|
||||
|
||||
|
||||
Under the following conditions:
|
||||
|
||||
Attribution — You must attribute the work in the manner specified by the author or licensor (but not in any way that suggests that they endorse you or your use of the work).
|
||||
|
||||
Share Alike — If you alter, transform, or build upon this work, you may distribute the resulting work only under the same or similar license to this one.
|
||||
|
||||
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
License
|
||||
|
||||
THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
|
||||
|
||||
BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
|
||||
|
||||
1. Definitions
|
||||
|
||||
"Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License.
|
||||
"Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(f) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined below) for the purposes of this License.
|
||||
"Creative Commons Compatible License" means a license that is listed at http://creativecommons.org/compatiblelicenses that has been approved by Creative Commons as being essentially equivalent to this License, including, at a minimum, because that license: (i) contains terms that have the same purpose, meaning and effect as the License Elements of this License; and, (ii) explicitly permits the relicensing of adaptations of works made available under that license under this License or a Creative Commons jurisdiction license with the same License Elements as this License.
|
||||
"Distribute" means to make available to the public the original and copies of the Work or Adaptation, as appropriate, through sale or other transfer of ownership.
|
||||
"License Elements" means the following high-level license attributes as selected by Licensor and indicated in the title of this License: Attribution, ShareAlike.
|
||||
"Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License.
|
||||
"Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast.
|
||||
"Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work.
|
||||
"You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
|
||||
"Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images.
|
||||
"Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium.
|
||||
|
||||
2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws.
|
||||
|
||||
3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
|
||||
|
||||
to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections;
|
||||
to create and Reproduce Adaptations provided that any such Adaptation, including any translation in any medium, takes reasonable steps to clearly label, demarcate or otherwise identify that changes were made to the original Work. For example, a translation could be marked "The original work was translated from English to Spanish," or a modification could indicate "The original work has been modified.";
|
||||
to Distribute and Publicly Perform the Work including as incorporated in Collections; and,
|
||||
to Distribute and Publicly Perform Adaptations.
|
||||
|
||||
For the avoidance of doubt:
|
||||
Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License;
|
||||
Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor waives the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; and,
|
||||
Voluntary License Schemes. The Licensor waives the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License.
|
||||
|
||||
The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. Subject to Section 8(f), all rights not expressly granted by Licensor are hereby reserved.
|
||||
|
||||
4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
|
||||
|
||||
You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(c), as requested. If You create an Adaptation, upon notice from any Licensor You must, to the extent practicable, remove from the Adaptation any credit as required by Section 4(c), as requested.
|
||||
You may Distribute or Publicly Perform an Adaptation only under the terms of: (i) this License; (ii) a later version of this License with the same License Elements as this License; (iii) a Creative Commons jurisdiction license (either this or a later license version) that contains the same License Elements as this License (e.g., Attribution-ShareAlike 3.0 US)); (iv) a Creative Commons Compatible License. If you license the Adaptation under one of the licenses mentioned in (iv), you must comply with the terms of that license. If you license the Adaptation under the terms of any of the licenses mentioned in (i), (ii) or (iii) (the "Applicable License"), you must comply with the terms of the Applicable License generally and the following provisions: (I) You must include a copy of, or the URI for, the Applicable License with every copy of each Adaptation You Distribute or Publicly Perform; (II) You may not offer or impose any terms on the Adaptation that restrict the terms of the Applicable License or the ability of the recipient of the Adaptation to exercise the rights granted to that recipient under the terms of the Applicable License; (III) You must keep intact all notices that refer to the Applicable License and to the disclaimer of warranties with every copy of the Work as included in the Adaptation You Distribute or Publicly Perform; (IV) when You Distribute or Publicly Perform the Adaptation, You may not impose any effective technological measures on the Adaptation that restrict the ability of a recipient of the Adaptation from You to exercise the rights granted to that recipient under the terms of the Applicable License. This Section 4(b) applies to the Adaptation as incorporated in a Collection, but this does not require the Collection apart from the Adaptation itself to be made subject to the terms of the Applicable License.
|
||||
If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and (iv) , consistent with Ssection 3(b), in the case of an Adaptation, a credit identifying the use of the Work in the Adaptation (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). The credit required by this Section 4(c) may be implemented in any reasonable manner; provided, however, that in the case of a Adaptation or Collection, at a minimum such credit will appear, if a credit for all contributing authors of the Adaptation or Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties.
|
||||
Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Adaptations or Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation. Licensor agrees that in those jurisdictions (e.g. Japan), in which any exercise of the right granted in Section 3(b) of this License (the right to make Adaptations) would be deemed to be a distortion, mutilation, modification or other derogatory action prejudicial to the Original Author's honor and reputation, the Licensor will waive or not assert, as appropriate, this Section, to the fullest extent permitted by the applicable national law, to enable You to reasonably exercise Your right under Section 3(b) of this License (right to make Adaptations) but not otherwise.
|
||||
|
||||
5. Representations, Warranties and Disclaimer
|
||||
|
||||
UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
|
||||
|
||||
6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
7. Termination
|
||||
|
||||
This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Adaptations or Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
|
||||
Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
|
||||
|
||||
8. Miscellaneous
|
||||
|
||||
Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
|
||||
Each time You Distribute or Publicly Perform an Adaptation, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
|
||||
If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
|
||||
No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
|
||||
This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
|
||||
The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law.
|
BIN
locks/textures/locks_key16.png
Normal file
After Width: | Height: | Size: 390 B |
BIN
locks/textures/locks_key32.png
Normal file
After Width: | Height: | Size: 726 B |
BIN
locks/textures/locks_keychain16.png
Normal file
After Width: | Height: | Size: 756 B |
BIN
locks/textures/locks_keychain32.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
locks/textures/locks_lock16.png
Normal file
After Width: | Height: | Size: 727 B |
BIN
locks/textures/locks_lock32.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
locks/textures/locks_xdoors2_bottom_1.png
Normal file
After Width: | Height: | Size: 227 B |
BIN
locks/textures/locks_xdoors2_bottom_2.png
Normal file
After Width: | Height: | Size: 230 B |
BIN
locks/textures/locks_xdoors2_door.png
Normal file
After Width: | Height: | Size: 266 B |
BIN
locks/textures/xdoors2_bottom.png
Normal file
After Width: | Height: | Size: 109 B |
BIN
locks/textures/xdoors2_side.png
Normal file
After Width: | Height: | Size: 109 B |
BIN
locks/textures/xdoors2_top.png
Normal file
After Width: | Height: | Size: 109 B |
BIN
locks/textures/xdoors2_top_1.png
Normal file
After Width: | Height: | Size: 144 B |
BIN
locks/textures/xdoors2_top_2.png
Normal file
After Width: | Height: | Size: 144 B |
BIN
locks/textures/xdoors2locked_bottom_1.png
Normal file
After Width: | Height: | Size: 227 B |
BIN
locks/textures/xdoors2locked_bottom_2.png
Normal file
After Width: | Height: | Size: 230 B |
BIN
locks/textures/xdoors2locked_door.png
Normal file
After Width: | Height: | Size: 266 B |
142
protector/README.md
Normal file
@ -0,0 +1,142 @@
|
||||
Protector Redo mod [protect]
|
||||
|
||||
Protector redo for minetest is a mod that protects a players builds by placing
|
||||
a block that stops other players from digging or placing blocks in that area.
|
||||
|
||||
based on glomie's mod, remade by Zeg9 and rewritten by TenPlus1.
|
||||
|
||||
https://forum.minetest.net/viewtopic.php?f=11&t=9376
|
||||
|
||||
Change log:
|
||||
|
||||
- 0.1 - Initial release
|
||||
- 0.2 - Texture update
|
||||
- 0.3 - Added Protection Logo to blend in with player builds
|
||||
- 0.4 - Code tweak for 0.4.10+
|
||||
- 0.5 - Added protector.radius variable in init.lua (default: 5)
|
||||
- 0.6 - Added Protected Doors (wood and steel) and Protected Chest
|
||||
- 0.7 - Protected Chests now have "To Chest" and "To Inventory" buttons to copy
|
||||
contents across, also chests can be named
|
||||
- 0.8 - Updated to work with Minetest 0.4.12, simplified textures
|
||||
- 0.9 - Tweaked code
|
||||
- 1.0 - Only owner can remove protector
|
||||
- 1.1 - Set 'protector_pvp = true' in minetest.conf to disable pvp in protected
|
||||
areas except your own, also setting protector_pvp_spawn higher than 0 will
|
||||
disable pvp around spawn area with the radius you entered
|
||||
- 1.2 - Shift and click support added with Minetest 0.4.13 to quickly copy stacks
|
||||
to and from protected chest
|
||||
- 1.3 - Moved protector on_place into node itself, protector zone display changed
|
||||
from 10 to 5 seconds, general code tidy
|
||||
- 1.4 - Changed protector recipes to give single item instead of 4, added + button
|
||||
to interface, tweaked and tidied code, added admin command /delprot to remove
|
||||
protectors in bulk from banned/old players
|
||||
- 1.5 - Added much requested protected trapdoor
|
||||
- 1.6 - Added protector_drop (true or false) and protector_hurt (hurt by this num)
|
||||
variables to minetest.conf settings to stop players breaking protected
|
||||
areas by dropping tools and hurting player.
|
||||
- 1.7 - Included an edited version of WTFPL doors mod since protected doors didn't
|
||||
work with the doors mod in the latest daily build... Now it's fine :)
|
||||
added support for "protection_bypass" privelage.
|
||||
- 1.8 - Added 'protector_flip' setting to stop players using lag to grief into
|
||||
another players house, it flips them around to stop them digging.
|
||||
- 1.9 - Renamed 'protector_pvp_spawn' setting to 'protector_spawn' which protects
|
||||
an area around static spawnpoint and disables pvp if active.
|
||||
(note: previous name can still be used)
|
||||
- 2.0 - Added protector placement tool (thanks to Shara) so that players can easily
|
||||
stand on a protector, face in a direction and it places a new one at a set
|
||||
distance to cover protection radius. Added /protector_show command (thanks agaran)
|
||||
Protectors and chest cannot be moved by mesecon pistons or machines.
|
||||
- 2.1 - Added 'protector_night_pvp' setting so night-time becomes a free for all and
|
||||
players can hurt one another even inside protected areas (not spawn protected)
|
||||
- 2.2 - Updated protector tool so that player only needs to stand nearby (2 block radius)
|
||||
It can also place vertically (up and down) as well. New protector recipe added.
|
||||
- 2.3 - Localise many of the protector functions and tidy code.
|
||||
- 2.4 - Update to newer functions, Minetest 0.4.16 needed to run now.
|
||||
- 2.5 - Added HUD text to show when player is inside a protected area (updates every 5 seconds)
|
||||
- 2.6 - Add protection against CSM tampering, updated Intllib support (thanks codexp), tweaked block textures
|
||||
- 2.7 - Remove protection field entity when protector has been dug
|
||||
- 2.8 - Added 'protector_show_interval' setting to minetest.conf [default is 5], make protection field glow in dark.
|
||||
- 2.9 - Added MineClone2 recipes for protection block but no official support as yet
|
||||
- 3.0 - Added PlayerFactions support, 'protector_hud_interval' setting and listing in advanced settings for mod values.
|
||||
- 3.1 - Ability to hide protection blocks using /protector_hide and /protector_show , italian local added (thanks Hamlet)
|
||||
- 3.2 - Defaults to Minetest translation if found, otherwise intllib fallback if loaded, locale files updated for both. Added 'protector_msg' setting for player text.
|
||||
- 3.3 - Added support for playerfactions new api (thanks louisroyer), added limiter to protection radius of 22.
|
||||
|
||||
Lucky Blocks: 10
|
||||
|
||||
|
||||
Usage: (requires server privelage)
|
||||
|
||||
list names to remove
|
||||
|
||||
/protector_remove
|
||||
|
||||
remove specific user names
|
||||
|
||||
/protector_remove name1 name2
|
||||
|
||||
remove all names from list
|
||||
|
||||
/protector_remove -
|
||||
|
||||
Whenever a player is near any protectors with name1 or name2 then it will be
|
||||
replaced by an air block.
|
||||
|
||||
|
||||
show owner name to replace
|
||||
|
||||
/protector_replace
|
||||
|
||||
replace owner with new name
|
||||
|
||||
/protector_replace owner new_owner
|
||||
|
||||
reset name list
|
||||
|
||||
/protector_replace -
|
||||
|
||||
|
||||
show protected areas of your nearby protectors (max of 5)
|
||||
/protector_show_area
|
||||
|
||||
|
||||
A players own protection blocks can be hidden and shown using the following:
|
||||
/protector_hide
|
||||
/protector_show
|
||||
|
||||
|
||||
The following lines can be added to your minetest.conf file to configure specific features of the mod:
|
||||
|
||||
protector_radius = 5
|
||||
- Sets the area around each protection node so that other players cannot dig, place or enter through protected doors or chests.
|
||||
|
||||
protector_pvp = true
|
||||
- true or false this setting disabled pvp inside of protected areas for all players apart from those listed on the protector node.
|
||||
|
||||
protector_night_pvp = false
|
||||
- when true this setting enables pvp at night time only, even inside protected areas, requires protector_pvp to be active to work.
|
||||
|
||||
protector_spawn = 10
|
||||
- Sets an area 10 nodes around static spawnpoint that is protected.
|
||||
|
||||
protector_hurt = 2
|
||||
- When set to above 0, players digging in protected areas will be hurt by 2 health points (or whichever number it's set to)
|
||||
|
||||
protector_flip = true
|
||||
- When true players who dig inside a protected area will flipped around to stop them using lag to grief into someone else's build
|
||||
|
||||
protector_show_interval
|
||||
- Number of seconds the protection field is visible, defaults to 5 seconds.
|
||||
|
||||
protector_recipe = true
|
||||
- When true allows players to craft protection blocks
|
||||
|
||||
protector_msg = true
|
||||
- When true shows protection messages in players chat when trying to interact in someone else's area
|
||||
|
||||
|
||||
Protector Tool
|
||||
|
||||
Can be crafted with a protector surrounded by steel ingots and is used to place new protectors at a set distance of protector.radius in all directions including up and down simply by looking in a direction.
|
||||
|
||||
Use by standing near an existing protector, looking in a direction and using as a tool, hold sneak/shift to place new protector containing member list from inside nearest one.
|
239
protector/admin.lua
Normal file
@ -0,0 +1,239 @@
|
||||
|
||||
local S = protector.intllib
|
||||
local removal_names = ""
|
||||
local replace_names = ""
|
||||
|
||||
minetest.register_chatcommand("protector_remove", {
|
||||
params = S("<names list>"),
|
||||
description = S("Remove Protectors around players (separate names with spaces)"),
|
||||
privs = {server = true},
|
||||
func = function(name, param)
|
||||
|
||||
if not param or param == "" then
|
||||
|
||||
minetest.chat_send_player(name,
|
||||
S("Protector Names to remove: @1",
|
||||
removal_names))
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
if param == "-" then
|
||||
|
||||
minetest.chat_send_player(name,
|
||||
S("Name List Reset"))
|
||||
|
||||
removal_names = ""
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
removal_names = param
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
minetest.register_chatcommand("protector_replace", {
|
||||
params = S("<owner name> <name to replace with>"),
|
||||
description = S("Replace Protector Owner with name provided"),
|
||||
privs = {server = true},
|
||||
func = function(name, param)
|
||||
|
||||
-- reset list to empty
|
||||
if param == "-" then
|
||||
|
||||
minetest.chat_send_player(name, S("Name List Reset"))
|
||||
|
||||
replace_names = ""
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
-- show name info
|
||||
if param == ""
|
||||
and replace_names ~= "" then
|
||||
|
||||
local names = replace_names:split(" ")
|
||||
|
||||
minetest.chat_send_player(name,
|
||||
S("Replacing Protector name @1 with @2",
|
||||
names[1] or "", names[2] or ""))
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
replace_names = param
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
minetest.register_abm({
|
||||
nodenames = {"protector:protect", "protector:protect2", "protector:protect_hidden"},
|
||||
interval = 8,
|
||||
chance = 1,
|
||||
catch_up = false,
|
||||
action = function(pos, node)
|
||||
|
||||
if removal_names == ""
|
||||
and replace_names == "" then
|
||||
return
|
||||
end
|
||||
|
||||
local meta = minetest.get_meta(pos)
|
||||
|
||||
if not meta then return end
|
||||
|
||||
local owner = meta:get_string("owner")
|
||||
|
||||
if removal_names ~= "" then
|
||||
|
||||
local names = removal_names:split(" ")
|
||||
|
||||
for _, n in pairs(names) do
|
||||
if n == owner then
|
||||
minetest.set_node(pos, {name = "air"})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if replace_names ~= "" then
|
||||
|
||||
local names = replace_names:split(" ")
|
||||
|
||||
if names[1] and names[2] and owner == names[1] then
|
||||
meta:set_string("owner", names[2])
|
||||
meta:set_string("infotext", S("Protection (owned by @1)", names[2]))
|
||||
end
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
-- get protection radius
|
||||
local r = tonumber(minetest.settings:get("protector_radius")) or 5
|
||||
|
||||
-- show protection areas of nearby protectors owned by you (thanks agaran)
|
||||
minetest.register_chatcommand("protector_show_area", {
|
||||
params = "",
|
||||
description = S("Show protected areas of your nearby protectors"),
|
||||
privs = {},
|
||||
func = function(name, param)
|
||||
|
||||
local player = minetest.get_player_by_name(name)
|
||||
local pos = player:get_pos()
|
||||
|
||||
-- find the protector nodes
|
||||
local pos = minetest.find_nodes_in_area(
|
||||
{x = pos.x - r, y = pos.y - r, z = pos.z - r},
|
||||
{x = pos.x + r, y = pos.y + r, z = pos.z + r},
|
||||
{"protector:protect", "protector:protect2", "protector:protect_hidden"})
|
||||
|
||||
local meta, owner
|
||||
|
||||
-- show a maximum of 5 protected areas only
|
||||
for n = 1, math.min(#pos, 5) do
|
||||
|
||||
meta = minetest.get_meta(pos[n])
|
||||
owner = meta:get_string("owner") or ""
|
||||
|
||||
if owner == name
|
||||
or minetest.check_player_privs(name, {protection_bypass = true}) then
|
||||
minetest.add_entity(pos[n], "protector:display")
|
||||
end
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
|
||||
-- ability to hide protection blocks (borrowed from doors mod :)
|
||||
minetest.register_node("protector:protect_hidden", {
|
||||
description = "Hidden Protector",
|
||||
drawtype = "airlike",
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
sunlight_propagates = true,
|
||||
-- has to be walkable for falling nodes to stop falling
|
||||
walkable = true,
|
||||
pointable = false,
|
||||
diggable = false,
|
||||
buildable_to = false,
|
||||
floodable = false,
|
||||
drop = "",
|
||||
groups = {not_in_creative_inventory = 1, unbreakable = 1},
|
||||
on_blast = function() end,
|
||||
-- 1px block inside door hinge near node top
|
||||
collision_box = {
|
||||
type = "fixed",
|
||||
fixed = {-15/32, 13/32, -15/32, -13/32, 1/2, -13/32},
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
minetest.register_chatcommand("protector_show", {
|
||||
params = "",
|
||||
description = S("Show your nearby protection blocks"),
|
||||
privs = {interact = true},
|
||||
|
||||
func = function(name, param)
|
||||
|
||||
local player = minetest.get_player_by_name(name)
|
||||
|
||||
if not player then
|
||||
return false, "Player not found"
|
||||
end
|
||||
|
||||
local pos = player:get_pos()
|
||||
|
||||
local a = minetest.find_nodes_in_area(
|
||||
{x = pos.x - r, y = pos.y - r, z = pos.z - r},
|
||||
{x = pos.x + r, y = pos.y + r, z = pos.z + r},
|
||||
{"protector:protect_hidden"})
|
||||
|
||||
local meta, owner
|
||||
|
||||
for _, row in pairs(a) do
|
||||
|
||||
meta = minetest.get_meta(row)
|
||||
owner = meta:get_string("owner") or ""
|
||||
|
||||
if owner == name
|
||||
or minetest.check_player_privs(name, {protection_bypass = true}) then
|
||||
minetest.swap_node(row, {name = "protector:protect"})
|
||||
end
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_chatcommand("protector_hide", {
|
||||
params = "",
|
||||
description = S("Hide your nearby protection blocks"),
|
||||
privs = {interact = true},
|
||||
|
||||
func = function(name, param)
|
||||
|
||||
local player = minetest.get_player_by_name(name)
|
||||
|
||||
if not player then
|
||||
return false, "Player not found"
|
||||
end
|
||||
|
||||
local pos = player:get_pos()
|
||||
|
||||
local a = minetest.find_nodes_in_area(
|
||||
{x = pos.x - r, y = pos.y - r, z = pos.z - r},
|
||||
{x = pos.x + r, y = pos.y + r, z = pos.z + r},
|
||||
{"protector:protect", "protector:protect2"})
|
||||
|
||||
local meta, owner
|
||||
|
||||
for _, row in pairs(a) do
|
||||
|
||||
meta = minetest.get_meta(row)
|
||||
owner = meta:get_string("owner") or ""
|
||||
|
||||
if owner == name
|
||||
or minetest.check_player_privs(name, {protection_bypass = true}) then
|
||||
minetest.swap_node(row, {name = "protector:protect_hidden"})
|
||||
end
|
||||
end
|
||||
end
|
||||
})
|
5
protector/depends.txt
Normal file
@ -0,0 +1,5 @@
|
||||
default?
|
||||
intllib?
|
||||
lucky_block?
|
||||
mesecons_mvps?
|
||||
playerfactions?
|
1
protector/description.txt
Normal file
@ -0,0 +1 @@
|
||||
Lets players craft special blocks to protect their builds or disable PVP in areas.
|
739
protector/doors_chest.lua
Normal file
@ -0,0 +1,739 @@
|
||||
|
||||
-- Since the doors mod has changed in the latest daily builds I have taken the
|
||||
-- WTFPL licenced code from the old doors mod and included an edited version
|
||||
-- within this mod for local use.
|
||||
|
||||
local S = protector.intllib
|
||||
local F = minetest.formspec_escape
|
||||
|
||||
-- MineClone2 support
|
||||
local mcl = not minetest.registered_items["default:steel_ingot"]
|
||||
|
||||
-- Registers a door
|
||||
function register_door(name, def)
|
||||
def.groups.not_in_creative_inventory = 1
|
||||
|
||||
local box = {{-0.5, -0.5, -0.5, 0.5, 0.5, -0.5+1.5/16}}
|
||||
|
||||
def.node_box_bottom = box
|
||||
def.node_box_top = box
|
||||
def.selection_box_bottom = box
|
||||
def.selection_box_top = box
|
||||
def.sound_close_door = "doors_door_close"
|
||||
def.sound_open_door = "doors_door_open"
|
||||
|
||||
minetest.register_craftitem(name, {
|
||||
description = def.description,
|
||||
inventory_image = def.inventory_image,
|
||||
|
||||
on_place = function(itemstack, placer, pointed_thing)
|
||||
if not pointed_thing.type == "node" then
|
||||
return itemstack
|
||||
end
|
||||
|
||||
local ptu = pointed_thing.under
|
||||
local nu = minetest.get_node(ptu)
|
||||
if minetest.registered_nodes[nu.name]
|
||||
and minetest.registered_nodes[nu.name].on_rightclick then
|
||||
return minetest.registered_nodes[nu.name].on_rightclick(ptu, nu, placer, itemstack)
|
||||
end
|
||||
|
||||
local pt = pointed_thing.above
|
||||
local pt2 = {x=pt.x, y=pt.y, z=pt.z}
|
||||
pt2.y = pt2.y+1
|
||||
if
|
||||
not minetest.registered_nodes[minetest.get_node(pt).name].buildable_to or
|
||||
not minetest.registered_nodes[minetest.get_node(pt2).name].buildable_to or
|
||||
not placer or
|
||||
not placer:is_player()
|
||||
then
|
||||
return itemstack
|
||||
end
|
||||
|
||||
if minetest.is_protected(pt, placer:get_player_name()) or
|
||||
minetest.is_protected(pt2, placer:get_player_name()) then
|
||||
minetest.record_protection_violation(pt, placer:get_player_name())
|
||||
return itemstack
|
||||
end
|
||||
|
||||
local p2 = minetest.dir_to_facedir(placer:get_look_dir())
|
||||
local pt3 = {x=pt.x, y=pt.y, z=pt.z}
|
||||
if p2 == 0 then
|
||||
pt3.x = pt3.x-1
|
||||
elseif p2 == 1 then
|
||||
pt3.z = pt3.z+1
|
||||
elseif p2 == 2 then
|
||||
pt3.x = pt3.x+1
|
||||
elseif p2 == 3 then
|
||||
pt3.z = pt3.z-1
|
||||
end
|
||||
if minetest.get_item_group(minetest.get_node(pt3).name, "door") == 0 then
|
||||
minetest.set_node(pt, {name=name.."_b_1", param2=p2})
|
||||
minetest.set_node(pt2, {name=name.."_t_1", param2=p2})
|
||||
else
|
||||
minetest.set_node(pt, {name=name.."_b_2", param2=p2})
|
||||
minetest.set_node(pt2, {name=name.."_t_2", param2=p2})
|
||||
minetest.get_meta(pt):set_int("right", 1)
|
||||
minetest.get_meta(pt2):set_int("right", 1)
|
||||
end
|
||||
|
||||
if not minetest.setting_getbool("creative_mode") then
|
||||
itemstack:take_item()
|
||||
end
|
||||
return itemstack
|
||||
end,
|
||||
})
|
||||
|
||||
local tt = def.tiles_top
|
||||
local tb = def.tiles_bottom
|
||||
|
||||
local function after_dig_node(pos, name, digger)
|
||||
local node = minetest.get_node(pos)
|
||||
if node.name == name then
|
||||
minetest.node_dig(pos, node, digger)
|
||||
end
|
||||
end
|
||||
|
||||
local function on_rightclick(pos, dir, check_name, replace, replace_dir, params)
|
||||
pos.y = pos.y+dir
|
||||
if minetest.get_node(pos).name ~= check_name then
|
||||
return
|
||||
end
|
||||
local p2 = minetest.get_node(pos).param2
|
||||
p2 = params[p2+1]
|
||||
|
||||
minetest.swap_node(pos, {name=replace_dir, param2=p2})
|
||||
|
||||
pos.y = pos.y-dir
|
||||
minetest.swap_node(pos, {name=replace, param2=p2})
|
||||
|
||||
local snd_1 = def.sound_close_door
|
||||
local snd_2 = def.sound_open_door
|
||||
if params[1] == 3 then
|
||||
snd_1 = def.sound_open_door
|
||||
snd_2 = def.sound_close_door
|
||||
end
|
||||
|
||||
if minetest.get_meta(pos):get_int("right") ~= 0 then
|
||||
minetest.sound_play(snd_1, {pos = pos, gain = 0.3, max_hear_distance = 10})
|
||||
else
|
||||
minetest.sound_play(snd_2, {pos = pos, gain = 0.3, max_hear_distance = 10})
|
||||
end
|
||||
end
|
||||
|
||||
local function on_rotate(pos, node, dir, user, check_name, mode, new_param2)
|
||||
|
||||
if mode ~= screwdriver.ROTATE_FACE then
|
||||
return false
|
||||
end
|
||||
|
||||
pos.y = pos.y + dir
|
||||
if not minetest.get_node(pos).name == check_name then
|
||||
return false
|
||||
end
|
||||
if minetest.is_protected(pos, user:get_player_name()) then
|
||||
minetest.record_protection_violation(pos, user:get_player_name())
|
||||
return false
|
||||
end
|
||||
|
||||
local node2 = minetest.get_node(pos)
|
||||
node2.param2 = (node2.param2 + 1) % 4
|
||||
minetest.swap_node(pos, node2)
|
||||
|
||||
pos.y = pos.y - dir
|
||||
node.param2 = (node.param2 + 1) % 4
|
||||
minetest.swap_node(pos, node)
|
||||
return true
|
||||
end
|
||||
|
||||
minetest.register_node(name.."_b_1", {
|
||||
tiles = {tb[2], tb[2], tb[2], tb[2], tb[1], tb[1].."^[transformfx"},
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
is_ground_content = false,
|
||||
drop = name,
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = def.node_box_bottom
|
||||
},
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = def.selection_box_bottom
|
||||
},
|
||||
groups = def.groups,
|
||||
|
||||
after_dig_node = function(pos, oldnode, oldmetadata, digger)
|
||||
pos.y = pos.y+1
|
||||
after_dig_node(pos, name.."_t_1", digger)
|
||||
end,
|
||||
|
||||
on_rightclick = function(pos, node, clicker)
|
||||
if not minetest.is_protected(pos, clicker:get_player_name()) then
|
||||
on_rightclick(pos, 1, name.."_t_1", name.."_b_2", name.."_t_2", {1,2,3,0})
|
||||
end
|
||||
end,
|
||||
|
||||
on_rotate = function(pos, node, user, mode, new_param2)
|
||||
return on_rotate(pos, node, 1, user, name.."_t_1", mode)
|
||||
end,
|
||||
|
||||
sounds = def.sounds,
|
||||
sunlight_propagates = def.sunlight,
|
||||
on_blast = function() end,
|
||||
})
|
||||
|
||||
minetest.register_node(name.."_t_1", {
|
||||
tiles = {tt[2], tt[2], tt[2], tt[2], tt[1], tt[1].."^[transformfx"},
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
is_ground_content = false,
|
||||
drop = "",
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = def.node_box_top
|
||||
},
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = def.selection_box_top
|
||||
},
|
||||
groups = def.groups,
|
||||
|
||||
after_dig_node = function(pos, oldnode, oldmetadata, digger)
|
||||
pos.y = pos.y-1
|
||||
after_dig_node(pos, name.."_b_1", digger)
|
||||
end,
|
||||
|
||||
on_rightclick = function(pos, node, clicker)
|
||||
if not minetest.is_protected(pos, clicker:get_player_name()) then
|
||||
on_rightclick(pos, -1, name.."_b_1", name.."_t_2", name.."_b_2", {1,2,3,0})
|
||||
end
|
||||
end,
|
||||
|
||||
on_rotate = function(pos, node, user, mode, new_param2)
|
||||
return on_rotate(pos, node, -1, user, name.."_b_1", mode)
|
||||
end,
|
||||
|
||||
sounds = def.sounds,
|
||||
sunlight_propagates = def.sunlight,
|
||||
on_blast = function() end,
|
||||
})
|
||||
|
||||
minetest.register_node(name.."_b_2", {
|
||||
tiles = {tb[2], tb[2], tb[2], tb[2], tb[1].."^[transformfx", tb[1]},
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
is_ground_content = false,
|
||||
drop = name,
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = def.node_box_bottom
|
||||
},
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = def.selection_box_bottom
|
||||
},
|
||||
groups = def.groups,
|
||||
|
||||
after_dig_node = function(pos, oldnode, oldmetadata, digger)
|
||||
pos.y = pos.y+1
|
||||
after_dig_node(pos, name.."_t_2", digger)
|
||||
end,
|
||||
|
||||
on_rightclick = function(pos, node, clicker)
|
||||
if not minetest.is_protected(pos, clicker:get_player_name()) then
|
||||
on_rightclick(pos, 1, name.."_t_2", name.."_b_1", name.."_t_1", {3,0,1,2})
|
||||
end
|
||||
end,
|
||||
|
||||
on_rotate = function(pos, node, user, mode, new_param2)
|
||||
return on_rotate(pos, node, 1, user, name.."_t_2", mode)
|
||||
end,
|
||||
|
||||
sounds = def.sounds,
|
||||
sunlight_propagates = def.sunlight,
|
||||
on_blast = function() end,
|
||||
})
|
||||
|
||||
minetest.register_node(name.."_t_2", {
|
||||
tiles = {tt[2], tt[2], tt[2], tt[2], tt[1].."^[transformfx", tt[1]},
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
is_ground_content = false,
|
||||
drop = "",
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = def.node_box_top
|
||||
},
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = def.selection_box_top
|
||||
},
|
||||
groups = def.groups,
|
||||
|
||||
after_dig_node = function(pos, oldnode, oldmetadata, digger)
|
||||
pos.y = pos.y-1
|
||||
after_dig_node(pos, name.."_b_2", digger)
|
||||
end,
|
||||
|
||||
on_rightclick = function(pos, node, clicker)
|
||||
if not minetest.is_protected(pos, clicker:get_player_name()) then
|
||||
on_rightclick(pos, -1, name.."_b_2", name.."_t_1", name.."_b_1", {3,0,1,2})
|
||||
end
|
||||
end,
|
||||
|
||||
on_rotate = function(pos, node, user, mode, new_param2)
|
||||
return on_rotate(pos, node, -1, user, name.."_b_2", mode)
|
||||
end,
|
||||
|
||||
sounds = def.sounds,
|
||||
sunlight_propagates = def.sunlight,
|
||||
on_blast = function() end,
|
||||
})
|
||||
|
||||
end
|
||||
|
||||
-- Protected Wooden Door
|
||||
|
||||
local name = "protector:door_wood"
|
||||
|
||||
register_door(name, {
|
||||
description = S("Protected Wooden Door"),
|
||||
inventory_image = "doors_wood.png^protector_logo.png",
|
||||
groups = {
|
||||
snappy = 1, choppy = 2, oddly_breakable_by_hand = 2,
|
||||
unbreakable = 1, --door = 1
|
||||
},
|
||||
tiles_bottom = {"doors_wood_b.png^protector_logo.png", "doors_brown.png"},
|
||||
tiles_top = {"doors_wood_a.png", "doors_brown.png"},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
sunlight = false,
|
||||
})
|
||||
|
||||
if mcl then
|
||||
minetest.register_craft({
|
||||
output = name,
|
||||
recipe = {
|
||||
{"mcl_doors:wooden_door", "mcl_core:gold_ingot"}
|
||||
}
|
||||
})
|
||||
else
|
||||
minetest.register_craft({
|
||||
output = name,
|
||||
recipe = {
|
||||
{"group:wood", "group:wood"},
|
||||
{"group:wood", "default:copper_ingot"},
|
||||
{"group:wood", "group:wood"}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = name,
|
||||
recipe = {
|
||||
{"doors:door_wood", "default:copper_ingot"}
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
-- Protected Steel Door
|
||||
|
||||
local name = "protector:door_steel"
|
||||
|
||||
register_door(name, {
|
||||
description = S("Protected Steel Door"),
|
||||
inventory_image = "doors_steel.png^protector_logo.png",
|
||||
groups = {
|
||||
snappy = 1, bendy = 2, cracky = 1,
|
||||
level = 2, unbreakable = 1, -- door = 1
|
||||
},
|
||||
tiles_bottom = {"doors_steel_b.png^protector_logo.png", "doors_grey.png"},
|
||||
tiles_top = {"doors_steel_a.png", "doors_grey.png"},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
sunlight = false,
|
||||
})
|
||||
|
||||
if mcl then
|
||||
minetest.register_craft({
|
||||
output = name,
|
||||
recipe = {
|
||||
{"mcl_doors:iron_door", "mcl_core:gold_ingot"}
|
||||
}
|
||||
})
|
||||
else
|
||||
minetest.register_craft({
|
||||
output = name,
|
||||
recipe = {
|
||||
{"default:steel_ingot", "default:steel_ingot"},
|
||||
{"default:steel_ingot", "default:copper_ingot"},
|
||||
{"default:steel_ingot", "default:steel_ingot"}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = name,
|
||||
recipe = {
|
||||
{"doors:door_steel", "default:copper_ingot"}
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
----trapdoor----
|
||||
|
||||
function register_trapdoor(name, def)
|
||||
local name_closed = name
|
||||
local name_opened = name.."_open"
|
||||
|
||||
def.on_rightclick = function (pos, node, clicker, itemstack, pointed_thing)
|
||||
if minetest.is_protected(pos, clicker:get_player_name()) then
|
||||
return
|
||||
end
|
||||
local newname = node.name == name_closed and name_opened or name_closed
|
||||
local sound = false
|
||||
if node.name == name_closed then sound = "doors_door_open" end
|
||||
if node.name == name_opened then sound = "doors_door_close" end
|
||||
if sound then
|
||||
minetest.sound_play(sound, {pos = pos, gain = 0.3, max_hear_distance = 10})
|
||||
end
|
||||
minetest.swap_node(pos, {name = newname, param1 = node.param1, param2 = node.param2})
|
||||
end
|
||||
|
||||
-- Common trapdoor configuration
|
||||
def.drawtype = "nodebox"
|
||||
def.paramtype = "light"
|
||||
def.paramtype2 = "facedir"
|
||||
def.is_ground_content = false
|
||||
|
||||
local def_opened = table.copy(def)
|
||||
local def_closed = table.copy(def)
|
||||
|
||||
def_closed.node_box = {
|
||||
type = "fixed",
|
||||
fixed = {-0.5, -0.5, -0.5, 0.5, -6/16, 0.5}
|
||||
}
|
||||
def_closed.selection_box = {
|
||||
type = "fixed",
|
||||
fixed = {-0.5, -0.5, -0.5, 0.5, -6/16, 0.5}
|
||||
}
|
||||
def_closed.tiles = { def.tile_front, def.tile_front, def.tile_side, def.tile_side,
|
||||
def.tile_side, def.tile_side }
|
||||
|
||||
def_opened.node_box = {
|
||||
type = "fixed",
|
||||
fixed = {-0.5, -0.5, 6/16, 0.5, 0.5, 0.5}
|
||||
}
|
||||
def_opened.selection_box = {
|
||||
type = "fixed",
|
||||
fixed = {-0.5, -0.5, 6/16, 0.5, 0.5, 0.5}
|
||||
}
|
||||
def_opened.tiles = { def.tile_side, def.tile_side,
|
||||
def.tile_side .. "^[transform3",
|
||||
def.tile_side .. "^[transform1",
|
||||
def.tile_front, def.tile_front }
|
||||
|
||||
def_opened.drop = name_closed
|
||||
def_opened.groups.not_in_creative_inventory = 1
|
||||
|
||||
minetest.register_node(name_opened, def_opened)
|
||||
minetest.register_node(name_closed, def_closed)
|
||||
end
|
||||
|
||||
-- Protected Wooden Trapdoor
|
||||
|
||||
register_trapdoor("protector:trapdoor", {
|
||||
description = S("Protected Trapdoor"),
|
||||
inventory_image = "doors_trapdoor.png^protector_logo.png",
|
||||
wield_image = "doors_trapdoor.png^protector_logo.png",
|
||||
tile_front = "doors_trapdoor.png^protector_logo.png",
|
||||
tile_side = "doors_trapdoor_side.png",
|
||||
groups = {
|
||||
snappy = 1, choppy = 2, oddly_breakable_by_hand = 2,
|
||||
unbreakable = 1, --door = 1
|
||||
},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
})
|
||||
|
||||
if mcl then
|
||||
minetest.register_craft({
|
||||
output = "protector:trapdoor",
|
||||
recipe = {
|
||||
{"mcl_doors:trapdoor", "mcl_core:gold_ingot"}
|
||||
}
|
||||
})
|
||||
else
|
||||
minetest.register_craft({
|
||||
output = "protector:trapdoor 2",
|
||||
recipe = {
|
||||
{"group:wood", "default:copper_ingot", "group:wood"},
|
||||
{"group:wood", "group:wood", "group:wood"},
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "protector:trapdoor",
|
||||
recipe = {
|
||||
{"doors:trapdoor", "default:copper_ingot"}
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
-- Protected Steel Trapdoor
|
||||
|
||||
register_trapdoor("protector:trapdoor_steel", {
|
||||
description = S("Protected Steel Trapdoor"),
|
||||
inventory_image = "doors_trapdoor_steel.png^protector_logo.png",
|
||||
wield_image = "doors_trapdoor_steel.png^protector_logo.png",
|
||||
tile_front = "doors_trapdoor_steel.png^protector_logo.png",
|
||||
tile_side = "doors_trapdoor_steel_side.png",
|
||||
groups = {
|
||||
snappy = 1, bendy = 2, cracky = 1, melty = 2, level = 2,
|
||||
unbreakable = 1, --door = 1
|
||||
},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
})
|
||||
|
||||
if mcl then
|
||||
minetest.register_craft({
|
||||
output = "protector:trapdoor_steel",
|
||||
recipe = {
|
||||
{"mcl_doors:iron_trapdoor", "mcl_core:gold_ingot"}
|
||||
}
|
||||
})
|
||||
else
|
||||
minetest.register_craft({
|
||||
output = "protector:trapdoor_steel",
|
||||
recipe = {
|
||||
{"default:copper_ingot", "default:steel_ingot"},
|
||||
{"default:steel_ingot", "default:steel_ingot"},
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "protector:trapdoor_steel",
|
||||
recipe = {
|
||||
{"doors:trapdoor_steel", "default:copper_ingot"}
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
-- Protected Chest
|
||||
|
||||
minetest.register_node("protector:chest", {
|
||||
description = S("Protected Chest"),
|
||||
tiles = {
|
||||
"default_chest_top.png", "default_chest_top.png",
|
||||
"default_chest_side.png", "default_chest_side.png",
|
||||
"default_chest_side.png", "default_chest_front.png^protector_logo.png"
|
||||
},
|
||||
paramtype2 = "facedir",
|
||||
groups = {choppy = 2, oddly_breakable_by_hand = 2, unbreakable = 1},
|
||||
legacy_facedir_simple = true,
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
|
||||
on_construct = function(pos)
|
||||
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
|
||||
meta:set_string("infotext", S("Protected Chest"))
|
||||
meta:set_string("name", "")
|
||||
inv:set_size("main", 8 * 4)
|
||||
end,
|
||||
|
||||
can_dig = function(pos,player)
|
||||
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
|
||||
if inv:is_empty("main") then
|
||||
|
||||
if not minetest.is_protected(pos, player:get_player_name()) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
on_metadata_inventory_put = function(pos, listname, index, stack, player)
|
||||
|
||||
minetest.log("action", player:get_player_name() ..
|
||||
" moves stuff to protected chest at " ..
|
||||
minetest.pos_to_string(pos))
|
||||
end,
|
||||
|
||||
on_metadata_inventory_take = function(pos, listname, index, stack, player)
|
||||
|
||||
minetest.log("action", player:get_player_name() ..
|
||||
" takes stuff from protected chest at " ..
|
||||
minetest.pos_to_string(pos))
|
||||
end,
|
||||
|
||||
on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
|
||||
|
||||
minetest.log("action", player:get_player_name() ..
|
||||
" moves stuff inside protected chest at " ..
|
||||
minetest.pos_to_string(pos))
|
||||
end,
|
||||
|
||||
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
|
||||
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return 0
|
||||
end
|
||||
|
||||
return stack:get_count()
|
||||
end,
|
||||
|
||||
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
|
||||
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return 0
|
||||
end
|
||||
|
||||
return stack:get_count()
|
||||
end,
|
||||
|
||||
allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
|
||||
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return 0
|
||||
end
|
||||
|
||||
return count
|
||||
end,
|
||||
|
||||
on_rightclick = function(pos, node, clicker)
|
||||
|
||||
if minetest.is_protected(pos, clicker:get_player_name()) then
|
||||
return
|
||||
end
|
||||
|
||||
local meta = minetest.get_meta(pos)
|
||||
|
||||
if not meta then
|
||||
return
|
||||
end
|
||||
|
||||
local spos = pos.x .. "," .. pos.y .. "," ..pos.z
|
||||
local formspec = "size[8,9]"
|
||||
-- .. default.gui_bg
|
||||
-- .. default.gui_bg_img
|
||||
-- .. default.gui_slots
|
||||
.. "list[nodemeta:".. spos .. ";main;0,0.3;8,4;]"
|
||||
.. "button[0,4.5;2,0.25;toup;" .. F(S("To Chest")) .. "]"
|
||||
.. "field[2.3,4.8;4,0.25;chestname;;"
|
||||
.. meta:get_string("name") .. "]"
|
||||
.. "button[6,4.5;2,0.25;todn;" .. F(S("To Inventory")) .. "]"
|
||||
.. "list[current_player;main;0,5;8,1;]"
|
||||
.. "list[current_player;main;0,6.08;8,3;8]"
|
||||
.. "listring[nodemeta:" .. spos .. ";main]"
|
||||
.. "listring[current_player;main]"
|
||||
|
||||
minetest.show_formspec(
|
||||
clicker:get_player_name(),
|
||||
"protector:chest_" .. minetest.pos_to_string(pos),
|
||||
formspec)
|
||||
end,
|
||||
|
||||
on_blast = function() end,
|
||||
})
|
||||
|
||||
-- Protected Chest formspec buttons
|
||||
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
|
||||
if string.sub(formname, 0, string.len("protector:chest_")) ~= "protector:chest_" then
|
||||
return
|
||||
end
|
||||
|
||||
local pos_s = string.sub(formname,string.len("protector:chest_") + 1)
|
||||
local pos = minetest.string_to_pos(pos_s)
|
||||
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return
|
||||
end
|
||||
|
||||
local meta = minetest.get_meta(pos) ; if not meta then return end
|
||||
local chest_inv = meta:get_inventory() ; if not chest_inv then return end
|
||||
local player_inv = player:get_inventory()
|
||||
local leftover
|
||||
|
||||
if fields.toup then
|
||||
|
||||
-- copy contents of players inventory to chest
|
||||
for i, v in ipairs(player_inv:get_list("main") or {}) do
|
||||
|
||||
if chest_inv:room_for_item("main", v) then
|
||||
|
||||
leftover = chest_inv:add_item("main", v)
|
||||
|
||||
player_inv:remove_item("main", v)
|
||||
|
||||
if leftover
|
||||
and not leftover:is_empty() then
|
||||
player_inv:add_item("main", v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
elseif fields.todn then
|
||||
|
||||
-- copy contents of chest to players inventory
|
||||
for i, v in ipairs(chest_inv:get_list("main") or {}) do
|
||||
|
||||
if player_inv:room_for_item("main", v) then
|
||||
|
||||
leftover = player_inv:add_item("main", v)
|
||||
|
||||
chest_inv:remove_item("main", v)
|
||||
|
||||
if leftover
|
||||
and not leftover:is_empty() then
|
||||
chest_inv:add_item("main", v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
elseif fields.chestname then
|
||||
|
||||
-- change chest infotext to display name
|
||||
if fields.chestname ~= "" then
|
||||
|
||||
meta:set_string("name", fields.chestname)
|
||||
meta:set_string("infotext",
|
||||
S("Protected Chest (@1)", fields.chestname))
|
||||
else
|
||||
meta:set_string("infotext", S("Protected Chest"))
|
||||
end
|
||||
|
||||
end
|
||||
end)
|
||||
|
||||
-- Protected Chest recipes
|
||||
|
||||
if mcl then
|
||||
minetest.register_craft({
|
||||
output = "protector:chest",
|
||||
recipe = {
|
||||
{"mcl_chests:chest", "mcl_core:gold_ingot"},
|
||||
}
|
||||
})
|
||||
else
|
||||
minetest.register_craft({
|
||||
output = "protector:chest",
|
||||
recipe = {
|
||||
{"group:wood", "group:wood", "group:wood"},
|
||||
{"group:wood", "default:copper_ingot", "group:wood"},
|
||||
{"group:wood", "group:wood", "group:wood"},
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "protector:chest",
|
||||
recipe = {
|
||||
{"default:chest", "default:copper_ingot"},
|
||||
}
|
||||
})
|
||||
end
|
67
protector/hud.lua
Normal file
@ -0,0 +1,67 @@
|
||||
|
||||
local S = protector.intllib
|
||||
local radius = (tonumber(minetest.settings:get("protector_radius")) or 5)
|
||||
|
||||
-- radius limiter (minetest cannot handle node volume of more than 4096000)
|
||||
if radius > 22 then radius = 22 end
|
||||
|
||||
local hud = {}
|
||||
local hud_timer = 0
|
||||
local hud_interval = (tonumber(minetest.settings:get("protector_hud_interval")) or 5)
|
||||
|
||||
if hud_interval > 0 then
|
||||
minetest.register_globalstep(function(dtime)
|
||||
|
||||
-- every 5 seconds
|
||||
hud_timer = hud_timer + dtime
|
||||
if hud_timer < hud_interval then
|
||||
return
|
||||
end
|
||||
hud_timer = 0
|
||||
|
||||
for _, player in pairs(minetest.get_connected_players()) do
|
||||
|
||||
local name = player:get_player_name()
|
||||
local pos = vector.round(player:get_pos())
|
||||
local hud_text = ""
|
||||
|
||||
local protectors = minetest.find_nodes_in_area(
|
||||
{x = pos.x - radius , y = pos.y - radius , z = pos.z - radius},
|
||||
{x = pos.x + radius , y = pos.y + radius , z = pos.z + radius},
|
||||
{"protector:protect","protector:protect2", "protector:protect_hidden"})
|
||||
|
||||
if #protectors > 0 then
|
||||
local npos = protectors[1]
|
||||
local meta = minetest.get_meta(npos)
|
||||
local nodeowner = meta:get_string("owner")
|
||||
|
||||
hud_text = S("Owner: @1", nodeowner)
|
||||
end
|
||||
|
||||
if not hud[name] then
|
||||
|
||||
hud[name] = {}
|
||||
|
||||
hud[name].id = player:hud_add({
|
||||
hud_elem_type = "text",
|
||||
name = "Protector Area",
|
||||
number = 0xFFFF22,
|
||||
position = {x = 0, y = 0.95},
|
||||
offset = {x = 8, y = -8},
|
||||
text = hud_text,
|
||||
scale = {x = 200, y = 60},
|
||||
alignment = {x = 1, y = -1},
|
||||
})
|
||||
|
||||
return
|
||||
else
|
||||
player:hud_change(hud[name].id, "text", hud_text)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
minetest.register_on_leaveplayer(function(player)
|
||||
hud[player:get_player_name()] = nil
|
||||
end)
|
||||
|
||||
end
|
809
protector/init.lua
Normal file
@ -0,0 +1,809 @@
|
||||
|
||||
-- default support (for use with MineClone2 and other [games]
|
||||
default = default or {
|
||||
node_sound_stone_defaults = function(table) end,
|
||||
node_sound_wood_defaults = function(table) end,
|
||||
gui_bg = "",
|
||||
gui_bg_img = "",
|
||||
gui_slots = "",
|
||||
}
|
||||
|
||||
-- Load support for intllib.
|
||||
local MP = minetest.get_modpath(minetest.get_current_modname())
|
||||
local F = minetest.formspec_escape
|
||||
local S = minetest.get_translator and minetest.get_translator("protector") or
|
||||
dofile(MP .. "/intllib.lua")
|
||||
|
||||
-- Load support for factions
|
||||
local factions_available = minetest.global_exists("factions")
|
||||
|
||||
protector = {
|
||||
mod = "redo",
|
||||
modpath = MP,
|
||||
intllib = S
|
||||
}
|
||||
|
||||
local protector_max_share_count = 12
|
||||
-- get minetest.conf settings
|
||||
local protector_radius = tonumber(minetest.settings:get("protector_radius")) or 5
|
||||
local protector_flip = minetest.settings:get_bool("protector_flip") or false
|
||||
local protector_hurt = tonumber(minetest.settings:get("protector_hurt")) or 0
|
||||
local protector_spawn = tonumber(minetest.settings:get("protector_spawn")
|
||||
or minetest.settings:get("protector_pvp_spawn")) or 0
|
||||
local protector_show = tonumber(minetest.settings:get("protector_show_interval")) or 5
|
||||
local protector_recipe = minetest.settings:get_bool("protector_recipe") ~= false
|
||||
local protector_msg = minetest.settings:get_bool("protector_msg") ~= false
|
||||
|
||||
-- radius limiter (minetest cannot handle node volume of more than 4096000)
|
||||
if protector_radius > 22 then protector_radius = 22 end
|
||||
|
||||
|
||||
-- get static spawn position
|
||||
local statspawn = minetest.string_to_pos(minetest.settings:get("static_spawnpoint"))
|
||||
or {x = 0, y = 2, z = 0}
|
||||
|
||||
|
||||
-- return list of members as a table
|
||||
local get_member_list = function(meta)
|
||||
|
||||
return meta:get_string("members"):split(" ")
|
||||
end
|
||||
|
||||
|
||||
-- write member list table in protector meta as string
|
||||
local set_member_list = function(meta, list)
|
||||
|
||||
meta:set_string("members", table.concat(list, " "))
|
||||
end
|
||||
|
||||
|
||||
-- check for owner name
|
||||
local is_owner = function(meta, name)
|
||||
|
||||
return name == meta:get_string("owner")
|
||||
end
|
||||
|
||||
|
||||
-- check for member name
|
||||
local is_member = function(meta, name)
|
||||
|
||||
if factions_available
|
||||
and meta:get_int("faction_members") == 1 then
|
||||
|
||||
if factions.version == nil then
|
||||
|
||||
-- backward compatibility
|
||||
if factions.get_player_faction(name) ~= nil
|
||||
and factions.get_player_faction(meta:get_string("owner")) ==
|
||||
factions.get_player_faction(name) then
|
||||
return true
|
||||
end
|
||||
else
|
||||
-- is member if player and owner share at least one faction
|
||||
local owner_factions = factions.get_player_factions(name)
|
||||
local owner = meta:get_string("owner")
|
||||
|
||||
if owner_factions ~= nil and owner_factions ~= false then
|
||||
|
||||
for _, f in ipairs(owner_factions) do
|
||||
|
||||
if factions.player_is_in_faction(f, owner) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for _, n in pairs(get_member_list(meta)) do
|
||||
|
||||
if n == name then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
-- add player name to table as member
|
||||
local add_member = function(meta, name)
|
||||
|
||||
-- Constant (20) defined by player.h
|
||||
if name:len() > 25 then
|
||||
return
|
||||
end
|
||||
|
||||
-- does name already exist?
|
||||
if is_owner(meta, name)
|
||||
or is_member(meta, name) then
|
||||
return
|
||||
end
|
||||
|
||||
local list = get_member_list(meta)
|
||||
|
||||
if #list >= protector_max_share_count then
|
||||
return
|
||||
end
|
||||
|
||||
table.insert(list, name)
|
||||
|
||||
set_member_list(meta, list)
|
||||
end
|
||||
|
||||
|
||||
-- remove player name from table
|
||||
local del_member = function(meta, name)
|
||||
|
||||
local list = get_member_list(meta)
|
||||
|
||||
for i, n in pairs(list) do
|
||||
|
||||
if n == name then
|
||||
table.remove(list, i)
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
set_member_list(meta, list)
|
||||
end
|
||||
|
||||
|
||||
-- protector interface
|
||||
local protector_formspec = function(meta)
|
||||
|
||||
local formspec = "size[8,7]"
|
||||
.. default.gui_bg
|
||||
.. default.gui_bg_img
|
||||
.. default.gui_slots
|
||||
.. "label[2.5,0;" .. F(S("-- Protector interface --")) .. "]"
|
||||
.. "label[0,1;" .. F(S("PUNCH node to show protected area")) .. "]"
|
||||
.. "label[0,2;" .. F(S("Members:")) .. "]"
|
||||
.. "button_exit[2.5,6.2;3,0.5;close_me;" .. F(S("Close")) .. "]"
|
||||
.. "field_close_on_enter[protector_add_member;false]"
|
||||
|
||||
local members = get_member_list(meta)
|
||||
local npp = protector_max_share_count -- max users added to protector list
|
||||
local i = 0
|
||||
local checkbox_faction = false
|
||||
|
||||
-- Display the checkbox only if the owner is member of at least 1 faction
|
||||
if factions_available then
|
||||
|
||||
if factions.version == nil then
|
||||
|
||||
-- backward compatibility
|
||||
if factions.get_player_faction(meta:get_string("owner")) then
|
||||
checkbox_faction = true
|
||||
end
|
||||
else
|
||||
if factions.get_player_factions(meta:get_string("owner")) ~= nil then
|
||||
if next(factions.get_player_faction(meta:get_string("owner"))) then
|
||||
checkbox_faction = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if checkbox_faction then
|
||||
|
||||
formspec = formspec .. "checkbox[0,5;faction_members;"
|
||||
.. F(S("Allow faction access"))
|
||||
.. ";" .. (meta:get_int("faction_members") == 1 and
|
||||
"true" or "false") .. "]"
|
||||
|
||||
if npp > 8 then
|
||||
npp = 8
|
||||
end
|
||||
end
|
||||
|
||||
for n = 1, #members do
|
||||
|
||||
if i < npp then
|
||||
|
||||
-- show username
|
||||
formspec = formspec .. "button[" .. (i % 4 * 2)
|
||||
.. "," .. math.floor(i / 4 + 3)
|
||||
.. ";1.5,.5;protector_member;" .. F(members[n]) .. "]"
|
||||
|
||||
-- username remove button
|
||||
.. "button[" .. (i % 4 * 2 + 1.25) .. ","
|
||||
.. math.floor(i / 4 + 3)
|
||||
.. ";.75,.5;protector_del_member_" .. F(members[n]) .. ";X]"
|
||||
end
|
||||
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
if i < npp then
|
||||
|
||||
-- user name entry field
|
||||
formspec = formspec .. "field[" .. (i % 4 * 2 + 1 / 3) .. ","
|
||||
.. (math.floor(i / 4 + 3) + 1 / 3)
|
||||
.. ";1.433,.5;protector_add_member;;]"
|
||||
|
||||
-- username add button
|
||||
.."button[" .. (i % 4 * 2 + 1.25) .. ","
|
||||
.. math.floor(i / 4 + 3) .. ";.75,.5;protector_submit;+]"
|
||||
|
||||
end
|
||||
|
||||
return formspec
|
||||
end
|
||||
|
||||
|
||||
-- check if pos is inside a protected spawn area
|
||||
local inside_spawn = function(pos, radius)
|
||||
|
||||
if protector_spawn <= 0 then
|
||||
return false
|
||||
end
|
||||
|
||||
if pos.x < statspawn.x + radius
|
||||
and pos.x > statspawn.x - radius
|
||||
and pos.y < statspawn.y + radius
|
||||
and pos.y > statspawn.y - radius
|
||||
and pos.z < statspawn.z + radius
|
||||
and pos.z > statspawn.z - radius then
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
-- show protection message if enabled
|
||||
local show_msg = function(player, msg)
|
||||
|
||||
-- if messages disabled or no player name provided
|
||||
if protector_msg == false or not player or player == "" then
|
||||
return
|
||||
end
|
||||
|
||||
minetest.chat_send_player(player, msg)
|
||||
end
|
||||
|
||||
|
||||
-- Infolevel:
|
||||
-- 0 for no info
|
||||
-- 1 for "This area is owned by <owner> !" if you can't dig
|
||||
-- 2 for "This area is owned by <owner>.
|
||||
-- 3 for checking protector overlaps
|
||||
|
||||
protector.can_dig = function(r, pos, digger, onlyowner, infolevel)
|
||||
|
||||
if not digger or not pos then
|
||||
return false
|
||||
end
|
||||
|
||||
-- protector_bypass privileged users can override protection
|
||||
if infolevel == 1
|
||||
and minetest.check_player_privs(digger, {protection_bypass = true}) then
|
||||
return true
|
||||
end
|
||||
|
||||
-- infolevel 3 is only used to bypass priv check, change to 1 now
|
||||
if infolevel == 3 then infolevel = 1 end
|
||||
|
||||
-- is spawn area protected ?
|
||||
if inside_spawn(pos, protector_spawn) then
|
||||
|
||||
show_msg(digger,
|
||||
S("Spawn @1 has been protected up to a @2 block radius.",
|
||||
minetest.pos_to_string(statspawn), protector_spawn))
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
-- find the protector nodes
|
||||
local pos = minetest.find_nodes_in_area(
|
||||
{x = pos.x - r, y = pos.y - r, z = pos.z - r},
|
||||
{x = pos.x + r, y = pos.y + r, z = pos.z + r},
|
||||
{"protector:protect", "protector:protect2", "protector:protect_hidden"})
|
||||
|
||||
local meta, owner, members
|
||||
|
||||
for n = 1, #pos do
|
||||
|
||||
meta = minetest.get_meta(pos[n])
|
||||
owner = meta:get_string("owner") or ""
|
||||
members = meta:get_string("members") or ""
|
||||
|
||||
-- node change and digger isn't owner
|
||||
if infolevel == 1 and owner ~= digger then
|
||||
|
||||
-- and you aren't on the member list
|
||||
if onlyowner or not is_member(meta, digger) then
|
||||
|
||||
show_msg(digger,
|
||||
S("This area is owned by @1", owner) .. "!")
|
||||
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- when using protector as tool, show protector information
|
||||
if infolevel == 2 then
|
||||
|
||||
minetest.chat_send_player(digger,
|
||||
S("This area is owned by @1", owner) .. ".")
|
||||
|
||||
minetest.chat_send_player(digger,
|
||||
S("Protection located at: @1", minetest.pos_to_string(pos[n])))
|
||||
|
||||
if members ~= "" then
|
||||
|
||||
minetest.chat_send_player(digger, S("Members: @1.", members))
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- show when you can build on unprotected area
|
||||
if infolevel == 2 then
|
||||
|
||||
if #pos < 1 then
|
||||
|
||||
minetest.chat_send_player(digger, S("This area is not protected."))
|
||||
end
|
||||
|
||||
minetest.chat_send_player(digger, S("You can build here."))
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
local old_is_protected = minetest.is_protected
|
||||
|
||||
-- check for protected area, return true if protected and digger isn't on list
|
||||
function minetest.is_protected(pos, digger)
|
||||
|
||||
digger = digger or "" -- nil check
|
||||
|
||||
-- is area protected against digger?
|
||||
if not protector.can_dig(protector_radius, pos, digger, false, 1) then
|
||||
|
||||
local player = minetest.get_player_by_name(digger)
|
||||
|
||||
if player and player:is_player() then
|
||||
|
||||
-- hurt player if protection violated
|
||||
if protector_hurt > 0 and player:get_hp() > 0 then
|
||||
|
||||
-- This delay fixes item duplication bug (thanks luk3yx)
|
||||
minetest.after(0.1, function()
|
||||
player:set_hp(player:get_hp() - protector_hurt)
|
||||
end)
|
||||
end
|
||||
|
||||
-- flip player when protection violated
|
||||
if protector_flip then
|
||||
|
||||
-- yaw + 180°
|
||||
local yaw = player:get_look_horizontal() + math.pi
|
||||
|
||||
if yaw > 2 * math.pi then
|
||||
yaw = yaw - 2 * math.pi
|
||||
end
|
||||
|
||||
player:set_look_horizontal(yaw)
|
||||
|
||||
-- invert pitch
|
||||
player:set_look_vertical(-player:get_look_vertical())
|
||||
|
||||
-- if digging below player, move up to avoid falling through hole
|
||||
local pla_pos = player:get_pos()
|
||||
|
||||
if pos.y < pla_pos.y then
|
||||
|
||||
player:set_pos({
|
||||
x = pla_pos.x,
|
||||
y = pla_pos.y + 0.8,
|
||||
z = pla_pos.z
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- otherwise can dig or place
|
||||
return old_is_protected(pos, digger)
|
||||
end
|
||||
|
||||
|
||||
-- make sure protection block doesn't overlap another protector's area
|
||||
local check_overlap = function(itemstack, placer, pointed_thing)
|
||||
|
||||
if pointed_thing.type ~= "node" then
|
||||
return itemstack
|
||||
end
|
||||
|
||||
local pos = pointed_thing.above
|
||||
local name = placer:get_player_name()
|
||||
|
||||
-- make sure protector doesn't overlap onto protected spawn area
|
||||
if inside_spawn(pos, protector_spawn + protector_radius) then
|
||||
|
||||
minetest.chat_send_player(name,
|
||||
S("Spawn @1 has been protected up to a @2 block radius.",
|
||||
minetest.pos_to_string(statspawn), protector_spawn))
|
||||
|
||||
return itemstack
|
||||
end
|
||||
|
||||
-- make sure protector doesn't overlap any other player's area
|
||||
if not protector.can_dig(protector_radius * 2, pos, name, true, 3) then
|
||||
|
||||
minetest.chat_send_player(name,
|
||||
S("Overlaps into above players protected area"))
|
||||
|
||||
return itemstack
|
||||
end
|
||||
|
||||
return minetest.item_place(itemstack, placer, pointed_thing)
|
||||
|
||||
end
|
||||
|
||||
|
||||
-- remove protector display entities
|
||||
local del_display = function(pos)
|
||||
|
||||
local objects = minetest.get_objects_inside_radius(pos, 0.5)
|
||||
|
||||
for _, v in ipairs(objects) do
|
||||
|
||||
if v and v:get_luaentity()
|
||||
and v:get_luaentity().name == "protector:display" then
|
||||
v:remove()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- temporary pos store
|
||||
local player_pos = {}
|
||||
|
||||
-- protection node
|
||||
minetest.register_node("protector:protect", {
|
||||
description = S("Protection Block") .. " (" .. S("USE for area check") .. ")",
|
||||
drawtype = "nodebox",
|
||||
tiles = {
|
||||
"default_stone.png^protector_overlay.png",
|
||||
"default_stone.png^protector_overlay.png",
|
||||
"default_stone.png^protector_overlay.png^protector_logo.png"
|
||||
},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
groups = {dig_immediate = 2, unbreakable = 1},
|
||||
is_ground_content = false,
|
||||
paramtype = "light",
|
||||
light_source = 4,
|
||||
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-0.5 ,-0.5, -0.5, 0.5, 0.5, 0.5},
|
||||
}
|
||||
},
|
||||
|
||||
on_place = check_overlap,
|
||||
|
||||
after_place_node = function(pos, placer)
|
||||
|
||||
local meta = minetest.get_meta(pos)
|
||||
|
||||
meta:set_string("owner", placer:get_player_name() or "")
|
||||
meta:set_string("members", "")
|
||||
meta:set_string("infotext",
|
||||
S("Protection (owned by @1)", meta:get_string("owner")))
|
||||
end,
|
||||
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
|
||||
if pointed_thing.type ~= "node" then
|
||||
return
|
||||
end
|
||||
|
||||
protector.can_dig(protector_radius, pointed_thing.under,
|
||||
user:get_player_name(), false, 2)
|
||||
end,
|
||||
|
||||
on_rightclick = function(pos, node, clicker, itemstack)
|
||||
|
||||
local meta = minetest.get_meta(pos)
|
||||
local name = clicker:get_player_name()
|
||||
|
||||
if meta
|
||||
and protector.can_dig(1, pos, name, true, 1) then
|
||||
|
||||
player_pos[name] = pos
|
||||
|
||||
minetest.show_formspec(name, "protector:node", protector_formspec(meta))
|
||||
end
|
||||
end,
|
||||
|
||||
on_punch = function(pos, node, puncher)
|
||||
|
||||
if minetest.is_protected(pos, puncher:get_player_name()) then
|
||||
return
|
||||
end
|
||||
|
||||
minetest.add_entity(pos, "protector:display")
|
||||
end,
|
||||
|
||||
can_dig = function(pos, player)
|
||||
|
||||
return player and protector.can_dig(1, pos, player:get_player_name(), true, 1)
|
||||
end,
|
||||
|
||||
on_blast = function() end,
|
||||
|
||||
after_destruct = del_display
|
||||
})
|
||||
|
||||
-- default recipe and alternative for MineClone2
|
||||
if protector_recipe then
|
||||
|
||||
if minetest.registered_items["default:stone"] then
|
||||
|
||||
minetest.register_craft({
|
||||
output = "protector:protect",
|
||||
recipe = {
|
||||
{"default:stone", "default:stone", "default:stone"},
|
||||
{"default:stone", "default:gold_ingot", "default:stone"},
|
||||
{"default:stone", "default:stone", "default:stone"},
|
||||
}
|
||||
})
|
||||
else
|
||||
|
||||
minetest.register_craft({
|
||||
output = "protector:protect",
|
||||
recipe = {
|
||||
{"mcl_core:stone", "mcl_core:stone", "mcl_core:stone"},
|
||||
{"mcl_core:stone", "mcl_core:gold_ingot", "mcl_core:stone"},
|
||||
{"mcl_core:stone", "mcl_core:stone", "mcl_core:stone"},
|
||||
}
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
-- protection logo
|
||||
minetest.register_node("protector:protect2", {
|
||||
description = S("Protection Logo") .. " (" .. S("USE for area check") .. ")",
|
||||
tiles = {"protector_logo.png"},
|
||||
wield_image = "protector_logo.png",
|
||||
inventory_image = "protector_logo.png",
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
groups = {dig_immediate = 2, unbreakable = 1},
|
||||
paramtype = "light",
|
||||
paramtype2 = "wallmounted",
|
||||
legacy_wallmounted = true,
|
||||
light_source = 4,
|
||||
drawtype = "nodebox",
|
||||
sunlight_propagates = true,
|
||||
walkable = true,
|
||||
node_box = {
|
||||
type = "wallmounted",
|
||||
wall_top = {-0.375, 0.4375, -0.5, 0.375, 0.5, 0.5},
|
||||
wall_bottom = {-0.375, -0.5, -0.5, 0.375, -0.4375, 0.5},
|
||||
wall_side = {-0.5, -0.5, -0.375, -0.4375, 0.5, 0.375},
|
||||
},
|
||||
selection_box = {type = "wallmounted"},
|
||||
|
||||
on_place = check_overlap,
|
||||
|
||||
after_place_node = function(pos, placer)
|
||||
|
||||
local meta = minetest.get_meta(pos)
|
||||
|
||||
meta:set_string("owner", placer:get_player_name() or "")
|
||||
meta:set_string("members", "")
|
||||
meta:set_string("infotext",
|
||||
S("Protection (owned by @1)", meta:get_string("owner")))
|
||||
end,
|
||||
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
|
||||
if pointed_thing.type ~= "node" then
|
||||
return
|
||||
end
|
||||
|
||||
protector.can_dig(protector_radius, pointed_thing.under,
|
||||
user:get_player_name(), false, 2)
|
||||
end,
|
||||
|
||||
on_rightclick = function(pos, node, clicker, itemstack)
|
||||
|
||||
local meta = minetest.get_meta(pos)
|
||||
local name = clicker:get_player_name()
|
||||
|
||||
if meta
|
||||
and protector.can_dig(1, pos, name, true, 1) then
|
||||
|
||||
player_pos[name] = pos
|
||||
|
||||
minetest.show_formspec(name, "protector:node", protector_formspec(meta))
|
||||
end
|
||||
end,
|
||||
|
||||
on_punch = function(pos, node, puncher)
|
||||
|
||||
if minetest.is_protected(pos, puncher:get_player_name()) then
|
||||
return
|
||||
end
|
||||
|
||||
minetest.add_entity(pos, "protector:display")
|
||||
end,
|
||||
|
||||
can_dig = function(pos, player)
|
||||
|
||||
return player and protector.can_dig(1, pos, player:get_player_name(), true, 1)
|
||||
end,
|
||||
|
||||
on_blast = function() end,
|
||||
|
||||
after_destruct = del_display
|
||||
})
|
||||
|
||||
-- recipes to switch between protectors
|
||||
minetest.register_craft({
|
||||
type = "shapeless",
|
||||
output = "protector:protect",
|
||||
recipe = {"protector:protect2"}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
type = "shapeless",
|
||||
output = "protector:protect2",
|
||||
recipe = {"protector:protect"}
|
||||
})
|
||||
|
||||
|
||||
-- check formspec buttons or when name entered
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
|
||||
if formname ~= "protector:node" then
|
||||
return
|
||||
end
|
||||
|
||||
local name = player:get_player_name()
|
||||
local pos = player_pos[name]
|
||||
|
||||
if not name or not pos then
|
||||
return
|
||||
end
|
||||
|
||||
local add_member_input = fields.protector_add_member
|
||||
|
||||
-- reset formspec until close button pressed
|
||||
if (fields.close_me or fields.quit)
|
||||
and (not add_member_input or add_member_input == "") then
|
||||
player_pos[name] = nil
|
||||
return
|
||||
end
|
||||
|
||||
-- only owner can add names
|
||||
if not protector.can_dig(1, pos, player:get_player_name(), true, 1) then
|
||||
return
|
||||
end
|
||||
|
||||
-- are we adding member to a protection node ? (csm protection)
|
||||
local nod = minetest.get_node(pos).name
|
||||
|
||||
if nod ~= "protector:protect"
|
||||
and nod ~= "protector:protect2" then
|
||||
player_pos[name] = nil
|
||||
return
|
||||
end
|
||||
|
||||
local meta = minetest.get_meta(pos)
|
||||
|
||||
if not meta then
|
||||
return
|
||||
end
|
||||
|
||||
-- add faction members
|
||||
if factions_available then
|
||||
meta:set_int("faction_members", fields.faction_members == "true" and 1 or 0)
|
||||
end
|
||||
|
||||
-- add member [+]
|
||||
if add_member_input then
|
||||
|
||||
for _, i in pairs(add_member_input:split(" ")) do
|
||||
add_member(meta, i)
|
||||
end
|
||||
end
|
||||
|
||||
-- remove member [x]
|
||||
for field, value in pairs(fields) do
|
||||
|
||||
if string.sub(field, 0,
|
||||
string.len("protector_del_member_")) == "protector_del_member_" then
|
||||
|
||||
del_member(meta,
|
||||
string.sub(field,string.len("protector_del_member_") + 1))
|
||||
end
|
||||
end
|
||||
|
||||
minetest.show_formspec(name, formname, protector_formspec(meta))
|
||||
end)
|
||||
|
||||
|
||||
-- display entity shown when protector node is punched
|
||||
minetest.register_entity("protector:display", {
|
||||
physical = false,
|
||||
collisionbox = {0, 0, 0, 0, 0, 0},
|
||||
visual = "wielditem",
|
||||
-- wielditem seems to be scaled to 1.5 times original node size
|
||||
visual_size = {x = 0.67, y = 0.67},
|
||||
textures = {"protector:display_node"},
|
||||
timer = 0,
|
||||
glow = 10,
|
||||
|
||||
on_step = function(self, dtime)
|
||||
|
||||
self.timer = self.timer + dtime
|
||||
|
||||
-- remove after set number of seconds
|
||||
if self.timer > protector_show then
|
||||
self.object:remove()
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
-- Display-zone node, Do NOT place the display as a node,
|
||||
-- it is made to be used as an entity (see above)
|
||||
|
||||
local x = protector_radius
|
||||
minetest.register_node("protector:display_node", {
|
||||
tiles = {"protector_display.png"},
|
||||
use_texture_alpha = "clip", -- true,
|
||||
walkable = false,
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
-- sides
|
||||
{-(x+.55), -(x+.55), -(x+.55), -(x+.45), (x+.55), (x+.55)},
|
||||
{-(x+.55), -(x+.55), (x+.45), (x+.55), (x+.55), (x+.55)},
|
||||
{(x+.45), -(x+.55), -(x+.55), (x+.55), (x+.55), (x+.55)},
|
||||
{-(x+.55), -(x+.55), -(x+.55), (x+.55), (x+.55), -(x+.45)},
|
||||
-- top
|
||||
{-(x+.55), (x+.45), -(x+.55), (x+.55), (x+.55), (x+.55)},
|
||||
-- bottom
|
||||
{-(x+.55), -(x+.55), -(x+.55), (x+.55), -(x+.45), (x+.55)},
|
||||
-- middle (surround protector)
|
||||
{-.55,-.55,-.55, .55,.55,.55},
|
||||
},
|
||||
},
|
||||
selection_box = {
|
||||
type = "regular",
|
||||
},
|
||||
paramtype = "light",
|
||||
groups = {dig_immediate = 3, not_in_creative_inventory = 1},
|
||||
drop = "",
|
||||
})
|
||||
|
||||
|
||||
dofile(MP .. "/doors_chest.lua")
|
||||
dofile(MP .. "/pvp.lua")
|
||||
dofile(MP .. "/admin.lua")
|
||||
dofile(MP .. "/tool.lua")
|
||||
dofile(MP .. "/hud.lua")
|
||||
dofile(MP .. "/lucky_block.lua")
|
||||
|
||||
|
||||
-- stop mesecon pistons from pushing protectors
|
||||
if minetest.get_modpath("mesecons_mvps") then
|
||||
mesecon.register_mvps_stopper("protector:protect")
|
||||
mesecon.register_mvps_stopper("protector:protect2")
|
||||
mesecon.register_mvps_stopper("protector:chest")
|
||||
end
|
||||
|
||||
|
||||
print (S("[MOD] Protector Redo loaded"))
|
45
protector/intllib.lua
Normal file
@ -0,0 +1,45 @@
|
||||
|
||||
-- Fallback functions for when `intllib` is not installed.
|
||||
-- Code released under Unlicense <http://unlicense.org>.
|
||||
|
||||
-- Get the latest version of this file at:
|
||||
-- https://raw.githubusercontent.com/minetest-mods/intllib/master/lib/intllib.lua
|
||||
|
||||
local function format(str, ...)
|
||||
local args = { ... }
|
||||
local function repl(escape, open, num, close)
|
||||
if escape == "" then
|
||||
local replacement = tostring(args[tonumber(num)])
|
||||
if open == "" then
|
||||
replacement = replacement..close
|
||||
end
|
||||
return replacement
|
||||
else
|
||||
return "@"..open..num..close
|
||||
end
|
||||
end
|
||||
return (str:gsub("(@?)@(%(?)(%d+)(%)?)", repl))
|
||||
end
|
||||
|
||||
local gettext, ngettext
|
||||
if minetest.get_modpath("intllib") then
|
||||
if intllib.make_gettext_pair then
|
||||
-- New method using gettext.
|
||||
gettext, ngettext = intllib.make_gettext_pair()
|
||||
else
|
||||
-- Old method using text files.
|
||||
gettext = intllib.Getter()
|
||||
end
|
||||
end
|
||||
|
||||
-- Fill in missing functions.
|
||||
|
||||
gettext = gettext or function(msgid, ...)
|
||||
return format(msgid, ...)
|
||||
end
|
||||
|
||||
ngettext = ngettext or function(msgid, msgid_plural, n, ...)
|
||||
return format(n==1 and msgid or msgid_plural, ...)
|
||||
end
|
||||
|
||||
return gettext, ngettext
|
21
protector/license.txt
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 TenPlus1
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
182
protector/locale/de.po
Normal file
@ -0,0 +1,182 @@
|
||||
# German translation for PROTECTOR MOD.
|
||||
# Copyright (C) 2018 TenPlus1
|
||||
# This file is distributed under the same license as the PROTECTOR MOD package.
|
||||
# Xanthin <xanthin@wiuwiu.de>, 2016.
|
||||
# CodeXP <codexp@gmx.net>, 2018.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROTECTOR MOD\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-07-10 17:33+0200\n"
|
||||
"PO-Revision-Date: \n"
|
||||
"Last-Translator: CodeXP <codexp@gmx.net>\n"
|
||||
"Language-Team: \n"
|
||||
"Language: de\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: admin.lua
|
||||
msgid "Remove Protectors around players (separate names with spaces)"
|
||||
msgstr "Entferne Störschützer von bestimmten Namen in der Nähe von Spielern (trenne "
|
||||
"Namen durch Leerzeichen)"
|
||||
|
||||
#: admin.lua
|
||||
msgid "<names list>"
|
||||
msgstr "<Namensliste der Spieler>"
|
||||
|
||||
#: admin.lua
|
||||
msgid "Replace Protector Owner with name provided"
|
||||
msgstr "Ersetze Besitzer der Störschützer mit neuem Besitzer"
|
||||
|
||||
#: admin.lua
|
||||
msgid "<owner name> <name to replace with>"
|
||||
msgstr "<Name des Besitzers> <Name des neuen Besitzers>"
|
||||
|
||||
#: admin.lua
|
||||
msgid "Replacing Protector name '@1' with '@2'"
|
||||
msgstr "Ersetze Besitzer der Störschützer von '@1' mit '@2'"
|
||||
|
||||
#: admin.lua
|
||||
msgid "Show protected areas of your nearby protectors"
|
||||
msgstr "Zeige geschützte Bereiche der Störschützer in der Nähe"
|
||||
|
||||
#: admin.lua
|
||||
msgid "Protector Names to remove: @1"
|
||||
msgstr "Störschutznamen zum Entfernen: @1"
|
||||
|
||||
#: admin.lua
|
||||
msgid "Name List Reset"
|
||||
msgstr "Namensliste zurückgesetzt"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "Protected Wooden Door"
|
||||
msgstr "Geschützte Holztür"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "Protected Steel Door"
|
||||
msgstr "Geschützte Stahltür"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "Protected Trapdoor"
|
||||
msgstr "Geschützte Falltür"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "Protected Steel Trapdoor"
|
||||
msgstr "Geschützte Stahlfalltür"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "Protected Chest"
|
||||
msgstr "Geschützte Truhe"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "To Chest"
|
||||
msgstr "Zur Truhe"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "To Inventory"
|
||||
msgstr "Zum Inventar"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "Protected Chest (@1)"
|
||||
msgstr "Geschützte Truhe (@1)"
|
||||
|
||||
#: init.lua
|
||||
msgid "-- Protector interface --"
|
||||
msgstr "-- Störschutz-Interface --"
|
||||
|
||||
#: init.lua
|
||||
msgid "PUNCH node to show protected area"
|
||||
msgstr "SCHLAGE Node, um geschützten Bereich anzuzeigen oder"
|
||||
|
||||
#: init.lua
|
||||
msgid "USE for area check"
|
||||
msgstr "BENUTZE für Bereichsprüfung"
|
||||
|
||||
#: init.lua
|
||||
msgid "Members:"
|
||||
msgstr "Mitglieder:"
|
||||
|
||||
#: init.lua
|
||||
msgid "Close"
|
||||
msgstr "Schließen"
|
||||
|
||||
#: init.lua
|
||||
msgid "Protection located at: @1"
|
||||
msgstr "Störschutz befindet sich bei: @1"
|
||||
|
||||
#: init.lua
|
||||
msgid "Members: @1."
|
||||
msgstr "Mitglieder: @1."
|
||||
|
||||
#: init.lua
|
||||
msgid "This area is not protected."
|
||||
msgstr "Dieser Bereich ist nicht geschützt."
|
||||
|
||||
#: init.lua
|
||||
msgid "You can build here."
|
||||
msgstr "Du kannst hier bauen."
|
||||
|
||||
#: init.lua tool.lua
|
||||
msgid "Overlaps into above players protected area"
|
||||
msgstr "Überlappung im geschützen Bereich eines Spielers"
|
||||
|
||||
#: init.lua
|
||||
msgid "Protection Block"
|
||||
msgstr "Störschutzblock"
|
||||
|
||||
#: admin.lua init.lua tool.lua
|
||||
msgid "Protection (owned by @1)"
|
||||
msgstr "Störschutz (gehört @1)"
|
||||
|
||||
#: init.lua
|
||||
msgid "Protection Logo"
|
||||
msgstr "Störschutzlogo"
|
||||
|
||||
#: init.lua
|
||||
msgid "[MOD] Protector Redo loaded"
|
||||
msgstr "[MOD] Protector Redo geladen"
|
||||
|
||||
#: init.lua
|
||||
msgid "Spawn @1 has been protected up to a @2 block radius."
|
||||
msgstr "Spawn @1 ist geschützt mit einem Radius von @2 Blöcke."
|
||||
|
||||
#: init.lua
|
||||
msgid "This area is owned by @1"
|
||||
msgstr "Dieser Bereich gehört @1"
|
||||
|
||||
#: pvp.lua
|
||||
msgid "[Protector] on_punchplayer called with nil objects"
|
||||
msgstr "on_punchplayer wurde durch \"nil objects\" aufgerufen"
|
||||
|
||||
#: pvp.lua
|
||||
msgid "[Protector] pvp_protect not active, update your version of Minetest"
|
||||
msgstr "pvp_protect ist nicht aktiv, aktualisiere deine Minetestversion"
|
||||
|
||||
#: pvp.lua
|
||||
msgid "[Protector] pvp_protect is disabled"
|
||||
msgstr "pvp_protect ist ausgeschaltet"
|
||||
|
||||
#: hud.lua
|
||||
msgid "Owner: @1"
|
||||
msgstr "Besitzer: @1"
|
||||
|
||||
#: tool.lua
|
||||
msgid "Protector Placer Tool (stand near protector, face direction and use)"
|
||||
msgstr ""
|
||||
"Störschutz Platzier-Werkzeug (stehe neben Störschutz, schaue in die "
|
||||
"gewünschte Richtung und anwenden)"
|
||||
|
||||
#: tool.lua
|
||||
msgid "Protector already in place!"
|
||||
msgstr "Störschutz is bereits platziert!"
|
||||
|
||||
#: tool.lua
|
||||
msgid "No protectors available to place!"
|
||||
msgstr "Keine Störschützer mehr im Inventar!"
|
||||
|
||||
#: tool.lua
|
||||
msgid "Protector placed at @1"
|
||||
msgstr "Störschutz befindet sich bei: @1"
|
177
protector/locale/es.po
Normal file
@ -0,0 +1,177 @@
|
||||
# Spanish translation for PROTECTOR MOD.
|
||||
# Copyright (C) 2018 universales
|
||||
# This file is distributed under the same license as the PROTECTOR MOD package.
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROTECTOR MOD\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-07-10 17:33+0200\n"
|
||||
"PO-Revision-Date: 2020-02-27\n"
|
||||
"Last-Translator: Universales\n"
|
||||
"Language-Team: \n"
|
||||
"Language: es\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: admin.lua
|
||||
msgid "Remove Protectors around players (separate names with spaces)"
|
||||
msgstr "Eliminar protectores alrededor de los jugadores (nombres separados con espacios)"
|
||||
|
||||
#: admin.lua
|
||||
msgid "<names list>"
|
||||
msgstr "<Lista de nombres>"
|
||||
|
||||
#: admin.lua
|
||||
msgid "Replace Protector Owner with name provided"
|
||||
msgstr "Reemplace el propietario del protector con el nombre proporcionado"
|
||||
|
||||
#: admin.lua
|
||||
msgid "<owner name> <name to replace with>"
|
||||
msgstr "<Nombre del propietario> <Nombre del nuevo propietario>"
|
||||
|
||||
#: admin.lua
|
||||
msgid "Replacing Protector name '@1' with '@2'"
|
||||
msgstr "Reemplazando el nombre del protector '@1' a '@2'"
|
||||
|
||||
#: admin.lua
|
||||
msgid "Show protected areas of your nearby protectors"
|
||||
msgstr "Mostrar áreas protegidas de sus protectores cercanos"
|
||||
|
||||
#: admin.lua
|
||||
msgid "Protector Names to remove: @1"
|
||||
msgstr "Nombres de protectores para eliminar: @1"
|
||||
|
||||
#: admin.lua
|
||||
msgid "Name List Reset"
|
||||
msgstr "Restablecer lista de nombres"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "Protected Wooden Door"
|
||||
msgstr "Puerta de madera protegida"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "Protected Steel Door"
|
||||
msgstr "Puerta de hierro protegida"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "Protected Trapdoor"
|
||||
msgstr "Trampilla Protegida"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "Protected Steel Trapdoor"
|
||||
msgstr "Trampilla de hierro protegida"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "Protected Chest"
|
||||
msgstr "Cofre protegido"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "To Chest"
|
||||
msgstr "Al cofre"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "To Inventory"
|
||||
msgstr "Al inventario"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "Protected Chest (@1)"
|
||||
msgstr "Cofre protegido (@1)"
|
||||
|
||||
#: init.lua
|
||||
msgid "-- Protector interface --"
|
||||
msgstr "-- Interfaz del protector --"
|
||||
|
||||
#: init.lua
|
||||
msgid "PUNCH node to show protected area"
|
||||
msgstr "nodo de perforación para mostrar el área protegida"
|
||||
|
||||
#: init.lua
|
||||
msgid "USE for area check"
|
||||
msgstr "Usar para chequeo del área"
|
||||
|
||||
#: init.lua
|
||||
msgid "Members:"
|
||||
msgstr "Miembros:"
|
||||
|
||||
#: init.lua
|
||||
msgid "Close"
|
||||
msgstr "Cerrar"
|
||||
|
||||
#: init.lua
|
||||
msgid "Protection located at: @1"
|
||||
msgstr "Protección ubicada en: @1"
|
||||
|
||||
#: init.lua
|
||||
msgid "Members: @1."
|
||||
msgstr "Miembros: @1."
|
||||
|
||||
#: init.lua
|
||||
msgid "This area is not protected."
|
||||
msgstr "Esta área no está protegida."
|
||||
|
||||
#: init.lua
|
||||
msgid "You can build here."
|
||||
msgstr "Puedes construir aquí."
|
||||
|
||||
#: init.lua tool.lua
|
||||
msgid "Overlaps into above players protected area"
|
||||
msgstr "Se superpone en el área protegida de los jugadores anteriores"
|
||||
|
||||
#: init.lua
|
||||
msgid "Protection Block"
|
||||
msgstr "Bloque de protección"
|
||||
|
||||
#: admin.lua init.lua tool.lua
|
||||
msgid "Protection (owned by @1)"
|
||||
msgstr "Protegido (Propiedad de @1)"
|
||||
|
||||
#: init.lua
|
||||
msgid "Protection Logo"
|
||||
msgstr "Logotipo de la protección"
|
||||
|
||||
#: init.lua
|
||||
msgid "[MOD] Protector Redo loaded"
|
||||
msgstr "[MOD] Protector recargado"
|
||||
|
||||
#: init.lua
|
||||
msgid "Spawn @1 has been protected up to a @2 block radius."
|
||||
msgstr "Spawn @1 ha sido protegido hasta un radio de bloque @2."
|
||||
|
||||
#: init.lua
|
||||
msgid "This area is owned by @1"
|
||||
msgstr "Esta área es propiedad de @1"
|
||||
|
||||
#: pvp.lua
|
||||
msgid "[Protector] on_punchplayer called with nil objects"
|
||||
msgstr "[Protector] on_punchplayer llamado con objetos nulos."
|
||||
|
||||
#: pvp.lua
|
||||
msgid "[Protector] pvp_protect not active, update your version of Minetest"
|
||||
msgstr "[Protector] pvp_protect no está activo, actualiza tu versión de Minetest"
|
||||
|
||||
#: pvp.lua
|
||||
msgid "[Protector] pvp_protect is disabled"
|
||||
msgstr "[Protector] pvp_protect está deshabilitado"
|
||||
|
||||
#: hud.lua
|
||||
msgid "Owner: @1"
|
||||
msgstr "Propietario: @1"
|
||||
|
||||
#: tool.lua
|
||||
msgid "Protector Placer Tool (stand near protector, face direction and use)"
|
||||
msgstr ""
|
||||
"Herramienta de colocación del protector (pararse cerca del protector, dirección de la cara y uso) "
|
||||
|
||||
#: tool.lua
|
||||
msgid "Protector already in place!"
|
||||
msgstr "¡El protector ya está en este lugar!"
|
||||
|
||||
#: tool.lua
|
||||
msgid "No protectors available to place!"
|
||||
msgstr "¡No hay protectores disponibles para colocar!"
|
||||
|
||||
#: tool.lua
|
||||
msgid "Protector placed at @1"
|
||||
msgstr "Protector colocado en @1"
|
183
protector/locale/fr.po
Normal file
@ -0,0 +1,183 @@
|
||||
# French translation for PROTECTOR MOD.
|
||||
# Copyright (C) 2018 TenPlus1
|
||||
# This file is distributed under the same license as the PROTECTOR MOD package.
|
||||
# Doloment, 2017.
|
||||
# CodeXP <codexp@gmx.net>, 2018.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROTECTOR MOD\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-07-10 17:33+0200\n"
|
||||
"PO-Revision-Date: \n"
|
||||
"Last-Translator: CodeXP <codexp@gmx.net>\n"
|
||||
"Language-Team: \n"
|
||||
"Language: fr\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: admin.lua
|
||||
msgid "Remove Protectors around players (separate names with spaces)"
|
||||
msgstr "Retirer les protecteurs près des joueurs avec les noms fournis (noms séparés "
|
||||
"avec des espaces)"
|
||||
|
||||
#: admin.lua
|
||||
msgid "<names list>"
|
||||
msgstr ""
|
||||
|
||||
#: admin.lua
|
||||
msgid "Replace Protector Owner with name provided"
|
||||
msgstr ""
|
||||
|
||||
#: admin.lua
|
||||
msgid "<owner name> <name to replace with>"
|
||||
msgstr ""
|
||||
|
||||
#: admin.lua
|
||||
msgid "Replacing Protector name '@1' with '@2'"
|
||||
msgstr ""
|
||||
|
||||
#: admin.lua
|
||||
msgid "Show protected areas of your nearby protectors"
|
||||
msgstr ""
|
||||
|
||||
#: admin.lua
|
||||
msgid "Protector Names to remove: @1"
|
||||
msgstr "Noms de protecteurs à supprimer: @1"
|
||||
|
||||
#: admin.lua
|
||||
msgid "Name List Reset"
|
||||
msgstr "Liste de noms réinitialiser"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "Protected Wooden Door"
|
||||
msgstr "Porte en bois protégée"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "Protected Steel Door"
|
||||
msgstr "Porte en acier protégée"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "Protected Trapdoor"
|
||||
msgstr "Trappe protégé"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "Protected Steel Trapdoor"
|
||||
msgstr "Trap en acier protégé"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "Protected Chest"
|
||||
msgstr "Coffre protégé"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "To Chest"
|
||||
msgstr "Vers le coffre"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "To Inventory"
|
||||
msgstr "Vers l'inventaire"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "Protected Chest (@1)"
|
||||
msgstr "Coffre protégé (@1)"
|
||||
|
||||
#: init.lua
|
||||
msgid "-- Protector interface --"
|
||||
msgstr "-- Interface Protector --"
|
||||
|
||||
#: init.lua
|
||||
msgid "PUNCH node to show protected area"
|
||||
msgstr "TAPÉ le bloc pour afficher la zone protégée"
|
||||
|
||||
#: init.lua
|
||||
msgid "USE for area check"
|
||||
msgstr "UTILISER pour vérifier la zone"
|
||||
|
||||
#: init.lua
|
||||
msgid "Members:"
|
||||
msgstr "Membres:"
|
||||
|
||||
#: init.lua
|
||||
msgid "Close"
|
||||
msgstr "Fermer"
|
||||
|
||||
#: init.lua
|
||||
msgid "Protection located at: @1"
|
||||
msgstr "Protection située à: @1"
|
||||
|
||||
#: init.lua
|
||||
msgid "Members: @1."
|
||||
msgstr "Membres: @1."
|
||||
|
||||
#: init.lua
|
||||
msgid "This area is not protected."
|
||||
msgstr "Cette zone n'est pas protégée."
|
||||
|
||||
#: init.lua
|
||||
msgid "You can build here."
|
||||
msgstr "Vous pouvez construire ici."
|
||||
|
||||
#: init.lua tool.lua
|
||||
msgid "Overlaps into above players protected area"
|
||||
msgstr "Vous chevauché une zone protégé."
|
||||
|
||||
#: init.lua
|
||||
msgid "Protection Block"
|
||||
msgstr "Bloc de protection"
|
||||
|
||||
#: admin.lua init.lua tool.lua
|
||||
msgid "Protection (owned by @1)"
|
||||
msgstr "Protection (détenue par @1)"
|
||||
|
||||
#: init.lua
|
||||
msgid "Protection Logo"
|
||||
msgstr "Logo de protection"
|
||||
|
||||
#: init.lua
|
||||
msgid "[MOD] Protector Redo loaded"
|
||||
msgstr "[MOD] Protector Redo chargé"
|
||||
|
||||
#: init.lua
|
||||
msgid "Spawn @1 has been protected up to a @2 block radius."
|
||||
msgstr ""
|
||||
|
||||
#: init.lua
|
||||
#, fuzzy
|
||||
msgid "This area is owned by @1"
|
||||
msgstr "Cette zone appartient à @1!"
|
||||
|
||||
#: pvp.lua
|
||||
msgid "[Protector] on_punchplayer called with nil objects"
|
||||
msgstr "[Protector] on_punchplayer appelé avec des objets nil"
|
||||
|
||||
#: pvp.lua
|
||||
msgid "[Protector] pvp_protect not active, update your version of Minetest"
|
||||
msgstr ""
|
||||
"[Protector] pvp_protect est désactivé, mettez à jour votre version de "
|
||||
"Minetest"
|
||||
|
||||
#: pvp.lua
|
||||
msgid "[Protector] pvp_protect is disabled"
|
||||
msgstr "[Protector] pvp_protect est désactivé"
|
||||
|
||||
#: hud.lua
|
||||
msgid "Owner: @1"
|
||||
msgstr ""
|
||||
|
||||
#: tool.lua
|
||||
msgid "Protector Placer Tool (stand near protector, face direction and use)"
|
||||
msgstr ""
|
||||
|
||||
#: tool.lua
|
||||
msgid "Protector already in place!"
|
||||
msgstr ""
|
||||
|
||||
#: tool.lua
|
||||
msgid "No protectors available to place!"
|
||||
msgstr ""
|
||||
|
||||
#: tool.lua
|
||||
msgid "Protector placed at @1"
|
||||
msgstr "Protection située à: @1"
|
180
protector/locale/it.po
Normal file
@ -0,0 +1,180 @@
|
||||
# Italian translation for PROTECTOR MOD.
|
||||
# Copyright (C) 2020 Hamlet
|
||||
# This file is distributed under the same license as the PROTECTOR MOD package.
|
||||
# Xanthin <xanthin@wiuwiu.de>, 2016.
|
||||
# CodeXP <codexp@gmx.net>, 2018.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROTECTOR MOD\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-07-10 17:33+0200\n"
|
||||
"PO-Revision-Date: \n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: Poedit 2.2.1\n"
|
||||
"Last-Translator: Hamlet <hamlatgitlab@riseup.net>\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"Language: it\n"
|
||||
|
||||
#: admin.lua
|
||||
msgid "Remove Protectors around players (separate names with spaces)"
|
||||
msgstr "Elimina i protettori attorno ai giocatori (separa i nomi con gli spazi)"
|
||||
|
||||
#: admin.lua
|
||||
msgid "<names list>"
|
||||
msgstr "<elenco nomi>"
|
||||
|
||||
#: admin.lua
|
||||
msgid "Replace Protector Owner with name provided"
|
||||
msgstr "Sostituisci il proprietario del protettore col nome fornito"
|
||||
|
||||
#: admin.lua
|
||||
msgid "<owner name> <name to replace with>"
|
||||
msgstr "<nome proprietario> <nome con cui sostituirlo>"
|
||||
|
||||
#: admin.lua
|
||||
msgid "Replacing Protector name '@1' with '@2'"
|
||||
msgstr "Sostituzione del nome del protettore '@1' con '@2'"
|
||||
|
||||
#: admin.lua
|
||||
msgid "Show protected areas of your nearby protectors"
|
||||
msgstr "Mostra le aree protette dei protettori vicino a te"
|
||||
|
||||
#: admin.lua
|
||||
msgid "Protector Names to remove: @1"
|
||||
msgstr "Nomi dei protettori da eliminare: @1"
|
||||
|
||||
#: admin.lua
|
||||
msgid "Name List Reset"
|
||||
msgstr "Azzera l'elenco dei nomi"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "Protected Wooden Door"
|
||||
msgstr "Porta di legno protetta"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "Protected Steel Door"
|
||||
msgstr "Porta d'acciaio protetta"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "Protected Trapdoor"
|
||||
msgstr "Botola protetta"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "Protected Steel Trapdoor"
|
||||
msgstr "Botola d'acciaio protetta"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "Protected Chest"
|
||||
msgstr "Baule protetto"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "To Chest"
|
||||
msgstr "Al baule"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "To Inventory"
|
||||
msgstr "All'inventario"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "Protected Chest (@1)"
|
||||
msgstr "Baule protetto (@1)"
|
||||
|
||||
#: init.lua
|
||||
msgid "-- Protector interface --"
|
||||
msgstr "-- Interfaccia protettore --"
|
||||
|
||||
#: init.lua
|
||||
msgid "PUNCH node to show protected area"
|
||||
msgstr "COLPISCI il nodo per mostrare l'area protetta"
|
||||
|
||||
#: init.lua
|
||||
msgid "USE for area check"
|
||||
msgstr "USA per controllare l'area"
|
||||
|
||||
#: init.lua
|
||||
msgid "Members:"
|
||||
msgstr "Membri:"
|
||||
|
||||
#: init.lua
|
||||
msgid "Close"
|
||||
msgstr "Chiudi"
|
||||
|
||||
#: init.lua
|
||||
msgid "Protection located at: @1"
|
||||
msgstr "Protezione collocata a: @1"
|
||||
|
||||
#: init.lua
|
||||
msgid "Members: @1."
|
||||
msgstr "Membri: @1."
|
||||
|
||||
#: init.lua
|
||||
msgid "This area is not protected."
|
||||
msgstr "Quest'area non è protetta."
|
||||
|
||||
#: init.lua
|
||||
msgid "You can build here."
|
||||
msgstr "Qui puoi costruire."
|
||||
|
||||
#: init.lua tool.lua
|
||||
msgid "Overlaps into above players protected area"
|
||||
msgstr "Si sovrappone ad un'area sovrastante protetta dai giocatori"
|
||||
|
||||
#: init.lua
|
||||
msgid "Protection Block"
|
||||
msgstr "Blocco di protezione"
|
||||
|
||||
#: admin.lua init.lua tool.lua
|
||||
msgid "Protection (owned by @1)"
|
||||
msgstr "Protezione (di proprietà di @1)"
|
||||
|
||||
#: init.lua
|
||||
msgid "Protection Logo"
|
||||
msgstr "Logo di protezione"
|
||||
|
||||
#: init.lua
|
||||
msgid "[MOD] Protector Redo loaded"
|
||||
msgstr "[MOD] Protector Redo caricato"
|
||||
|
||||
#: init.lua
|
||||
msgid "Spawn @1 has been protected up to a @2 block radius."
|
||||
msgstr "Lo spawn @1 è stato protetto fino a un raggio di @2 blocchi."
|
||||
|
||||
#: init.lua
|
||||
msgid "This area is owned by @1"
|
||||
msgstr "Quest'area è di proprietà di @1"
|
||||
|
||||
#: pvp.lua
|
||||
msgid "[Protector] on_punchplayer called with nil objects"
|
||||
msgstr "[Protector] on_punchplayer chiamato con oggetti nil"
|
||||
|
||||
#: pvp.lua
|
||||
msgid "[Protector] pvp_protect not active, update your version of Minetest"
|
||||
msgstr "[Protector] pvp_protect non attiva, aggiorna la tua versione di Minetest"
|
||||
|
||||
#: pvp.lua
|
||||
msgid "[Protector] pvp_protect is disabled"
|
||||
msgstr "[Protector] pvp_protect è disattivato"
|
||||
|
||||
#: hud.lua
|
||||
msgid "Owner: @1"
|
||||
msgstr "Proprietario: @1"
|
||||
|
||||
#: tool.lua
|
||||
msgid "Protector Placer Tool (stand near protector, face direction and use)"
|
||||
msgstr "Strumento di posizionamento protettore (stai vicino al protettore, guarda la direzione e usa)"
|
||||
|
||||
#: tool.lua
|
||||
msgid "Protector already in place!"
|
||||
msgstr "Protettore già presente!"
|
||||
|
||||
#: tool.lua
|
||||
msgid "No protectors available to place!"
|
||||
msgstr "Nessun protettore disponibile da posizionare!"
|
||||
|
||||
#: tool.lua
|
||||
msgid "Protector placed at @1"
|
||||
msgstr "Protettore posizionato a @1"
|
55
protector/locale/protector.de.tr
Normal file
@ -0,0 +1,55 @@
|
||||
# textdomain: protector
|
||||
# author: Xanthin and CodeXP
|
||||
# last update: 2020/Jul/12
|
||||
|
||||
### admin.lua ###
|
||||
Remove Protectors around players (separate names with spaces)=Entferne Störschützer von bestimmten Namen in der Nähe von Spielern (trenne Namen durch Leerzeichen)
|
||||
<names list>=<Namensliste der Spieler>
|
||||
Replace Protector Owner with name provided=Ersetze Besitzer der Störschützer mit neuem Besitzer
|
||||
<owner name> <name to replace with>=<Name des Besitzers> <Name des neuen Besitzers>
|
||||
Replacing Protector name '@1' with '@2'=Ersetze Besitzer der Störschützer von '@1' mit '@2'
|
||||
Show protected areas of your nearby protectors=Zeige geschützte Bereiche der Störschützer in der Nähe
|
||||
Protector Names to remove: @1=Störschutznamen zum Entfernen: @1
|
||||
Name List Reset=Namensliste zurückgesetzt
|
||||
|
||||
### doors_chest.lua ###
|
||||
Protected Wooden Door=Geschützte Holztür
|
||||
Protected Steel Door=Geschützte Stahltür
|
||||
Protected Trapdoor=Geschützte Falltür
|
||||
Protected Steel Trapdoor=Geschützte Stahlfalltür
|
||||
Protected Chest=Geschützte Truhe
|
||||
To Chest=Zur Truhe
|
||||
To Inventory=Zum Inventar
|
||||
Protected Chest (@1)=Geschützte Truhe (@1)
|
||||
|
||||
### init.lua ###
|
||||
-- Protector interface --=-- Störschutz-Interface --
|
||||
PUNCH node to show protected area=SCHLAGE Node, um geschützten Bereich anzuzeigen oder
|
||||
USE for area check=BENUTZE für Bereichsprüfung
|
||||
Members:=Mitglieder:
|
||||
Close=Schließen
|
||||
Protection located at: @1=Störschutz befindet sich bei: @1
|
||||
Members: @1.=Mitglieder: @1.
|
||||
This area is not protected.=Dieser Bereich ist nicht geschützt.
|
||||
You can build here.=Du kannst hier bauen.
|
||||
Overlaps into above players protected area=Überlappung im geschützen Bereich eines Spielers
|
||||
Protection Block=Störschutzblock
|
||||
Protection (owned by @1)=Störschutz (gehört @1)
|
||||
Protection Logo=Störschutzlogo
|
||||
[MOD] Protector Redo loaded=[MOD] Protector Redo geladen
|
||||
Spawn @1 has been protected up to a @2 block radius.=Spawn @1 ist geschützt mit einem Radius von @2 Blöcke.
|
||||
This area is owned by @1=Dieser Bereich gehört @1
|
||||
|
||||
### pvp.lua ###
|
||||
[Protector] on_punchplayer called with nil objects=on_punchplayer wurde durch \"nil objects\" aufgerufen
|
||||
[Protector] pvp_protect not active, update your version of Minetest=pvp_protect ist nicht aktiv, aktualisiere deine Minetestversion
|
||||
[Protector] pvp_protect is disabled=pvp_protect ist ausgeschaltet
|
||||
|
||||
### hud.lua ###
|
||||
Owner: @1=Besitzer: @1
|
||||
|
||||
### tool.lua ###
|
||||
Protector Placer Tool (stand near protector, face direction and use)=Störschutz Platzier-Werkzeug (stehe neben Störschutz, schaue in die gewünschte Richtung und anwenden)
|
||||
Protector already in place!=Störschutz is bereits platziert!
|
||||
No protectors available to place!=Keine Störschützer mehr im Inventar!
|
||||
"Protector placed at @1"=Störschutz befindet sich bei: @1
|
55
protector/locale/protector.es.tr
Normal file
@ -0,0 +1,55 @@
|
||||
# textdomain: protector
|
||||
# author: universales
|
||||
# last update: 2020-02-27
|
||||
|
||||
### admin.lua ###
|
||||
Remove Protectors around players (separate names with spaces)=Eliminar protectores alrededor de los jugadores (nombres separados con espacios)
|
||||
<names list>=<Lista de nombres>
|
||||
Replace Protector Owner with name provided=Reemplace el propietario del protector con el nombre proporcionado
|
||||
<owner name> <name to replace with>=<Nombre del propietario> <Nombre del nuevo propietario>
|
||||
Replacing Protector name '@1' with '@2'=Reemplazando el nombre del protector '@1' a '@2'
|
||||
Show protected areas of your nearby protectors=Mostrar áreas protegidas de sus protectores cercanos
|
||||
Protector Names to remove: @1=Nombres de protectores para eliminar: @1
|
||||
Name List Reset=Restablecer lista de nombres
|
||||
|
||||
### doors_chest.lua ###
|
||||
Protected Wooden Door=Puerta de madera protegida
|
||||
Protected Steel Door=Puerta de hierro protegida
|
||||
Protected Trapdoor=Trampilla Protegida
|
||||
Protected Steel Trapdoor=Trampilla de hierro protegida
|
||||
Protected Chest=Cofre protegido
|
||||
To Chest=Al cofre
|
||||
To Inventory=Al inventario
|
||||
Protected Chest (@1)=Cofre protegido (@1)
|
||||
|
||||
### init.lua ###
|
||||
-- Protector interface --=-- Interfaz del protector --
|
||||
PUNCH node to show protected area=nodo de perforación para mostrar el área protegida
|
||||
USE for area check=Usar para chequeo del área
|
||||
Members:=Miembros:
|
||||
Close=Cerrar
|
||||
Protection located at: @1=Protección ubicada en: @1
|
||||
Members: @1.=Miembros: @1.
|
||||
This area is not protected.=Esta área no está protegida.
|
||||
You can build here.=Puedes construir aquí.
|
||||
Overlaps into above players protected area=Se superpone en el área protegida de los jugadores anteriores
|
||||
Protection Block=Bloque de protección
|
||||
Protection (owned by @1)=Protegido (Propiedad de @1)
|
||||
Protection Logo=Logotipo de la protección
|
||||
[MOD] Protector Redo loaded=[MOD] Protector recargado
|
||||
Spawn @1 has been protected up to a @2 block radius.=Spawn @1 ha sido protegido hasta un radio de bloque @2.
|
||||
This area is owned by @1=Esta área es propiedad de @1
|
||||
|
||||
### pvp.lua ###
|
||||
[Protector] on_punchplayer called with nil objects=[Protector] on_punchplayer llamado con objetos nulos.
|
||||
[Protector] pvp_protect not active, update your version of Minetest=[Protector] pvp_protect no está activo, actualiza tu versión de Minetest
|
||||
[Protector] pvp_protect is disabled=[Protector] pvp_protect está deshabilitado
|
||||
|
||||
### hud.lua ###
|
||||
Owner: @1=Propietario: @1
|
||||
|
||||
### tool.lua ###
|
||||
Protector Placer Tool (stand near protector, face direction and use)=Herramienta de colocación del protector (pararse cerca del protector, dirección de la cara y uso)
|
||||
Protector already in place!=¡El protector ya está en este lugar!
|
||||
No protectors available to place!=¡No hay protectores disponibles para colocar!
|
||||
Protector placed at @1=Protector colocado en @1
|
55
protector/locale/protector.fr.tr
Normal file
@ -0,0 +1,55 @@
|
||||
# textdomain: protector
|
||||
# author: CodeXP and TenPlus1
|
||||
# last update: 2020/Jul/12
|
||||
|
||||
### admin.lua ###
|
||||
Remove Protectors around players (separate names with spaces)=Retirer les protecteurs près des joueurs avec les noms fournis (noms séparés avec des espaces)
|
||||
<names list>=<liste de noms>
|
||||
Replace Protector Owner with name provided=Remplacer le propriétaire du protecteur par le nom fourni
|
||||
<owner name> <name to replace with>=<nom du propriétaire> <nom à remplacer>
|
||||
Replacing Protector name '@1' with '@2'=
|
||||
Show protected areas of your nearby protectors=Affichez les zones protégées de vos protecteurs à proximité
|
||||
Protector Names to remove: @1=Noms de protecteurs à supprimer: @1
|
||||
Name List Reset=Liste de noms réinitialiser
|
||||
|
||||
### doors_chest.lua ###
|
||||
Protected Wooden Door=Porte en bois protégée
|
||||
Protected Steel Door=Porte en acier protégée
|
||||
Protected Trapdoor=Trappe protégé
|
||||
Protected Steel Trapdoor=Trap en acier protégé
|
||||
Protected Chest=Coffre protégé
|
||||
To Chest=Vers le coffre
|
||||
To Inventory=Vers l'inventaire
|
||||
Protected Chest (@1)=Coffre protégé (@1)
|
||||
|
||||
### init.lua ###
|
||||
-- Protector interface --=-- Interface Protector --
|
||||
PUNCH node to show protected area=TAPÉ le bloc pour afficher la zone protégée
|
||||
USE for area check=UTILISER pour vérifier la zone
|
||||
Members:=Membres:
|
||||
Close=Fermer
|
||||
Protection located at: @1=Protection située à: @1
|
||||
Members: @1.=Membres: @1.
|
||||
This area is not protected.=msgstr "Cette zone n'est pas protégée.
|
||||
You can build here.=Vous pouvez construire ici.
|
||||
Overlaps into above players protected area=Vous chevauché une zone protégé.
|
||||
Protection Block=Bloc de protection
|
||||
Protection (owned by @1)=Protection (détenue par @1)
|
||||
Protection Logo=Logo de protection
|
||||
[MOD] Protector Redo loaded=[MOD] Protector Redo chargé
|
||||
Spawn @1 has been protected up to a @2 block radius.=
|
||||
This area is owned by @1=Cette zone appartient à @1!
|
||||
|
||||
### pvp.lua ###
|
||||
[Protector] on_punchplayer called with nil objects=[Protector] on_punchplayer appelé avec des objets nil
|
||||
[Protector] pvp_protect not active, update your version of Minetest=[Protector] pvp_protect est désactivé, mettez à jour votre version de Minetest
|
||||
[Protector] pvp_protect is disabled=[Protector] pvp_protect est désactivé
|
||||
|
||||
### hud.lua ###
|
||||
Owner: @1=Propriétaire: @1
|
||||
|
||||
### tool.lua ###
|
||||
Protector Placer Tool (stand near protector, face direction and use)=Outil de placement du protecteur (se tenir près du protecteur, direction du visage et utilisation)
|
||||
Protector already in place!=Protecteur déjà en place!
|
||||
No protectors available to place!=Aucun protecteur disponible à placer!
|
||||
Protector placed at @1=Protection située à: @1
|
55
protector/locale/protector.it.tr
Normal file
@ -0,0 +1,55 @@
|
||||
# textdomain: protector
|
||||
# author: Xanthin and CodeXP
|
||||
# last update: 2018/Jul/10
|
||||
|
||||
### admin.lua ###
|
||||
Remove Protectors around players (separate names with spaces)=Elimina i protettori attorno ai giocatori (separa i nomi con gli spazi)
|
||||
<names list>=<elenco nomi>
|
||||
Replace Protector Owner with name provided=Sostituisci il proprietario del protettore col nome fornito
|
||||
<owner name> <name to replace with>=<nome proprietario> <nome con cui sostituirlo>
|
||||
Replacing Protector name '@1' with '@2'=Sostituzione del nome del protettore '@1' con '@2'
|
||||
Show protected areas of your nearby protectors=Mostra le aree protette dei protettori vicino a te
|
||||
Protector Names to remove: @1=Nomi dei protettori da eliminare: @1
|
||||
Name List Reset=Azzera l'elenco dei nomi
|
||||
|
||||
### doors_chest.lua ###
|
||||
Protected Wooden Door=Porta di legno protetta
|
||||
Protected Steel Door=Porta d'acciaio protetta
|
||||
Protected Trapdoor=Botola protetta
|
||||
Protected Steel Trapdoor=Botola d'acciaio protetta
|
||||
Protected Chest=Baule protetto
|
||||
To Chest=Al baule
|
||||
To Inventory=All'inventario
|
||||
Protected Chest (@1)=Baule protetto (@1)
|
||||
|
||||
### init.lua ###
|
||||
-- Protector interface --=-- Interfaccia protettore --
|
||||
PUNCH node to show protected area=COLPISCI il nodo per mostrare l'area protetta
|
||||
USE for area check=USA per controllare l'area
|
||||
Members:=Membri:
|
||||
Close=Chiudi
|
||||
Protection located at: @1=Protezione collocata a: @1
|
||||
Members: @1.=Membri: @1.
|
||||
This area is not protected.=Quest'area non è protetta.
|
||||
You can build here.=Qui puoi costruire.
|
||||
Overlaps into above players protected area=Si sovrappone ad un'area sovrastante protetta dai giocatori
|
||||
Protection Block=Blocco di protezione
|
||||
Protection (owned by @1)=Protezione (di proprietà di @1)
|
||||
Protection Logo=Logo di protezione
|
||||
[MOD] Protector Redo loaded=[MOD] Protector Redo caricato
|
||||
Spawn @1 has been protected up to a @2 block radius.=Lo spawn @1 è stato protetto fino a un raggio di @2 blocchi.
|
||||
This area is owned by @1=Quest'area è di proprietà di @1
|
||||
|
||||
### pvp.lua ###
|
||||
[Protector] on_punchplayer called with nil objects=[Protector] on_punchplayer chiamato con oggetti nil
|
||||
[Protector] pvp_protect not active, update your version of Minetest=[Protector] pvp_protect non attiva, aggiorna la tua versione di Minetest
|
||||
[Protector] pvp_protect is disabled=[Protector] pvp_protect è disattivato
|
||||
|
||||
### hud.lua ###
|
||||
Owner: @1=Proprietario: @1
|
||||
|
||||
### tool.lua ###
|
||||
Protector Placer Tool (stand near protector, face direction and use)=Strumento di posizionamento protettore (stai vicino al protettore, guarda la direzione e usa)
|
||||
Protector already in place!=Protettore già presente!
|
||||
No protectors available to place!=Nessun protettore disponibile da posizionare!
|
||||
Protector placed at @1=Protettore posizionato a @1
|
55
protector/locale/protector.ru.tr
Normal file
@ -0,0 +1,55 @@
|
||||
# textdomain: protector
|
||||
# author: CodeXP
|
||||
# last update: 2018/Jul/10
|
||||
|
||||
### admin.lua ###
|
||||
Remove Protectors around players (separate names with spaces)=Удалить защиту рядом с игроками (перечислить имена, разделяя пробелами)
|
||||
<names list>=<список имён>
|
||||
Replace Protector Owner with name provided=Заменить владельца защиты новым владельцем
|
||||
<owner name> <name to replace with>=<имя владельца> <имя нового владельца>
|
||||
Replacing Protector name '@1' with '@2'=Заменяется владелец защиты с '@1' на '@2'
|
||||
Show protected areas of your nearby protectors=Показать ближайшие защищённые территории
|
||||
Protector Names to remove: @1=Имена, подлежащие удалению: @1
|
||||
Name List Reset=Очистить список имён
|
||||
|
||||
### doors_chest.lua ###
|
||||
Protected Wooden Door=Защищённая яблоневая дверь
|
||||
Protected Steel Door=Защищённая стальная дверь
|
||||
Protected Trapdoor=Защищённый яблоневый люк
|
||||
Protected Steel Trapdoor=Защищённый стальной люк
|
||||
Protected Chest=Защищённый сундук
|
||||
To Chest=В сундук
|
||||
To Inventory=В инвентрарь
|
||||
Protected Chest (@1)=Защищённый сундук (@1)
|
||||
|
||||
### init.lua ###
|
||||
-- Protector interface --=-- Настройка защиты --
|
||||
PUNCH node to show protected area=СТУКНУТЬ узел для подсветки защищённой территории
|
||||
USE for area check=ЛКМ для проверки защищённой территории
|
||||
Members:=Участники:
|
||||
Close=Закрыть
|
||||
Protection located at: @1=Защита находится на координатах @1
|
||||
Members: @1.=Участники: @1.
|
||||
This area is not protected.=Территория свободна.
|
||||
You can build here.=Здесь можно ставить блоки.
|
||||
Overlaps into above players protected area=Защитный блок не может быть установлен: пересечение с областями, защищёнными другими игроками
|
||||
Protection Block=Защитный блок
|
||||
Protection (owned by @1)=Защита игрока @1
|
||||
Protection Logo=Защитный знак
|
||||
[MOD] Protector Redo loaded=[МОД] Protector Redo загружен
|
||||
Spawn @1 has been protected up to a @2 block radius.=Спаун @1 защищён в радиусе @2 блока.
|
||||
This area is owned by @1=Эта территория пренадлежит @1
|
||||
|
||||
### pvp.lua ###
|
||||
[Protector] on_punchplayer called with nil objects=[Защита] on_punchplayer вызвана с нулевыми объектами
|
||||
[Protector] pvp_protect not active, update your version of Minetest=[Защита] pvp_protect неактивен, обновите версию Minetest
|
||||
[Protector] pvp_protect is disabled=[Защита] pvp_protect отключён
|
||||
|
||||
### hud.lua ###
|
||||
Owner: @1=Владелец: @1
|
||||
|
||||
### tool.lua ###
|
||||
Protector Placer Tool (stand near protector, face direction and use)=Инструмент установки защиты (встаньте рядом с защитой, повернитесь в нужном направлении и используйте)
|
||||
Protector already in place!=Защита уже установлена!
|
||||
No protectors available to place!=У вас нет защитных блоков в инвентаре!
|
||||
Protector placed at @1=Защита находится на координатах @1
|
55
protector/locale/protector.tr.tr
Normal file
@ -0,0 +1,55 @@
|
||||
# textdomain: protector
|
||||
# author: CodeXP and TenPlus1
|
||||
# last update: 2020/Jul/12
|
||||
|
||||
### admin.lua ###
|
||||
Remove Protectors around players (separate names with spaces)=Ismi verilen oyuncuların yanındaki korumaları kaldır. (İsimleri boşlukla ayır)
|
||||
<names list>=<isim listesi>
|
||||
Replace Protector Owner with name provided=Koruyucu Sahibini belirtilen adla değiştirin
|
||||
<owner name> <name to replace with>=<sahip adı> <değiştirilecek ad>
|
||||
Replacing Protector name '@1' with '@2'='@ 1' Koruyucu adını '@ 2' ile değiştirin
|
||||
Show protected areas of your nearby protectors=Yakındaki koruyucuların korunan alanlarını göster
|
||||
Protector Names to remove: @1=Silinecek korumaların isimleri: @1
|
||||
Name List Reset=İsim listesini sıfırla
|
||||
|
||||
### doors_chest.lua ###
|
||||
Protected Wooden Door=Korumalı ahşap kapı
|
||||
Protected Steel Door=Korumalı çelik kapı
|
||||
Protected Trapdoor=Korumalı tuzak kapısı
|
||||
Protected Steel Trapdoor=Korumalı çelik tuzak kapısı
|
||||
Protected Chest=Korumalı sandık
|
||||
To Chest=Sandığa
|
||||
To Inventory=Envantere
|
||||
Protected Chest (@1)=Korumalı sandık (@1)
|
||||
|
||||
### init.lua ###
|
||||
-- Protector interface --=-- Koruyucu arayüz --
|
||||
PUNCH node to show protected area=Korunan alanı göstermek için yumruk
|
||||
USE for area check=Bölge kontrolü için kullan
|
||||
Members:=Üyeler
|
||||
Close=Kapat
|
||||
Protection located at: @1=Korumanın bulunduğu yer @1
|
||||
Members: @1.=Üyeler @1.
|
||||
This area is not protected.=Bu alan korumalı değildir.
|
||||
You can build here.=Buraya inşaa edebilirsiniz.
|
||||
Overlaps into above players protected area=Yukarıdaki oyuncuların koruma alanı ile çakışıyor
|
||||
Protection Block=Koruma kutusu
|
||||
Protection (owned by @1)=Koruma (@1 sahibidir)
|
||||
Protection Logo=Koruma arması
|
||||
[MOD] Protector Redo loaded=[MOD] Protector Redo yüklendi
|
||||
Spawn @1 has been protected up to a @2 block radius.=Spawn @1, @2 blok yarıçapa kadar korunur.
|
||||
This area is owned by @1=Burasının sahibi @1!
|
||||
|
||||
### pvp.lua ###
|
||||
[Protector] on_punchplayer called with nil objects=[Protector] on_punchplayer boş objelerle çağrıldı
|
||||
[Protector] pvp_protect not active, update your version of Minetest=[Protector] pvp_protect aktif değil, Minetest sürümünüzü güncelleyin.
|
||||
[Protector] pvp_protect is disabled=[Protector] pvp_protect kapatıldı.
|
||||
|
||||
### hud.lua ###
|
||||
Owner: @1=Sahip: @1
|
||||
|
||||
### tool.lua ###
|
||||
Protector Placer Tool (stand near protector, face direction and use)=Koruyucu Yerleştirme Aleti (koruyucunun yanında durun, yüz yönü ve kullanım)
|
||||
Protector already in place!=Koruyucu zaten yerinde!
|
||||
No protectors available to place!=Yerleştirilecek koruyucu yok!
|
||||
Protector placed at @1=Korumanın bulunduğu yer @1
|
183
protector/locale/ru.po
Normal file
@ -0,0 +1,183 @@
|
||||
# Russian translation for PROTECTOR MOD.
|
||||
# Copyright (C) 2018 TenPlus1
|
||||
# This file is distributed under the same license as the PROTECTOR MOD package.
|
||||
# Doloment, 2017.
|
||||
# CodeXP <codexp@gmx.net>, 2018.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROTECTOR MOD\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-07-10 17:33+0200\n"
|
||||
"PO-Revision-Date: \n"
|
||||
"Last-Translator: CodeXP <codexp@gmx.net>\n"
|
||||
"Language-Team: \n"
|
||||
"Language: ru\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: admin.lua
|
||||
msgid "Remove Protectors around players (separate names with spaces)"
|
||||
msgstr "Удалить защиту рядом с игроками (перечислить имена, разделяя пробелами)"
|
||||
|
||||
#: admin.lua
|
||||
msgid "<names list>"
|
||||
msgstr "<список имён>"
|
||||
|
||||
#: admin.lua
|
||||
msgid "Replace Protector Owner with name provided"
|
||||
msgstr "Заменить владельца защиты новым владельцем"
|
||||
|
||||
#: admin.lua
|
||||
msgid "<owner name> <name to replace with>"
|
||||
msgstr "<имя владельца> <имя нового владельца>"
|
||||
|
||||
#: admin.lua
|
||||
msgid "Replacing Protector name '@1' with '@2'"
|
||||
msgstr "Заменяется владелец защиты с '@1' на '@2'"
|
||||
|
||||
#: admin.lua
|
||||
msgid "Show protected areas of your nearby protectors"
|
||||
msgstr "Показать защищенные территории с ближней защитой"
|
||||
|
||||
#: admin.lua
|
||||
msgid "Protector Names to remove: @1"
|
||||
msgstr "Имена защит, подлежащих удалению: @1"
|
||||
|
||||
#: admin.lua
|
||||
msgid "Name List Reset"
|
||||
msgstr "Сброс списка имен"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "Protected Wooden Door"
|
||||
msgstr "Защищенная яблоневая дверь"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "Protected Steel Door"
|
||||
msgstr "Защищенная стальная дверь"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "Protected Trapdoor"
|
||||
msgstr "Защищенный яблоневый люк"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "Protected Steel Trapdoor"
|
||||
msgstr "Защищенный стальной люк"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "Protected Chest"
|
||||
msgstr "Защищенный сундук"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "To Chest"
|
||||
msgstr "В сундук"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "To Inventory"
|
||||
msgstr "В инвентрарь"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "Protected Chest (@1)"
|
||||
msgstr "Защищенный сундук (@1)"
|
||||
|
||||
#: init.lua
|
||||
msgid "-- Protector interface --"
|
||||
msgstr "-- Настройка защиты --"
|
||||
|
||||
#: init.lua
|
||||
msgid "PUNCH node to show protected area"
|
||||
msgstr "СТУКНУТЬ узел для подсведки защищенной территории"
|
||||
|
||||
#: init.lua
|
||||
msgid "USE for area check"
|
||||
msgstr "ЛКМ для проверки защищенной территории"
|
||||
|
||||
#: init.lua
|
||||
msgid "Members:"
|
||||
msgstr "Участники:"
|
||||
|
||||
#: init.lua
|
||||
msgid "Close"
|
||||
msgstr "Закрыть"
|
||||
|
||||
#: init.lua
|
||||
msgid "Protection located at: @1"
|
||||
msgstr "Защита находится на координатах @1"
|
||||
|
||||
#: init.lua
|
||||
msgid "Members: @1."
|
||||
msgstr "Участники: @1."
|
||||
|
||||
#: init.lua
|
||||
msgid "This area is not protected."
|
||||
msgstr "Территория свободна."
|
||||
|
||||
#: init.lua
|
||||
msgid "You can build here."
|
||||
msgstr "Здесь можно ставить блоки."
|
||||
|
||||
#: init.lua tool.lua
|
||||
msgid "Overlaps into above players protected area"
|
||||
msgstr ""
|
||||
"Защитный блок не может быть установлен: пересечение с областями, защищенными "
|
||||
"другими игроками"
|
||||
|
||||
#: init.lua
|
||||
msgid "Protection Block"
|
||||
msgstr "Защитный блок"
|
||||
|
||||
#: admin.lua init.lua tool.lua
|
||||
msgid "Protection (owned by @1)"
|
||||
msgstr "Защита игрока @1"
|
||||
|
||||
#: init.lua
|
||||
msgid "Protection Logo"
|
||||
msgstr "Защитный знак"
|
||||
|
||||
#: init.lua
|
||||
msgid "[MOD] Protector Redo loaded"
|
||||
msgstr "[MOD] Модификация \"Переделанная Защита\" загружена"
|
||||
|
||||
#: init.lua
|
||||
msgid "Spawn @1 has been protected up to a @2 block radius."
|
||||
msgstr "Спаун @1 защищен в радиусе @2 блока."
|
||||
|
||||
#: init.lua
|
||||
msgid "This area is owned by @1"
|
||||
msgstr "Эта территория пренадлежит @1"
|
||||
|
||||
#: pvp.lua
|
||||
msgid "[Protector] on_punchplayer called with nil objects"
|
||||
msgstr "[Защита] on_punchplayer вызвана с нулевыми объектами"
|
||||
|
||||
#: pvp.lua
|
||||
msgid "[Protector] pvp_protect not active, update your version of Minetest"
|
||||
msgstr "[Защита] pvp_protect не активен, обновите версию Minetest"
|
||||
|
||||
#: pvp.lua
|
||||
msgid "[Protector] pvp_protect is disabled"
|
||||
msgstr "[Защита] pvp_protect отключен"
|
||||
|
||||
#: hud.lua
|
||||
msgid "Owner: @1"
|
||||
msgstr "Владелец: @1"
|
||||
|
||||
#: tool.lua
|
||||
msgid "Protector Placer Tool (stand near protector, face direction and use)"
|
||||
msgstr ""
|
||||
"Инструмент установки защиты (станьте рядом с защитой, повернитесь в нужное "
|
||||
"направление и используйте)"
|
||||
|
||||
#: tool.lua
|
||||
msgid "Protector already in place!"
|
||||
msgstr "Защита уже установлена!"
|
||||
|
||||
#: tool.lua
|
||||
msgid "No protectors available to place!"
|
||||
msgstr "У вас нет защитных блоков в инвентаре!"
|
||||
|
||||
#: tool.lua
|
||||
msgid "Protector placed at @1"
|
||||
msgstr "Защита находится на координатах @1"
|
179
protector/locale/template.pot
Normal file
@ -0,0 +1,179 @@
|
||||
# Template for PROTECTOR MOD.
|
||||
# Copyright (C) 2018 TenPlus1
|
||||
# This file is distributed under the same license as the PROTECTOR MOD package.
|
||||
# Xanthin <xanthin@wiuwiu.de>, 2016.
|
||||
# CodeXP <codexp@gmx.net>, 2018.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROTECTOR MOD\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-07-10 17:33+0200\n"
|
||||
"PO-Revision-Date: \n"
|
||||
"Last-Translator: CodeXP <codexp@gmx.net>\n"
|
||||
"Language-Team: \n"
|
||||
"Language: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: admin.lua
|
||||
msgid "Remove Protectors around players (separate names with spaces)"
|
||||
msgstr ""
|
||||
|
||||
#: admin.lua
|
||||
msgid "<names list>"
|
||||
msgstr ""
|
||||
|
||||
#: admin.lua
|
||||
msgid "Replace Protector Owner with name provided"
|
||||
msgstr ""
|
||||
|
||||
#: admin.lua
|
||||
msgid "<owner name> <name to replace with>"
|
||||
msgstr ""
|
||||
|
||||
#: admin.lua
|
||||
msgid "Replacing Protector name '@1' with '@2'"
|
||||
msgstr ""
|
||||
|
||||
#: admin.lua
|
||||
msgid "Show protected areas of your nearby protectors"
|
||||
msgstr ""
|
||||
|
||||
#: admin.lua
|
||||
msgid "Protector Names to remove: @1"
|
||||
msgstr ""
|
||||
|
||||
#: admin.lua
|
||||
msgid "Name List Reset"
|
||||
msgstr ""
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "Protected Wooden Door"
|
||||
msgstr ""
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "Protected Steel Door"
|
||||
msgstr ""
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "Protected Trapdoor"
|
||||
msgstr ""
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "Protected Steel Trapdoor"
|
||||
msgstr ""
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "Protected Chest"
|
||||
msgstr ""
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "To Chest"
|
||||
msgstr ""
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "To Inventory"
|
||||
msgstr ""
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "Protected Chest (@1)"
|
||||
msgstr ""
|
||||
|
||||
#: init.lua
|
||||
msgid "-- Protector interface --"
|
||||
msgstr ""
|
||||
|
||||
#: init.lua
|
||||
msgid "PUNCH node to show protected area"
|
||||
msgstr ""
|
||||
|
||||
#: init.lua
|
||||
msgid "USE for area check"
|
||||
msgstr ""
|
||||
|
||||
#: init.lua
|
||||
msgid "Members:"
|
||||
msgstr ""
|
||||
|
||||
#: init.lua
|
||||
msgid "Close"
|
||||
msgstr ""
|
||||
|
||||
#: init.lua
|
||||
msgid "Protection located at: @1"
|
||||
msgstr ""
|
||||
|
||||
#: init.lua
|
||||
msgid "Members: @1."
|
||||
msgstr ""
|
||||
|
||||
#: init.lua
|
||||
msgid "This area is not protected."
|
||||
msgstr ""
|
||||
|
||||
#: init.lua
|
||||
msgid "You can build here."
|
||||
msgstr ""
|
||||
|
||||
#: init.lua tool.lua
|
||||
msgid "Overlaps into above players protected area"
|
||||
msgstr ""
|
||||
|
||||
#: init.lua
|
||||
msgid "Protection Block"
|
||||
msgstr ""
|
||||
|
||||
#: admin.lua init.lua tool.lua
|
||||
msgid "Protection (owned by @1)"
|
||||
msgstr ""
|
||||
|
||||
#: init.lua
|
||||
msgid "Protection Logo"
|
||||
msgstr ""
|
||||
|
||||
#: init.lua
|
||||
msgid "[MOD] Protector Redo loaded"
|
||||
msgstr ""
|
||||
|
||||
#: init.lua
|
||||
msgid "Spawn @1 has been protected up to a @2 block radius."
|
||||
msgstr ""
|
||||
|
||||
#: init.lua
|
||||
msgid "This area is owned by @1"
|
||||
msgstr ""
|
||||
|
||||
#: pvp.lua
|
||||
msgid "[Protector] on_punchplayer called with nil objects"
|
||||
msgstr ""
|
||||
|
||||
#: pvp.lua
|
||||
msgid "[Protector] pvp_protect not active, update your version of Minetest"
|
||||
msgstr ""
|
||||
|
||||
#: pvp.lua
|
||||
msgid "[Protector] pvp_protect is disabled"
|
||||
msgstr ""
|
||||
|
||||
#: hud.lua
|
||||
msgid "Owner: @1"
|
||||
msgstr ""
|
||||
|
||||
#: tool.lua
|
||||
msgid "Protector Placer Tool (stand near protector, face direction and use)"
|
||||
msgstr ""
|
||||
|
||||
#: tool.lua
|
||||
msgid "Protector already in place!"
|
||||
msgstr ""
|
||||
|
||||
#: tool.lua
|
||||
msgid "No protectors available to place!"
|
||||
msgstr ""
|
||||
|
||||
#: tool.lua
|
||||
msgid "Protector placed at @1"
|
||||
msgstr ""
|
180
protector/locale/tr.po
Normal file
@ -0,0 +1,180 @@
|
||||
# Turkish translation for PROTECTOR MOD.
|
||||
# Copyright (C) 2018 TenPlus1
|
||||
# This file is distributed under the same license as the PROTECTOR MOD package.
|
||||
# mahmutelmas06, 2016.
|
||||
# CodeXP <codexp@gmx.net>, 2018.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROTECTOR MOD\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-07-10 17:33+0200\n"
|
||||
"PO-Revision-Date: \n"
|
||||
"Last-Translator: CodeXP <codexp@gmx.net>\n"
|
||||
"Language-Team: \n"
|
||||
"Language: tr\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: admin.lua
|
||||
msgid "Remove Protectors around players (separate names with spaces)"
|
||||
msgstr "Ismi verilen oyuncuların yanındaki korumaları kaldır. (İsimleri boşlukla "
|
||||
"ayır)"
|
||||
|
||||
#: admin.lua
|
||||
msgid "<names list>"
|
||||
msgstr ""
|
||||
|
||||
#: admin.lua
|
||||
msgid "Replace Protector Owner with name provided"
|
||||
msgstr ""
|
||||
|
||||
#: admin.lua
|
||||
msgid "<owner name> <name to replace with>"
|
||||
msgstr ""
|
||||
|
||||
#: admin.lua
|
||||
msgid "Replacing Protector name '@1' with '@2'"
|
||||
msgstr ""
|
||||
|
||||
#: admin.lua
|
||||
msgid "Show protected areas of your nearby protectors"
|
||||
msgstr ""
|
||||
|
||||
#: admin.lua
|
||||
msgid "Protector Names to remove: @1"
|
||||
msgstr "Silinecek korumaların isimleri: @1"
|
||||
|
||||
#: admin.lua
|
||||
msgid "Name List Reset"
|
||||
msgstr "İsim listesini sıfırla"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "Protected Wooden Door"
|
||||
msgstr "Korumalı ahşap kapı"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "Protected Steel Door"
|
||||
msgstr "Korumalı çelik kapı"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "Protected Trapdoor"
|
||||
msgstr "Korumalı tuzak kapısı"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "Protected Steel Trapdoor"
|
||||
msgstr "Korumalı çelik tuzak kapısı"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "Protected Chest"
|
||||
msgstr "Korumalı sandık"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "To Chest"
|
||||
msgstr "Sandığa"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "To Inventory"
|
||||
msgstr "Envantere"
|
||||
|
||||
#: doors_chest.lua
|
||||
msgid "Protected Chest (@1)"
|
||||
msgstr "Korumalı sandık (@1)"
|
||||
|
||||
#: init.lua
|
||||
msgid "-- Protector interface --"
|
||||
msgstr ""
|
||||
|
||||
#: init.lua
|
||||
msgid "PUNCH node to show protected area"
|
||||
msgstr "Korunan alanı göstermek için yumruk"
|
||||
|
||||
#: init.lua
|
||||
msgid "USE for area check"
|
||||
msgstr "Bölge kontrolü için kullan"
|
||||
|
||||
#: init.lua
|
||||
msgid "Members:"
|
||||
msgstr "Üyeler"
|
||||
|
||||
#: init.lua
|
||||
msgid "Close"
|
||||
msgstr "Kapat"
|
||||
|
||||
#: init.lua
|
||||
msgid "Protection located at: @1"
|
||||
msgstr "Korumanın bulunduğu yer @1"
|
||||
|
||||
#: init.lua
|
||||
msgid "Members: @1."
|
||||
msgstr "Üyeler @1."
|
||||
|
||||
#: init.lua
|
||||
msgid "This area is not protected."
|
||||
msgstr "Bu alan korumalı değildir."
|
||||
|
||||
#: init.lua
|
||||
msgid "You can build here."
|
||||
msgstr "Buraya inşaa edebilirsiniz."
|
||||
|
||||
#: init.lua tool.lua
|
||||
msgid "Overlaps into above players protected area"
|
||||
msgstr ""
|
||||
|
||||
#: init.lua
|
||||
msgid "Protection Block"
|
||||
msgstr "Koruma kutusu"
|
||||
|
||||
#: admin.lua init.lua tool.lua
|
||||
msgid "Protection (owned by @1)"
|
||||
msgstr "Koruma (@1 sahibidir)"
|
||||
|
||||
#: init.lua
|
||||
msgid "Protection Logo"
|
||||
msgstr "Koruma arması"
|
||||
|
||||
#: init.lua
|
||||
msgid "[MOD] Protector Redo loaded"
|
||||
msgstr "[MOD] Protector Redo yüklendi"
|
||||
|
||||
#: init.lua
|
||||
msgid "Spawn @1 has been protected up to a @2 block radius."
|
||||
msgstr ""
|
||||
|
||||
#: init.lua
|
||||
msgid "This area is owned by @1"
|
||||
msgstr "Burasının sahibi @1!"
|
||||
|
||||
#: pvp.lua
|
||||
msgid "[Protector] on_punchplayer called with nil objects"
|
||||
msgstr "[Protector] on_punchplayer boş objelerle çağrıldı"
|
||||
|
||||
#: pvp.lua
|
||||
msgid "[Protector] pvp_protect not active, update your version of Minetest"
|
||||
msgstr "[Protector] pvp_protect aktif değil, Minetest sürümünüzü güncelleyin."
|
||||
|
||||
#: pvp.lua
|
||||
msgid "[Protector] pvp_protect is disabled"
|
||||
msgstr "[Protector] pvp_protect kapatıldı."
|
||||
|
||||
#: hud.lua
|
||||
msgid "Owner: @1"
|
||||
msgstr ""
|
||||
|
||||
#: tool.lua
|
||||
msgid "Protector Placer Tool (stand near protector, face direction and use)"
|
||||
msgstr ""
|
||||
|
||||
#: tool.lua
|
||||
msgid "Protector already in place!"
|
||||
msgstr ""
|
||||
|
||||
#: tool.lua
|
||||
msgid "No protectors available to place!"
|
||||
msgstr ""
|
||||
|
||||
#: tool.lua
|
||||
msgid "Protector placed at @1"
|
||||
msgstr "Korumanın bulunduğu yer @1"
|
18
protector/lucky_block.lua
Normal file
@ -0,0 +1,18 @@
|
||||
|
||||
-- add lucky blocks
|
||||
|
||||
if minetest.get_modpath("lucky_block") then
|
||||
|
||||
lucky_block:add_blocks({
|
||||
{"dro", {"protector:protect"}, 3},
|
||||
{"dro", {"protector:protect2"}, 3},
|
||||
{"dro", {"protector:door_wood"}, 1},
|
||||
{"dro", {"protector:door_steel"}, 1},
|
||||
{"exp", 5, true},
|
||||
{"dro", {"protector:trapdoor"}, 1},
|
||||
{"dro", {"protector:trapdoor_steel"}, 1},
|
||||
{"dro", {"protector:tool"}, 1},
|
||||
{"dro", {"protector:chest"}, 1},
|
||||
{"exp"},
|
||||
})
|
||||
end
|
6
protector/mod.conf
Normal file
@ -0,0 +1,6 @@
|
||||
name = protector
|
||||
description = Lets players craft special blocks to protect their builds or disable PVP in areas.
|
||||
optional_depends = default, intllib, lucky_block, mesecons_mvps, playerfactions
|
||||
release = 6779
|
||||
title = Protector Redo
|
||||
author = TenPlus1
|
72
protector/pvp.lua
Normal file
@ -0,0 +1,72 @@
|
||||
|
||||
local S = protector.intllib
|
||||
|
||||
-- get static spawn position
|
||||
local statspawn = minetest.string_to_pos(minetest.settings:get("static_spawnpoint"))
|
||||
or {x = 0, y = 2, z = 0}
|
||||
|
||||
-- is spawn protected
|
||||
local protector_spawn = tonumber(minetest.settings:get("protector_spawn")
|
||||
or minetest.settings:get("protector_pvp_spawn")) or 0
|
||||
|
||||
-- is night-only pvp enabled
|
||||
local protector_night_pvp = minetest.settings:get_bool("protector_night_pvp")
|
||||
|
||||
-- disables PVP in your own protected areas
|
||||
if minetest.settings:get_bool("enable_pvp")
|
||||
and minetest.settings:get_bool("protector_pvp") then
|
||||
|
||||
if minetest.register_on_punchplayer then
|
||||
|
||||
minetest.register_on_punchplayer(function(player, hitter,
|
||||
time_from_last_punch, tool_capabilities, dir, damage)
|
||||
|
||||
if not player
|
||||
or not hitter then
|
||||
print(S("[Protector] on_punchplayer called with nil objects"))
|
||||
end
|
||||
|
||||
if not hitter:is_player() then
|
||||
return false
|
||||
end
|
||||
|
||||
-- no pvp at spawn area
|
||||
local pos = player:get_pos()
|
||||
|
||||
if pos.x < statspawn.x + protector_spawn
|
||||
and pos.x > statspawn.x - protector_spawn
|
||||
and pos.y < statspawn.y + protector_spawn
|
||||
and pos.y > statspawn.y - protector_spawn
|
||||
and pos.z < statspawn.z + protector_spawn
|
||||
and pos.z > statspawn.z - protector_spawn then
|
||||
return true
|
||||
end
|
||||
|
||||
-- do we enable pvp at night time only ?
|
||||
if protector_night_pvp then
|
||||
|
||||
-- get time of day
|
||||
local tod = minetest.get_timeofday() or 0
|
||||
|
||||
if tod > 0.2 and tod < 0.8 then
|
||||
--
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- is player being punched inside a protected area ?
|
||||
if minetest.is_protected(pos, hitter:get_player_name()) then
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
|
||||
end)
|
||||
else
|
||||
print(S("[Protector] pvp_protect not active, update your version of Minetest"))
|
||||
|
||||
end
|
||||
else
|
||||
print(S("[Protector] pvp_protect is disabled"))
|
||||
end
|
BIN
protector/screenshot.png
Normal file
After Width: | Height: | Size: 12 KiB |
32
protector/settingtypes.txt
Normal file
@ -0,0 +1,32 @@
|
||||
# Size of protected area around protection node limiting player interaction
|
||||
protector_radius (Protector Radius [max 22]) int 5
|
||||
|
||||
# Flips player around when accessing protected area to stop lag griefing
|
||||
protector_flip (Protector Flip) bool false
|
||||
|
||||
# Hurts player by amount entered when accessing protected area, 0 to disable
|
||||
protector_hurt (Protector Hurt) int 0
|
||||
|
||||
# Sets a protected area around spawn by node radius given
|
||||
protector_spawn (Protector Spawn) int 0
|
||||
|
||||
# Enables PVP inside of protected areas
|
||||
protector_pvp (Protector PVP) bool false
|
||||
|
||||
# When true will allow PVP inside protected spawn area
|
||||
protector_pvp_spawn (Protector PVP Spawn) int 0
|
||||
|
||||
# When true will allow PVP inside all protected areas at night time only
|
||||
protector_night_pvp (Protector Night PVP) bool false
|
||||
|
||||
# Interval in seconds that protection field is shown
|
||||
protector_show_interval (Protector Show Interval) int 5
|
||||
|
||||
# Interval in seconds that HUD ownership text is updated, 0 to disable
|
||||
protector_hud_interval (Protector HUD Interval) int 5
|
||||
|
||||
# Enables craft recipe for protection block
|
||||
protector_recipe (Enable Protector recipe) bool true
|
||||
|
||||
# Enables protection messages in player chat
|
||||
protector_msg (Enable Protector Messages) bool true
|
BIN
protector/textures/default_chest_front.png
Normal file
After Width: | Height: | Size: 421 B |