init.lua 38 KB

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