Build 01
- initial beta version - included support files for public release
|
@ -0,0 +1,129 @@
|
||||||
|
Protector Redux Mod v1.0
|
||||||
|
By Leslie Krause
|
||||||
|
|
||||||
|
Protector Redux offers an efficient and flexible node-based protection scheme for players
|
||||||
|
on survival and creative servers. The mod was inspired by TenPlus1's "Protector Redo",
|
||||||
|
which continues the legacy of Zeg9 and glomie's original protection mods.
|
||||||
|
|
||||||
|
Repository
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
* Browse source code:
|
||||||
|
https://bitbucket.org/sorcerykid/protector
|
||||||
|
|
||||||
|
* Download archive:
|
||||||
|
https://bitbucket.org/sorcerykid/protector/get/master.zip
|
||||||
|
https://bitbucket.org/sorcerykid/protector/get/master.tar.gz
|
||||||
|
|
||||||
|
Revision History
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
Version 1.0b (09-Apr-2019)
|
||||||
|
- initial beta version
|
||||||
|
|
||||||
|
Dependencies
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
* Default Mod (required)
|
||||||
|
https://github.com/minetest/minetest_game/default
|
||||||
|
|
||||||
|
* ActiveFormspecs Mod (required)
|
||||||
|
https://bitbucket.org/sorcerykid/formspecs
|
||||||
|
|
||||||
|
Compatability
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
Minetest 0.4.15+ required
|
||||||
|
|
||||||
|
Installation
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
1) Unzip the archive into the mods directory of your game
|
||||||
|
2) Rename the protector-master directory to "protector"
|
||||||
|
|
||||||
|
Source Code License
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2019, Leslie Krause
|
||||||
|
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.
|
||||||
|
|
||||||
|
For more details:
|
||||||
|
https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
Multimedia License (textures, sounds, and models)
|
||||||
|
----------------------------------------------------------
|
||||||
|
|
||||||
|
Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)
|
||||||
|
|
||||||
|
/textures/protector_display.png
|
||||||
|
by TenPlus1
|
||||||
|
modified by maikerumine
|
||||||
|
|
||||||
|
/textures/protector_logo.png
|
||||||
|
by TenPlus1
|
||||||
|
|
||||||
|
/textures/protector_mask.png
|
||||||
|
by sorcerykid
|
||||||
|
|
||||||
|
/textures/protector_mask_saved.png
|
||||||
|
by sorcerykid
|
||||||
|
|
||||||
|
/textures/protector_side1.png (Originally WTFPL)
|
||||||
|
by Zeg9
|
||||||
|
|
||||||
|
/textures/protector_side2.png (Originally WTFPL)
|
||||||
|
by by AndrejIT
|
||||||
|
|
||||||
|
/textures/protector_top1.png (Originally WTFPL)
|
||||||
|
by Hugo Locurcio
|
||||||
|
|
||||||
|
/textures/protector_top2.png (Originally WTFPL)
|
||||||
|
by AndrejIT
|
||||||
|
|
||||||
|
/textures/protector_wand.png (Unspecified License)
|
||||||
|
by Anonymous_moose
|
||||||
|
http://forum.minetest.net/viewtopic.php?f=95&t=12703
|
||||||
|
|
||||||
|
|
||||||
|
You are free to:
|
||||||
|
Share — copy and redistribute the material in any medium or format.
|
||||||
|
Adapt — remix, transform, and build upon the material for any purpose, even commercially.
|
||||||
|
The licensor cannot revoke these freedoms as long as you follow the license terms.
|
||||||
|
|
||||||
|
Under the following terms:
|
||||||
|
|
||||||
|
Attribution — You must give appropriate credit, provide a link to the license, and
|
||||||
|
indicate if changes were made. You may do so in any reasonable manner, but not in any way
|
||||||
|
that suggests the licensor endorses you or your use.
|
||||||
|
|
||||||
|
No additional restrictions — You may not apply legal terms or technological measures that
|
||||||
|
legally restrict others from doing anything the license permits.
|
||||||
|
|
||||||
|
Notices:
|
||||||
|
|
||||||
|
You do not have to comply with the license for elements of the material in the public
|
||||||
|
domain or where your use is permitted by an applicable exception or limitation.
|
||||||
|
No warranties are given. The license may not give you all of the permissions necessary
|
||||||
|
for your intended use. For example, other rights such as publicity, privacy, or moral
|
||||||
|
rights may limit how you use the material.
|
||||||
|
|
||||||
|
For more details:
|
||||||
|
http://creativecommons.org/licenses/by-sa/3.0/
|
|
@ -0,0 +1,2 @@
|
||||||
|
default
|
||||||
|
formspecs
|
|
@ -0,0 +1,2 @@
|
||||||
|
Protector Redux offers an efficient and flexible node-based protection scheme for players
|
||||||
|
on survival and creative servers.
|
|
@ -0,0 +1,920 @@
|
||||||
|
--------------------------------------------------------
|
||||||
|
-- Minetest :: Protection Redux Mod (protector)
|
||||||
|
--
|
||||||
|
-- See README.txt for licensing and other information.
|
||||||
|
-- Copyright (c) 2016-2019, Leslie E. Krause
|
||||||
|
--
|
||||||
|
-- ./games/minetest_game/mods/protector/init.lua
|
||||||
|
--------------------------------------------------------
|
||||||
|
|
||||||
|
local OWNER_ANYBODY = "_anybody"
|
||||||
|
local OWNER_SOMEBODY = "_somebody"
|
||||||
|
local OWNER_NOBODY = "" -- do not change this value!
|
||||||
|
|
||||||
|
local member_limit = 8
|
||||||
|
local max_tool_range = 10
|
||||||
|
local protector_radius = tonumber( minetest.setting_get( "protector_radius" ) or 5 )
|
||||||
|
|
||||||
|
minetest.register_privilege( "superuser", {
|
||||||
|
description = "Bypass ownership and protection checks.",
|
||||||
|
give_to_singleplayer = true,
|
||||||
|
} )
|
||||||
|
|
||||||
|
---------------------------
|
||||||
|
-- Helper Functions
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
local function is_superuser( name )
|
||||||
|
return minetest.check_player_privs( name, "superuser" )
|
||||||
|
end
|
||||||
|
|
||||||
|
local function get_members( meta )
|
||||||
|
return meta:get_string( "members" ):split( " " )
|
||||||
|
end
|
||||||
|
|
||||||
|
local function set_members( meta, members )
|
||||||
|
meta:set_string( "members", table.concat( members, " " ) )
|
||||||
|
end
|
||||||
|
|
||||||
|
local function is_member( meta, name )
|
||||||
|
for _, n in pairs( get_members( meta ) ) do
|
||||||
|
if n == name then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local function is_owner( meta, name )
|
||||||
|
return name == meta:get_string( "owner" )
|
||||||
|
end
|
||||||
|
|
||||||
|
local function add_member( meta, name )
|
||||||
|
if is_member( meta, name ) or is_owner( meta, name ) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local members = get_members( meta )
|
||||||
|
|
||||||
|
if #members < member_limit then
|
||||||
|
table.insert( members, name )
|
||||||
|
end
|
||||||
|
|
||||||
|
set_members( meta, members )
|
||||||
|
end
|
||||||
|
|
||||||
|
local function del_member( meta, name )
|
||||||
|
local members = get_members( meta )
|
||||||
|
|
||||||
|
for i, n in pairs( members ) do
|
||||||
|
if n == name then
|
||||||
|
table.remove( members, i )
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
set_members( meta, members )
|
||||||
|
end
|
||||||
|
|
||||||
|
local function get_area_stats( meta )
|
||||||
|
local bitmap = meta:get_string( "bitmap" )
|
||||||
|
local node_total = #bitmap
|
||||||
|
local lock_count = 0
|
||||||
|
|
||||||
|
for node_count = 1, node_total do
|
||||||
|
if string.byte( bitmap, node_count ) == 120 then
|
||||||
|
lock_count = lock_count + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return node_total, lock_count
|
||||||
|
end
|
||||||
|
|
||||||
|
local function is_area_locked( meta )
|
||||||
|
local bitmap = meta:get_string( "bitmap" )
|
||||||
|
return bitmap ~= ""
|
||||||
|
end
|
||||||
|
|
||||||
|
local function is_area_locked_at( meta, source_pos, target_pos )
|
||||||
|
local bitmap = meta:get_string( "bitmap" )
|
||||||
|
|
||||||
|
-- if there's no bitmap, then the entire area is unlocked
|
||||||
|
if bitmap == "" then return false end
|
||||||
|
|
||||||
|
-- check for dot in bitmap, indicating that position is locked
|
||||||
|
local voxel_area = VoxelArea:new( {
|
||||||
|
MinEdge = vector.subtract( source_pos, protector_radius ),
|
||||||
|
MaxEdge = vector.add( source_pos, protector_radius )
|
||||||
|
} )
|
||||||
|
|
||||||
|
-- TODO: Sanity check for correct bitmap length!
|
||||||
|
|
||||||
|
local idx = voxel_area:indexp( target_pos )
|
||||||
|
return string.byte( bitmap, idx ) == 120
|
||||||
|
end
|
||||||
|
|
||||||
|
local function lock_area( meta, source_pos, content_ids )
|
||||||
|
local pos1 = vector.subtract( source_pos, protector_radius )
|
||||||
|
local pos2 = vector.add( source_pos, protector_radius )
|
||||||
|
|
||||||
|
local voxel_manip = minetest.get_voxel_manip( )
|
||||||
|
local min_pos, max_pos = voxel_manip:read_from_map( pos1, pos2 )
|
||||||
|
local map_buffer = voxel_manip:get_data( )
|
||||||
|
local voxel_area = VoxelArea:new( { MinEdge = min_pos, MaxEdge = max_pos } )
|
||||||
|
|
||||||
|
local node_data = { }
|
||||||
|
local lock_count = 0
|
||||||
|
|
||||||
|
-- indicate all non-buildable nodes as dot in bitmap
|
||||||
|
for idx in voxel_area:iterp( pos1, pos2 ) do
|
||||||
|
local is_buildable = content_ids[ map_buffer[ idx ] ]
|
||||||
|
|
||||||
|
table.insert( node_data, is_buildable and " " or "x" )
|
||||||
|
|
||||||
|
if not is_buildable then
|
||||||
|
lock_count = lock_count + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- bitmap is compressed by engine, so no need for optimization
|
||||||
|
meta:set_string( "bitmap", table.concat( node_data ) )
|
||||||
|
|
||||||
|
return #node_data, lock_count
|
||||||
|
end
|
||||||
|
|
||||||
|
local function unlock_area( meta )
|
||||||
|
meta:set_string( "bitmap", "" )
|
||||||
|
end
|
||||||
|
|
||||||
|
local function create_bitmap( is_buildable )
|
||||||
|
return string.rep( is_buildable and " " or "x", math.pow( protector_radius * 2 + 1, 3 ) )
|
||||||
|
end
|
||||||
|
|
||||||
|
local function get_bitmap_raw( meta )
|
||||||
|
local bitmap = meta:get_string( "bitmap" )
|
||||||
|
return bitmap ~= "" and bitmap or nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local function set_bitmap_raw( meta, bitmap )
|
||||||
|
meta:set_string( "bitmap", bitmap )
|
||||||
|
return get_area_stats( meta )
|
||||||
|
end
|
||||||
|
|
||||||
|
---------------------------
|
||||||
|
-- Formspec Handlers
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
local function open_shared_editor( pos, meta, player_name )
|
||||||
|
local ignore_air = true
|
||||||
|
local ignore_water = true
|
||||||
|
local ignore_lava = true
|
||||||
|
|
||||||
|
local function get_formspec( )
|
||||||
|
local formspec = "size[8,7]"
|
||||||
|
.. default.gui_bg
|
||||||
|
.. default.gui_bg_img
|
||||||
|
.. default.gui_slots
|
||||||
|
.. "label[0.0,0.0;Protector Properties (Shared)]"
|
||||||
|
.. "box[0.0,0.6;7.9,0.1;#111111]"
|
||||||
|
.. "box[0.0,6.1;7.9,0.1;#111111]"
|
||||||
|
.. "label[0.0,1.0;Members: (type player name then click '+' to add)]"
|
||||||
|
.. "button_exit[6.0,6.5;2.0,0.5;close;Close]"
|
||||||
|
|
||||||
|
if is_area_locked( meta, pos ) then
|
||||||
|
local node_total, lock_count = get_area_stats( meta )
|
||||||
|
|
||||||
|
formspec = formspec
|
||||||
|
.. "label[0.0,4.5;Bitmap Mask: (click 'Unlock' to disengage bitmap mask)]"
|
||||||
|
.. "button_exit[0.0,5.2;2.0,0.5;unlock;Unlock]"
|
||||||
|
.. string.format( "label[2.0,5.2;Contains %d nodes (%d locked, %d unlocked)]", node_total, lock_count, node_total - lock_count )
|
||||||
|
else
|
||||||
|
formspec = formspec
|
||||||
|
.. "label[0.0,4.5;Bitmap Mask: (click 'Lock' to engage bitmap mask)]"
|
||||||
|
.. "button_exit[0.0,5.2;2.0,0.5;lock;Lock]"
|
||||||
|
.. "checkbox[2.0,5.0;ignore_air;Ignore Air;" .. tostring( ignore_air ) .. "]"
|
||||||
|
.. "checkbox[3.8,5.0;ignore_water;Ignore Water;" .. tostring( ignore_water ) .. "]"
|
||||||
|
.. "checkbox[6.0,5.0;ignore_lava;Ignore Lava;" .. tostring( ignore_lava ) .. "]"
|
||||||
|
end
|
||||||
|
|
||||||
|
local members = get_members( meta )
|
||||||
|
local count = 0
|
||||||
|
|
||||||
|
for _, member in pairs( members ) do
|
||||||
|
if count < member_limit then
|
||||||
|
formspec = formspec
|
||||||
|
.. string.format( "button[%0.2f,%0.2f;1.5,0.5;;%s]", count % 4 * 2, 0.8 + math.floor( count / 4 + 1 ), member )
|
||||||
|
.. string.format( "button[%0.2f,%0.2f;0.75,0.5;del_member_%s;X]", count % 4 * 2 + 1.25, 0.8 + math.floor( count / 4 + 1 ), member )
|
||||||
|
end
|
||||||
|
count = count + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
if count < member_limit then
|
||||||
|
formspec = formspec
|
||||||
|
.. string.format( "field[%0.2f,%0.2f;1.433,0.5;member_name;;]", count % 4 * 2 + 1 / 3, 0.8 + math.floor( count / 4 + 1 ) + 1 / 3 )
|
||||||
|
.. string.format( "button[%0.2f,%0.2f;0.75,0.5;add_member;+]", count % 4 * 2 + 1.25, 0.8 + math.floor( count / 4 + 1 ) )
|
||||||
|
end
|
||||||
|
|
||||||
|
return formspec
|
||||||
|
end
|
||||||
|
|
||||||
|
local function on_close( pos, player, fields )
|
||||||
|
if fields.close then
|
||||||
|
return
|
||||||
|
|
||||||
|
elseif fields.ignore_air then
|
||||||
|
ignore_air = fields.ignore_air == "true"
|
||||||
|
|
||||||
|
elseif fields.ignore_water then
|
||||||
|
ignore_water = fields.ignore_water == "true"
|
||||||
|
|
||||||
|
elseif fields.ignore_lava then
|
||||||
|
ignore_lava = fields.ignore_lava == "true"
|
||||||
|
|
||||||
|
elseif fields.lock then
|
||||||
|
local content_ids = { }
|
||||||
|
|
||||||
|
if ignore_water then
|
||||||
|
content_ids[ minetest.get_content_id( "default:water_flowing" ) ] = true
|
||||||
|
content_ids[ minetest.get_content_id( "default:water_source" ) ] = true
|
||||||
|
end
|
||||||
|
if ignore_lava then
|
||||||
|
content_ids[ minetest.get_content_id( "default:lava_flowing" ) ] = true
|
||||||
|
content_ids[ minetest.get_content_id( "default:lava_source" ) ] = true
|
||||||
|
end
|
||||||
|
if ignore_air then
|
||||||
|
content_ids[ minetest.get_content_id( "air" ) ] = true
|
||||||
|
end
|
||||||
|
|
||||||
|
local node_total, lock_count = lock_area( meta, pos, content_ids )
|
||||||
|
minetest.chat_send_player( player_name, string.format( "Protection area updated (%d of %d nodes locked).", lock_count, node_total ) )
|
||||||
|
|
||||||
|
elseif fields.unlock then
|
||||||
|
unlock_area( meta )
|
||||||
|
minetest.chat_send_player( player_name, "Protection area updated (all nodes unlocked)." )
|
||||||
|
|
||||||
|
elseif fields.add_member then
|
||||||
|
if string.match( fields.member_name, "^[a-zA-Z0-9_-]+$" ) and string.len( fields.member_name ) <= 25 then
|
||||||
|
add_member( meta, fields.member_name )
|
||||||
|
minetest.update_form( player:get_player_name( ), get_formspec( meta ) )
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif not fields.quit then
|
||||||
|
fields.member_name = nil
|
||||||
|
|
||||||
|
local fname = next( fields, nil ) -- use next since we only care about the name of the first button
|
||||||
|
if fname then
|
||||||
|
local member_name = string.match( fname, "^del_member_(.+)" )
|
||||||
|
if member_name then
|
||||||
|
del_member( meta, member_name )
|
||||||
|
minetest.update_form( player:get_player_name( ), get_formspec( meta ) )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest.create_form( pos, player_name, get_formspec( ), on_close )
|
||||||
|
end
|
||||||
|
|
||||||
|
local function open_public_editor( pos, meta, player_name )
|
||||||
|
local ignore_air = true
|
||||||
|
local ignore_water = false
|
||||||
|
local ignore_lava = false
|
||||||
|
local allow_doors = meta:get_string( "allow_doors" ) == "true"
|
||||||
|
local allow_chests = meta:get_string( "allow_chests" ) == "true"
|
||||||
|
|
||||||
|
local function get_formspec( )
|
||||||
|
local formspec = "size[8,5]"
|
||||||
|
.. default.gui_bg
|
||||||
|
.. default.gui_bg_img
|
||||||
|
.. default.gui_slots
|
||||||
|
.. "label[0.0,0.0;Protector Properties (Public)]"
|
||||||
|
.. "box[0.0,0.6;7.9,0.1;#111111]"
|
||||||
|
.. "box[0.0,4.1;7.9,0.1;#111111]"
|
||||||
|
.. "label[0.0,1.0;Permissions:]"
|
||||||
|
.. "button_exit[6.0,4.5;2.0,0.5;close;Close]"
|
||||||
|
|
||||||
|
.. "checkbox[0.0,1.4;allow_doors;Allow Steel Doors;" .. tostring( allow_doors ) .. "]"
|
||||||
|
.. "checkbox[4.0,1.4;allow_chests;Allow Locked Chests;" .. tostring( allow_chests ) .. "]"
|
||||||
|
|
||||||
|
if is_area_locked( meta, pos ) then
|
||||||
|
local node_total, lock_count = get_area_stats( meta )
|
||||||
|
|
||||||
|
formspec = formspec
|
||||||
|
.. "label[0.0,2.5;Bitmap Mask: (click 'Unlock' to disengage bitmap mask)]"
|
||||||
|
.. "button_exit[0.0,3.2;2.0,0.5;unlock;Unlock]"
|
||||||
|
.. string.format( "label[2.0,3.2;Contains %d nodes (%d locked, %d unlocked)]", node_total, lock_count, node_total - lock_count )
|
||||||
|
else
|
||||||
|
formspec = formspec
|
||||||
|
.. "label[0.0,2.5;Bitmap Mask: (click 'Lock' to engage bitmap mask)]"
|
||||||
|
.. "button_exit[0.0,3.2;2.0,0.5;lock;Lock]"
|
||||||
|
.. "checkbox[2.0,3.0;ignore_air;Ignore Air;" .. tostring( ignore_air ) .. "]"
|
||||||
|
.. "checkbox[3.8,3.0;ignore_water;Ignore Water;" .. tostring( ignore_water ) .. "]"
|
||||||
|
.. "checkbox[6.0,3.0;ignore_lava;Ignore Lava;" .. tostring( ignore_lava ) .. "]"
|
||||||
|
end
|
||||||
|
|
||||||
|
return formspec
|
||||||
|
end
|
||||||
|
|
||||||
|
local function on_close( pos, player, fields )
|
||||||
|
if fields.close then
|
||||||
|
return
|
||||||
|
|
||||||
|
elseif fields.ignore_air then
|
||||||
|
ignore_air = fields.ignore_air == "true"
|
||||||
|
|
||||||
|
elseif fields.ignore_water then
|
||||||
|
ignore_water = fields.ignore_water == "true"
|
||||||
|
|
||||||
|
elseif fields.ignore_lava then
|
||||||
|
ignore_lava = fields.ignore_lava == "true"
|
||||||
|
|
||||||
|
elseif fields.allow_chests then
|
||||||
|
allow_chests = fields.allow_chests == "true"
|
||||||
|
meta:set_string( "allow_chests", allow_chests and "true" or "false" )
|
||||||
|
|
||||||
|
elseif fields.allow_doors then
|
||||||
|
allow_doors = fields.allow_doors == "true"
|
||||||
|
meta:set_string( "allow_doors", allow_doors and "true" or "false" )
|
||||||
|
|
||||||
|
elseif fields.lock then
|
||||||
|
local content_ids = { }
|
||||||
|
|
||||||
|
if ignore_water then
|
||||||
|
content_ids[ minetest.get_content_id( "default:water_flowing" ) ] = true
|
||||||
|
content_ids[ minetest.get_content_id( "default:water_source" ) ] = true
|
||||||
|
end
|
||||||
|
if ignore_lava then
|
||||||
|
content_ids[ minetest.get_content_id( "default:lava_flowing" ) ] = true
|
||||||
|
content_ids[ minetest.get_content_id( "default:lava_source" ) ] = true
|
||||||
|
end
|
||||||
|
if ignore_air then
|
||||||
|
content_ids[ minetest.get_content_id( "air" ) ] = true
|
||||||
|
end
|
||||||
|
|
||||||
|
local node_total, lock_count = lock_area( meta, pos, content_ids )
|
||||||
|
minetest.chat_send_player( player_name, string.format( "Protection area updated (%d of %d nodes locked).", lock_count, node_total ) )
|
||||||
|
|
||||||
|
elseif fields.unlock then
|
||||||
|
unlock_area( meta )
|
||||||
|
minetest.chat_send_player( player_name, "Protection area updated (all nodes unlocked)." )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest.create_form( pos, player_name, get_formspec( ), on_close )
|
||||||
|
end
|
||||||
|
|
||||||
|
---------------------------
|
||||||
|
-- Protection Handlers
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
-- Info Level:
|
||||||
|
-- 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
|
||||||
|
|
||||||
|
local function can_dig( radius, target_pos, player_name, is_strict, info_level )
|
||||||
|
if not player_name then return false end
|
||||||
|
|
||||||
|
-- Privileged users can override protection
|
||||||
|
if minetest.check_player_privs( player_name, "superuser" ) and info_level == 1 then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
if info_level == 3 then info_level = 1 end
|
||||||
|
|
||||||
|
local pos_list = minetest.find_nodes_in_area(
|
||||||
|
vector.subtract( target_pos, radius or protector_radius ),
|
||||||
|
vector.add( target_pos, radius or protector_radius ),
|
||||||
|
{ "protector:protect", "protector:protect2", "protector:protect3" }
|
||||||
|
)
|
||||||
|
|
||||||
|
for _, pos in pairs( pos_list ) do
|
||||||
|
local meta = minetest.get_meta( pos )
|
||||||
|
local owner = meta:get_string( "owner" )
|
||||||
|
local members = meta:get_string( "members" )
|
||||||
|
local is_public = minetest.get_node( pos ).name == "protector:protect3"
|
||||||
|
|
||||||
|
if owner ~= player_name then
|
||||||
|
|
||||||
|
if is_strict or not is_public and not is_member( meta, player_name ) or is_area_locked_at( meta, pos, target_pos ) then
|
||||||
|
if info_level == 1 then
|
||||||
|
minetest.chat_send_player( player_name, "This area is owned by " .. owner .. "!" )
|
||||||
|
elseif info_level == 2 then
|
||||||
|
minetest.chat_send_player( player_name, "This area is owned by " .. owner .. "." )
|
||||||
|
end
|
||||||
|
|
||||||
|
if members ~= "" then
|
||||||
|
minetest.chat_send_player( player_name, "Protector located at " .. minetest.pos_to_string( pos ) .. " with members: " .. members .. "." )
|
||||||
|
else
|
||||||
|
minetest.chat_send_player( player_name, "Protector located at " .. minetest.pos_to_string( pos ) .. "." )
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if info_level == 2 then
|
||||||
|
minetest.chat_send_player( player_name, "This area is owned by " .. owner .. "." )
|
||||||
|
|
||||||
|
if members ~= "" then
|
||||||
|
minetest.chat_send_player( player_name, "Protector located at " .. minetest.pos_to_string( pos ) .. " with members: " .. members .. "." )
|
||||||
|
else
|
||||||
|
minetest.chat_send_player( player_name, "Protector located at " .. minetest.pos_to_string( pos ) .. "." )
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if info_level == 2 then
|
||||||
|
if #positions == 0 then
|
||||||
|
minetest.chat_send_player( player_name, "This area is not protected." )
|
||||||
|
end
|
||||||
|
minetest.chat_send_player( digger, "You can build here." )
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
local old_is_protected = minetest.is_protected
|
||||||
|
|
||||||
|
minetest.is_protected = function ( pos, player_name )
|
||||||
|
local owner = minetest.get_meta( pos ):get_string( "owner" )
|
||||||
|
local player = minetest.get_player_by_name( player_name )
|
||||||
|
|
||||||
|
-- owner = _anybody -> always FALSE
|
||||||
|
-- owner = <digger> -> always FALSE
|
||||||
|
-- owner = _somebody -> always TRUE
|
||||||
|
-- owner = <stranger> -> always TRUE
|
||||||
|
-- owner = _nobody / null -> return protection
|
||||||
|
|
||||||
|
-- never allow digging of nodes owned by stranger or by OWNER_SOMEBODY (strictly private ownership)
|
||||||
|
-- always allow digging of nodes owned by digger or by OWNER_ANYBODY (strictly public ownership)
|
||||||
|
-- otherwise verify protection rules for nodes owned by OWNER_NOBODY (default ownership, when undefined)
|
||||||
|
|
||||||
|
-- prevent long-range dig exploit (evading protectors in unloaded areas)
|
||||||
|
if vector.distance( pos, player:getpos( ) ) > max_tool_range then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
if owner == player_name or owner == OWNER_ANYBODY or owner == OWNER_NOBODY and can_dig( protector_radius, pos, player_name, false, 1 ) then
|
||||||
|
return old_is_protected( pos, player_name )
|
||||||
|
else
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---------------------------
|
||||||
|
-- Anti-Grief Hooks
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
local function allow_place( target_pos, player_name, node_name )
|
||||||
|
if is_superuser( player_name ) then return true end
|
||||||
|
|
||||||
|
local pos_list = minetest.find_nodes_in_area(
|
||||||
|
vector.subtract( target_pos, protector_radius ),
|
||||||
|
vector.add( target_pos, protector_radius ),
|
||||||
|
{ "protector:protect3" }
|
||||||
|
)
|
||||||
|
|
||||||
|
for _, pos in pairs( pos_list ) do
|
||||||
|
local meta = minetest.get_meta( pos )
|
||||||
|
local allow_doors = meta:get_string( "allow_doors" ) == "true"
|
||||||
|
local allow_chests = meta:get_string( "allow_chests" ) == "true"
|
||||||
|
|
||||||
|
-- check restrictions of public protector
|
||||||
|
if node_name == "default:chest_locked" and not allow_chests or node_name == "doors:door_steel" and not allow_doors then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest.override_item( "default:chest_locked", {
|
||||||
|
allow_place = function ( target_pos, player )
|
||||||
|
local player_name = player:get_player_name( )
|
||||||
|
if not allow_place( target_pos, player_name, "default:chest_locked" ) then
|
||||||
|
minetest.chat_send_player( player_name, "You are not allowed to place locked chests here!" )
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
} )
|
||||||
|
|
||||||
|
minetest.override_item( "doors:door_steel", {
|
||||||
|
allow_place = function ( target_pos, player )
|
||||||
|
local player_name = player:get_player_name( )
|
||||||
|
if not allow_place( target_pos, player_name, "doors:door_steel" ) then
|
||||||
|
minetest.chat_send_player( player_name, "You are not allowed to place steel doors here!" )
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
} )
|
||||||
|
|
||||||
|
---------------------------
|
||||||
|
-- Tool Definitions
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
minetest.register_tool( "protector:protection_wand", {
|
||||||
|
description = "Protection Wand",
|
||||||
|
range = 5,
|
||||||
|
inventory_image = "protector_wand.png",
|
||||||
|
on_use = function( itemstack, player, pointed_thing )
|
||||||
|
local pos = pointed_thing.under or vector.round( vector.offset_y( player:getpos( ) ) ) -- if pointing at air, get player position instead
|
||||||
|
local player_name = player:get_player_name( )
|
||||||
|
|
||||||
|
-- find the protector nodes
|
||||||
|
local pos_list = minetest.find_nodes_in_area(
|
||||||
|
vector.subtract( pos, protector_radius ),
|
||||||
|
vector.add( pos, protector_radius ),
|
||||||
|
{ "protector:protect", "protector:protect2", "protector:protect3" }
|
||||||
|
)
|
||||||
|
|
||||||
|
if #pos_list == 0 then
|
||||||
|
minetest.chat_send_player( player_name, "This area is not protected." )
|
||||||
|
return
|
||||||
|
end
|
||||||
|
for i = 1, math.min( 5, #pos_list ) do
|
||||||
|
local owner = minetest.get_meta( pos_list[ i ] ):get_string( "owner" ) or ""
|
||||||
|
local members = minetest.get_meta( pos_list[ i ] ):get_string( "members" ) or ""
|
||||||
|
|
||||||
|
if i == 1 then
|
||||||
|
minetest.chat_send_player( player_name, "This area is owned by " .. owner .. "." )
|
||||||
|
end
|
||||||
|
if members ~= "" then
|
||||||
|
minetest.chat_send_player( player_name, "Protector located at " .. minetest.pos_to_string( pos_list[ i ] ) .. " with members: " .. members .. "." )
|
||||||
|
else
|
||||||
|
minetest.chat_send_player( player_name, "Protector located at " .. minetest.pos_to_string( pos_list[ i ] ) .. "." )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
} )
|
||||||
|
|
||||||
|
minetest.register_craftitem( "protector:protection_mask", {
|
||||||
|
description = "Protection Mask (Point to protector and use)",
|
||||||
|
inventory_image = "protector_mask.png",
|
||||||
|
wield_image = "protector_mask.png",
|
||||||
|
groups = { flammable = 3 },
|
||||||
|
|
||||||
|
on_use = function( cur_stack, player, pointed_thing )
|
||||||
|
if pointed_thing.type == "node" then
|
||||||
|
local player_name = player:get_player_name( )
|
||||||
|
local player_inv = player:get_inventory( )
|
||||||
|
local node_meta = minetest.get_meta( pointed_thing.under )
|
||||||
|
local node_name = minetest.get_node( pointed_thing.under ).name
|
||||||
|
|
||||||
|
if node_name ~= "protector:protect" and node_name ~= "protector:protect2" and node_name ~= "protector:protect3" then
|
||||||
|
return cur_stack
|
||||||
|
end
|
||||||
|
|
||||||
|
-- only owner of protector is permitted to copy mask
|
||||||
|
if not is_superuser( player_name ) and not is_owner( node_meta, player_name ) then
|
||||||
|
minetest.chat_send_player( player_name, "Access denied. Failed to copy protection mask." )
|
||||||
|
return cur_stack
|
||||||
|
end
|
||||||
|
|
||||||
|
local new_stack = ItemStack( "protector:protection_mask_saved" )
|
||||||
|
local new_stack_meta = new_stack:get_meta( )
|
||||||
|
|
||||||
|
local bitmap = get_bitmap_raw( node_meta ) or create_bitmap( true )
|
||||||
|
local node_total, lock_count = set_bitmap_raw( new_stack_meta, bitmap )
|
||||||
|
local title = string.format( "%d of %d nodes locked", lock_count, node_total )
|
||||||
|
|
||||||
|
new_stack_meta:set_string( "title", title )
|
||||||
|
new_stack_meta:set_string( "description", "Protection Mask (" .. title .. ")" )
|
||||||
|
new_stack_meta:set_string( "owner", player_name )
|
||||||
|
|
||||||
|
minetest.chat_send_player( player_name, "Protection mask copied (" .. title .. ")" )
|
||||||
|
|
||||||
|
cur_stack:take_item( )
|
||||||
|
|
||||||
|
if cur_stack:is_empty( ) then
|
||||||
|
cur_stack:replace( new_stack )
|
||||||
|
elseif player_inv:room_for_item( "main", new_stack ) then
|
||||||
|
player_inv:add_item( "main", new_stack )
|
||||||
|
else
|
||||||
|
minetest.add_item( player:getpos( ), new_stack )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return cur_stack
|
||||||
|
end
|
||||||
|
} )
|
||||||
|
|
||||||
|
minetest.register_craftitem( "protector:protection_mask_saved", {
|
||||||
|
inventory_image = "protector_mask_saved.png",
|
||||||
|
wield_image = "protector_mask_saved.png",
|
||||||
|
stack_max = 1,
|
||||||
|
groups = { flammable = 3, not_in_creative_inventory = 1 },
|
||||||
|
|
||||||
|
on_use = function( cur_stack, player, pointed_thing )
|
||||||
|
if pointed_thing.type == "node" then
|
||||||
|
local player_name = player:get_player_name( )
|
||||||
|
local node_meta = minetest.get_meta( pointed_thing.under )
|
||||||
|
local node_name = minetest.get_node( pointed_thing.under ).name
|
||||||
|
|
||||||
|
if node_name ~= "protector:protect" and node_name ~= "protector:protect2" and node_name ~= "protector:protect3" then
|
||||||
|
return cur_stack
|
||||||
|
end
|
||||||
|
|
||||||
|
-- only owner of protector is permitted to paste mask
|
||||||
|
if not is_superuser( player_name ) and not is_owner( node_meta, player_name ) then
|
||||||
|
minetest.chat_send_player( player_name, "Access denied. Failed to paste protection mask." )
|
||||||
|
return cur_stack
|
||||||
|
end
|
||||||
|
|
||||||
|
local cur_stack_meta = cur_stack:get_meta( )
|
||||||
|
local bitmap = get_bitmap_raw( cur_stack_meta )
|
||||||
|
local node_total, lock_count = set_bitmap_raw( node_meta, bitmap )
|
||||||
|
|
||||||
|
minetest.chat_send_player( player_name, string.format( "Protection mask pasted (%d of %d nodes locked).", lock_count, node_total ) )
|
||||||
|
end
|
||||||
|
|
||||||
|
return cur_stack
|
||||||
|
end
|
||||||
|
} )
|
||||||
|
|
||||||
|
---------------------------
|
||||||
|
-- Node Definitions
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
local function on_place( itemstack, placer, pointed_thing )
|
||||||
|
if pointed_thing.type == "node" then
|
||||||
|
if not can_dig( protector_radius * 2, pointed_thing.above, placer:get_player_name( ), true, 3 ) then
|
||||||
|
minetest.chat_send_player( placer:get_player_name( ), "Overlaps into above player's protected area." )
|
||||||
|
else
|
||||||
|
return minetest.item_place( itemstack, placer, pointed_thing )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return itemstack
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest.register_node( "protector:protect", {
|
||||||
|
description = "Protection Stone (Shared)",
|
||||||
|
tiles = {
|
||||||
|
"protector_top1.png",
|
||||||
|
"protector_top1.png",
|
||||||
|
"protector_side1.png"
|
||||||
|
},
|
||||||
|
sounds = default.node_sound_stone_defaults( ),
|
||||||
|
groups = { dig_immediate = 2, unbreakable = 1 },
|
||||||
|
is_ground_content = false,
|
||||||
|
paramtype = "light",
|
||||||
|
light_source = 4,
|
||||||
|
|
||||||
|
can_dig = function( pos, player )
|
||||||
|
return can_dig( 1, pos, player:get_player_name( ), true, 1 )
|
||||||
|
end,
|
||||||
|
|
||||||
|
on_place = on_place,
|
||||||
|
|
||||||
|
after_place_node = function( pos, placer )
|
||||||
|
local meta = minetest.get_meta( pos )
|
||||||
|
local player_name = placer:get_player_name( ) or "singleplayer"
|
||||||
|
|
||||||
|
meta:set_string( "owner", player_name )
|
||||||
|
meta:set_string( "infotext", "Protection (owned by " .. player_name .. ")" )
|
||||||
|
end,
|
||||||
|
|
||||||
|
on_rightclick = function( pos, node, clicker, itemstack )
|
||||||
|
local meta = minetest.get_meta( pos )
|
||||||
|
local player_name = clicker:get_player_name( )
|
||||||
|
|
||||||
|
if is_owner( meta, player_name ) or is_superuser( player_name ) then
|
||||||
|
open_shared_editor( pos, meta, player_name )
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
on_punch = function( pos, node, puncher )
|
||||||
|
local meta = minetest.get_meta( pos )
|
||||||
|
local player_name = puncher:get_player_name( )
|
||||||
|
|
||||||
|
if is_owner( meta, player_name ) or is_superuser( player_name ) then
|
||||||
|
minetest.add_entity( pos, "protector:display")
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
on_blast = function() end,
|
||||||
|
} )
|
||||||
|
|
||||||
|
minetest.register_node( "protector:protect2", {
|
||||||
|
description = "Protection Badge (Shared)",
|
||||||
|
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 = false,
|
||||||
|
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" },
|
||||||
|
|
||||||
|
can_dig = function( pos, player )
|
||||||
|
return can_dig( 1, pos, player:get_player_name( ), true, 1 )
|
||||||
|
end,
|
||||||
|
|
||||||
|
on_place = on_place,
|
||||||
|
|
||||||
|
after_place_node = function( pos, placer )
|
||||||
|
local meta = minetest.get_meta( pos )
|
||||||
|
local player_name = placer:get_player_name( ) or "singleplayer"
|
||||||
|
|
||||||
|
meta:set_string( "owner", player_name )
|
||||||
|
meta:set_string( "infotext", "Protection (owned by " .. player_name .. ")" )
|
||||||
|
meta:set_string( "members", "" )
|
||||||
|
end,
|
||||||
|
|
||||||
|
on_rightclick = function( pos, node, clicker, itemstack )
|
||||||
|
local meta = minetest.get_meta( pos )
|
||||||
|
local player_name = clicker:get_player_name( )
|
||||||
|
|
||||||
|
if is_owner( meta, player_name ) or is_superuser( player_name ) then
|
||||||
|
open_shared_editor( pos, meta, player_name )
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
on_punch = function( pos, node, puncher )
|
||||||
|
local meta = minetest.get_meta( pos )
|
||||||
|
local player_name = puncher:get_player_name( )
|
||||||
|
|
||||||
|
if is_owner( meta, player_name ) or is_superuser( player_name ) then
|
||||||
|
minetest.add_entity( pos, "protector:display" )
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
on_blast = function( ) end,
|
||||||
|
} )
|
||||||
|
|
||||||
|
minetest.register_node( "protector:protect3", {
|
||||||
|
description = "Protection Stone (Public)",
|
||||||
|
tiles = {
|
||||||
|
"protector_top2.png",
|
||||||
|
"protector_top2.png",
|
||||||
|
"protector_side2.png"
|
||||||
|
},
|
||||||
|
sounds = default.node_sound_stone_defaults( ),
|
||||||
|
groups = { dig_immediate = 2, unbreakable = 1 },
|
||||||
|
is_ground_content = false,
|
||||||
|
paramtype = "light",
|
||||||
|
light_source = 4,
|
||||||
|
|
||||||
|
can_dig = function( pos, player )
|
||||||
|
return can_dig( 1, pos, player:get_player_name( ), true, 1 )
|
||||||
|
end,
|
||||||
|
|
||||||
|
on_place = on_place,
|
||||||
|
|
||||||
|
after_place_node = function( pos, placer )
|
||||||
|
local meta = minetest.get_meta( pos )
|
||||||
|
local player_name = placer:get_player_name( ) or "singleplayer"
|
||||||
|
|
||||||
|
meta:set_string( "owner", player_name )
|
||||||
|
meta:set_string( "infotext", "Protection (owned by " .. player_name .. ")" )
|
||||||
|
meta:set_string( "allow_doors", "false" )
|
||||||
|
meta:set_string( "allow_chests", "false" )
|
||||||
|
|
||||||
|
local node_total, lock_count = lock_area( meta, pos, {
|
||||||
|
[minetest.get_content_id( "default:water_flowing" )] = true,
|
||||||
|
[minetest.get_content_id( "default:water_source" )] = true,
|
||||||
|
[minetest.get_content_id( "default:lava_flowing" )] = true,
|
||||||
|
[minetest.get_content_id( "default:lava_source" )] = true,
|
||||||
|
[minetest.get_content_id( "air" )] = true
|
||||||
|
} )
|
||||||
|
minetest.chat_send_player( player_name, string.format( "Protection area updated (%d of %d nodes locked).", lock_count, node_total ) )
|
||||||
|
end,
|
||||||
|
|
||||||
|
on_rightclick = function( pos, node, clicker, itemstack )
|
||||||
|
local meta = minetest.get_meta( pos )
|
||||||
|
local player_name = clicker:get_player_name( )
|
||||||
|
|
||||||
|
if is_owner( meta, player_name ) or is_superuser( player_name ) then
|
||||||
|
open_public_editor( pos, meta, player_name )
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
on_punch = function( pos, node, puncher )
|
||||||
|
local meta = minetest.get_meta( pos )
|
||||||
|
local player_name = puncher:get_player_name( )
|
||||||
|
|
||||||
|
if is_owner( meta, player_name ) or is_superuser( player_name ) then
|
||||||
|
minetest.add_entity( pos, "protector:display" )
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
on_blast = function() end,
|
||||||
|
} )
|
||||||
|
|
||||||
|
---------------------------
|
||||||
|
-- Entity Definition
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
minetest.register_entity( "protector:display", {
|
||||||
|
physical = false,
|
||||||
|
collisionbox = { 0, 0, 0, 0, 0, 0 },
|
||||||
|
visual = "wielditem",
|
||||||
|
-- wielditem is scaled to 1.5 times original node size?
|
||||||
|
visual_size = { x = 1.0 / 1.5, y = 1.0 / 1.5 },
|
||||||
|
textures = { "protector:display_node" },
|
||||||
|
timer = 0,
|
||||||
|
|
||||||
|
on_step = function( self, dtime )
|
||||||
|
self.timer = self.timer + dtime
|
||||||
|
|
||||||
|
if self.timer > 7 then
|
||||||
|
self.object:remove( )
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
} )
|
||||||
|
|
||||||
|
local r = protector_radius
|
||||||
|
|
||||||
|
-- NB: this node definition is only a basis for the entity above
|
||||||
|
minetest.register_node( "protector:display_node", {
|
||||||
|
tiles = { "protector_display.png" },
|
||||||
|
use_texture_alpha = true,
|
||||||
|
walkable = false,
|
||||||
|
drawtype = "nodebox",
|
||||||
|
node_box = {
|
||||||
|
type = "fixed",
|
||||||
|
fixed = {
|
||||||
|
-- west face
|
||||||
|
{ -r + 0.55, -r + 0.55, -r + 0.55, -r + 0.45, r + 0.55, r + 0.55 },
|
||||||
|
-- north face
|
||||||
|
{ -r + 0.55, -r + 0.55, r + 0.45, r + 0.55, r + 0.55, r + 0.55 },
|
||||||
|
-- east face
|
||||||
|
{ r + 0.45, -r + 0.55, -r + 0.55, r + 0.55, r + 0.55, r + 0.55 },
|
||||||
|
-- south face
|
||||||
|
{ -r + 0.55, -r + 0.55, -r + 0.55, r + 0.55, r + 0.55, -r + 0.45 },
|
||||||
|
-- top face
|
||||||
|
{ -r + 0.55, r + 0.45, -r + 0.55, r + 0.55, r + 0.55, r + 0.55 },
|
||||||
|
-- bottom face
|
||||||
|
{ -r + 0.55, -r + 0.55, -r + 0.55, r + 0.55, -r + 0.45, r +0.55 },
|
||||||
|
-- center (surround protector)
|
||||||
|
{ -0.55, -0.55, -0.55, 0.55, 0.55, 0.55 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
selection_box = {
|
||||||
|
type = "regular",
|
||||||
|
},
|
||||||
|
paramtype = "light",
|
||||||
|
groups = { dig_immediate = 3, not_in_creative_inventory = 1 },
|
||||||
|
drop = "",
|
||||||
|
} )
|
||||||
|
|
||||||
|
---------------------------
|
||||||
|
-- Crafting Recipes
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
minetest.register_craft( {
|
||||||
|
output = "protector:protect3",
|
||||||
|
recipe = {
|
||||||
|
{ "default:stone", "default:stone", "default:stone" },
|
||||||
|
{ "default:stone", "default:mese", "default:stone" },
|
||||||
|
{ "default:stone", "default:stone", "default:stone" },
|
||||||
|
}
|
||||||
|
} )
|
||||||
|
|
||||||
|
minetest.register_craft( {
|
||||||
|
output = "protector:protect2",
|
||||||
|
recipe = {
|
||||||
|
{ "default:stone", "default:copper_ingot", "default:stone" },
|
||||||
|
{ "default:copper_ingot", "default:mese", "default:copper_ingot" },
|
||||||
|
{ "default:stone", "default:copper_ingot", "default:stone" },
|
||||||
|
}
|
||||||
|
} )
|
||||||
|
|
||||||
|
minetest.register_craft( {
|
||||||
|
output = "protector:protect",
|
||||||
|
recipe = {
|
||||||
|
{ "default:stone", "default:steel_ingot", "default:stone" },
|
||||||
|
{ "default:steel_ingot", "default:mese", "default:steel_ingot" },
|
||||||
|
{ "default:stone", "default:steel_ingot", "default:stone" },
|
||||||
|
}
|
||||||
|
} )
|
||||||
|
|
||||||
|
minetest.register_craft( {
|
||||||
|
output = "protector:protection_wand",
|
||||||
|
recipe = {
|
||||||
|
{ "default:mese_crystal" },
|
||||||
|
{ "default:stick" },
|
||||||
|
}
|
||||||
|
} )
|
||||||
|
|
||||||
|
minetest.register_craft( {
|
||||||
|
output = "protector:protection_mask",
|
||||||
|
recipe = {
|
||||||
|
{ "", "default:steel_ingot", "" },
|
||||||
|
{ "default:steel_ingot", "default:mese_crystal", "default:steel_ingot" },
|
||||||
|
{ "", "default:steel_ingot", "" },
|
||||||
|
}
|
||||||
|
} )
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
name = protector
|
||||||
|
title = Protector Redux
|
||||||
|
author = sorcerykid
|
||||||
|
license = MIT
|
After Width: | Height: | Size: 163 B |
After Width: | Height: | Size: 862 B |
After Width: | Height: | Size: 225 B |
After Width: | Height: | Size: 224 B |
After Width: | Height: | Size: 727 B |
After Width: | Height: | Size: 992 B |
After Width: | Height: | Size: 668 B |
After Width: | Height: | Size: 267 B |
After Width: | Height: | Size: 415 B |