init.lua 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383
  1. if not minetest.global_exists("protector") then protector = {} end
  2. protector.players = protector.players or {}
  3. protector.mod = "redo"
  4. protector.modpath = minetest.get_modpath("protector")
  5. protector.radius = 5
  6. protector.radius_small = 3 -- Must always be smaller than primary radius.
  7. protector.max_share_count = 12
  8. -- Localize for performance.
  9. local math_floor = math.floor
  10. local math_random = math.random
  11. protector.flip = minetest.settings:get_bool("protector_flip") or false
  12. protector.hurt = (tonumber(minetest.settings:get("protector_hurt")) or 0)
  13. protector.display_time = 60*2
  14. dofile(protector.modpath .. "/hud.lua")
  15. dofile(protector.modpath .. "/tool.lua")
  16. -- Temporary pos store.
  17. local player_pos = protector.players
  18. local get_public_time = function()
  19. return os.date("!%Y/%m/%d UTC")
  20. end
  21. -- Use this function ONLY when calling a Minetest API in
  22. -- a situation where protection interferes!
  23. local PROTECTION_ENABLED = true
  24. function protector.enable_protection(enable)
  25. PROTECTION_ENABLED = enable
  26. end
  27. -- Function to determine whether player may place protection in a realm.
  28. -- Admin can always place protection regardless of realm.
  29. local function realm_allows_protection(pos, pname)
  30. if gdac.player_is_admin(pname) then
  31. return true
  32. end
  33. -- Do NOT allow players to place protection in the Void, or the Outback.
  34. local rn = rc.current_realm_at_pos(pos)
  35. if rn == "abyss" or rn == "" then
  36. return false
  37. end
  38. return true
  39. end
  40. -- Singular function to find relevant protector nodes in an area.
  41. function protector.find_protector_nodes(pos, r, mult, nodename)
  42. -- Arguments:
  43. -- `pos` = the point of interaction.
  44. -- `r` = the primary radius, either 1 or 5. If 1 then we're only looking for the protector itself.
  45. -- `mult` = how much to scale the primary radius by. 1 or 2. 2 is for checking overlaps.
  46. -- `nodename` = the name of the protector calling this function. May be "".
  47. -- The logic is complex.
  48. r = r * mult
  49. local positions, counts = minetest.find_nodes_in_area(
  50. {x = pos.x - r, y = pos.y - r, z = pos.z - r},
  51. {x = pos.x + r, y = pos.y + r, z = pos.z + r},
  52. {"protector:protect", "protector:protect2", "protector:protect3", "protector:protect4"})
  53. local p1 = counts["protector:protect"] or 0
  54. local p2 = counts["protector:protect2"] or 0
  55. -- Does scanned zone contain any smaller protectors?
  56. local p3 = counts["protector:protect3"] or 0
  57. local p4 = counts["protector:protect4"] or 0
  58. if p1 > 0 or p2 > 0 then
  59. -- Determine necessary culling radius based on what we're doing.
  60. local r2 = protector.radius * mult
  61. if mult == 2 and (nodename == "protector:protect3" or nodename == "protector:protect4") then
  62. r2 = protector.radius + protector.radius_small
  63. end
  64. -- Remove irrelevant protectors.
  65. -- These are protectors that are too far away for their protection radius to matter to us.
  66. ::redo::
  67. for i = 1, #positions, 1 do
  68. local p = positions[i]
  69. local nn = minetest.get_node(p).name
  70. if nn == "protector:protect" or nn == "protector:protect2" then
  71. local minp = {x=pos.x-r2, y=pos.y-r2, z=pos.z-r2}
  72. local maxp = {x=pos.x+r2, y=pos.y+r2, z=pos.z+r2}
  73. if p.x > maxp.x or p.x < minp.x or p.y > maxp.y or p.y < minp.y or p.z > maxp.z or p.z < minp.z then
  74. table.remove(positions, i)
  75. goto redo
  76. end
  77. end
  78. end
  79. end
  80. if p3 > 0 or p4 > 0 then
  81. -- Determine necessary culling radius based on what we're doing.
  82. local r2 = protector.radius_small * mult
  83. if mult == 2 and (nodename == "protector:protect" or nodename == "protector:protect2") then
  84. r2 = protector.radius + protector.radius_small
  85. end
  86. -- Remove irrelevant protectors.
  87. -- These are protectors that are too far away for their protection radius to matter to us.
  88. ::redo::
  89. for i = 1, #positions, 1 do
  90. local p = positions[i]
  91. local nn = minetest.get_node(p).name
  92. if nn == "protector:protect3" or nn == "protector:protect4" then
  93. local minp = {x=pos.x-r2, y=pos.y-r2, z=pos.z-r2}
  94. local maxp = {x=pos.x+r2, y=pos.y+r2, z=pos.z+r2}
  95. if p.x > maxp.x or p.x < minp.x or p.y > maxp.y or p.y < minp.y or p.z > maxp.z or p.z < minp.z then
  96. table.remove(positions, i)
  97. goto redo
  98. end
  99. end
  100. end
  101. end
  102. -- Return only positions that matter.
  103. return positions
  104. end
  105. minetest.register_privilege("delprotect", {
  106. description = "Ignore player protection.",
  107. give_to_singleplayer = false,
  108. })
  109. protector.get_member_list = function(meta)
  110. return meta:get_string("members"):split(" ")
  111. end
  112. protector.set_member_list = function(meta, list)
  113. meta:set_string("members", table.concat(list, " "))
  114. end
  115. protector.is_member = function(meta, name)
  116. for _, n in pairs(protector.get_member_list(meta)) do
  117. if n == name then
  118. return true
  119. end
  120. end
  121. return false
  122. end
  123. protector.add_member = function(meta, name)
  124. -- Constant (20) defined by player.h
  125. if name:len() > 25 then
  126. return
  127. end
  128. name = rename.grn(name)
  129. if protector.is_member(meta, name) then
  130. return
  131. end
  132. local list = protector.get_member_list(meta)
  133. if #list >= protector.max_share_count then
  134. return
  135. end
  136. table.insert(list, name)
  137. protector.set_member_list(meta, list)
  138. end
  139. protector.del_member = function(meta, name)
  140. name = rename.grn(name)
  141. local list = protector.get_member_list(meta)
  142. for i, n in pairs(list) do
  143. if n == name then
  144. table.remove(list, i)
  145. break
  146. end
  147. end
  148. protector.set_member_list(meta, list)
  149. end
  150. -- Protector Interface
  151. protector.generate_formspec = function(meta)
  152. local formspec = "size[8,7]"
  153. .. default.formspec.get_form_colors()
  154. .. default.formspec.get_form_image()
  155. .. default.formspec.get_slot_colors()
  156. .. "label[0,0;" .. "11x11x11 Protector Interface" .. "]"
  157. .. "label[0,0.5;" .. "PUNCH node to show protected area or USE for area check." .. "]"
  158. .. "label[0,2;" .. "Members:" .. "]"
  159. .. "button_exit[3,6.2;2,0.5;close_me;" .. "Close" .. "]"
  160. .. "field_close_on_enter[protector_add_member;false]"
  161. local members = protector.get_member_list(meta)
  162. local npp = protector.max_share_count -- max users added onto protector list
  163. local i = 0
  164. for n = 1, #members do
  165. if i < npp then
  166. -- show username
  167. formspec = formspec .. "button[" .. (i % 4 * 2)
  168. .. "," .. math_floor(i / 4 + 3)
  169. .. ";1.5,.5;protector_member;" .. rename.gpn(members[n]) .. "]"
  170. -- username remove button
  171. .. "button[" .. (i % 4 * 2 + 1.25) .. ","
  172. .. math_floor(i / 4 + 3)
  173. .. ";.75,.5;protector_del_member_" .. members[n] .. ";X]"
  174. end
  175. i = i + 1
  176. end
  177. if i < npp then
  178. -- user name entry field
  179. formspec = formspec .. "field[" .. (i % 4 * 2 + 1 / 3) .. ","
  180. .. (math_floor(i / 4 + 3) + 1 / 3)
  181. .. ";1.433,.5;protector_add_member;;]"
  182. -- username add button
  183. .."button[" .. (i % 4 * 2 + 1.25) .. ","
  184. .. math_floor(i / 4 + 3) .. ";.75,.5;protector_submit;+]"
  185. end
  186. return formspec
  187. end
  188. protector.get_node_owner = function(pos)
  189. local r = protector.radius
  190. local positions = protector.find_protector_nodes(pos, r, 1, "")
  191. for n = 1, #positions do
  192. local meta = minetest.get_meta(positions[n])
  193. local owner = meta:get_string("owner")
  194. return owner
  195. end
  196. end
  197. -- Shall return true if 'pname' is owner or member of protected area.
  198. protector.is_owner_or_member = function(pos, pname)
  199. local r = protector.radius
  200. local positions = protector.find_protector_nodes(pos, r, 1, "")
  201. for n = 1, #positions do
  202. local meta = minetest.get_meta(positions[n])
  203. local owner = meta:get_string("owner")
  204. if owner == pname or protector.is_member(meta, pname) then
  205. return true
  206. end
  207. end
  208. end
  209. -- This is called by the node inspector tool to toggle display entities for buried protectors.
  210. protector.toggle_protector_entities_in_area = function(pname, pos)
  211. local r = protector.radius
  212. local positions = protector.find_protector_nodes(pos, r, 1, "")
  213. for n = 1, #positions do
  214. local meta = minetest.get_meta(positions[n])
  215. local owner = meta:get_string("owner")
  216. if owner == pname then -- Can only toggle display entities for owned protectors.
  217. local node = minetest.get_node(positions[n])
  218. if node.name == "protector:protect" or node.name == "protector:protect2" then
  219. protector.toggle_area_display(positions[n], "protector:display")
  220. elseif node.name == "protector:protect3" or node.name == "protector:protect4" then
  221. protector.toggle_area_display(positions[n], "protector:display_small")
  222. end
  223. end
  224. end
  225. end
  226. -- Infolevel:
  227. -- 0 for no info
  228. -- 1 for "This area is owned by <owner> !" if you can't dig
  229. -- 2 for "This area is owned by <owner>.
  230. -- 3 for checking protector overlaps
  231. protector.can_dig = function(r, mult, nodename, pos, digger, onlyowner, infolevel)
  232. if not digger or not pos then
  233. return false
  234. end
  235. -- Bedrock is always protected.
  236. local node = minetest.get_node(pos)
  237. local ndef = minetest.reg_ns_nodes[node.name] or
  238. minetest.registered_nodes[node.name]
  239. if not ndef then
  240. return false
  241. end
  242. if ndef.always_protected then
  243. return false
  244. end
  245. -- Delprotect privileged users can override protections.
  246. if (minetest.check_player_privs(digger, {delprotect = true}) or minetest.check_player_privs(digger, {protection_bypass = true}))
  247. and
  248. (infolevel == 1 or infolevel == 0) then
  249. return true
  250. end
  251. -- Prevent users from modifying the map outside of any realm.
  252. if not rc.is_valid_realm_pos(pos) then
  253. return false
  254. end
  255. if infolevel == 3 then
  256. infolevel = 1
  257. end
  258. -- Find the protector nodes.
  259. local positions = protector.find_protector_nodes(pos, r, mult, nodename)
  260. local meta, owner, members
  261. -- Anyone can dig nodes marked as override in their meta.
  262. -- We don't bother checking the meta unless a protector is present.
  263. if #positions > 0 then
  264. -- `protection_cancel' only applies if the node is NOT buildable_to.
  265. -- This ensures players cannot break protection by dropping `buildable_to' nodes, and then placing into them.
  266. -- The falling block code is responsible for ensuring that a `buildable_to'
  267. -- node can never be dropped into a protected zone.
  268. local def = minetest.reg_ns_nodes[node.name] or
  269. minetest.registered_nodes[node.name]
  270. if def and not def.buildable_to then
  271. local meta2 = minetest.get_meta(pos)
  272. -- This is generally only set on nodes which have fallen.
  273. if meta2:get_int("protection_cancel") == 1 then
  274. -- Pretend that we found no protectors.
  275. -- Continue code as if we found none.
  276. positions = {}
  277. end
  278. end
  279. end
  280. for n = 1, #positions do
  281. meta = minetest.get_meta(positions[n])
  282. owner = meta:get_string("owner") or ""
  283. members = meta:get_string("members") or ""
  284. if owner ~= digger then
  285. if onlyowner or not protector.is_member(meta, digger) then
  286. if infolevel == 1 then
  287. minetest.chat_send_player(digger, "# Server: This area is protected by <" .. rename.gpn(owner) .. ">!")
  288. elseif infolevel == 2 then
  289. minetest.chat_send_player(digger, "# Server: This area is protected by <" .. rename.gpn(owner) .. ">.")
  290. minetest.chat_send_player(digger, "# Server: Protection located at: " .. rc.pos_to_namestr(positions[n]))
  291. if members ~= "" then
  292. minetest.chat_send_player(digger, "# Server: Members: [" .. members .. "].")
  293. end
  294. end
  295. return false
  296. end
  297. end
  298. if infolevel == 2 then
  299. minetest.chat_send_player(digger, "# Server: This area is protected by <" .. rename.gpn(owner) .. ">.")
  300. minetest.chat_send_player(digger, "# Server: Protection located at: " .. rc.pos_to_namestr(positions[n]))
  301. if members ~= "" then
  302. minetest.chat_send_player(digger, "# Server: Members: [" .. members .. "].")
  303. end
  304. return false
  305. end
  306. end
  307. if infolevel == 2 then
  308. if #positions < 1 then
  309. minetest.chat_send_player(digger, "# Server: This area is not protected.")
  310. end
  311. minetest.chat_send_player(digger, "# Server: You can build here.")
  312. end
  313. return true
  314. end
  315. -- Can node be added or removed, if so return node else true (for protected)
  316. function protector.punish_player(pos, pname)
  317. if not pname or pname == "" then
  318. return
  319. end
  320. local player = minetest.get_player_by_name(pname)
  321. if not player or not player:is_player() then
  322. return
  323. end
  324. -- hurt player if protection violated
  325. if protector.hurt > 0 and not armor.player_in_duel(pname) then
  326. local hp = player:get_hp()
  327. if hp > 0 then -- Avoid writing message twice.
  328. player:set_hp(hp - (protector.hurt*500))
  329. if player:get_hp() <= 0 then
  330. minetest.chat_send_all("# Server: <" .. rename.gpn(pname) .. "> was killed by a protection block.")
  331. end
  332. end
  333. end
  334. -- flip player when protection violated
  335. if protector.flip then
  336. -- yaw +/- 180°
  337. local yaw = player:get_look_horizontal() + (math_random(-(math.pi*100), (math.pi*100))/100)
  338. player:set_look_horizontal(yaw)
  339. -- Invert pitch.
  340. player:set_look_vertical(-player:get_look_vertical())
  341. -- if digging below player, move up to avoid falling through hole
  342. local pla_pos = player:get_pos()
  343. if pos.y < pla_pos.y then
  344. player:set_pos({
  345. x = pla_pos.x,
  346. y = pla_pos.y + 0.8,
  347. z = pla_pos.z
  348. })
  349. end
  350. end
  351. -- drop tool/item if protection violated
  352. -- This is disabled because it is too easy to exploit using lag -- TenPlus1.
  353. --[[
  354. if protector.drop == true then
  355. local holding = player:get_wielded_item()
  356. if holding:to_string() ~= "" then
  357. -- take stack
  358. local sta = holding:take_item(holding:get_count())
  359. player:set_wielded_item(holding)
  360. -- incase of lag, reset stack
  361. minetest.after(0.1, function()
  362. -- Get player reference anew, in case player has left game.
  363. local player = minetest.get_player_by_name(pname)
  364. if not player then
  365. return
  366. end
  367. player:set_wielded_item(holding)
  368. -- drop stack
  369. local obj = minetest.add_item(player:get_pos(), sta)
  370. if obj then
  371. obj:set_velocity({x = math_random(-5, 5), y = 5, z = math_random(-5, 5)})
  372. end
  373. end)
  374. end
  375. end
  376. --]]
  377. end
  378. protector.old_is_protected = minetest.is_protected
  379. function minetest.is_protected(pos, digger, nodename)
  380. digger = digger or "" -- nil check
  381. -- Allow protection to be temporarily disabled for API purposes.
  382. if not PROTECTION_ENABLED then
  383. return protector.old_is_protected(pos, digger)
  384. end
  385. if not protector.can_dig(protector.radius, 1, "", pos, digger, false, 1) then
  386. -- Slight delay to separate call stacks; hopefully this fixes the rare recursion/crash issue.
  387. -- Update: it seems to have fixed the rare crashes, there have been no more related to this since some months.
  388. minetest.after(0, function()
  389. protector.punish_player(pos, digger)
  390. end)
  391. return true
  392. end
  393. return protector.old_is_protected(pos, digger)
  394. end
  395. -- Called when protection should be checked, but no retaliation should be carried out.
  396. function minetest.test_protection(pos, digger)
  397. digger = digger or "" -- nil check
  398. -- Allow protection to be temporarily disabled for API purposes.
  399. if not PROTECTION_ENABLED then
  400. return protector.old_is_protected(pos, digger)
  401. end
  402. -- Don't give chat messages. Infolevel 0.
  403. if not protector.can_dig(protector.radius, 1, "", pos, digger, false, 0) then
  404. return true
  405. end
  406. return protector.old_is_protected(pos, digger)
  407. end
  408. -- Make sure protection block doesn't overlap another protector's area
  409. function protector.check_overlap_main(protname, pname, spos)
  410. if not realm_allows_protection(spos, pname) then
  411. return false, 4
  412. end
  413. -- Do not allow protections to be placed near the Outback gate's exit coordinates.
  414. -- Note: this prevents the admin from placing protection here, too.
  415. -- Existing protections (if any) remain untouched.
  416. local realmname = rc.current_realm_at_pos(spos)
  417. if not serveressentials.protector_can_place(spos, realmname) then
  418. return false, 5
  419. end
  420. if not protector.can_dig(protector.radius, 2, protname, spos, pname, true, 3) then
  421. -- Overlap with other player's protection.
  422. return false, 1
  423. end
  424. local pos = {x=spos.x, y=spos.y, z=spos.z}
  425. local rad = protector.radius
  426. local bones = minetest.find_nodes_in_area(
  427. {x = pos.x - rad, y = pos.y - rad, z = pos.z - rad},
  428. {x = pos.x + rad, y = pos.y + rad, z = pos.z + rad},
  429. {"bones:bones"})
  430. if bones and #bones > 0 then
  431. for k, v in ipairs(bones) do
  432. local meta = minetest.get_meta(v)
  433. local owner = meta:get_string("owner") or ""
  434. if owner ~= "" and owner ~= "server" then
  435. -- fresh bones nearby
  436. return false, 2
  437. end
  438. local oldowner = meta:get_string("oldowner") or ""
  439. if oldowner ~= "" and oldowner ~= "server" then
  440. -- old bones nearby
  441. return false, 3
  442. end
  443. end
  444. end
  445. -- no overlap with other protection
  446. return true, 0
  447. end
  448. function protector.check_overlap(itemstack, placer, pt)
  449. if pt.type ~= "node" then
  450. return itemstack
  451. end
  452. local pname = placer:get_player_name()
  453. local prot_type = itemstack:get_name()
  454. local success, reason = protector.check_overlap_main(prot_type, pname, pt.above)
  455. if not success then
  456. if reason == 1 then
  457. minetest.chat_send_player(pname, "# Server: Protection bounds overlap into another person's area claim.")
  458. elseif reason == 2 then
  459. minetest.chat_send_player(pname, "# Server: You cannot claim this area while someone's fresh corpse is nearby!")
  460. elseif reason == 3 then
  461. minetest.chat_send_player(pname, "# Server: You must remove all corpses before you can claim this area.")
  462. elseif reason == 4 then
  463. minetest.chat_send_player(pname, "# Server: Cannot claim protection here, there is no land authority.")
  464. elseif reason == 5 then
  465. minetest.chat_send_player(pname, "# Server: The area near the Outback gate's exit is public. Cannot claim land here.")
  466. else
  467. minetest.chat_send_player(pname, "# Server: Cannot place protection for unknown reason.")
  468. end
  469. return
  470. end
  471. return minetest.item_place(itemstack, placer, pt)
  472. end
  473. -- Called in the `after_place_node` of protector node.
  474. function protector.timed_setup(pos, placer, meta)
  475. local pname = placer:get_player_name()
  476. local is_temp_prot = false
  477. -- If protector was placed by someone without a Key, then it is a temporary protector.
  478. if not passport.player_has_key(pname, placer) then
  479. is_temp_prot = true
  480. end
  481. -- Determining how soon a protector should expire is tricky. On the one
  482. -- wing, if protection expires too quickly, players feel like they HAVE to
  483. -- login and do work often in order to save their claims. On the other wing,
  484. -- if protection lasts too long, it becomes too easy to deny land to other
  485. -- players, if someone decides to mass-protect some spot. This can be
  486. -- particularlly troublesome if the amount of land available is small (e.g.,
  487. -- certain small realms).
  488. local protection_time = 60*60*24*30
  489. -- Check if realm restricts protection to temporary mode only.
  490. local realmdata = rc.get_realm_data(rc.current_realm_at_pos(pos))
  491. if realmdata then
  492. if realmdata.protection_temporary then
  493. is_temp_prot = true
  494. meta:set_int("realmdisable", 1)
  495. end
  496. if realmdata.protection_time then
  497. protection_time = realmdata.protection_time
  498. end
  499. end
  500. -- Set up timer stuff if needed.
  501. if is_temp_prot then
  502. local timer = minetest.get_node_timer(pos)
  503. timer:start(60) -- Run once a minute.
  504. -- Set "timerot" to a date in the future (in seconds).
  505. meta:set_int("temprot", 1)
  506. meta:set_int("timerot", (os.time() + protection_time))
  507. meta:mark_as_private({"temprot", "timerot", "realmdisable"})
  508. end
  509. end
  510. -- Called in the `on_timer` of protector node.
  511. function protector.on_timer(pos, elapsed)
  512. local meta = minetest.get_meta(pos)
  513. if meta:get_int("temprot") == 1 then
  514. local timefut = meta:get_int("timerot")
  515. if (timefut - os.time()) > 0 then
  516. meta:set_string("infotext", protector.get_infotext(meta))
  517. -- Rerun timer for same timeout.
  518. minetest.get_node_timer(pos):start(60)
  519. return
  520. end
  521. -- Replace protector with expired protector node.
  522. -- Preserve param2 and node appearance.
  523. local node = minetest.get_node(pos)
  524. local ndef = minetest.registered_nodes[node.name]
  525. node.name = ndef._expired_protector_name
  526. minetest.set_node(pos, node)
  527. end
  528. end
  529. -- Using data from the meta, assemble and return an infotext string.
  530. function protector.get_infotext(meta)
  531. local owner = meta:get_string("owner")
  532. local dname = rename.gpn(owner)
  533. local pdate = meta:get_string("placedate")
  534. if not pdate or pdate == "" then
  535. pdate = "an unknown date!"
  536. end
  537. local timeout = ""
  538. if meta:get_int("temprot") == 1 then
  539. local timefut = meta:get_int("timerot")
  540. local seconds = (timefut - os.time())
  541. if seconds < 0 then seconds = 0 end
  542. local hours = math_floor((seconds / 60) / 60)
  543. timeout = "\n------------------------------------------\n"
  544. if hours <= 24 then
  545. timeout = timeout .. "Expires in " .. hours .. " hours"
  546. else
  547. timeout = timeout .. "Expires in " .. math_floor(hours / 24) .. " days"
  548. end
  549. if meta:get_int("realmdisable") ~= 1 then
  550. timeout = timeout .. "\nGet KEY to make permanent claims"
  551. else
  552. timeout = timeout .. "\nRealm disallows perpetual claims"
  553. end
  554. end
  555. return "Protection (Owned by <" .. dname .. ">!)\nPlaced on " .. pdate .. timeout
  556. end
  557. --= Protection Lock
  558. minetest.register_node("protector:protect", {
  559. description = "Advanced Protection Lock\nArea Protected: 11x11x11",
  560. drawtype = "nodebox",
  561. tiles = {
  562. "moreblocks_circle_stone_bricks.png",
  563. "moreblocks_circle_stone_bricks.png",
  564. "moreblocks_circle_stone_bricks.png^protector_logo.png"
  565. },
  566. sounds = default.node_sound_stone_defaults(),
  567. groups = utility.dig_groups("bigitem", {
  568. immovable = 1, -- No pistons, no nothing.
  569. protector = 1,
  570. }),
  571. is_ground_content = false,
  572. paramtype = "light",
  573. movement_speed_multiplier = default.NORM_SPEED,
  574. -- Protectors shall not emit light. By MustTest
  575. --light_source = 4,
  576. node_box = {
  577. type = "fixed",
  578. fixed = {
  579. {-0.5 ,-0.5, -0.5, 0.5, 0.5, 0.5},
  580. }
  581. },
  582. on_place = protector.check_overlap,
  583. _expired_protector_name = "protector:expired1",
  584. on_timer = protector.on_timer,
  585. after_place_node = function(pos, placer)
  586. local meta = minetest.get_meta(pos)
  587. protector.timed_setup(pos, placer, meta)
  588. local placedate = get_public_time()
  589. local pname = placer:get_player_name() or ""
  590. local dname = rename.gpn(pname)
  591. meta:set_string("placedate", placedate)
  592. meta:set_string("owner", pname)
  593. meta:set_string("rename", dname)
  594. meta:set_string("infotext", protector.get_infotext(meta))
  595. meta:set_string("members", "")
  596. -- Notify nearby players.
  597. protector.update_nearby_players(pos)
  598. end,
  599. on_use = function(itemstack, user, pointed_thing)
  600. if pointed_thing.type ~= "node" then
  601. return
  602. end
  603. protector.can_dig(protector.radius, 1, "protector:protect", pointed_thing.under, user:get_player_name(), false, 2)
  604. end,
  605. on_rightclick = function(pos, node, clicker, itemstack)
  606. local meta = minetest.get_meta(pos)
  607. local name = clicker:get_player_name() or ""
  608. if meta and protector.can_dig(1, 1, "protector:protect", pos, name, true, 1) then
  609. player_pos[name] = pos
  610. minetest.show_formspec(name, "protector:node", protector.generate_formspec(meta))
  611. end
  612. end,
  613. on_punch = function(pos, node, puncher)
  614. if minetest.test_protection(pos, puncher:get_player_name()) then
  615. return
  616. end
  617. protector.toggle_area_display(pos, "protector:display")
  618. end,
  619. can_dig = function(pos, player)
  620. return player and protector.can_dig(1, 1, "protector:protect", pos, player:get_player_name(), true, 1)
  621. end,
  622. -- TNT-proof.
  623. on_blast = function() end,
  624. -- Called by rename LBM.
  625. _on_update_infotext = function(pos)
  626. local meta = minetest.get_meta(pos)
  627. local owner = meta:get_string("owner")
  628. -- Nobody placed this block.
  629. if owner == "" then
  630. return
  631. end
  632. local cname = meta:get_string("rename")
  633. local dname = rename.gpn(owner)
  634. meta:set_string("rename", dname)
  635. meta:set_string("infotext", protector.get_infotext(meta))
  636. end,
  637. on_destruct = function(pos)
  638. -- Notify nearby players.
  639. minetest.after(0, protector.update_nearby_players, pos)
  640. return protector.remove_area_display(pos)
  641. end,
  642. })
  643. minetest.register_node("protector:protect3", {
  644. description = "Protection Lock\nArea Protected: 7x7x7",
  645. drawtype = "nodebox",
  646. tiles = {"cityblock.png"},
  647. sounds = default.node_sound_stone_defaults(),
  648. groups = utility.dig_groups("bigitem", {
  649. immovable = 1, -- No pistons, no nothing.
  650. protector = 1,
  651. }),
  652. is_ground_content = false,
  653. paramtype = "light",
  654. movement_speed_multiplier = default.NORM_SPEED,
  655. -- Protectors shall not emit light. By MustTest
  656. --light_source = 4,
  657. node_box = {
  658. type = "fixed",
  659. fixed = {
  660. {-0.5 ,-0.5, -0.5, 0.5, 0.5, 0.5},
  661. }
  662. },
  663. on_place = protector.check_overlap,
  664. after_place_node = function(pos, placer)
  665. local meta = minetest.get_meta(pos)
  666. protector.timed_setup(pos, placer, meta)
  667. local placedate = get_public_time()
  668. local pname = placer:get_player_name() or ""
  669. local dname = rename.gpn(pname)
  670. meta:set_string("placedate", placedate)
  671. meta:set_string("owner", pname)
  672. meta:set_string("rename", dname)
  673. meta:set_string("infotext", protector.get_infotext(meta))
  674. meta:set_string("members", "")
  675. -- Notify nearby players.
  676. protector.update_nearby_players(pos)
  677. end,
  678. _expired_protector_name = "protector:expired1",
  679. on_timer = protector.on_timer,
  680. on_use = function(itemstack, user, pointed_thing)
  681. if pointed_thing.type ~= "node" then
  682. return
  683. end
  684. protector.can_dig(protector.radius, 1, "protector:protect3", pointed_thing.under, user:get_player_name(), false, 2)
  685. end,
  686. -- This protector does not have a formspec, no on_rightclick defined.
  687. on_punch = function(pos, node, puncher)
  688. if minetest.test_protection(pos, puncher:get_player_name()) then
  689. return
  690. end
  691. protector.toggle_area_display(pos, "protector:display_small")
  692. end,
  693. can_dig = function(pos, player)
  694. return player and protector.can_dig(1, 1, "protector:protect3", pos, player:get_player_name(), true, 1)
  695. end,
  696. -- TNT-proof.
  697. on_blast = function() end,
  698. -- Called by rename LBM.
  699. _on_update_infotext = function(pos)
  700. local meta = minetest.get_meta(pos)
  701. local owner = meta:get_string("owner")
  702. local placedate = meta:get_string("placedate")
  703. -- Nobody placed this block.
  704. if owner == "" then
  705. return
  706. end
  707. if placedate == "" then
  708. placedate = "an unknown date!"
  709. end
  710. local cname = meta:get_string("rename")
  711. local dname = rename.gpn(owner)
  712. meta:set_string("rename", dname)
  713. meta:set_string("infotext", protector.get_infotext(meta))
  714. end,
  715. on_destruct = function(pos)
  716. -- Notify nearby players.
  717. minetest.after(0, protector.update_nearby_players, pos)
  718. return protector.remove_area_display(pos)
  719. end,
  720. })
  721. --= Protection Logo
  722. minetest.register_node("protector:protect2", {
  723. description = "Advanced Protection Logo\nArea Protected: 11x11x11",
  724. tiles = {"protector_logo.png"},
  725. wield_image = "protector_logo.png",
  726. inventory_image = "protector_logo.png",
  727. sounds = default.node_sound_stone_defaults(),
  728. groups = utility.dig_groups("bigitem", {
  729. immovable = 1, -- No pistons, no nothing.
  730. protector = 1,
  731. }),
  732. paramtype = 'light',
  733. paramtype2 = "wallmounted",
  734. legacy_wallmounted = true,
  735. -- Protectors shall not emit light. By MustTest
  736. --light_source = 4,
  737. drawtype = "nodebox",
  738. sunlight_propagates = true,
  739. walkable = false,
  740. node_box = {
  741. type = "wallmounted",
  742. wall_top = {-0.375, 0.4375, -0.5, 0.375, 0.5, 0.5},
  743. wall_bottom = {-0.375, -0.5, -0.5, 0.375, -0.4375, 0.5},
  744. wall_side = {-0.5, -0.5, -0.375, -0.4375, 0.5, 0.375},
  745. },
  746. selection_box = {type = "wallmounted"},
  747. _expired_protector_name = "protector:expired2",
  748. on_timer = protector.on_timer,
  749. on_place = protector.check_overlap,
  750. after_place_node = function(pos, placer)
  751. local meta = minetest.get_meta(pos)
  752. protector.timed_setup(pos, placer, meta)
  753. local placedate = get_public_time()
  754. local pname = placer:get_player_name() or ""
  755. local dname = rename.gpn(pname)
  756. meta:set_string("placedate", placedate)
  757. meta:set_string("owner", pname)
  758. meta:set_string("rename", dname)
  759. meta:set_string("infotext", protector.get_infotext(meta))
  760. meta:set_string("members", "")
  761. -- Notify nearby players.
  762. protector.update_nearby_players(pos)
  763. end,
  764. on_use = function(itemstack, user, pointed_thing)
  765. if pointed_thing.type ~= "node" then
  766. return
  767. end
  768. protector.can_dig(protector.radius, 1, "protector:protect2", pointed_thing.under, user:get_player_name(), false, 2)
  769. end,
  770. on_rightclick = function(pos, node, clicker, itemstack)
  771. local meta = minetest.get_meta(pos)
  772. local name = clicker:get_player_name() or ""
  773. if meta and protector.can_dig(1, 1, "protector:protect2", pos, name, true, 1) then
  774. player_pos[name] = pos
  775. minetest.show_formspec(name, "protector:node", protector.generate_formspec(meta))
  776. end
  777. end,
  778. on_punch = function(pos, node, puncher)
  779. if minetest.test_protection(pos, puncher:get_player_name()) then
  780. return
  781. end
  782. protector.toggle_area_display(pos, "protector:display")
  783. end,
  784. can_dig = function(pos, player)
  785. return player and protector.can_dig(1, 1, "protector:protect2", pos, player:get_player_name(), true, 1)
  786. end,
  787. -- TNT-proof.
  788. on_blast = function() end,
  789. -- Called by rename LBM.
  790. _on_update_infotext = function(pos)
  791. local meta = minetest.get_meta(pos)
  792. local owner = meta:get_string("owner")
  793. local placedate = meta:get_string("placedate")
  794. -- Nobody placed this block.
  795. if owner == "" then
  796. return
  797. end
  798. if placedate == "" then
  799. placedate = "an unknown date!"
  800. end
  801. local cname = meta:get_string("rename")
  802. local dname = rename.gpn(owner)
  803. meta:set_string("rename", dname)
  804. meta:set_string("infotext", protector.get_infotext(meta))
  805. end,
  806. on_destruct = function(pos)
  807. -- Notify nearby players.
  808. minetest.after(0, protector.update_nearby_players, pos)
  809. return protector.remove_area_display(pos)
  810. end,
  811. })
  812. minetest.register_node("protector:protect4", {
  813. description = "Protection Logo\nArea Protected: 7x7x7",
  814. tiles = {"protector_lock.png"},
  815. wield_image = "protector_lock.png",
  816. inventory_image = "protector_lock.png",
  817. sounds = default.node_sound_stone_defaults(),
  818. groups = utility.dig_groups("bigitem", {
  819. immovable = 1, -- No pistons, no nothing.
  820. protector = 1,
  821. }),
  822. paramtype = 'light',
  823. paramtype2 = "wallmounted",
  824. legacy_wallmounted = true,
  825. -- Protectors shall not emit light. By MustTest
  826. --light_source = 4,
  827. drawtype = "nodebox",
  828. sunlight_propagates = true,
  829. walkable = false,
  830. node_box = {
  831. type = "wallmounted",
  832. wall_top = {-0.375, 0.4375, -0.5, 0.375, 0.5, 0.5},
  833. wall_bottom = {-0.375, -0.5, -0.5, 0.375, -0.4375, 0.5},
  834. wall_side = {-0.5, -0.5, -0.375, -0.4375, 0.5, 0.375},
  835. },
  836. selection_box = {type = "wallmounted"},
  837. on_place = protector.check_overlap,
  838. after_place_node = function(pos, placer)
  839. local meta = minetest.get_meta(pos)
  840. protector.timed_setup(pos, placer, meta)
  841. local placedate = get_public_time()
  842. local pname = placer:get_player_name() or ""
  843. local dname = rename.gpn(pname)
  844. meta:set_string("placedate", placedate)
  845. meta:set_string("owner", pname)
  846. meta:set_string("rename", dname)
  847. meta:set_string("infotext", protector.get_infotext(meta))
  848. meta:set_string("members", "")
  849. -- Notify nearby players.
  850. protector.update_nearby_players(pos)
  851. end,
  852. _expired_protector_name = "protector:expired2",
  853. on_timer = protector.on_timer,
  854. on_use = function(itemstack, user, pointed_thing)
  855. if pointed_thing.type ~= "node" then
  856. return
  857. end
  858. protector.can_dig(protector.radius, 1, "protector:protect4", pointed_thing.under, user:get_player_name(), false, 2)
  859. end,
  860. -- This protector does not have a formspec, no on_rightclick defined.
  861. on_punch = function(pos, node, puncher)
  862. if minetest.test_protection(pos, puncher:get_player_name()) then
  863. return
  864. end
  865. protector.toggle_area_display(pos, "protector:display_small")
  866. end,
  867. can_dig = function(pos, player)
  868. return player and protector.can_dig(1, 1, "protector:protect4", pos, player:get_player_name(), true, 1)
  869. end,
  870. -- TNT-proof.
  871. on_blast = function() end,
  872. -- Called by rename LBM.
  873. _on_update_infotext = function(pos)
  874. local meta = minetest.get_meta(pos)
  875. local owner = meta:get_string("owner")
  876. local placedate = meta:get_string("placedate")
  877. -- Nobody placed this block.
  878. if owner == "" then
  879. return
  880. end
  881. if placedate == "" then
  882. placedate = "an unknown date!"
  883. end
  884. local cname = meta:get_string("rename")
  885. local dname = rename.gpn(owner)
  886. meta:set_string("rename", dname)
  887. meta:set_string("infotext", protector.get_infotext(meta))
  888. end,
  889. on_destruct = function(pos)
  890. -- Notify nearby players.
  891. minetest.after(0, protector.update_nearby_players, pos)
  892. return protector.remove_area_display(pos)
  893. end,
  894. })
  895. -- If name entered or button press
  896. minetest.register_on_player_receive_fields(function(player, formname, fields)
  897. if formname ~= "protector:node" then
  898. return
  899. end
  900. local pname = player:get_player_name() or "" -- Nil check.
  901. local pos = player_pos[pname] -- Context should have been created during on_rightclick. CSM protection.
  902. -- Localize field member.
  903. local add_member_input = fields.protector_add_member
  904. -- Reset formspec until close button pressed.
  905. if (fields.close_me or fields.quit)
  906. and (not add_member_input or add_member_input == "") then
  907. player_pos[pname] = nil
  908. return true
  909. end
  910. if not pos then
  911. return true
  912. end
  913. local meta = minetest.get_meta(pos)
  914. local node = minetest.get_node(pos)
  915. -- Meta nil check.
  916. if not meta then
  917. return true
  918. end
  919. -- Are we actually working on a protection node? (CSM protection.)
  920. if node.name ~= "protector:protect"
  921. and node.name ~= "protector:protect2"
  922. and node.name ~= "protector:protect3"
  923. and node.name ~= "protector:protect4" then
  924. player_pos[pname] = nil
  925. return true
  926. end
  927. -- Only advanced protectors support member names.
  928. if node.name == "protector:protect3" or node.name == "protector:protect4" then
  929. minetest.chat_send_player(pname, "# Server: Sharing feature not supported by basic protectors!")
  930. return true
  931. end
  932. -- Do not permit caller to modify a protector they do not own.
  933. if not protector.can_dig(1, 1, node.name, pos, pname, true, 1) then
  934. return true
  935. end
  936. if add_member_input then
  937. for _, i in pairs(add_member_input:split(" ")) do
  938. protector.add_member(meta, i)
  939. end
  940. end
  941. for field, value in pairs(fields) do
  942. if string.sub(field, 0, string.len("protector_del_member_")) == "protector_del_member_" then
  943. protector.del_member(meta, string.sub(field,string.len("protector_del_member_") + 1))
  944. end
  945. end
  946. -- Clear formspec context.
  947. minetest.show_formspec(pname, formname, protector.generate_formspec(meta))
  948. return true
  949. end)
  950. -- Display entity shown when protector node is punched
  951. minetest.register_entity("protector:display", {
  952. physical = false,
  953. collisionbox = {0, 0, 0, 0, 0, 0},
  954. visual = "wielditem",
  955. -- wielditem seems to be scaled to 1.5 times original node size
  956. visual_size = {x = 1.0 / 1.5, y = 1.0 / 1.5},
  957. textures = {"protector:display_node"},
  958. timer = 0,
  959. glow = 14,
  960. on_step = function(self, dtime)
  961. self.timer = self.timer + dtime
  962. if self.timer > protector.display_time then
  963. self.object:remove()
  964. end
  965. end,
  966. on_blast = function(self, damage)
  967. return false, false, {}
  968. end,
  969. })
  970. minetest.register_entity("protector:display_small", {
  971. physical = false,
  972. collisionbox = {0, 0, 0, 0, 0, 0},
  973. visual = "wielditem",
  974. -- wielditem seems to be scaled to 1.5 times original node size
  975. visual_size = {x = 1.0 / 1.5, y = 1.0 / 1.5},
  976. textures = {"protector:display_node_small"},
  977. timer = 0,
  978. glow = 14,
  979. on_step = function(self, dtime)
  980. self.timer = self.timer + dtime
  981. if self.timer > protector.display_time then
  982. self.object:remove()
  983. end
  984. end,
  985. on_blast = function(self, damage)
  986. return false, false, {}
  987. end,
  988. })
  989. -- Display-zone node, Do NOT place the display as a node,
  990. -- it is made to be used as an entity (see above)
  991. do
  992. local x = protector.radius
  993. minetest.register_node("protector:display_node", {
  994. tiles = {"protector_display.png"},
  995. use_texture_alpha = "blend",
  996. walkable = false,
  997. drawtype = "nodebox",
  998. node_box = {
  999. type = "fixed",
  1000. fixed = {
  1001. -- sides
  1002. {-(x+.55), -(x+.55), -(x+.55), -(x+.45), (x+.55), (x+.55)},
  1003. {-(x+.55), -(x+.55), (x+.45), (x+.55), (x+.55), (x+.55)},
  1004. {(x+.45), -(x+.55), -(x+.55), (x+.55), (x+.55), (x+.55)},
  1005. {-(x+.55), -(x+.55), -(x+.55), (x+.55), (x+.55), -(x+.45)},
  1006. -- top
  1007. {-(x+.55), (x+.45), -(x+.55), (x+.55), (x+.55), (x+.55)},
  1008. -- bottom
  1009. {-(x+.55), -(x+.55), -(x+.55), (x+.55), -(x+.45), (x+.55)},
  1010. -- middle (surround protector)
  1011. {-.55,-.55,-.55, .55,.55,.55},
  1012. },
  1013. },
  1014. selection_box = {
  1015. type = "regular",
  1016. },
  1017. paramtype = "light",
  1018. groups = utility.dig_groups("item", {not_in_creative_inventory = 1}),
  1019. drop = "",
  1020. })
  1021. end
  1022. do
  1023. local x = protector.radius_small
  1024. minetest.register_node("protector:display_node_small", {
  1025. tiles = {"protector_display.png"},
  1026. use_texture_alpha = "blend",
  1027. walkable = false,
  1028. drawtype = "nodebox",
  1029. node_box = {
  1030. type = "fixed",
  1031. fixed = {
  1032. -- sides
  1033. {-(x+.55), -(x+.55), -(x+.55), -(x+.45), (x+.55), (x+.55)},
  1034. {-(x+.55), -(x+.55), (x+.45), (x+.55), (x+.55), (x+.55)},
  1035. {(x+.45), -(x+.55), -(x+.55), (x+.55), (x+.55), (x+.55)},
  1036. {-(x+.55), -(x+.55), -(x+.55), (x+.55), (x+.55), -(x+.45)},
  1037. -- top
  1038. {-(x+.55), (x+.45), -(x+.55), (x+.55), (x+.55), (x+.55)},
  1039. -- bottom
  1040. {-(x+.55), -(x+.55), -(x+.55), (x+.55), -(x+.45), (x+.55)},
  1041. -- middle (surround protector)
  1042. {-.55,-.55,-.55, .55,.55,.55},
  1043. },
  1044. },
  1045. selection_box = {
  1046. type = "regular",
  1047. },
  1048. paramtype = "light",
  1049. groups = utility.dig_groups("item", {not_in_creative_inventory = 1}),
  1050. drop = "",
  1051. })
  1052. end
  1053. function protector.remove_area_display(pos)
  1054. local ents = minetest.get_objects_inside_radius(pos, 0.5)
  1055. for k, n in ipairs(ents) do
  1056. if not n:is_player() and n:get_luaentity() then
  1057. local name = n:get_luaentity().name or ""
  1058. if name == "protector:display" or name == "protector:display_small" then
  1059. n:remove()
  1060. end
  1061. end
  1062. end
  1063. end
  1064. function protector.toggle_area_display(pos, entity)
  1065. local got_any = false
  1066. local ents = minetest.get_objects_inside_radius(pos, 0.5)
  1067. for k, n in ipairs(ents) do
  1068. if not n:is_player() and n:get_luaentity() then
  1069. local name = n:get_luaentity().name or ""
  1070. if name == "protector:display" or name == "protector:display_small" then
  1071. n:remove()
  1072. got_any = true
  1073. end
  1074. end
  1075. end
  1076. if not got_any then
  1077. minetest.add_entity(pos, entity)
  1078. end
  1079. end
  1080. dofile(protector.modpath .. "/crafts.lua")
  1081. -- Expired protector node.
  1082. minetest.register_node("protector:expired1", {
  1083. description = "Expired Protector",
  1084. drawtype = "nodebox",
  1085. tiles = {"cityblock.png"},
  1086. sounds = default.node_sound_stone_defaults(),
  1087. groups = utility.dig_groups("bigitem"),
  1088. paramtype = "light",
  1089. movement_speed_multiplier = default.NORM_SPEED,
  1090. node_box = {
  1091. type = "fixed",
  1092. fixed = {
  1093. {-0.5 ,-0.5, -0.5, 0.5, 0.5, 0.5},
  1094. }
  1095. },
  1096. })
  1097. minetest.register_node("protector:expired2", {
  1098. description = "Expired Protector",
  1099. tiles = {"protector_lock.png"},
  1100. wield_image = "protector_lock.png",
  1101. inventory_image = "protector_lock.png",
  1102. sounds = default.node_sound_stone_defaults(),
  1103. groups = utility.dig_groups("bigitem"),
  1104. paramtype = 'light',
  1105. paramtype2 = "wallmounted",
  1106. legacy_wallmounted = true,
  1107. drawtype = "nodebox",
  1108. sunlight_propagates = true,
  1109. walkable = false,
  1110. node_box = {
  1111. type = "wallmounted",
  1112. wall_top = {-0.375, 0.4375, -0.5, 0.375, 0.5, 0.5},
  1113. wall_bottom = {-0.375, -0.5, -0.5, 0.375, -0.4375, 0.5},
  1114. wall_side = {-0.5, -0.5, -0.375, -0.4375, 0.5, 0.375},
  1115. },
  1116. selection_box = {type = "wallmounted"},
  1117. })