Don't sell now actually doesn't sell the node
[protector.git] / init.lua
1
2 minetest.register_privilege("delprotect","Ignore player protection")
3
4 protector = {}
5 protector.mod = "redo"
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)
10
11 -- Intllib
12 local S
13 if minetest.get_modpath("intllib") then
14 S = intllib.Getter()
15 else
16 S = function(s, a, ...)
17 if a == nil then
18 return s
19 end
20 a = {a, ...}
21 return s:gsub("(@?)@(%(?)(%d+)(%)?)",
22 function(e, o, n, c)
23 if e == ""then
24 return a[tonumber(n)] .. (o == "" and c or "")
25 else
26 return "@" .. o .. n .. c
27 end
28 end)
29 end
30 end
31 protector.intllib = S
32
33 protector.get_member_list = function(meta)
34
35 return meta:get_string("members"):split(" ")
36 end
37
38 protector.set_member_list = function(meta, list)
39
40 meta:set_string("members", table.concat(list, " "))
41 end
42
43 protector.is_member = function (meta, name)
44
45 for _, n in pairs(protector.get_member_list(meta)) do
46
47 if n == name then
48 return true
49 end
50 end
51
52 return false
53 end
54
55 protector.add_member = function(meta, name)
56
57 if protector.is_member(meta, name) then
58 return
59 end
60
61 local list = protector.get_member_list(meta)
62
63 table.insert(list, name)
64
65 protector.set_member_list(meta, list)
66 end
67
68 protector.del_member = function(meta, name)
69
70 local list = protector.get_member_list(meta)
71
72 for i, n in pairs(list) do
73
74 if n == name then
75 table.remove(list, i)
76 break
77 end
78 end
79
80 protector.set_member_list(meta, list)
81 end
82
83 -- Protector Interface
84
85 protector.generate_formspec = function(meta, pos)
86 local sell = ""
87 if not protector.overlaps(pos) then
88 sell = "button[7,1;1,1;sell;sell]"
89 end
90 local formspec = "size[8,7]"
91 .. default.gui_bg
92 .. default.gui_bg_img
93 .. default.gui_slots
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
98
99
100 local members = protector.get_member_list(meta)
101 local npp = 12 -- max users added onto protector list
102 local i = 0
103
104 for n = 1, #members do
105
106 if i < npp then
107
108 -- show username
109 formspec = formspec .. "button[" .. (i % 4 * 2)
110 .. "," .. math.floor(i / 4 + 3)
111 .. ";1.5,.5;protector_member;" .. members[n] .. "]"
112
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]"
117 end
118
119 i = i + 1
120 end
121
122 if i < npp then
123
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;;]"
128
129 -- username add button
130 .."button[" .. (i % 4 * 2 + 1.25) .. ","
131 .. math.floor(i / 4 + 3) .. ";.75,.5;protector_submit;+]"
132
133 end
134
135 return formspec
136 end
137
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]"
141 .. default.gui_bg
142 .. default.gui_bg_img
143 .. default.gui_slots
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!]"
153 return formspec
154 end
155
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
160 atm.saveaccounts()
161 end
162 local formspec = "size[8,7]"
163 .. default.gui_bg
164 .. default.gui_bg_img
165 .. default.gui_slots
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]"
170 return formspec
171 end
172
173
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"})
180 if (#pos > 1) then
181 return true
182 end
183 return false
184 end
185
186 -- Infolevel:
187 -- 0 for no info
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
191
192 protector.can_dig = function(r, pos, digger, onlyowner, infolevel)
193
194 if not digger
195 or not pos then
196 return false
197 end
198
199 -- Delprotect privileged users can override protections
200
201 if ( minetest.check_player_privs(digger, {delprotect = true})
202 or minetest.check_player_privs(digger, {protection_bypass = true}) )
203 and infolevel == 1 then
204 return true
205 end
206
207 if infolevel == 3 then infolevel = 1 end
208
209 -- Find the protector nodes
210
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"})
215
216 local meta, owner, members
217
218 for n = 1, #pos do
219
220 meta = minetest.get_meta(pos[n])
221 owner = meta:get_string("owner") or ""
222 members = meta:get_string("members") or ""
223
224 if owner ~= digger then
225
226 if onlyowner
227 or not protector.is_member(meta, digger) then
228
229 if infolevel == 1 then
230
231 minetest.chat_send_player(digger,
232 S("This area is owned by @1!", owner))
233
234 elseif infolevel == 2 then
235
236 minetest.chat_send_player(digger,
237 S("This area is owned by @1.", owner))
238
239 minetest.chat_send_player(digger,
240 S("Protection located at: @1", minetest.pos_to_string(pos[n])))
241
242 if members ~= "" then
243
244 minetest.chat_send_player(digger,
245 S("Members: @1.", members))
246 end
247 end
248
249 return false
250 end
251 end
252
253 if infolevel == 2 then
254
255 minetest.chat_send_player(digger,
256 S("This area is owned by @1.", owner))
257
258 minetest.chat_send_player(digger,
259 S("Protection located at: @1", minetest.pos_to_string(pos[n])))
260
261 if members ~= "" then
262
263 minetest.chat_send_player(digger,
264 S("Members: @1.", members))
265 end
266
267 return false
268 end
269
270 end
271
272 if infolevel == 2 then
273
274 if #pos < 1 then
275
276 minetest.chat_send_player(digger,
277 S("This area is not protected."))
278 end
279
280 minetest.chat_send_player(digger, S("You can build here."))
281 end
282
283 return true
284 end
285
286 -- Can node be added or removed, if so return node else true (for protected)
287
288 protector.old_is_protected = minetest.is_protected
289
290 function minetest.is_protected(pos, digger)
291
292 if not protector.can_dig(protector.radius, pos, digger, false, 1) then
293
294 local player = minetest.get_player_by_name(digger)
295
296 -- hurt player if protection violated
297 if protector.hurt > 0
298 and player then
299 player:set_hp(player:get_hp() - protector.hurt)
300 end
301
302 -- drop tool/item if protection violated
303 if protector.drop == true
304 and player then
305
306 local holding = player:get_wielded_item()
307
308 if holding:to_string() ~= "" then
309
310 -- take stack
311 local sta = holding:take_item(holding:get_count())
312 player:set_wielded_item(holding)
313
314 -- incase of lag, reset stack
315 minetest.after(0.1, function()
316 player:set_wielded_item(holding)
317
318 -- drop stack
319 local obj = minetest.add_item(player:getpos(), sta)
320 if obj then
321 obj:setvelocity({x = 0, y = 5, z = 0})
322 end
323 end)
324
325 end
326
327 end
328
329 return true
330 end
331
332 return protector.old_is_protected(pos, digger)
333
334 end
335
336 -- Make sure protection block doesn't overlap another protector's area
337
338 function protector.check_overlap(itemstack, placer, pointed_thing)
339
340 if pointed_thing.type ~= "node" then
341 return itemstack
342 end
343
344 if not protector.can_dig(protector.radius * 2, pointed_thing.above,
345 placer:get_player_name(), true, 3) then
346
347 minetest.chat_send_player(placer:get_player_name(),
348 S("Overlaps into above players protected area"))
349
350 return
351 end
352
353 return minetest.item_place(itemstack, placer, pointed_thing)
354
355 end
356
357 --= Protection Block
358
359 minetest.register_node("protector:protect", {
360 description = S("Protection Block"),
361 drawtype = "nodebox",
362 tiles = {
363 "moreblocks_circle_stone_bricks.png",
364 "moreblocks_circle_stone_bricks.png",
365 "moreblocks_circle_stone_bricks.png^protector_logo.png"
366 },
367 sounds = default.node_sound_stone_defaults(),
368 groups = {dig_immediate = 2, unbreakable = 1},
369 is_ground_content = false,
370 paramtype = "light",
371 light_source = 4,
372
373 node_box = {
374 type = "fixed",
375 fixed = {
376 {-0.5 ,-0.5, -0.5, 0.5, 0.5, 0.5},
377 }
378 },
379
380 on_place = protector.check_overlap,
381
382 after_place_node = function(pos, placer)
383
384 local meta = minetest.get_meta(pos)
385
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", "")
389 end,
390
391 on_use = function(itemstack, user, pointed_thing)
392
393 if pointed_thing.type ~= "node" then
394 return
395 end
396
397 protector.can_dig(protector.radius, pointed_thing.under, user:get_player_name(), false, 2)
398 end,
399
400 on_rightclick = function(pos, node, clicker, itemstack)
401
402 local meta = minetest.get_meta(pos)
403
404 if meta
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))
408 end
409 end,
410
411 on_punch = function(pos, node, puncher)
412
413 if minetest.is_protected(pos, puncher:get_player_name()) then
414 return
415 end
416
417 minetest.add_entity(pos, "protector:display")
418 end,
419
420 can_dig = function(pos, player)
421
422 return protector.can_dig(1, pos, player:get_player_name(), true, 1)
423 end,
424
425 on_blast = function() end,
426 })
427
428 minetest.register_node("protector:protect_sell", {
429 description = S("Protection Block For Sale"),
430 drawtype = "nodebox",
431 tiles = {
432 "forsale.png",
433 "forsale.png",
434 "forsale.png^protector_logo.png"
435 },
436 sounds = default.node_sound_stone_defaults(),
437 groups = {dig_immediate = 2, unbreakable = 1},
438 is_ground_content = false,
439 paramtype = "light",
440 light_source = 4,
441 drop = "protector:protect",
442 node_box = {
443 type = "fixed",
444 fixed = {
445 {-0.5 ,-0.5, -0.5, 0.5, 0.5, 0.5},
446 }
447 },
448
449 on_punch = function(pos, node, puncher)
450
451 if minetest.is_protected(pos, puncher:get_player_name()) then
452 return
453 end
454
455 minetest.add_entity(pos, "protector:display")
456 end,
457 on_rightclick = function(pos, node, clicker, itemstack)
458
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()))
462 end,
463
464 can_dig = function(pos, player)
465
466 return protector.can_dig(1, pos, player:get_player_name(), true, 1)
467 end,
468
469 on_blast = function() end,
470 })
471
472 if protector.craft then
473 minetest.register_craft({
474 output = "protector:protect",
475 recipe = {
476 {"default:stone", "default:stone", "default:stone"},
477 {"default:stone", "default:steel_ingot", "default:stone"},
478 {"default:stone", "default:stone", "default:stone"},
479 }
480 })
481 minetest.register_craft({
482 output = "protector:protect2",
483 recipe = {
484 {"default:stone", "default:stone", "default:stone"},
485 {"default:stone", "default:copper_ingot", "default:stone"},
486 {"default:stone", "default:stone", "default:stone"},
487 }
488 })
489 end
490 --= Protection Logo
491
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},
499 paramtype = 'light',
500 paramtype2 = "wallmounted",
501 legacy_wallmounted = true,
502 light_source = 4,
503 drawtype = "nodebox",
504 sunlight_propagates = true,
505 walkable = true,
506 node_box = {
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},
511 },
512 selection_box = {type = "wallmounted"},
513
514 on_place = protector.check_overlap,
515
516 after_place_node = function(pos, placer)
517
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", "")
523 end,
524
525 on_use = function(itemstack, user, pointed_thing)
526
527 if pointed_thing.type ~= "node" then
528 return
529 end
530
531 protector.can_dig(protector.radius, pointed_thing.under, user:get_player_name(), false, 2)
532 end,
533
534 on_rightclick = function(pos, node, clicker, itemstack)
535
536 local meta = minetest.get_meta(pos)
537
538 if protector.can_dig(1, pos, clicker:get_player_name(), true, 1) then
539
540 minetest.show_formspec(clicker:get_player_name(),
541 "protector:node_" .. minetest.pos_to_string(pos), protector.generate_formspec(meta,pos))
542 end
543 end,
544
545 on_punch = function(pos, node, puncher)
546
547 if minetest.is_protected(pos, puncher:get_player_name()) then
548 return
549 end
550
551 minetest.add_entity(pos, "protector:display")
552 end,
553
554 can_dig = function(pos, player)
555
556 return protector.can_dig(1, pos, player:get_player_name(), true, 1)
557 end,
558
559 on_blast = function() end,
560 })
561
562
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
567 return
568 end
569 node.name = name
570 minetest.swap_node(pos, node)
571 end
572
573 minetest.register_on_player_receive_fields(function(player, formname, fields)
574
575 if string.sub(formname, 0, string.len("protector:node_")) == "protector:node_" then
576
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)
580
581 if not protector.can_dig(1, pos, player:get_player_name(), true, 1) then
582 return
583 end
584
585 if fields.protector_add_member then
586
587 for _, i in pairs(fields.protector_add_member:split(" ")) do
588 protector.add_member(meta, i)
589 end
590 end
591
592 for field, value in pairs(fields) do
593
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))
596 end
597 end
598
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))
601 end
602
603 if fields.sell then
604 minetest.show_formspec(player:get_player_name(), "sell"..formname, protector.generate_sale_formspec(meta,pos))
605 end
606
607
608 end
609 if string.sub(formname, 0, string.len("sellprotector:node_")) == "sellprotector:node_" then
610
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
617 price = price + i
618 if price < 0 then
619 price = 0
620 end
621 end
622 end
623 meta:set_string("price", minetest.serialize(price))
624 if fields.ok then
625 swap_node(pos, "protector:protect_sell")
626 meta:set_string("infotext", S("Protection for sale (owned by @1)", meta:get_string("owner")))
627 end
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))
630 end
631 end
632
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
641 return
642 end
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")
645 return
646 else
647 atm.balance[name] = atm.balance[name] - price
648 atm.balance[oldowner] = atm.balance[oldowner] + price
649 atm.saveaccounts()
650 end
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")
656 end
657 end)
658
659
660 -- Display entity shown when protector node is punched
661
662 minetest.register_entity("protector:display", {
663 physical = false,
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"},
669 timer = 0,
670
671 on_activate = function(self, staticdata)
672
673 -- Xanadu server only
674 if (mobs and mobs.entity and mobs.entity == false)
675 or not self then
676 self.object:remove()
677 end
678 end,
679
680 on_step = function(self, dtime)
681
682 self.timer = self.timer + dtime
683
684 if self.timer > 5 then
685 self.object:remove()
686 end
687 end,
688 })
689
690 -- Display-zone node, Do NOT place the display as a node,
691 -- it is made to be used as an entity (see above)
692
693 local x = protector.radius
694 minetest.register_node("protector:display_node", {
695 tiles = {"protector_display.png"},
696 use_texture_alpha = true,
697 walkable = false,
698 drawtype = "nodebox",
699 node_box = {
700 type = "fixed",
701 fixed = {
702 -- sides
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)},
707 -- top
708 {-(x+.55), (x+.45), -(x+.55), (x+.55), (x+.55), (x+.55)},
709 -- bottom
710 {-(x+.55), -(x+.55), -(x+.55), (x+.55), -(x+.45), (x+.55)},
711 -- middle (surround protector)
712 {-.55,-.55,-.55, .55,.55,.55},
713 },
714 },
715 selection_box = {
716 type = "regular",
717 },
718 paramtype = "light",
719 groups = {dig_immediate = 3, not_in_creative_inventory = 1},
720 drop = "",
721 })
722
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")
726
727 print (S("[MOD] Protector Redo loaded"))