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