This commit is contained in:
WilLiam12 2021-03-26 20:31:18 -03:00
parent 6c50af7c22
commit d29b471221
158 changed files with 14630 additions and 0 deletions

2
apartment/README.md Normal file
View File

@ -0,0 +1,2 @@
Still experimental. Use /giveme apartment:apartment to get a control panel.

View 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
View 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
View 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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

17
areas/.github/workflows/luacheck.yml vendored Normal file
View 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
View File

@ -0,0 +1,2 @@
*~

24
areas/.luacheckrc Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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 nexiste 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 nexiste 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 dune 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 lutilisation 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.=Lautoprotection est désactivée.
Self protection is enabled.=Lautoprotection est activée.
That area doesn't exist.=La zone nexiste pas.
The player "@1" does not exist.=Le joueur "@1" nexiste 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 navez 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 lautorisation 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 dabord.
### 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 dadministrer 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.=Lautoprotection est désactivée ou vous navez 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 nexiste pas.
Unable to get position.=Impossible dobtenir la position.
Unknown subcommand: @1=Sous-commande inconnue : @1

135
areas/locale/areas.it.tr Normal file
View 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
View 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
View 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
View 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
View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 B

View 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

View File

@ -0,0 +1,2 @@
locks
travelnet

30
locked_travelnet/init.lua Normal file
View 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");

View 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)");

View 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");

View 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");

View File

@ -0,0 +1,5 @@
description = Protect travelnet stations with locks and/or passwords
author = Sokomine
title = Locked Travelnet
release = 1159
name = locked_travelnet

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 323 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 277 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 553 B

36
locks/README.md Normal file
View 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
View File

@ -0,0 +1,2 @@
default
pipeworks?

803
locks/init.lua Normal file
View 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
View 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
View File

@ -0,0 +1,5 @@
description = Shared chests, doors, furnaces and signs - with keys
author = Sokomine
title = Locks
release = 1160
name = locks

View 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");

View 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");

View 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");

View 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");

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

View 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.

Binary file not shown.

After

Width:  |  Height:  |  Size: 390 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 726 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 756 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 727 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 266 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 266 B

142
protector/README.md Normal file
View 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
View 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
View File

@ -0,0 +1,5 @@
default?
intllib?
lucky_block?
mesecons_mvps?
playerfactions?

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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"

View 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

View 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

View 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

View 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

View 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

View 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
View 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"

View 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
View 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
View 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
View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 B

Some files were not shown because too many files have changed in this diff Show More