2 minetest.register_privilege("delprotect","Ignore player protection")
6 protector.radius = (tonumber(minetest.setting_get("protector_radius")) or 7)
7 protector.drop = minetest.setting_getbool("protector_drop") or false
8 protector.craft = minetest.setting_getbool("protector_craft") or false
9 protector.hurt = (tonumber(minetest.setting_get("protector_hurt")) or 0)
13 if minetest.get_modpath("intllib") then
16 S = function(s, a, ...)
21 return s:gsub("(@?)@(%(?)(%d+)(%)?)",
24 return a[tonumber(n)] .. (o == "" and c or "")
26 return "@" .. o .. n .. c
33 protector.get_member_list = function(meta)
35 return meta:get_string("members"):split(" ")
38 protector.set_member_list = function(meta, list)
40 meta:set_string("members", table.concat(list, " "))
43 protector.is_member = function (meta, name)
45 for _, n in pairs(protector.get_member_list(meta)) do
55 protector.add_member = function(meta, name)
57 if protector.is_member(meta, name) then
61 local list = protector.get_member_list(meta)
63 table.insert(list, name)
65 protector.set_member_list(meta, list)
68 protector.del_member = function(meta, name)
70 local list = protector.get_member_list(meta)
72 for i, n in pairs(list) do
80 protector.set_member_list(meta, list)
83 -- Protector Interface
85 protector.generate_formspec = function(meta, pos)
87 if not protector.overlaps(pos) then
88 sell = "button[7,1;1,1;sell;sell]"
90 local formspec = "size[8,7]"
94 .. "label[2.5,0;" .. S("-- Protector interface --") .. "]"
95 .. "label[0,1;" .. S("PUNCH node to show protected area or USE for area check") .. "]"
96 .. "label[0,2;" .. S("Members:") .. "]"
97 .. "button_exit[2.5,6.2;3,0.5;close_me;" .. S("Close") .. "]" .. sell
100 local members = protector.get_member_list(meta)
101 local npp = 12 -- max users added onto protector list
104 for n = 1, #members do
109 formspec = formspec .. "button[" .. (i % 4 * 2)
110 .. "," .. math.floor(i / 4 + 3)
111 .. ";1.5,.5;protector_member;" .. members[n] .. "]"
113 -- username remove button
114 .. "button[" .. (i % 4 * 2 + 1.25) .. ","
115 .. math.floor(i / 4 + 3)
116 .. ";.75,.5;protector_del_member_" .. members[n] .. ";X]"
124 -- user name entry field
125 formspec = formspec .. "field[" .. (i % 4 * 2 + 1 / 3) .. ","
126 .. (math.floor(i / 4 + 3) + 1 / 3)
127 .. ";1.433,.5;protector_add_member;;]"
129 -- username add button
130 .."button[" .. (i % 4 * 2 + 1.25) .. ","
131 .. math.floor(i / 4 + 3) .. ";.75,.5;protector_submit;+]"
138 protector.generate_sale_formspec = function(meta, pos)
139 local price = minetest.deserialize(meta:get_string("price")) or 0
140 local formspec = "size[8,7]"
142 .. default.gui_bg_img
144 .. "label[2.5,0;" .. S("-- Sell area --") .. "]"
145 .. "label[2.5,1;" .. S("Price: $") .. price .. "]" ..
146 "item_image_button[2.5,2;1,1;".. "currency:minegeld" ..";i1;\n\n\b\b\b\b\b" .. "1" .."]" ..
147 "item_image_button[2.5,3;1,1;".. "currency:minegeld_5" ..";i5;\n\n\b\b\b\b\b" .. "1" .."]" ..
148 "item_image_button[2.5,4;1,1;".. "currency:minegeld_10" ..";i10;\n\n\b\b\b\b\b" .. "1" .."]" ..
149 "item_image_button[4.5,2;1,1;".. "currency:minegeld" ..";i-1;\n\n\b\b\b\b\b" .. "-1" .."]" ..
150 "item_image_button[4.5,3;1,1;".. "currency:minegeld_5" ..";i-5;\n\n\b\b\b\b\b" .. "-1" .."]" ..
151 "item_image_button[4.5,4;1,1;".. "currency:minegeld_10" ..";i-10;\n\n\b\b\b\b\b" .. "-1" .."]" ..
152 "button_exit[1.5,6.2;3,0.5;dont_sell;" .. S("Don't sell!") .. "]" .. "button_exit[4.6,6.2;3,0.5;ok;Sell!]"
156 protector.generate_buy_formspec = function(meta, player)
157 local price = minetest.deserialize(meta:get_string("price")) or 15
158 if not atm.balance[player] then
159 atm.balance[player] = 0
162 local formspec = "size[8,7]"
164 .. default.gui_bg_img
166 .. "label[2.5,0;" .. S("-- Sell area --") .. "]"
167 .. "label[2.5,1;" .. S("Price: $") .. price .. "]" ..
168 "label[2.5,2;" .. S("Your account balance: $") .. atm.balance[player] .. "]" ..
169 "button_exit[1.5,6.2;3,0.5;cancel;" .. S("Cancel") .. "]" .. "button_exit[4.6,6.2;3,0.5;ok;Buy]"
174 protector.overlaps = function(p)
175 local r = protector.radius * 2
176 local pos = minetest.find_nodes_in_area(
177 {x = p.x - r, y = p.y - r, z = p.z - r},
178 {x = p.x + r, y = p.y + r, z = p.z + r},
179 {"protector:protect", "protector:protect2", "protector:protect_sell"})
188 -- 1 for "This area is owned by <owner> !" if you can't dig
189 -- 2 for "This area is owned by <owner>.
190 -- 3 for checking protector overlaps
192 protector.can_dig = function(r, pos, digger, onlyowner, infolevel)
199 -- Delprotect privileged users can override protections
201 if ( minetest.check_player_privs(digger, {delprotect = true})
202 or minetest.check_player_privs(digger, {protection_bypass = true}) )
203 and infolevel == 1 then
207 if infolevel == 3 then infolevel = 1 end
209 -- Find the protector nodes
211 local pos = minetest.find_nodes_in_area(
212 {x = pos.x - r, y = pos.y - r, z = pos.z - r},
213 {x = pos.x + r, y = pos.y + r, z = pos.z + r},
214 {"protector:protect", "protector:protect2", "protector:protect_sell"})
216 local meta, owner, members
220 meta = minetest.get_meta(pos[n])
221 owner = meta:get_string("owner") or ""
222 members = meta:get_string("members") or ""
224 if owner ~= digger then
227 or not protector.is_member(meta, digger) then
229 if infolevel == 1 then
231 minetest.chat_send_player(digger,
232 S("This area is owned by @1!", owner))
234 elseif infolevel == 2 then
236 minetest.chat_send_player(digger,
237 S("This area is owned by @1.", owner))
239 minetest.chat_send_player(digger,
240 S("Protection located at: @1", minetest.pos_to_string(pos[n])))
242 if members ~= "" then
244 minetest.chat_send_player(digger,
245 S("Members: @1.", members))
253 if infolevel == 2 then
255 minetest.chat_send_player(digger,
256 S("This area is owned by @1.", owner))
258 minetest.chat_send_player(digger,
259 S("Protection located at: @1", minetest.pos_to_string(pos[n])))
261 if members ~= "" then
263 minetest.chat_send_player(digger,
264 S("Members: @1.", members))
272 if infolevel == 2 then
276 minetest.chat_send_player(digger,
277 S("This area is not protected."))
280 minetest.chat_send_player(digger, S("You can build here."))
286 -- Can node be added or removed, if so return node else true (for protected)
288 protector.old_is_protected = minetest.is_protected
290 function minetest.is_protected(pos, digger)
292 if not protector.can_dig(protector.radius, pos, digger, false, 1) then
294 local player = minetest.get_player_by_name(digger)
296 -- hurt player if protection violated
297 if protector.hurt > 0
299 player:set_hp(player:get_hp() - protector.hurt)
302 -- drop tool/item if protection violated
303 if protector.drop == true
306 local holding = player:get_wielded_item()
308 if holding:to_string() ~= "" then
311 local sta = holding:take_item(holding:get_count())
312 player:set_wielded_item(holding)
314 -- incase of lag, reset stack
315 minetest.after(0.1, function()
316 player:set_wielded_item(holding)
319 local obj = minetest.add_item(player:getpos(), sta)
321 obj:setvelocity({x = 0, y = 5, z = 0})
332 return protector.old_is_protected(pos, digger)
336 -- Make sure protection block doesn't overlap another protector's area
338 function protector.check_overlap(itemstack, placer, pointed_thing)
340 if pointed_thing.type ~= "node" then
344 if not protector.can_dig(protector.radius * 2, pointed_thing.above,
345 placer:get_player_name(), true, 3) then
347 minetest.chat_send_player(placer:get_player_name(),
348 S("Overlaps into above players protected area"))
353 return minetest.item_place(itemstack, placer, pointed_thing)
359 minetest.register_node("protector:protect", {
360 description = S("Protection Block"),
361 drawtype = "nodebox",
363 "moreblocks_circle_stone_bricks.png",
364 "moreblocks_circle_stone_bricks.png",
365 "moreblocks_circle_stone_bricks.png^protector_logo.png"
367 sounds = default.node_sound_stone_defaults(),
368 groups = {dig_immediate = 2, unbreakable = 1},
369 is_ground_content = false,
376 {-0.5 ,-0.5, -0.5, 0.5, 0.5, 0.5},
380 on_place = protector.check_overlap,
382 after_place_node = function(pos, placer)
384 local meta = minetest.get_meta(pos)
386 meta:set_string("owner", placer:get_player_name() or "")
387 meta:set_string("infotext", S("Protection (owned by @1)", meta:get_string("owner")))
388 meta:set_string("members", "")
391 on_use = function(itemstack, user, pointed_thing)
393 if pointed_thing.type ~= "node" then
397 protector.can_dig(protector.radius, pointed_thing.under, user:get_player_name(), false, 2)
400 on_rightclick = function(pos, node, clicker, itemstack)
402 local meta = minetest.get_meta(pos)
405 and protector.can_dig(1, pos,clicker:get_player_name(), true, 1) then
406 minetest.show_formspec(clicker:get_player_name(),
407 "protector:node_" .. minetest.pos_to_string(pos), protector.generate_formspec(meta, pos))
411 on_punch = function(pos, node, puncher)
413 if minetest.is_protected(pos, puncher:get_player_name()) then
417 minetest.add_entity(pos, "protector:display")
420 can_dig = function(pos, player)
422 return protector.can_dig(1, pos, player:get_player_name(), true, 1)
425 on_blast = function() end,
428 minetest.register_node("protector:protect_sell", {
429 description = S("Protection Block For Sale"),
430 drawtype = "nodebox",
434 "forsale.png^protector_logo.png"
436 sounds = default.node_sound_stone_defaults(),
437 groups = {dig_immediate = 2, unbreakable = 1},
438 is_ground_content = false,
441 drop = "protector:protect",
445 {-0.5 ,-0.5, -0.5, 0.5, 0.5, 0.5},
449 on_punch = function(pos, node, puncher)
451 if minetest.is_protected(pos, puncher:get_player_name()) then
455 minetest.add_entity(pos, "protector:display")
457 on_rightclick = function(pos, node, clicker, itemstack)
459 local meta = minetest.get_meta(pos)
460 minetest.show_formspec(clicker:get_player_name(),
461 "protector_sell:node_" .. minetest.pos_to_string(pos), protector.generate_buy_formspec(meta, clicker:get_player_name()))
464 can_dig = function(pos, player)
466 return protector.can_dig(1, pos, player:get_player_name(), true, 1)
469 on_blast = function() end,
472 if protector.craft then
473 minetest.register_craft({
474 output = "protector:protect",
476 {"default:stone", "default:stone", "default:stone"},
477 {"default:stone", "default:steel_ingot", "default:stone"},
478 {"default:stone", "default:stone", "default:stone"},
481 minetest.register_craft({
482 output = "protector:protect2",
484 {"default:stone", "default:stone", "default:stone"},
485 {"default:stone", "default:copper_ingot", "default:stone"},
486 {"default:stone", "default:stone", "default:stone"},
492 minetest.register_node("protector:protect2", {
493 description = S("Protection Logo"),
494 tiles = {"protector_logo.png"},
495 wield_image = "protector_logo.png",
496 inventory_image = "protector_logo.png",
497 sounds = default.node_sound_stone_defaults(),
498 groups = {dig_immediate = 2, unbreakable = 1},
500 paramtype2 = "wallmounted",
501 legacy_wallmounted = true,
503 drawtype = "nodebox",
504 sunlight_propagates = true,
507 type = "wallmounted",
508 wall_top = {-0.375, 0.4375, -0.5, 0.375, 0.5, 0.5},
509 wall_bottom = {-0.375, -0.5, -0.5, 0.375, -0.4375, 0.5},
510 wall_side = {-0.5, -0.5, -0.375, -0.4375, 0.5, 0.375},
512 selection_box = {type = "wallmounted"},
514 on_place = protector.check_overlap,
516 after_place_node = function(pos, placer)
518 local meta = minetest.get_meta(pos)
519 meta:set_string("price", minetest.serialize(0))
520 meta:set_string("owner", placer:get_player_name() or "")
521 meta:set_string("infotext", S("Protection (owned by @1)", meta:get_string("owner")))
522 meta:set_string("members", "")
525 on_use = function(itemstack, user, pointed_thing)
527 if pointed_thing.type ~= "node" then
531 protector.can_dig(protector.radius, pointed_thing.under, user:get_player_name(), false, 2)
534 on_rightclick = function(pos, node, clicker, itemstack)
536 local meta = minetest.get_meta(pos)
538 if protector.can_dig(1, pos, clicker:get_player_name(), true, 1) then
540 minetest.show_formspec(clicker:get_player_name(),
541 "protector:node_" .. minetest.pos_to_string(pos), protector.generate_formspec(meta,pos))
545 on_punch = function(pos, node, puncher)
547 if minetest.is_protected(pos, puncher:get_player_name()) then
551 minetest.add_entity(pos, "protector:display")
554 can_dig = function(pos, player)
556 return protector.can_dig(1, pos, player:get_player_name(), true, 1)
559 on_blast = function() end,
563 -- If name entered or button press
564 local function swap_node(pos, name)
565 local node = minetest.get_node(pos)
566 if node.name == name then
570 minetest.swap_node(pos, node)
573 minetest.register_on_player_receive_fields(function(player, formname, fields)
575 if string.sub(formname, 0, string.len("protector:node_")) == "protector:node_" then
577 local pos_s = string.sub(formname, string.len("protector:node_") + 1)
578 local pos = minetest.string_to_pos(pos_s)
579 local meta = minetest.get_meta(pos)
581 if not protector.can_dig(1, pos, player:get_player_name(), true, 1) then
585 if fields.protector_add_member then
587 for _, i in pairs(fields.protector_add_member:split(" ")) do
588 protector.add_member(meta, i)
592 for field, value in pairs(fields) do
594 if string.sub(field, 0, string.len("protector_del_member_")) == "protector_del_member_" then
595 protector.del_member(meta, string.sub(field,string.len("protector_del_member_") + 1))
599 if not fields.close_me and not fields.sell then
600 minetest.show_formspec(player:get_player_name(), formname, protector.generate_formspec(meta,pos))
604 minetest.show_formspec(player:get_player_name(), "sell"..formname, protector.generate_sale_formspec(meta,pos))
609 if string.sub(formname, 0, string.len("sellprotector:node_")) == "sellprotector:node_" then
611 local pos_s = string.sub(formname, string.len("sellprotector:node_") + 1)
612 local pos = minetest.string_to_pos(pos_s)
613 local meta = minetest.get_meta(pos)
614 local price = minetest.deserialize(meta:get_string("price")) or 0
615 for _,i in pairs({"1","5","10", "-1", "-5", "-10"}) do
616 if fields["i"..i] then
623 meta:set_string("price", minetest.serialize(price))
625 swap_node(pos, "protector:protect_sell")
626 meta:set_string("infotext", S("Protection for sale (owned by @1)", meta:get_string("owner")))
628 if not fields.ok and not fields.dont_sell then
629 minetest.show_formspec(player:get_player_name(), formname, protector.generate_sale_formspec(meta,pos))
633 if string.sub(formname, 0, string.len("protector_sell:node_")) == "protector_sell:node_" then
634 local pos_s = string.sub(formname, string.len("protector_sell:node_") + 1)
635 local pos = minetest.string_to_pos(pos_s)
636 local meta = minetest.get_meta(pos)
637 local price = minetest.deserialize(meta:get_string("price")) or 0
638 local name = player:get_player_name()
639 local oldowner = meta:get_string("owner")
640 if fields.cancel then
643 if (price > atm.balance[name]) and not (name == oldowner) then
644 minetest.chat_send_player(name, "Not enough money on your account. Please recharge on the atm")
647 atm.balance[name] = atm.balance[name] - price
648 atm.balance[oldowner] = atm.balance[oldowner] + price
651 swap_node(pos, "protector:protect")
652 meta:set_string("owner", name)
653 meta:set_string("members", "")
654 meta:set_string("infotext", S("Protection (owned by @1)", meta:get_string("owner")))
655 minetest.chat_send_player(name, "Bought protector")
660 -- Display entity shown when protector node is punched
662 minetest.register_entity("protector:display", {
664 collisionbox = {0, 0, 0, 0, 0, 0},
665 visual = "wielditem",
666 -- wielditem seems to be scaled to 1.5 times original node size
667 visual_size = {x = 1.0 / 1.5, y = 1.0 / 1.5},
668 textures = {"protector:display_node"},
671 on_activate = function(self, staticdata)
673 -- Xanadu server only
674 if (mobs and mobs.entity and mobs.entity == false)
680 on_step = function(self, dtime)
682 self.timer = self.timer + dtime
684 if self.timer > 5 then
690 -- Display-zone node, Do NOT place the display as a node,
691 -- it is made to be used as an entity (see above)
693 local x = protector.radius
694 minetest.register_node("protector:display_node", {
695 tiles = {"protector_display.png"},
696 use_texture_alpha = true,
698 drawtype = "nodebox",
703 {-(x+.55), -(x+.55), -(x+.55), -(x+.45), (x+.55), (x+.55)},
704 {-(x+.55), -(x+.55), (x+.45), (x+.55), (x+.55), (x+.55)},
705 {(x+.45), -(x+.55), -(x+.55), (x+.55), (x+.55), (x+.55)},
706 {-(x+.55), -(x+.55), -(x+.55), (x+.55), (x+.55), -(x+.45)},
708 {-(x+.55), (x+.45), -(x+.55), (x+.55), (x+.55), (x+.55)},
710 {-(x+.55), -(x+.55), -(x+.55), (x+.55), -(x+.45), (x+.55)},
711 -- middle (surround protector)
712 {-.55,-.55,-.55, .55,.55,.55},
719 groups = {dig_immediate = 3, not_in_creative_inventory = 1},
723 dofile(minetest.get_modpath("protector") .. "/doors_chest.lua")
724 dofile(minetest.get_modpath("protector") .. "/pvp.lua")
725 dofile(minetest.get_modpath("protector") .. "/admin.lua")
727 print (S("[MOD] Protector Redo loaded"))