diff options
Diffstat (limited to 'markers')
| -rw-r--r-- | markers/README.md | 3 | ||||
| -rw-r--r-- | markers/areas.lua | 865 | ||||
| -rw-r--r-- | markers/config.lua | 36 | ||||
| -rw-r--r-- | markers/depends.txt | 1 | ||||
| -rw-r--r-- | markers/init.lua | 536 | ||||
| -rw-r--r-- | markers/land_title_register.lua | 74 | ||||
| -rw-r--r-- | markers/marker_stone.lua | 24 | ||||
| -rw-r--r-- | markers/textures/compass_side_top.png | bin | 0 -> 684 bytes | |||
| -rw-r--r-- | markers/textures/markers_mark.png | bin | 0 -> 377 bytes | |||
| -rw-r--r-- | markers/textures/markers_stone.png | bin | 0 -> 793 bytes | |||
| -rw-r--r-- | markers/textures/markers_stone_side.png | bin | 0 -> 832 bytes | 
11 files changed, 1539 insertions, 0 deletions
| diff --git a/markers/README.md b/markers/README.md new file mode 100644 index 0000000..ffa3179 --- /dev/null +++ b/markers/README.md @@ -0,0 +1,3 @@ + +This is work in progress. + diff --git a/markers/areas.lua b/markers/areas.lua new file mode 100644 index 0000000..f804f7f --- /dev/null +++ b/markers/areas.lua @@ -0,0 +1,865 @@ + +-- TODO: offer teleport button? + +-- Temporary compatibility function - see minetest PR#1180 +if not vector.interpolate then +    vector.interpolate = function(pos1, pos2, factor) +	return {x = pos1.x + (pos2.x - pos1.x) * factor, +		y = pos1.y + (pos2.y - pos1.y) * factor, +		z = pos1.z + (pos2.z - pos1.z) * factor} +    end +end + +-- taken from mobf +local COLOR_RED   = "#FF0000"; +local COLOR_GREEN = "#00FF00"; +local COLOR_WHITE = "#FFFFFF"; + + +-- we need to store which list we present to which player +markers.menu_data_by_player = {} + + +markers.get_area_by_pos = function(pos) + +   local found_areas = {}; +   for id, area in pairs(areas.areas) do +      if( pos.x >= area.pos1.x and pos.x <= area.pos2.x and +          pos.y >= area.pos1.y and pos.y <= area.pos2.y and +          pos.z >= area.pos1.z and pos.z <= area.pos2.z )then + +         area[ 'id' ] = id; +         table.insert(found_areas, area ); +      end +   end +   return found_areas; +end + + + +-- ppos: current player (or marker stone) position - used for sorting the list +-- mode: can be pos, player, all, subarea, main_areas +-- mode_data: content depends on mode +-- selected: display information about the area the player single-clicked on +markers.get_area_list_formspec = function(ppos, player, mode, pos, mode_data, selected ) + + +   local id_list = {}; +   local title   = '???'; +   local tlabel  = ''; + +   -- expects a position in mode_data +   if(     mode=='pos' ) then +      -- title would be too long for a label +      title  = 'All areas which contain position..'; +      tlabel = '<'..minetest.pos_to_string( mode_data )..'>:'; + +      for id, area in pairs(areas.areas) do + +         if( mode_data.x >= area.pos1.x and mode_data.x <= area.pos2.x and +             mode_data.y >= area.pos1.y and mode_data.y <= area.pos2.y and +             mode_data.z >= area.pos1.z and mode_data.z <= area.pos2.z )then + +            table.insert( id_list, id ); +         end +      end + +   -- expects a playername in mode_data +   elseif( mode=='player' ) then + +      title  = 'All areas owned by player..'; +      tlabel = '<'..tostring( mode_data )..'>:'; + +      for id, area in pairs(areas.areas) do + +         if( area.owner == mode_data ) then +            table.insert( id_list, id ); +         end +      end + +   -- expects an area_id in mode_data +   elseif( mode=='subareas' ) then + +      title  = 'All subareas of area..'; +      tlabel = '<'..tostring( areas.areas[ mode_data ].name )..'> ['..tostring( mode_data )..']:'; + +      for id, area in pairs(areas.areas) do + +         if( area.parent and area.parent == mode_data ) then +            table.insert( id_list, id ); +         end +      end + +   -- show only areas that do not have parents +   elseif( mode=='main_areas' ) then +      title  = 'All main areas withhin '..tostring( markers.AREA_RANGE )..' m:'; +      tlabel = '*all main areas*'; +      for id, area in pairs(areas.areas) do + +         if( not( area.parent ) +           -- ppos is always available +           and(   (area.pos1.x >= ppos.x-markers.AREA_RANGE and area.pos1.x <= ppos.x+markers.AREA_RANGE ) +                or(area.pos2.x >= ppos.x-markers.AREA_RANGE and area.pos2.x <= ppos.x+markers.AREA_RANGE )) +           and(   (area.pos1.y >= ppos.y-markers.AREA_RANGE and area.pos1.y <= ppos.y+markers.AREA_RANGE ) +                or(area.pos2.y >= ppos.y-markers.AREA_RANGE and area.pos2.y <= ppos.y+markers.AREA_RANGE )) +           and(   (area.pos1.z >= ppos.z-markers.AREA_RANGE and area.pos1.z <= ppos.z+markers.AREA_RANGE ) +                or(area.pos2.z >= ppos.z-markers.AREA_RANGE and area.pos2.z <= ppos.z+markers.AREA_RANGE ))) then +            table.insert( id_list, id ); +         end +      end +  + +   elseif( mode=='all' ) then +      title  = 'All areas withhin '..tostring( markers.AREA_RANGE )..' m:'; +      tlabel = '*all areas*'; + +      for id, area in pairs(areas.areas) do +         if(  (   (area.pos1.x >= ppos.x-markers.AREA_RANGE and area.pos1.x <= ppos.x+markers.AREA_RANGE ) +                or(area.pos2.x >= ppos.x-markers.AREA_RANGE and area.pos2.x <= ppos.x+markers.AREA_RANGE )) +           and(   (area.pos1.y >= ppos.y-markers.AREA_RANGE and area.pos1.y <= ppos.y+markers.AREA_RANGE ) +                or(area.pos2.y >= ppos.y-markers.AREA_RANGE and area.pos2.y <= ppos.y+markers.AREA_RANGE )) +           and(   (area.pos1.z >= ppos.z-markers.AREA_RANGE and area.pos1.z <= ppos.z+markers.AREA_RANGE ) +                or(area.pos2.z >= ppos.z-markers.AREA_RANGE and area.pos2.z <= ppos.z+markers.AREA_RANGE ))) then +            table.insert( id_list, id ); +         end +      end +   end + +   -- Sort the list of areas so the nearest comes first +   local nearsorter = function(a, b) +        return vector.distance(vector.interpolate(areas.areas[a].pos1, areas.areas[a].pos2, 0.5), ppos) < +		vector.distance(vector.interpolate(areas.areas[b].pos1, areas.areas[b].pos2, 0.5), ppos) +   end +   table.sort(id_list, nearsorter) + +   local formspec = 'size[10,9]'; + +   title  = minetest.formspec_escape( title ); +   tlabel = minetest.formspec_escape( tlabel ); + +   formspec = formspec.. +	"label[0.5,0;"..title.."]".. +	"label[4.7,0;"..tlabel.."]".. +	"label[0.5,8.5;Doubleclick to select area.]".. +	"label[4.7,8.5;Areas found: "..tostring( #id_list )..".]".. +	"textlist[0.5,0.5;7,8;markers_area_list_selection;";  + +   local liste = ''; +   for i,v in ipairs( id_list ) do +      if( liste ~= '' ) then +         liste = liste..','; +      end +      liste = liste..minetest.formspec_escape( areas:toString( v ) ); +          +   end + +   -- highlight selected entry +   if( selected ) then +      formspec = formspec..liste..';'..selected..';false]'; +   else +      formspec = formspec..liste..';]'; +   end +    +   local pname = player:get_player_name(); +   if( not( markers.menu_data_by_player[ pname ] )) then +      markers.menu_data_by_player[ pname ] = {}; +   end + +   -- display information about the location of the area the player clicked on +   if( selected  +      and id_list[ selected ] +      and areas.areas[ id_list[ selected ]] ) then + +      local this_area = areas.areas[ id_list[ selected ]]; + +      local subareas = {}; +      for i,v in pairs( areas.areas ) do +         if( v.parent and v.parent == id_list[ selected ]) then +            table.insert( subareas, i ); +         end +      end + +      formspec = formspec.. +               markers.show_compass_marker( 8.5, 3.0, false, pos, this_area.pos1, this_area.pos2 ); + + +      if( this_area.parent) then +         formspec = formspec.. +               'button[8.0,0.5;2,0.5;show_parent;'..  +			minetest.formspec_escape( areas.areas[ this_area.parent ].name )..']'; +      end + +      if( #subareas > 0 ) then +         formspec = formspec.. +               'button[8.0,1.0;2,0.5;list_subareas;'.. +                        minetest.formspec_escape( 'List subareas ('..tostring( #subareas )..')')..']'; +      end +  + +      if( mode=='player' ) then +         formspec = formspec.. +               'label[8.0,1.5;'.. +			minetest.formspec_escape( this_area.owner..'\'s areas')..']'; +      else +         formspec = formspec.. +               'button[8.0,1.5;2,0.5;list_player_areas;'.. +			minetest.formspec_escape( this_area.owner..'\'s areas')..']'; +      end + +   end + +   formspec = formspec.. +               'button[8.0,8.5;2,0.5;list_main_areas;List all main areas]'; + +   -- we need to remember especially the id_list - else it would be impossible to know what the +   -- player selected +   markers.menu_data_by_player[ pname ] = { +	  typ       = 'area_list', +          mode      = mode, +          pos       = pos, +          mode_data = mode_data, +          list      = id_list, + +	  selected  = id_list[ selected ], +	}; +          +	   +   return formspec; +end + + + + +-- shows a formspec with information about a particular area +-- pos is the position of the marker stone or place where the player clicked +--  with the land title register; it is used for relative display of coordinates +markers.get_area_desc_formspec = function( id, player, pos ) + +   if( not( areas.areas ) or not( id ) or not( areas.areas[ id ] )) then +      return 'field[info;Error:;Area not found.]'; +   end +   local this_area = areas.areas[ id ]; + +   local pname     = player:get_player_name(); + +   -- show some buttons only if area is owned by the player +   local is_owner  = false; + +   if( this_area.owner == pname ) then +      is_owner     = true; +   end + +   local formspec  = 'size[10,9]'.. +	'label[2.5,0.0;Area information and management]'.. +	'button_exit[4.7,7.0;1,0.5;abort;OK]'; + +   -- general information about the area +   formspec = formspec.. +	'label[0.5,1.0;This is area number ]'.. +        'label[4.7,1.0;'..tostring( id )..']'.. + +	'label[0.5,1.5;The area is called ]'.. +        'label[4.7,1.5;'..minetest.formspec_escape( this_area.name or '-not set-')..']'.. + +	'label[0.5,2.0;It is owned by ]'.. +        'label[4.7,2.0;'..minetest.formspec_escape( this_area.owner)..']'; + + +   -- these functions are only available to the owner of the area +   if( is_owner ) then +      formspec = formspec.. +        'button_exit[8.0,0.0;2,0.5;change_owner;Change owner]'.. +        'button_exit[8.0,1.0;2,0.5;delete;Delete]'.. +        'button_exit[8.0,1.5;2,0.5;rename;Rename]'.. +        'button_exit[8.0,2.0;2,0.5;list_player_areas;My areas]'; + +   -- subareas of own areas can be deleted (but not renamed) +   elseif( not( is_owner ) +      and this_area.parent +      and areas.areas[ this_area.parent ] +      and areas.areas[ this_area.parent ].owner == pname ) then +  +      formspec = formspec.. +        'button_exit[8.0,1.0;2,0.5;delete;Delete subarea]'.. +        'button_exit[8.0,2.0;2,0.5;list_player_areas;Player\'s areas]'; + +   else +      formspec = formspec.. +        'button_exit[8.0,2.0;2,0.5;list_player_areas;Player\'s areas]'; +   end + + +   -- players with the areas priv get an extra menu +   if( minetest.check_player_privs(pname, {areas=true})) then +      formspec = formspec.. +              'label[8.0,6.0;Admin commands:]'.. +        'button_exit[8.0,6.5;2,0.5;change_owner;Change owner]'.. +        'button_exit[8.0,7.0;2,0.5;delete;Delete]'.. +        'button_exit[8.0,7.5;2,0.5;rename;Rename]'; +   end + + +   -- show subowners and areas with the same coordinates +   formspec = formspec.. +	'label[0.5,2.5;Further owners of the entire area:]'; +   local further_owners = {}; +   for i, area in pairs(areas.areas) do +      if( i ~= id and +          this_area.pos1.x == area.pos1.x and this_area.pos2.x == area.pos2.x and +          this_area.pos1.y == area.pos1.y and this_area.pos2.y == area.pos2.y and +          this_area.pos1.z == area.pos1.z and this_area.pos2.z == area.pos2.z )then + +         table.insert( further_owners, area.owner ); +      end +   end +   if( #further_owners > 0 ) then + +      formspec = formspec.. +        'label[4.7,2.5;'..minetest.formspec_escape( table.concat( further_owners, ', '))..'.]'; + +      -- deleting subowners is done by deleting their areas +      if( is_owner ) then +         formspec = formspec.. +	        'button_exit[8.0,2.5;2,0.5;add_owner;Add]'; +      end +   else +      formspec = formspec.. +        'label[4.7,2.5;-none-]'; +  +      if( is_owner ) then +         formspec = formspec.. +		'button_exit[8.0,2.5;2,0.5;add_owner;Add]'; +      end +   end   + + +   -- is the area a subarea? +   if( this_area.parent ) then +      formspec = formspec.. +	'label[0.5,3.0;This area is a sub-area of area]'.. +	'label[4.7,3.0;'..minetest.formspec_escape( areas.areas[ this_area.parent ].name..' ['..this_area.parent..']' )..']'.. +        'button_exit[8.0,3.0;2,0.5;show_parent;Show main area]'; +   end +  + +   -- does the area have subareas, i.e. is it a parent area for others? +   local sub_areas = {}; +   for i, area in pairs(areas.areas) do +      if( i ~= id and +          area.parent == id ) then + +         table.insert( sub_areas, i ); +      end +   end +   if( #sub_areas > 0 ) then + +      formspec = formspec.. +	'label[0.5,4.0;Number of defined subareas:]'.. +        'label[4.7,4.0;'..tostring( #sub_areas )..']'.. +        'button_exit[8.0,4.0;2,0.5;list_subareas;List subareas]'; +   else +      formspec = formspec.. +	'label[0.5,4.0;There are no subareas defined.]'; +   end + + +   -- give information about the size of the area +   local length_x = (math.abs( this_area.pos2.x - this_area.pos1.x )+1); +   local length_y = (math.abs( this_area.pos2.y - this_area.pos1.y )+1); +   local length_z = (math.abs( this_area.pos2.z - this_area.pos1.z )+1); + +   formspec = formspec.. +               'label[0.5,4.5;The area extends from]'.. +               'label[4.7,4.5;'..minetest.pos_to_string( this_area.pos1 )..' to '..minetest.pos_to_string( this_area.pos2 )..'.]'.. +               'label[4.7,4.75;It spans '..tostring( length_x ).. +                                   ' x '..tostring( length_z ).. +                                   ' = '..tostring( length_x * length_z ).. +                                   ' m^2. Height: '..tostring( length_y )..' m.]'; + + +   formspec = formspec.. +               markers.show_compass_marker( 2.0, 7.0, true, pos, this_area.pos1, this_area.pos2 ); + +-- TODO: buy / sell button + +   local pname = player:get_player_name(); +   if( not( markers.menu_data_by_player[ pname ] )) then +      markers.menu_data_by_player[ pname ] = {}; +   end + +   -- we need to remember especially the id_list - else it would be impossible to know what the +   -- player selected +   markers.menu_data_by_player[ pname ] = +	{ typ       = 'show_area', +          mode      = nil,  +          pos       = pos, +          mode_data = nil, +          list      = nil, + +	  selected  = id, +	}; +          +   return formspec; +end + + + +-- shows where the area (defined by pos1/pos2) is located relative to the given position pos +-- row_offset/col_offset are offsets for the formspec +markers.show_compass_marker = function( col_offset, row_offset, with_text, pos, pos1, pos2 ) + +   local formspec = ''; +-- TODO: show up/down information somehow +-- TODO: what if checked with a land claim register? + +   -- if possible, show how far the area streches into each direction relative to pos +   if(     pos.x >= pos1.x and pos.x <= pos2.x  +       and pos.y >= pos1.y and pos.y <= pos2.y  +       and pos.z >= pos1.z and pos.z <= pos2.z ) then +  +      if( with_text ) then +         formspec = formspec.. +		'label[0.5,5.5;Dimensions of the area in relation to..]'.. +-- TODO: check if there is a marker; else write 'position you clicked on' +		'label[4.7,5.5;the marker at '..minetest.pos_to_string( pos )..':]'.. +		'button_exit[8.0,5.5;2,0.5;list_areas_at;Local areas]'; +      end +      formspec = formspec.. +		'image['..col_offset..','..row_offset..';1,1;markers_stone.png]'.. +		'label['..(col_offset-0.8)..','..(row_offset+0.05)..';'..tostring( pos.x - pos1.x         )..' m W]'.. +		'label['..(col_offset+1.0)..','..(row_offset+0.05)..';'..tostring(         pos2.x - pos.x )..' m E]'.. +		'label['..(col_offset+0.1)..','..(row_offset+0.80)..';'..tostring( pos.z - pos1.z         )..' m S]'.. +		'label['..(col_offset+0.1)..','..(row_offset-0.80)..';'..tostring(         pos2.z - pos.z )..' m N]'; + +   -- else show how far the area is away +   else + +      local starts_north = ''; +      local starts_south = ''; +      local starts_east  = ''; +      local starts_west  = ''; +      if( pos.z > pos2.z ) then +         starts_north = ''; +         starts_south = tostring( pos.z - pos2.z         )..' m S'; +      else +         starts_north = tostring(         pos1.z - pos.z )..' m N'; +         starts_south = ''; +      end +      if( pos.x > pos2.x ) then +         starts_east  = ''; +         starts_west  = tostring( pos.x - pos2.x         )..' m W'; +      else +         starts_east  = tostring(         pos1.x - pos.x )..' m E'; +         starts_west  = ''; +      end + + +      if( with_text ) then +         formspec = formspec.. +		'label[0.5,5.5;Position of the area in relation to..]'.. +-- TODO: check if there is a marker; else write 'position you clicked on' +		'label[4.7,5.5;the marker at '..minetest.pos_to_string( pos )..':]'.. +		'button_exit[8.0,5.5;2,0.5;list_areas_at;Local areas]'; +      end +      formspec = formspec.. +		'image['..col_offset..','..row_offset..';1,1;compass_side_top.png]'.. +		'label['..(col_offset-0.8)..','..(row_offset+0.05)..';'..starts_west..']'.. +		'label['..(col_offset+1.0)..','..(row_offset+0.05)..';'..starts_east..']'.. +		'label['..(col_offset+0.1)..','..(row_offset-0.80)..';'..starts_north..']'.. +		'label['..(col_offset+0.1)..','..(row_offset+0.80)..';'..starts_south..']'; +   end +  +   return formspec; +end + + + +-- formspec input needs to be handled diffrently +markers.form_input_handler_areas = function( player, formname, fields) + +   local pname = player:get_player_name(); +   local ppos = player:getpos() + +   if( formname ~= "markers:info" +      or not( player ) +      or not(  markers.menu_data_by_player[ pname ] )) then +    +      return false; +   end +   +   local menu_data = markers.menu_data_by_player[ pname ]; +   local formspec = ''; + + +   -- rename an area +   if( fields.rename  +          and menu_data.selected +          and areas.areas[ menu_data.selected ] +          and areas.areas[ menu_data.selected ].owner == pname ) then +    +      local area = areas.areas[ menu_data.selected ]; +      if( not( area.name )) then +         area.name = '-enter area name-'; +      end +      formspec = 'field[rename_new_name;Enter new name for area:;'..minetest.formspec_escape( area.name )..']'; + +   elseif( fields.rename_new_name  +          and menu_data.selected +          and  areas.areas[ menu_data.selected ] +          and ((areas.areas[ menu_data.selected ].owner == pname )  +            or minetest.check_player_privs(pname, {areas=true}))) then +    +      local area = areas.areas[ menu_data.selected ]; + +      -- actually rename the area +      areas.areas[ menu_data.selected ].name = fields.rename_new_name; +      areas:save(); + +      minetest.chat_send_player( pname, 'Area successfully renamed.'); +      -- shwo the renamed area +      formspec = markers.get_area_desc_formspec( menu_data.selected, player, menu_data.pos ); +  + +    +   -- change owner the area +   elseif( fields.change_owner +          and menu_data.selected +          and areas.areas[ menu_data.selected ] ) then +    +      -- there are no checks here - those happen when the area is transferred +      local area = areas.areas[ menu_data.selected ]; +      formspec = 'field[change_owner_name;Give area \"'..minetest.formspec_escape( area.name )..'\" to player:;-enter name of NEW OWNER-]'; + +   elseif( fields.change_owner_name +          and menu_data.selected +          and areas.areas[ menu_data.selected ] ) then + +      local area = areas.areas[ menu_data.selected ]; + +      -- only own areas can be transfered to another player (or if the areas priv is there) +      if( area.owner ~= pname  +        and not( minetest.check_player_privs(pname, {areas=true}))) then +  +         minetest.chat_send_player( pname, 'Permission denied. You do not own the area.'); + +      elseif( not( areas:player_exists( fields.change_owner_name ))) then + +         minetest.chat_send_player( pname, 'That player does not exist.'); + +      else +         -- actually change the owner +         areas.areas[ menu_data.selected ].owner = fields.change_owner_name; +         areas:save() + +         minetest.chat_send_player( pname, 'Your area '..tostring( area.name )..' has been transfered to '..tostring( fields.change_owner_name )..'.'); + +         minetest.chat_send_player( fields.change_owner_name, pname..'" has given you control over an area.') +      end + +      formspec = markers.get_area_desc_formspec( menu_data.selected, player, menu_data.pos ); + + +   -- add an owner to the entire area +   elseif( fields.add_owner +          and menu_data.selected +          and areas.areas[ menu_data.selected ] +          and areas.areas[ menu_data.selected ].owner == pname ) then +    +      local area = areas.areas[ menu_data.selected ]; +      formspec = 'field[add_owner_name;Grant access to area \"'..minetest.formspec_escape( area.name )..'\" to player:;-enter player name-]'; + +   elseif( fields.add_owner_name  +              -- the player has to own the area already; we need a diffrent name here +          and fields.add_owner_name ~= pname +          and menu_data.selected +          and areas.areas[ menu_data.selected ] +          and areas.areas[ menu_data.selected ].owner == pname ) then + +      local area = areas.areas[ menu_data.selected ]; + +      -- does the player exist? +      if( not( areas:player_exists( fields.add_owner_name ))) then +         minetest.chat_send_player( pname, 'That player does not exist.'); +         -- show the formspec +         formspec = markers.get_area_desc_formspec( menu_data.selected, player, menu_data.pos ); + +      else +         -- log the creation of the new area +         minetest.log("action", pname.." runs /add_owner through the markers-mod. Owner = "..fields.add_owner_name.. +                                " AreaName = "..area.name.." ParentID = "..menu_data.selected.. +                                " StartPos = "..area.pos1.x..","..area.pos1.y..","..area.pos1.z.. +                                " EndPos = "  ..area.pos2.x..","..area.pos2.y..","..area.pos2.z) + +         -- take the name from the old/parent area +         local new_id = areas:add( fields.add_owner_name, area.name, area.pos1, area.pos2, menu_data.selected ); +         areas:save() + + +         minetest.chat_send_player( fields.add_owner_name, +                                "You have been granted control over area #".. +                                new_id..". Type /list_areas to show your areas.") + +         minetest.chat_send_player( pname, 'The player may now build and dig in your area.'); +         -- shwo the new area +         markers.menu_data_by_player[ pname ].selected = new_id; +         formspec = markers.get_area_desc_formspec( new_id, player, menu_data.pos ); +      end + + + +   -- delete area +   elseif( fields.delete  +          and menu_data.selected +          and areas.areas[ menu_data.selected ] ) then + +      local area = areas.areas[ menu_data.selected ]; + +      -- a player can only delete own areas or subareas of own areas +      if( area.owner ~= pname  +        and not(     area.parent  +                 and areas.areas[ area.parent ]  +                 and areas.areas[ area.parent ].owner +                 and areas.areas[ area.parent ].owner == pname ) +        and not( minetest.check_player_privs(pname, {areas=true}))) then +  +         minetest.chat_send_player( pname, 'Permission denied. You own neither the area itshelf nor its parent area.'); +         -- shwo the area where the renaming failed +         formspec = markers.get_area_desc_formspec( menu_data.selected, player, menu_data.pos ); + +      else + +         formspec = 'field[rename_new_name;Enter new name for area:;'..minetest.formspec_escape( area.name )..']'; +         formspec = 'field[delete_confirm;'..minetest.formspec_escape( 'Really delete area \"'..area.name.. +                            '\" (owned by '..area.owner..')? Confirm with YES:')..';-type yes in capitals to confirm-]'; + +      end + +   elseif( fields.delete_confirm  +          and menu_data.selected +          and areas.areas[ menu_data.selected ] ) then +    +      local area = areas.areas[ menu_data.selected ]; +      local old_owner = area.owner; + +      local subareas = {}; +      for i,v in pairs( areas.areas ) do +         if( v.parent and v.parent == menu_data.selected ) then +            table.insert( subareas, i ); +         end +      end + +      -- a player can only delete own areas or subareas of own areas +      if( area.owner ~= pname  +        and not(     area.parent  +                 and areas.areas[ area.parent ]  +                 and areas.areas[ area.parent ].owner +                 and areas.areas[ area.parent ].owner == pname ) +        and not( minetest.check_player_privs(pname, {areas=true}))) then +  +         minetest.chat_send_player( pname, 'Permission denied. You own neither the area itshelf nor its parent area.'); +         -- shwo the renamed area +         formspec = markers.get_area_desc_formspec( menu_data.selected, player, menu_data.pos ); + +      -- avoid accidents +      elseif( fields.delete_confirm ~= 'YES' ) then +         minetest.chat_send_player( pname, 'Delition of area \"'..tostring( area.name )..'\" (owned by '..old_owner..') aborted.'); +         formspec = markers.get_area_desc_formspec( menu_data.selected, player, menu_data.pos ); + +      -- only areas without subareas can be deleted +      elseif( #subareas > 0 ) then  +         minetest.chat_send_player( pname, 'The area has '..tostring( #subareas )..' subarea(s). Please delete those first!'); +         formspec = markers.get_area_desc_formspec( menu_data.selected, player, menu_data.pos ); + +      else + +         minetest.chat_send_player( pname, 'Area \"'..tostring( area.name )..'\" (owned by '..old_owner..') deleted.'); +         -- really delete +         areas:remove( menu_data.selected, false ); -- no recursive delete +         areas:save(); +         -- show the list of areas owned by the previous owner +         formspec = markers.get_area_list_formspec(ppos, player, 'player',   menu_data.pos, old_owner, nil ); +      end + +    + +   elseif( fields.show_parent  +          and menu_data.selected +          and areas.areas[ menu_data.selected ] +          and areas.areas[ menu_data.selected ].parent ) then + +      formspec = markers.get_area_desc_formspec( areas.areas[ menu_data.selected ].parent, player, menu_data.pos ); + + +   elseif( fields.list_player_areas +          and menu_data.selected +          and areas.areas[ menu_data.selected ] ) then + +      formspec = markers.get_area_list_formspec(ppos, player, 'player',   menu_data.pos, areas.areas[ menu_data.selected ].owner, nil ); + + +   elseif( fields.list_subareas +          and menu_data.selected +          and areas.areas[ menu_data.selected ] ) then + +      formspec = markers.get_area_list_formspec(ppos, player, 'subareas', menu_data.pos, menu_data.selected, nil ); + + +   elseif( fields.list_main_areas ) then + +      formspec = markers.get_area_list_formspec(ppos, player, 'main_areas', menu_data.pos, nil, nil ); +           +   elseif( fields.list_areas_at +          and menu_data.pos ) then + +      formspec = markers.get_area_list_formspec(ppos, player, 'pos',      menu_data.pos, menu_data.pos, nil ); + + +   elseif( fields.markers_area_list_selection +          and menu_data.typ +          and menu_data.typ == 'area_list' +          and menu_data.list +          and #menu_data.list > 0 ) then + + +      local field_data = fields.markers_area_list_selection:split( ':' ); +      if( not( field_data ) or #field_data < 2 ) then +         field_data = { '', '' }; +      end + +      local selected = tonumber( field_data[ 2 ] ); +      if( field_data[1]=='DCL' ) then + +         -- on doubleclick, show detailed area information +         formspec = markers.get_area_desc_formspec( tonumber( menu_data.list[ selected ] ), player, menu_data.pos ); +      else + +         -- on single click, just show the position of that particular area +         formspec = markers.get_area_list_formspec(ppos, player, menu_data.mode, menu_data.pos, menu_data.mode_data, selected ); +      end + +   else +      return false; +   end + + +   minetest.show_formspec( pname, "markers:info", formspec ) +   return true; +end + + + +-- search the area at the given position pos that might be of most intrest to the player +markers.show_marker_stone_formspec = function( player, pos ) + +   local pname       = player:get_player_name(); +   local ppos = pos + +   -- this table stores the list the player may have selected from; at the beginning, there is no list  +   if( not( markers.menu_data_by_player[ pname ]  )) then +      markers.menu_data_by_player[ pname ] = { +	  typ       = 'area_list', +          mode      = 'main_areas', +          pos       = pos, +          mode_data = pos, +          list      = {}, + +	  selected  = nil, +      }; +   end + + +   local formspec        = ''; + +   local found_areas     = {}; +   local min_area_size   = 100000000000; + +   for id, area in pairs(areas.areas) do +      if( pos.x >= area.pos1.x and pos.x <= area.pos2.x and +          pos.y >= area.pos1.y and pos.y <= area.pos2.y and +          pos.z >= area.pos1.z and pos.z <= area.pos2.z )then + +         -- ignore y (height) value because some areas may go from bottom to top +         local area_size = math.abs( area.pos2.x - area.pos1.x ) +                         * math.abs( area.pos2.z - area.pos1.z ); + +         -- collect subareas that have the same size +         if( area_size == min_area_size ) then +            table.insert(found_areas, id ); +         -- we have found a smaller area - that is more intresting here +         elseif( area_size <= min_area_size ) then +            found_areas = {}; +            min_area_size = area_size; +            table.insert(found_areas, id ); +         end +      end +   end + +   -- no areas found; display error message and selection menu +   if(     #found_areas < 1 ) then + +      formspec = 'size[4,3]'.. + 		'label[0.5,0.5;This position is not protected.]'.. +		     'button[1.0,1.5;2,0.5;list_main_areas;List all main areas]'.. +		'button_exit[3.0,1.5;1,0.5;abort;OK]'; +  +   -- found exactly one areaa - display it +   elseif( #found_areas == 1 ) then + +      formspec = markers.get_area_desc_formspec( found_areas[ 1 ], player, pos ); + +   -- found more than one area; we have saved only those with the smallest size +   else + +      local own_area    = 0; +      local parent_area = 0; +      local upper_area  = 0; +      for i,v in ipairs( found_areas ) do + +         local area = areas.areas[ v ]; + +         -- owned by player? +         if(          area.owner == pname ) then +            own_area    = v; + +         -- parentless area? +         elseif( not( area.parent )) then +            parent_area = v; + +         -- the parent has diffrent coordinates? +         elseif(      areas.areas[ area.parent ].pos1.x ~= area.pos1.x +                   or areas.areas[ area.parent ].pos1.y ~= area.pos1.y +                   or areas.areas[ area.parent ].pos1.z ~= area.pos1.z +                   or areas.areas[ area.parent ].pos2.x ~= area.pos2.x +                   or areas.areas[ area.parent ].pos2.y ~= area.pos2.y +                   or areas.areas[ area.parent ].pos2.z ~= area.pos2.z ) then +            upper_area = v; +         end +      end + +      -- the area owned by the player is most intresting +      if(     own_area    > 0 ) then + +         formspec = markers.get_area_desc_formspec( own_area,    player, pos ); + +      -- if the player owns none of these areas, show the topmost (parentless) area +      elseif( parent_area > 0 ) then + +         formspec = markers.get_area_desc_formspec( parent_area, player, pos ); + +      -- an area which has a parent with diffrent coordinates from its child may (or may not) be the +      -- parent of all these subareas we've found here; there is no guarantee, but it's a best guess. +      -- If it is not good enough, then the player can still search for himshelf. +      elseif( upper_area  > 0 ) then + +         formspec = markers.get_area_desc_formspec( upper_area,  player, pos ); + +      -- our superficial analysis of the structure of the areas failed; it is up to the player to +      -- find out which of the candidates he is intrested in; we list them all +      else + +        formspec = markers.get_area_list_formspec(ppos, player, 'pos', pos, pos, nil ); +      end +   end + +   minetest.show_formspec( player:get_player_name(), "markers:info", formspec ); +end diff --git a/markers/config.lua b/markers/config.lua new file mode 100644 index 0000000..f29e46e --- /dev/null +++ b/markers/config.lua @@ -0,0 +1,36 @@ + +-- stores up to 4 marker positions for each player +markers.positions = {} + +-- store the positions of that many markers for each player (until server restart) +markers.MAX_MARKERS  = 50; + +-- the protection against digging of the marker by other players expires after this time +markers.EXPIRE_AFTER = 60*60*24; + +-- self-protected areas can not get higher than 100 blocks +markers.MAX_HEIGHT   = 100; + +-- only areas up to this size (in square meters) can be protected +markers.MAX_SIZE     = 1024; -- 32m * 32m = 1024 m^2 + +-- show only areas withhin this range when showing the list of ALL areas +-- (else it does get too crowded on multiplayer servers) +-- set to something >60000 in order to view all areas; set to a smaller +-- value (i.e. 500) on multiplayer servers with many protected areas + +if(     #areas.areas > 1000 ) then +	markers.AREA_RANGE   = 100; +elseif( #areas.areas > 100 ) then +	markers.AREA_RANGE   = 1000; +else +	markers.AREA_RANGE   = 100000; +end + +-- for most cases, the default values ought to work +--markers.AREA_RANGE   = 100000; + +if( io.open(minetest.get_worldpath().."/markers_settings.txt","r")) then +	io.close() +	dofile(minetest.get_worldpath().."/markers_settings.txt") +end diff --git a/markers/depends.txt b/markers/depends.txt new file mode 100644 index 0000000..a18cd91 --- /dev/null +++ b/markers/depends.txt @@ -0,0 +1 @@ +areas diff --git a/markers/init.lua b/markers/init.lua new file mode 100644 index 0000000..2e72af0 --- /dev/null +++ b/markers/init.lua @@ -0,0 +1,536 @@ + +-- markers are useful for measuring distances and for marking areas +-- markers are protected from digging by other players for one day +-- (the protection for the *marker* auto-expires then, and it can be digged) + +markers = {} + +dofile(minetest.get_modpath("markers").."/config.lua"); +dofile(minetest.get_modpath("markers").."/areas.lua"); +dofile(minetest.get_modpath("markers").."/marker_stone.lua"); +dofile(minetest.get_modpath("markers").."/land_title_register.lua"); + + +-- returns the first area found +markers.get_area_by_pos1_pos2 = function(pos1, pos2) +   for id, area in pairs(areas.areas) do + +      if( ((area.pos1.x == pos1.x and area.pos1.z == pos1.z ) +        or (area.pos1.x == pos1.x and area.pos1.z == pos2.z ) +        or (area.pos1.x == pos2.x and area.pos1.z == pos1.z ) +        or (area.pos1.x == pos2.x and area.pos1.z == pos2.z )) + +       and((area.pos2.x == pos1.x and area.pos2.z == pos1.z ) +        or (area.pos2.x == pos1.x and area.pos2.z == pos2.z ) +        or (area.pos2.x == pos2.x and area.pos2.z == pos1.z ) +        or (area.pos2.x == pos2.x and area.pos2.z == pos2.z ))) then + +          -- at least pos1 needs to have a hight value that fits in +          if(  (area.pos1.y <= pos1.y and area.pos2.y >= pos1.y) +            or (area.pos1.y >= pos1.y and area.pos2.y <= pos1.y)) then + +             local found = area; +             found[ 'id' ] = id; +             return found; + +          end +      end +   end +   return nil; +end + + + + + + +-- this function is supposed to return a text string describing the price of the land between po1 and pos2 +-- You can return somethiing like "for free" or "the promise to build anything good" as well as any +-- real prices in credits or materials - it's really just a text here. +-- Make sure you do not charge the player more than what you ask here. +markers.calculate_area_price_text = function( pos1, pos2, playername ) + +   local price = ( math.abs( pos1.x - pos2.x )+1 ) +               * ( math.abs( pos1.z - pos2.z )+1 ); + +--               * math.ceil( ( math.abs( pos1.y - pos2.y )+1 )/10); + +   return tostring( price )..' credits'; +end + + + + +markers.marker_placed = function( pos, placer, itemstack ) + +   if( not( pos ) or not( placer )) then +      return; +   end + +   local meta = minetest.get_meta( pos ); +   local name = placer:get_player_name(); + +   meta:set_string( 'infotext', 'Marker at '..minetest.pos_to_string( pos ).. +				' (placed by '..tostring( name )..'). '.. +				'Right-click to update.'); +   meta:set_string( 'owner',    name ); +   -- this allows protection of this particular marker to expire +   meta:set_string( 'time',     tostring( os.time()) ); + +   local txt = ''; + +   if( not( markers.positions[ name ] ) or #markers.positions[name]<1) then +      markers.positions[ name ] = {}; +      markers.positions[ name ][ 1 ] = pos; + +      minetest.chat_send_player( name, +		'First marker set to position '.. +		minetest.pos_to_string( markers.positions[ name ][ 1 ] ).. +		'. Please place a second marker to measure distance. '.. +		'Place four markers in a square to define an area.'); +   else +      table.insert( markers.positions[ name ], pos ); + +      local n = #markers.positions[ name ]; + +      local dx = markers.positions[ name ][ n ].x - markers.positions[ name ][ n-1 ].x; +      local dy = markers.positions[ name ][ n ].y - markers.positions[ name ][ n-1 ].y; +      local dz = markers.positions[ name ][ n ].z - markers.positions[ name ][ n-1 ].z; + +      local dir_name = "unknown"; +      local d = 0; +      if(     dx == 0 and dz > 0 )             then dir_name = "north"; d = math.abs(dz); +      elseif( dx == 0 and dz < 0 )             then dir_name = "south"; d = math.abs(dz); +      elseif( dz == 0 and dx > 0 )             then dir_name = "east";  d = math.abs(dx); +      elseif( dz == 0 and dx < 0 )             then dir_name = "west";  d = math.abs(dx); +      elseif( dx == 0 and dz == 0 and dy > 0 ) then dir_name = "above"; d = math.abs(dy); +      elseif( dx == 0 and dz == 0 and dy < 0 ) then dir_name = "below"; d = math.abs(dy); +      else + +         local area   =        (math.abs( dx )+1) +                             * (math.abs( dz )+1); +         local volume = area * (math.abs( dy )+1); + +         minetest.chat_send_player( name, 'This marker is at '.. +               minetest.pos_to_string( markers.positions[ name ][ n ] )..', while the last one is at '.. +               minetest.pos_to_string( markers.positions[ name ][ n-1 ] )..'. Distance (x/y/z): '.. +               tostring(math.abs(dx))..'/'.. +               tostring(math.abs(dy))..'/'.. +               tostring(math.abs(dz)).. +              '. Area: '..tostring( area )..' m^2. Volume: '..tostring( volume )..' m^3.'); +      end + +      -- this marker is aligned to the last one +      if( d > 0 ) then +         minetest.chat_send_player( name, 'Marker placed at '..minetest.pos_to_string( pos ).. +				'. Relative to the marker you placed before, this one is '.. + 				tostring( d )..' m '..dir_name..'.'); +      end + +      -- make sure the list does not grow too large +      if( n > markers.MAX_MARKERS ) then +         table.remove( markers.positions[ name ], 1 ); +      end +   end +end + + + +markers.marker_can_dig = function(pos,player) + +   if( not( pos ) or not( player )) then +      return true; +   end + +   local meta  = minetest.get_meta( pos ); +   local owner = meta:get_string( 'owner' ); +   local time  = meta:get_string( 'time' ); + +   -- can the marker be removed? +   if( not( owner ) +       or owner=='' +       or not( time ) +       or time=='' +       or (os.time() - tonumber( time )) > markers.EXPIRE_AFTER ) then + +      return true; + +   -- marker whose data got lost anyway +   elseif( not( markers.positions[ owner ] ) +            or #markers.positions[ owner ] < 1 ) then + +     return true; + +   -- marker owned by someone else and still in use +   elseif( owner ~= player:get_player_name()) then + +      minetest.chat_send_player( player:get_player_name(), +		'Sorry, this marker belongs to '..tostring( owner ).. +		'. If you still want to remove it, try again in '.. +		( tostring( markers.EXPIRE_AFTER + tonumber( time ) - os.time()))..' seconds.'); +      return false; + +   end + +   return true; +end + + + + +markers.marker_after_dig_node = function(pos, oldnode, oldmetadata, digger) + +   if( not(oldmetadata ) or not(oldmetadata['fields'])) then +      return; +   end + +   local owner = oldmetadata['fields']['owner']; +   if( not( owner ) +       or owner=='' +       or not( markers.positions[ owner ] ) +       or     #markers.positions[ owner ] < 1 ) then + +      return; +   end + +   -- remove the markers position from our table of stored positions +   local found = 0; +   for i,v in ipairs( markers.positions[ owner ] ) do +      if(   v.x == pos.x +        and v.y == pos.y +        and v.z == pos.z ) then +         found = i; +      end +   end +   if( found ~= 0 ) then +      table.remove( markers.positions[ owner ], found ); +   end +   return true; +end + + +--this function returns a min_pos and max_pos that are the corners +--of a box that contains ALL of the players active markers. +markers.get_box_from_markers = function(name) +  if (not name) or (not (markers.positions[ name ][ 1 ] )) then +	  return {x=0,y=0,z=0},{x=1,y=1,z=1} +	end +  local min_pos={} +	min_pos.x = markers.positions[ name ][ 1 ].x +	min_pos.y = markers.positions[ name ][ 1 ].y +	min_pos.z = markers.positions[ name ][ 1 ].z +	local max_pos={} +	max_pos.x = markers.positions[ name ][ 1 ].x +	max_pos.y = markers.positions[ name ][ 1 ].y +	max_pos.z = markers.positions[ name ][ 1 ].z +	for i,p in ipairs( markers.positions[ name ] ) do +	  if p.x < min_pos.x then min_pos.x = p.x end +		if p.x > max_pos.x then max_pos.x = p.x end +		if p.y < min_pos.y then min_pos.y = p.y end +		if p.y > max_pos.y then max_pos.y = p.y end +		if p.z < min_pos.z then min_pos.z = p.z end +		if p.z > max_pos.z then max_pos.z = p.z end +	end +	--print("getbox: min_pos.x="..min_pos.x.." y="..min_pos.y.." z="..min_pos.z) +	--print("      : max_pos.x="..max_pos.x.." y="..max_pos.y.." z="..max_pos.z) +  return min_pos, max_pos +end --get_box_from_markers + + + +markers.get_marker_formspec = function(player, pos, error_msg) +   local formspec = ""; + +   local meta  = minetest.get_meta( pos ); +   local owner = meta:get_string( 'owner' ); + +   local name  = player:get_player_name(); + +   local formspec_info = "size[6,4]".. +             "button_exit[2,2.5;1,0.5;abort;OK]".. +             "textarea[1,1;4,2;info;Information;"; +   if( owner ~= nil and owner ~= '' and owner ~= name ) then +      return formspec_info.."This marker\ncan only be used by\n"..tostring( owner )..", who\nplaced the markers.]"; +   end + +   if( not( markers.positions[ name ]) or #markers.positions[name]<1) then +      return formspec_info.."Information about the positions\nof your other markers\ngot lost.\nPlease dig and place\nyour markers again!]"; +   end + +   local n = #markers.positions[ name ]; + +	 if ( n < 2 ) then +	   return formspec_info.."Please place 2 or more markers\n - at least one in each corner\n of your area first]"; +   end + + +	 local coords={} +	 coords[1],coords[2] = markers.get_box_from_markers(name) + +   -- save data +      meta:set_string( 'coords', minetest.serialize( coords ) ); + +   if( not( coords ) or #coords < 2 or not( coords[1] ) or not( coords[2] )) then +      return formspec_info.."Error in markers.]"; +   end + +   -- the coordinates are set; we may present an input form now + +    -- has the area already been defined? +    local area = markers.get_area_by_pos1_pos2( coords[1], coords[2] ); + + +    local size = (math.abs( coords[1].x - coords[2].x )+1) +               * (math.abs( coords[1].z - coords[2].z )+1); + +    -- check if area is too large +    if( markers.MAX_SIZE < size ) then +       return formspec_info.."Error: You can only protect\nareas of up to "..tostring( markers.MAX_SIZE ).."m^2.\n".. +                             "Your marked area is "..tostring( size ).." m^2 large.]"; +    end + +    local formspec = 'size[10,7]'.. +               'label[0.5,1;The area you marked extends from]'.. +               'label[4.7,1;'..minetest.pos_to_string( coords[ 1 ] )..' to '..minetest.pos_to_string( coords[ 2 ] )..'.]'.. +               'label[4.7,1.5;It spans '..tostring( math.abs( coords[1].x - coords[2].x )+1 ).. +                               ' x '..tostring( math.abs( coords[1].z - coords[2].z )+1 ).. +                               ' = '..tostring( size )..' m^2.]'; + +    -- display the error message (if there is any) +    if( error_msg ~= nil ) then +       formspec = formspec.. +                    'label[0.5,0.0;Error: ]'.. +                    'textarea[5.0,0;4,1.5;info;;'..error_msg..']'; +    end + +    if( area and area['id'] ) then +       formspec =   formspec.. +                    'label[0.5,2.0;This is area number ]'.. +                    'label[4.7,2.0;'..tostring( area['id'] )..'.]'.. +                    'label[0.5,2.5;It is owned by ]'.. +                    'label[4.7,2.5;'..tostring( area['owner'] )..'.]'.. +                    'label[0.5,3.0;The area is called ]'.. +                    'label[4.7,3.0;'..tostring( area['name'] )..'.]'.. +                    "button_exit[2,6.0;2,0.5;abort;OK]"; +    else +       formspec =   formspec.. +--                    'label[0.5,2.0;Buying this area will cost you ]'.. +--                    'label[4.7,2.0;'..markers.calculate_area_price_text( coords[1], coords[2], name )..'.]'.. + +                    'label[0.5,3.0;Your area ought to go..]'.. +                    'label[0.5,3.5;this many blocks up:]'.. +                    'field[5.0,4.0;1,0.5;add_height;;40]'.. +                    'label[6.0,3.5;(above '..coords[2].y..' )]'.. + +                    'label[0.5,4.0;and this many blocks down:]'.. +                    'field[5.0,4.5;1,0.5;add_depth;;10]'.. +                    'label[6.0,4.0;(below '..coords[1].y..' )]'.. + +                    'label[0.5,4.5;The area shall be named]'.. +                    'field[5.0,5.0;6,0.5;set_area_name;;please enter a name]'.. + +                    "button_exit[2,6.0;2,0.5;abort;Abort]".. +                    -- code the position in the "Buy area" field +                    "button_exit[6,6.0;2,0.5;"..minetest.pos_to_string(pos)..";Protect area]"; +    end + +   return formspec; +end + + + +-- protect/buy an area +markers.marker_on_receive_fields = function(pos, formname, fields, sender) + +   if( not( pos )) then +      minetest.chat_send_player( name, 'Sorry, could not find the marker you where using to access this formspec.' ); +      return; +   end + + +   local meta  = minetest.get_meta( pos ); + +   local name  = sender:get_player_name(); + +   local coords_string = meta:get_string( 'coords' ); +   if( not( coords_string ) or coords_string == '' ) then +      minetest.chat_send_player( name, 'Could not find marked area. Please dig and place your markers again!'); +      return; +   end +   local coords = minetest.deserialize( coords_string ); + + +   -- do not protect areas twice +   local area = markers.get_area_by_pos1_pos2( coords[1], coords[2] ); +   if( area ) then + +      minetest.chat_send_player( name, 'This area is already protected.'); +      return; +   end + + +   -- check input +   local add_height = tonumber( fields['add_height'] ); +   local add_depth  = tonumber( fields['add_depth']  ); + +   local error_msg = ''; +   if(     not( add_height ) or add_height < 0 or add_height > markers.MAX_HEIGHT ) then +      minetest.chat_send_player( name, 'Please enter a number between 0 and '..tostring( markers.MAX_HEIGHT ).. +		' in the field where the height of your area is requested. Your area will stretch that many blocks '.. +		'up into the sky from the position of this marker onward.'); +      error_msg = 'The height value\nhas to be larger than 0\nand smaller than '..tostring( markers.MAX_HEIGHT ); + +   elseif( not( add_depth  ) or add_depth  < 0 or add_depth  > markers.MAX_HEIGHT ) then +      minetest.chat_send_player( name, 'Please enter a number between 0 and '..tostring( markers.MAX_HEIGHT ).. +		' in the field where the depth of your area is requested. Your area will stretch that many blocks '.. +		'into the ground from the position of this marker onward.'); +      error_msg = 'The depth value\nhas to be larger than 0\nand smaller than '..tostring( markers.MAX_HEIGHT ); + +   elseif( add_height + add_depth > markers.MAX_HEIGHT ) then +      minetest.chat_send_player( name,  'Sorry, your area exceeds the height limit. Height and depth added have to '.. +		'be smaller than '..tostring( markers.MAX_HEIGHT )..'.'); +      error_msg = 'height + depth has to\nbe smaller than '..tostring( markers.MAX_HEIGHT )..'.' + +   elseif( not( fields[ 'set_area_name' ] ) or fields['set_area_name'] == 'please enter a name' ) then +      minetest.chat_send_player( name, 'Please provide a name for your area, i.e. \"'.. +		tostring( name )..'s first house\" The name ought to describe what you intend to build here.'); +      error_msg = 'Please provide a\nname for your area!'; + +   else +      error_msg = nil; +   end + + +   if( error_msg ~= nil ) then +      minetest.show_formspec( name, "markers:mark", markers.get_marker_formspec(sender, pos, error_msg)); +      return; +   end + + +   -- those coords lack the height component +   local pos1 = coords[1]; +   local pos2 = coords[2]; +   -- apply height values from the formspeck +   pos1.y = pos1.y - add_depth; +   pos2.y = pos2.y + add_height; + +   pos1, pos2 = areas:sortPos( pos1, pos2 ); + +   --minetest.chat_send_player('singleplayer','INPUT: '..minetest.serialize( pos1  )..' pos2: '..minetest.serialize( pos2 )); +   minetest.log("action", "[markers] /protect invoked, owner="..name.. +                                " areaname="..fields['set_area_name'].. +                                " startpos="..minetest.pos_to_string(pos1).. +                                " endpos="  ..minetest.pos_to_string(pos2)); + +   local canAdd, errMsg = areas:canPlayerAddArea(pos1, pos2, name) +   if not canAdd then +      minetest.chat_send_player(name, "You can't protect that area: "..errMsg) +      minetest.show_formspec( name, "markers:mark", markers.get_marker_formspec(sender, pos, errMsg)); +      return +   end + +   local id = areas:add(name, fields['set_area_name'], pos1, pos2, nil) +   areas:save() + +   minetest.chat_send_player(name, "Area protected. ID: "..id) + +   minetest.show_formspec( name, "markers:mark", markers.get_marker_formspec(sender, pos, nil)); +end + + + +-- formspec input needs to be handled diffrently +markers.form_input_handler = function( player, formname, fields) + +   if( formname == "markers:mark" ) then + + +      if( not(fields) or fields['abort']) then +         return true; +      end + +      --- decode the position of the marker (which is hidden in the Buy-buttons name +      local pos = {}; +      for k, v in pairs( fields ) do +         if( v == 'Protect area' ) then +            pos = minetest.string_to_pos( k ); +         end +      end +      if( pos and pos.x and pos.y and pos.z ) then +         markers.marker_on_receive_fields(pos, formname, fields, player); +      end +      return true; + + +   elseif( formname == "markers:info" +      and player +      and markers.menu_data_by_player[ player:get_player_name() ] ) then + +      local res = markers.form_input_handler_areas( player, formname, fields); +      if( res ) then +         return true; +      end + +      -- TODO +--      minetest.chat_send_player('singleplayer','MARKERS:INFO WITH '..minetest.serialize( fields )); + +   else +      -- TODO +--      minetest.chat_send_player('singleplayer','YOU CALLED '..tostring( formname )..' WITH '..minetest.serialize( fields )); + +   end + +   return false; + +end + +minetest.register_on_player_receive_fields( markers.form_input_handler) + + + + +minetest.register_node("markers:mark", { +	description = "Marker", +	tiles = {"markers_mark.png"}, +	drawtype = "nodebox", +	paramtype = "light", +	paramtype2 = "facedir", +	groups = {snappy=2,choppy=2,oddly_breakable_by_hand=1}, --fixed on both buttons dig client crash +	light_source = 1, +	node_box = { +		type = "fixed", +		fixed = { +			{ -0.1, -0.5, -0.1, 0.1, 1.5, 0.1 }, +			}, +		}, + +        after_place_node = function(pos, placer, itemstack) +           markers.marker_placed( pos, placer, itemstack ); +        end, + +        -- the node is digged immediately, so we may as well do all the work in can_dig (any wrong digs are not that critical) +        can_dig = function(pos,player) +           return markers.marker_can_dig( pos, player ); +        end, + +        after_dig_node = function(pos, oldnode, oldmetadata, digger) +           return markers.marker_after_dig_node( pos, oldnode, oldmetadata, digger ); +        end, + +	on_rightclick = function(pos, node, clicker) + +           minetest.show_formspec( clicker:get_player_name(), +				   "markers:mark", +				   markers.get_marker_formspec(clicker, pos, nil) +			); +	end, +}) + + +minetest.register_craft({ +   output = "markers:mark 4", +   recipe = { { "group:stick" }, +              { "default:apple" }, +              { "group:stick" }, +             } }); + diff --git a/markers/land_title_register.lua b/markers/land_title_register.lua new file mode 100644 index 0000000..4156acb --- /dev/null +++ b/markers/land_title_register.lua @@ -0,0 +1,74 @@ + + +minetest.register_tool( "markers:land_title_register", +{ +    description = "Land title register. Left-click with it to get information about who owns the land you clicked on.", +    groups = {},  +    inventory_image = "default_book.png", -- TODO +    wield_image = "", +    wield_scale = {x=1,y=1,z=1}, +    stack_max = 1, -- there is no need to have more than one +    liquids_pointable = true, -- ground with only water on can be owned as well +    -- the tool_capabilities are completely irrelevant here - no need to dig +    tool_capabilities = { +        full_punch_interval = 1.0, +        max_drop_level=0, +        groupcaps={ +            fleshy={times={[2]=0.80, [3]=0.40}, maxwear=0.05, maxlevel=1}, +            snappy={times={[2]=0.80, [3]=0.40}, maxwear=0.05, maxlevel=1}, +            choppy={times={[3]=0.90}, maxwear=0.05, maxlevel=0} +        } +    }, +    node_placement_prediction = nil, + +    on_place = function(itemstack, placer, pointed_thing) + +       if( placer == nil or pointed_thing == nil) then +          return itemstack; -- nothing consumed +       end +       local name = placer:get_player_name(); + +       -- the position is what we're actually looking for +       local pos  = minetest.get_pointed_thing_position( pointed_thing, false ); -- not above +        +       if( not( pos ) or not( pos.x )) then +          minetest.chat_send_player( name, "Position not found."); +          return itemstack; +       end + +       -- this function shows the formspec with the information about the area(s) +       markers.show_marker_stone_formspec( placer, pos ); + +       return itemstack; -- nothing consumed, nothing changed +    end, +      + +    on_use = function(itemstack, placer, pointed_thing) + +       if( placer == nil or pointed_thing == nil) then +          return itemstack; -- nothing consumed +       end +       local name = placer:get_player_name(); + +       local pos  = minetest.get_pointed_thing_position( pointed_thing, false ); -- not above +        +       if( not( pos ) or not( pos.x )) then +          minetest.chat_send_player( name, "Position not found."); +          return itemstack; +       end + +       -- this function shows the formspec with the information about the area(s) +       markers.show_marker_stone_formspec( placer, pos ); + +       return itemstack; -- nothing consumed, nothing changed +    end, +}) + + +minetest.register_craft({ +   output = "markers:land_title_register", +   recipe = { { "markers:mark" }, +              { "markers:stone" }, +              { "default:book"} +             } }); + diff --git a/markers/marker_stone.lua b/markers/marker_stone.lua new file mode 100644 index 0000000..c687502 --- /dev/null +++ b/markers/marker_stone.lua @@ -0,0 +1,24 @@ + +minetest.register_node("markers:stone", { +	description = "Boundary marker for land administration", +	tiles = {"markers_stone.png", "markers_stone.png", "markers_stone_side.png", +                "markers_stone_side.png", "markers_stone_side.png", "markers_stone_side.png" }, +-- no facedir here - we want a fixed north indication! +--	paramtype2 = "facedir", +	groups = {cracky=2}, +	legacy_facedir_simple = true, +	is_ground_content = false, + +	on_rightclick = function(pos, node, clicker) + +           markers.show_marker_stone_formspec( clicker, pos ); +	end, +}) + + +minetest.register_craft({ +   output = "markers:stone", +   recipe = { { "markers:mark" }, +              { "default:cobble" }, +             } }); + diff --git a/markers/textures/compass_side_top.png b/markers/textures/compass_side_top.pngBinary files differ new file mode 100644 index 0000000..3c87c57 --- /dev/null +++ b/markers/textures/compass_side_top.png diff --git a/markers/textures/markers_mark.png b/markers/textures/markers_mark.pngBinary files differ new file mode 100644 index 0000000..76cc6c0 --- /dev/null +++ b/markers/textures/markers_mark.png diff --git a/markers/textures/markers_stone.png b/markers/textures/markers_stone.pngBinary files differ new file mode 100644 index 0000000..73962bb --- /dev/null +++ b/markers/textures/markers_stone.png diff --git a/markers/textures/markers_stone_side.png b/markers/textures/markers_stone_side.pngBinary files differ new file mode 100644 index 0000000..7230e74 --- /dev/null +++ b/markers/textures/markers_stone_side.png | 
