4cd23f2f5037b71f26f913cd4c1e0f8a950a3b00
[protector.git] / init.lua
1 minetest.register_privilege("delprotect","Ignore player protection")
2
3 protector = {}
4 protector.radius = (tonumber(minetest.setting_get("protector_radius")) or 5)
5
6 protector.get_member_list = function(meta)
7 return meta:get_string("members"):split(" ")
8 end
9
10 protector.set_member_list = function(meta, list)
11 meta:set_string("members", table.concat(list, " "))
12 end
13
14 protector.is_member = function (meta, name)
15 for _, n in ipairs(protector.get_member_list(meta)) do
16 if n == name then
17 return true
18 end
19 end
20 return false
21 end
22
23 protector.add_member = function(meta, name)
24 if protector.is_member(meta, name) then return end
25 local list = protector.get_member_list(meta)
26 table.insert(list, name)
27 protector.set_member_list(meta,list)
28 end
29
30 protector.del_member = function(meta, name)
31 local list = protector.get_member_list(meta)
32 for i, n in ipairs(list) do
33 if n == name then
34 table.remove(list, i)
35 break
36 end
37 end
38 protector.set_member_list(meta, list)
39 end
40
41 -- Protector Interface
42
43 protector.generate_formspec = function(meta)
44
45 local formspec = "size[8,7]"
46 ..default.gui_bg..default.gui_bg_img..default.gui_slots
47 .."label[2.5,0;-- Protector interface --]"
48 .."label[0,1;PUNCH node to show protected area or USE for area check]"
49 .."label[0,2;Members: (type player name then press Enter to add)]"
50
51 local members = protector.get_member_list(meta)
52 local npp = 12
53 local i = 0
54 for _, member in ipairs(members) do
55 if i < npp then
56 formspec = formspec .. "button[" .. (i % 4 * 2)
57 .. "," .. math.floor(i / 4 + 3)
58 .. ";1.5,.5;protector_member;" .. member .. "]"
59 .. "button[" .. (i % 4 * 2 + 1.25) .. ","
60 .. math.floor(i / 4 + 3)
61 .. ";.75,.5;protector_del_member_" .. member .. ";X]"
62 end
63 i = i + 1
64 end
65
66 if i < npp then
67 formspec = formspec .. "field[" .. (i % 4 * 2 + 1 / 3) .. ","
68 .. (math.floor(i / 4 + 3) + 1 / 3) .. ";1.433,.5;protector_add_member;;]"
69 end
70
71 formspec = formspec .. "button_exit[2.5,6.2;3,0.5;close_me;Close]"
72
73 return formspec
74 end
75
76 -- ACTUAL PROTECTION SECTION
77
78 -- Infolevel:
79 -- 0 for no info
80 -- 1 for "This area is owned by <owner> !" if you can't dig
81 -- 2 for "This area is owned by <owner>.
82 -- 3 for checking protector overlaps
83
84 protector.can_dig = function(r, pos, digger, onlyowner, infolevel)
85
86 if not digger then
87 return false
88 end
89
90 -- Delprotect privileged users can override protections
91
92 if minetest.check_player_privs(digger, {delprotect = true})
93 and infolevel == 1 then
94 return true
95 end
96
97 if infolevel == 3 then infolevel = 1 end
98
99 -- Find the protector nodes
100
101 local positions = minetest.find_nodes_in_area(
102 {x = pos.x - r, y = pos.y - r, z = pos.z - r},
103 {x = pos.x + r, y = pos.y + r, z = pos.z + r},
104 {"protector:protect", "protector:protect2"})
105
106 local meta, owner, members
107 for _, pos in ipairs(positions) do
108 meta = minetest.get_meta(pos)
109 owner = meta:get_string("owner")
110 members = meta:get_string("members")
111
112 if owner ~= digger then
113 if onlyowner or not protector.is_member(meta, digger) then
114 if infolevel == 1 then
115 minetest.chat_send_player(digger,
116 "This area is owned by " .. owner .. " !")
117 elseif infolevel == 2 then
118 minetest.chat_send_player(digger,
119 "This area is owned by " .. owner .. ".")
120 minetest.chat_send_player(digger,
121 "Protection located at: " .. minetest.pos_to_string(pos))
122 if members ~= "" then
123 minetest.chat_send_player(digger,
124 "Members: " .. members .. ".")
125 end
126 end
127 return false
128 end
129 end
130
131 if infolevel == 2 then
132 minetest.chat_send_player(digger,
133 "This area is owned by " .. owner .. ".")
134 minetest.chat_send_player(digger,
135 "Protection located at: " .. minetest.pos_to_string(positions[1]))
136 if members ~= "" then
137 minetest.chat_send_player(digger,
138 "Members: " .. members .. ".")
139 end
140 end
141
142 end
143
144 if infolevel == 2 then
145 if #positions < 1 then
146 minetest.chat_send_player(digger,
147 "This area is not protected.")
148 end
149 minetest.chat_send_player(digger, "You can build here.")
150 end
151
152 return true
153 end
154
155 -- Can node be added or removed, if so return node else true (for protected)
156
157 protector.old_is_protected = minetest.is_protected
158
159 function minetest.is_protected(pos, digger)
160
161 if not protector.can_dig(protector.radius, pos, digger, false, 1) then
162 return true
163 end
164 return protector.old_is_protected(pos, digger)
165
166 end
167
168 -- Make sure protection block doesn't overlap another protector's area
169
170 protector.old_node_place = minetest.item_place
171
172 function minetest.item_place(itemstack, placer, pointed_thing)
173
174 if itemstack:get_name() == "protector:protect"
175 or itemstack:get_name() == "protector:protect2" then
176 local user = placer:get_player_name()
177 if not protector.can_dig(protector.radius * 2, pointed_thing.above, user, true, 3) then
178 minetest.chat_send_player(user,
179 "Overlaps into another protected area")
180 return protector.old_node_place(itemstack, placer, pointed_thing.above)
181 end
182 end
183
184 return protector.old_node_place(itemstack, placer, pointed_thing)
185 end
186
187 -- END
188
189 --= Protection Block
190
191 minetest.register_node("protector:protect", {
192 description = "Protection Block",
193 tiles = {
194 "moreblocks_circle_stone_bricks.png",
195 "moreblocks_circle_stone_bricks.png",
196 "moreblocks_circle_stone_bricks.png^protector_logo.png"
197 },
198 sounds = default.node_sound_stone_defaults(),
199 groups = {dig_immediate = 2, unbreakable = 1},
200 paramtype = "light",
201 light_source = 2,
202
203 after_place_node = function(pos, placer)
204 local meta = minetest.get_meta(pos)
205 meta:set_string("owner", placer:get_player_name() or "")
206 meta:set_string("infotext", "Protection (owned by " .. meta:get_string("owner") .. ")")
207 meta:set_string("members", "")
208 end,
209
210 on_use = function(itemstack, user, pointed_thing)
211 if pointed_thing.type ~= "node" then return end
212 protector.can_dig(protector.radius, pointed_thing.under, user:get_player_name(), false, 2)
213 end,
214
215 on_rightclick = function(pos, node, clicker, itemstack)
216 local meta = minetest.get_meta(pos)
217 if protector.can_dig(1, pos,clicker:get_player_name(), true, 1) then
218 minetest.show_formspec(clicker:get_player_name(),
219 "protector:node_" .. minetest.pos_to_string(pos), protector.generate_formspec(meta))
220 end
221 end,
222
223 on_punch = function(pos, node, puncher)
224 if not protector.can_dig(1, pos, puncher:get_player_name(), true, 1) then
225 return
226 end
227 minetest.add_entity(pos, "protector:display")
228 end,
229
230 can_dig = function(pos, player)
231 return protector.can_dig(1, pos, player:get_player_name(), true, 1)
232 end,
233 })
234
235 minetest.register_craft({
236 output = "protector:protect 4",
237 recipe = {
238 {"default:stone", "default:stone", "default:stone"},
239 {"default:stone", "default:steel_ingot", "default:stone"},
240 {"default:stone", "default:stone", "default:stone"},
241 }
242 })
243
244 --= Protection Logo
245
246 minetest.register_node("protector:protect2", {
247 description = "Protection Logo",
248 tiles = {"protector_logo.png"},
249 wield_image = "protector_logo.png",
250 inventory_image = "protector_logo.png",
251 sounds = default.node_sound_stone_defaults(),
252 groups = {dig_immediate = 2, unbreakable = 1},
253 paramtype = 'light',
254 paramtype2 = "wallmounted",
255 light_source = 2,
256 drawtype = "nodebox",
257 sunlight_propagates = true,
258 walkable = true,
259 node_box = {
260 type = "wallmounted",
261 wall_top = {-0.375, 0.4375, -0.5, 0.375, 0.5, 0.5},
262 wall_bottom = {-0.375, -0.5, -0.5, 0.375, -0.4375, 0.5},
263 wall_side = {-0.5, -0.5, -0.375, -0.4375, 0.5, 0.375},
264 },
265 selection_box = {type = "wallmounted"},
266
267 after_place_node = function(pos, placer)
268 local meta = minetest.get_meta(pos)
269 meta:set_string("owner", placer:get_player_name() or "")
270 meta:set_string("infotext", "Protection (owned by " .. meta:get_string("owner") .. ")")
271 meta:set_string("members", "")
272 end,
273
274 on_use = function(itemstack, user, pointed_thing)
275 if pointed_thing.type ~= "node" then return end
276 protector.can_dig(protector.radius, pointed_thing.under, user:get_player_name(), false, 2)
277 end,
278
279 on_rightclick = function(pos, node, clicker, itemstack)
280 local meta = minetest.get_meta(pos)
281 if protector.can_dig(1, pos, clicker:get_player_name(), true, 1) then
282 minetest.show_formspec(clicker:get_player_name(),
283 "protector:node_" .. minetest.pos_to_string(pos), protector.generate_formspec(meta))
284 end
285 end,
286
287 on_punch = function(pos, node, puncher)
288 if not protector.can_dig(1, pos, puncher:get_player_name(), true, 1) then
289 return
290 end
291 minetest.add_entity(pos, "protector:display")
292 end,
293
294 can_dig = function(pos, player)
295 return protector.can_dig(1, pos, player:get_player_name(), true, 1)
296 end,
297 })
298
299 minetest.register_craft({
300 output = "protector:protect2 4",
301 recipe = {
302 {"default:stone", "default:stone", "default:stone"},
303 {"default:stone", "default:copper_ingot", "default:stone"},
304 {"default:stone", "default:stone", "default:stone"},
305 }
306 })
307
308 -- If name entered or button press
309 minetest.register_on_player_receive_fields(function(player,formname,fields)
310
311 if string.sub(formname, 0, string.len("protector:node_")) == "protector:node_" then
312
313 local pos_s = string.sub(formname, string.len("protector:node_") + 1)
314 local pos = minetest.string_to_pos(pos_s)
315 local meta = minetest.get_meta(pos)
316
317 if not protector.can_dig(1, pos, player:get_player_name(), true, 1) then
318 return
319 end
320
321 if fields.protector_add_member then
322 for _, i in ipairs(fields.protector_add_member:split(" ")) do
323 protector.add_member(meta, i)
324 end
325 end
326
327 for field, value in pairs(fields) do
328 if string.sub(field, 0, string.len("protector_del_member_")) == "protector_del_member_" then
329 protector.del_member(meta, string.sub(field,string.len("protector_del_member_") + 1))
330 end
331 end
332
333 if not fields.close_me then
334 minetest.show_formspec(player:get_player_name(), formname, protector.generate_formspec(meta))
335 end
336
337 end
338
339 end)
340
341 minetest.register_entity("protector:display", {
342 physical = false,
343 collisionbox = {0, 0, 0, 0, 0, 0},
344 visual = "wielditem",
345 -- wielditem seems to be scaled to 1.5 times original node size
346 visual_size = {x = 1.0 / 1.5, y = 1.0 / 1.5},
347 textures = {"protector:display_node"},
348 on_step = function(self, dtime)
349 self.timer = (self.timer or 0) + dtime
350 if self.timer > 10 then
351 self.object:remove()
352 end
353 end,
354 })
355
356 -- Display-zone node, Do NOT place the display as a node,
357 -- it is made to be used as an entity (see above)
358 local x = protector.radius
359 minetest.register_node("protector:display_node", {
360 tiles = {"protector_display.png"},
361 use_texture_alpha = true,
362 walkable = false,
363 drawtype = "nodebox",
364 node_box = {
365 type = "fixed",
366 fixed = {
367 -- sides
368 {-(x+.55), -(x+.55), -(x+.55), -(x+.45), (x+.55), (x+.55)},
369 {-(x+.55), -(x+.55), (x+.45), (x+.55), (x+.55), (x+.55)},
370 {(x+.45), -(x+.55), -(x+.55), (x+.55), (x+.55), (x+.55)},
371 {-(x+.55), -(x+.55), -(x+.55), (x+.55), (x+.55), -(x+.45)},
372 -- top
373 {-(x+.55), (x+.45), -(x+.55), (x+.55), (x+.55), (x+.55)},
374 -- bottom
375 {-(x+.55), -(x+.55), -(x+.55), (x+.55), -(x+.45), (x+.55)},
376 -- middle (surround protector)
377 {-.55,-.55,-.55, .55,.55,.55},
378 },
379 },
380 selection_box = {
381 type = "regular",
382 },
383 paramtype = "light",
384 groups = {dig_immediate = 3, not_in_creative_inventory = 1},
385 drop = "",
386 })
387
388 -- Register Protected Doors
389
390 local function on_rightclick(pos, dir, check_name, replace, replace_dir, params)
391 pos.y = pos.y+dir
392 if not minetest.get_node(pos).name == check_name then
393 return
394 end
395 local p2 = minetest.get_node(pos).param2
396 p2 = params[p2 + 1]
397
398 minetest.swap_node(pos, {name = replace_dir, param2 = p2})
399
400 pos.y = pos.y-dir
401 minetest.swap_node(pos, {name = replace, param2 = p2})
402
403 local snd_1 = "doors_door_close"
404 local snd_2 = "doors_door_open"
405 if params[1] == 3 then
406 snd_1 = "doors_door_open"
407 snd_2 = "doors_door_close"
408 end
409
410 if minetest.get_meta(pos):get_int("right") ~= 0 then
411 minetest.sound_play(snd_1, {
412 pos = pos, gain = 0.3, max_hear_distance = 10})
413 else
414 minetest.sound_play(snd_2, {
415 pos = pos, gain = 0.3, max_hear_distance = 10})
416 end
417 end
418
419 -- Protected Wooden Door
420
421 local name = "protector:door_wood"
422
423 doors.register_door(name, {
424 description = "Protected Wooden Door",
425 inventory_image = "doors_wood.png^protector_logo.png",
426 groups = {
427 snappy = 1, choppy = 2, oddly_breakable_by_hand = 2,
428 door = 1, unbreakable = 1
429 },
430 tiles_bottom = {"doors_wood_b.png^protector_logo.png", "doors_brown.png"},
431 tiles_top = {"doors_wood_a.png", "doors_brown.png"},
432 sounds = default.node_sound_wood_defaults(),
433 sunlight = false,
434 })
435
436 minetest.override_item(name .. "_b_1", {
437 on_rightclick = function(pos, node, clicker)
438 if not minetest.is_protected(pos, clicker:get_player_name()) then
439 on_rightclick(pos, 1,
440 name .. "_t_1", name .. "_b_2", name .. "_t_2", {1, 2, 3, 0})
441 end
442 end,
443 })
444
445 minetest.override_item(name.."_t_1", {
446 on_rightclick = function(pos, node, clicker)
447 if not minetest.is_protected(pos, clicker:get_player_name()) then
448 on_rightclick(pos, -1,
449 name .. "_b_1", name .. "_t_2", name .. "_b_2", {1, 2, 3, 0})
450 end
451 end,
452 })
453
454 minetest.override_item(name.."_b_2", {
455 on_rightclick = function(pos, node, clicker)
456 if not minetest.is_protected(pos, clicker:get_player_name()) then
457 on_rightclick(pos, 1,
458 name .. "_t_2", name .. "_b_1", name .. "_t_1", {3, 0, 1, 2})
459 end
460 end,
461 })
462
463 minetest.override_item(name.."_t_2", {
464 on_rightclick = function(pos, node, clicker)
465 if not minetest.is_protected(pos, clicker:get_player_name()) then
466 on_rightclick(pos, -1,
467 name .. "_b_2", name .. "_t_1", name .. "_b_1", {3, 0, 1, 2})
468 end
469 end,
470 })
471
472 minetest.register_craft({
473 output = name,
474 recipe = {
475 {"group:wood", "group:wood"},
476 {"group:wood", "default:copper_ingot"},
477 {"group:wood", "group:wood"}
478 }
479 })
480
481 minetest.register_craft({
482 output = name,
483 recipe = {
484 {"doors:door_wood", "default:copper_ingot"}
485 }
486 })
487
488 -- Protected Steel Door
489
490 local name = "protector:door_steel"
491
492 doors.register_door(name, {
493 description = "Protected Steel Door",
494 inventory_image = "doors_steel.png^protector_logo.png",
495 groups = {
496 snappy = 1, bendy = 2, cracky = 1,
497 level = 2, door = 1, unbreakable = 1
498 },
499 tiles_bottom = {"doors_steel_b.png^protector_logo.png", "doors_grey.png"},
500 tiles_top = {"doors_steel_a.png", "doors_grey.png"},
501 sounds = default.node_sound_wood_defaults(),
502 sunlight = false,
503 })
504
505 minetest.override_item(name.."_b_1", {
506 on_rightclick = function(pos, node, clicker)
507 if not minetest.is_protected(pos, clicker:get_player_name()) then
508 on_rightclick(pos, 1,
509 name .. "_t_1", name .. "_b_2", name .. "_t_2", {1, 2, 3, 0})
510 end
511 end,
512 })
513
514 minetest.override_item(name.."_t_1", {
515 on_rightclick = function(pos, node, clicker)
516 if not minetest.is_protected(pos, clicker:get_player_name()) then
517 on_rightclick(pos, -1,
518 name .. "_b_1", name .. "_t_2", name .. "_b_2", {1, 2, 3, 0})
519 end
520 end,
521 })
522
523 minetest.override_item(name.."_b_2", {
524 on_rightclick = function(pos, node, clicker)
525 if not minetest.is_protected(pos, clicker:get_player_name()) then
526 on_rightclick(pos, 1,
527 name .. "_t_2", name .. "_b_1", name .. "_t_1", {3, 0, 1, 2})
528 end
529 end,
530 })
531
532 minetest.override_item(name.."_t_2", {
533 on_rightclick = function(pos, node, clicker)
534 if not minetest.is_protected(pos, clicker:get_player_name()) then
535 on_rightclick(pos, -1,
536 name .. "_b_2", name .. "_t_1", name .. "_b_1", {3, 0, 1, 2})
537 end
538 end,
539 })
540
541 minetest.register_craft({
542 output = name,
543 recipe = {
544 {"default:steel_ingot", "default:steel_ingot"},
545 {"default:steel_ingot", "default:copper_ingot"},
546 {"default:steel_ingot", "default:steel_ingot"}
547 }
548 })
549
550 minetest.register_craft({
551 output = name,
552 recipe = {
553 {"doors:door_steel", "default:copper_ingot"}
554 }
555 })
556
557 -- Protected Chest
558
559 minetest.register_node("protector:chest", {
560 description = "Protected Chest",
561 tiles = {
562 "default_chest_top.png", "default_chest_top.png",
563 "default_chest_side.png", "default_chest_side.png",
564 "default_chest_side.png", "default_chest_front.png^protector_logo.png"
565 },
566 paramtype2 = "facedir",
567 groups = {choppy = 2, oddly_breakable_by_hand = 2, unbreakable = 1},
568 legacy_facedir_simple = true,
569 is_ground_content = false,
570 sounds = default.node_sound_wood_defaults(),
571
572 on_construct = function(pos)
573 local meta = minetest.get_meta(pos)
574 meta:set_string("infotext", "Protected Chest")
575 meta:set_string("name", "")
576 local inv = meta:get_inventory()
577 inv:set_size("main", 8 * 4)
578 end,
579
580 can_dig = function(pos,player)
581 local meta = minetest.get_meta(pos)
582 local inv = meta:get_inventory()
583 if inv:is_empty("main") then
584 if not minetest.is_protected(pos, player:get_player_name()) then
585 return true
586 end
587 end
588 end,
589
590 on_metadata_inventory_put = function(pos, listname, index, stack, player)
591 minetest.log("action", player:get_player_name() ..
592 " moves stuff to protected chest at " .. minetest.pos_to_string(pos))
593 end,
594
595 on_metadata_inventory_take = function(pos, listname, index, stack, player)
596 minetest.log("action", player:get_player_name() ..
597 " takes stuff from protected chest at " .. minetest.pos_to_string(pos))
598 end,
599
600 on_rightclick = function(pos, node, clicker)
601 if not minetest.is_protected(pos, clicker:get_player_name()) then
602 local meta = minetest.get_meta(pos)
603 local spos = pos.x .. "," .. pos.y .. "," ..pos.z
604 local formspec = "size[8,9]"..
605 default.gui_bg..default.gui_bg_img..default.gui_slots
606 .. "list[nodemeta:".. spos .. ";main;0,0.3;8,4;]"
607 .. "button[0,4.5;2,0.25;toup;To Chest]"
608 .. "field[2.3,4.8;4,0.25;chestname;;"
609 .. meta:get_string("name") .. "]"
610 .. "button[6,4.5;2,0.25;todn;To Inventory]"
611 .. "list[current_player;main;0,5;8,1;]"
612 .. "list[current_player;main;0,6.08;8,3;8]"
613 .. default.get_hotbar_bg(0,5)
614
615 minetest.show_formspec(
616 clicker:get_player_name(),
617 "protector:chest_" .. minetest.pos_to_string(pos),
618 formspec)
619 end
620 end,
621 })
622
623 -- Protected Chest formspec buttons
624
625 minetest.register_on_player_receive_fields(function(player, formname, fields)
626
627 if string.sub(formname, 0, string.len("protector:chest_")) == "protector:chest_" then
628
629 local pos_s = string.sub(formname,string.len("protector:chest_") + 1)
630 local pos = minetest.string_to_pos(pos_s)
631 local meta = minetest.get_meta(pos)
632 local chest_inv = meta:get_inventory()
633 local player_inv = player:get_inventory()
634
635 if fields.toup then
636
637 -- copy contents of players inventory to chest
638 for i, v in ipairs (player_inv:get_list("main") or {}) do
639 if (chest_inv and chest_inv:room_for_item('main', v)) then
640 local leftover = chest_inv:add_item('main', v)
641 player_inv:remove_item("main", v)
642 if (leftover and not(leftover:is_empty())) then
643 player_inv:add_item("main", v)
644 end
645 end
646 end
647
648 elseif fields.todn then
649
650 -- copy contents of chest to players inventory
651 for i, v in ipairs (chest_inv:get_list('main') or {}) do
652 if (player_inv:room_for_item("main", v)) then
653 local leftover = player_inv:add_item("main", v)
654 chest_inv:remove_item('main', v)
655 if( leftover and not(leftover:is_empty())) then
656 chest_inv:add_item('main', v)
657 end
658 end
659 end
660
661 elseif fields.chestname then
662
663 -- change chest infotext to display name
664 if fields.chestname ~= "" then
665 meta:set_string("name", fields.chestname)
666 meta:set_string("infotext",
667 "Protected Chest (" .. fields.chestname .. ")")
668 else
669 meta:set_string("infotext", "Protected Chest")
670 end
671
672 end
673 end
674
675 end)
676
677 -- Protected Chest recipe
678
679 minetest.register_craft({
680 output = 'protector:chest',
681 recipe = {
682 {'group:wood', 'group:wood', 'group:wood'},
683 {'group:wood', 'default:copper_ingot', 'group:wood'},
684 {'group:wood', 'group:wood', 'group:wood'},
685 }
686 })
687
688 minetest.register_craft({
689 output = 'protector:chest',
690 recipe = {
691 {'default:chest', 'default:copper_ingot', ''},
692 }
693 })