tool.lua 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. -- protector placement tool (thanks to Shara for code and idea)
  2. -- Localize for performance.
  3. local vector_round = vector.round
  4. local get_public_time = function()
  5. return os.date("!%Y/%m/%d UTC")
  6. end
  7. local function pos_has_inventory(pos)
  8. local meta = minetest.get_meta(pos)
  9. local inv = minetest.get_inventory({type = "node", pos = pos})
  10. if not inv then
  11. return false
  12. end
  13. local lists = inv:get_lists()
  14. if next(lists) == nil then
  15. return false
  16. end
  17. return true
  18. end
  19. minetest.register_craftitem("protector:tool", {
  20. description = "Claim Expansion Tool\n\nStand near protector, face direction and use.\nHold sneak to copy member names.\nHold 'E' to double the gap distance.",
  21. inventory_image = "nodeinspector.png^protector_lock.png",
  22. stack_max = 1,
  23. groups = {not_repaired_by_anvil=1},
  24. on_use = function(itemstack, user, pointed_thing)
  25. local name = user:get_player_name()
  26. -- check for protector near player (2 block radius)
  27. local pos = vector_round(user:get_pos())
  28. local pp = minetest.find_nodes_in_area(
  29. vector.subtract(pos, 2), vector.add(pos, 2),
  30. {"protector:protect", "protector:protect2",
  31. "protector:protect3", "protector:protect4"})
  32. if #pp == 0 then return end -- none found
  33. if #pp > 1 then
  34. minetest.chat_send_player(name, "# Server: Too many protectors nearby, choice would be ambiguous.")
  35. return
  36. end
  37. pos = pp[1] -- take position of first protector found
  38. -- get type of protector, its radius and size class
  39. local r -- Protector radius
  40. local s -- Small protector: true, else false
  41. local node = minetest.get_node(pos)
  42. local protname
  43. if node.name == "protector:protect" or node.name == "protector:protect2" then
  44. r = protector.radius
  45. s = false
  46. protname = node.name
  47. elseif node.name == "protector:protect3" or node.name == "protector:protect4" then
  48. r = protector.radius_small
  49. s = true
  50. protname = node.name
  51. else
  52. minetest.chat_send_player(name, "# Server: PPT internal error!")
  53. return
  54. end
  55. -- get members on protector
  56. local meta = minetest.get_meta(pos)
  57. local members = meta:get_string("members") or ""
  58. local owner = meta:get_string("owner") or ""
  59. -- require the tool user to be the owner of the initial protector node
  60. if owner ~= name then
  61. minetest.chat_send_player(name, "# Server: Cannot expand claim from origin, the protector is not yours!")
  62. return
  63. end
  64. -- get direction player is facing
  65. local dir = minetest.dir_to_facedir( user:get_look_dir() )
  66. local vec = {x = 0, y = 0, z = 0}
  67. local gap = (r * 2) + 1
  68. local pit = user:get_look_vertical()
  69. -- double the gap distance if player is holding 'E'
  70. if user:get_player_control().aux1 then
  71. gap = gap * 2
  72. end
  73. -- set placement coords
  74. if pit > 1.2 then
  75. vec.y = -gap -- up
  76. elseif pit < -1.2 then
  77. vec.y = gap -- down
  78. elseif dir == 0 then
  79. vec.z = gap -- north
  80. elseif dir == 1 then
  81. vec.x = gap -- east
  82. elseif dir == 2 then
  83. vec.z = -gap -- south
  84. elseif dir == 3 then
  85. vec.x = -gap -- west
  86. end
  87. -- new position
  88. pos.x = pos.x + vec.x
  89. pos.y = pos.y + vec.y
  90. pos.z = pos.z + vec.z
  91. -- ensure position is within a valid realm
  92. if not rc.is_valid_realm_pos(pos) then
  93. minetest.chat_send_player(name, "# Server: Cannot place protector in the Void!")
  94. return
  95. end
  96. if not minetest.get_node_or_nil(pos) then
  97. minetest.chat_send_player(name, "# Server: Cannot place protector within IGNORE!")
  98. return
  99. end
  100. -- does placing a protector overlap existing area
  101. -- this is the most important check! must not mess this up!
  102. local success, reason = protector.check_overlap_main(protname, name, pos)
  103. if not success then
  104. if reason == 1 then
  105. minetest.chat_send_player(name, "# Server: Protection bounds overlap into another person's area claim.")
  106. elseif reason == 2 then
  107. minetest.chat_send_player(name, "# Server: You cannot claim this area while someone's fresh corpse is nearby!")
  108. elseif reason == 3 then
  109. minetest.chat_send_player(name, "# Server: You must remove all corpses before you can claim this area.")
  110. else
  111. minetest.chat_send_player(name, "# Server: Cannot place protection for unknown reason.")
  112. end
  113. return
  114. end
  115. -- Does location already have a protector?
  116. if minetest.get_node(pos).name:find("^protector:protect") then
  117. minetest.chat_send_player(name, "# Server: Protector already in place!")
  118. prospector.ptool_mark_single(name, pos, "Protector")
  119. return
  120. end
  121. -- does a protector already exist nearby?
  122. local nearby_protectors = minetest.find_nodes_in_area(vector.subtract(pos, 1), vector.add(pos, 1),
  123. {"protector:protect", "protector:protect2", "protector:protect3", "protector:protect4"})
  124. if #nearby_protectors > 0 then
  125. minetest.chat_send_player(name, "# Server: Protector already near target!")
  126. for k, v in ipairs(nearby_protectors) do
  127. prospector.ptool_mark_single(name, v, "Protector")
  128. end
  129. return
  130. end
  131. -- do not replace containers with inventory space.
  132. if pos_has_inventory(pos) then
  133. minetest.chat_send_player(name, "# Server: Cannot place protector, container at " .. rc.pos_to_namestr(pos) .. ".")
  134. prospector.ptool_mark_single(name, pos, "Blockage")
  135. return
  136. end
  137. -- protection check for other stuff, like bedrock, etc
  138. if minetest.is_protected(pos, name) then
  139. minetest.chat_send_player(name, "Cannot place protector, already protected at " .. rc.pos_to_namestr(pos) .. ".")
  140. prospector.ptool_mark_single(name, pos, "Blockage")
  141. return
  142. end
  143. -- check not replacing an immovable object
  144. local node = minetest.get_node(pos)
  145. if minetest.get_item_group(node.name, "immovable") ~= 0
  146. or minetest.get_item_group(node.name, "unbreakable") ~= 0
  147. then
  148. minetest.chat_send_player(name, "# Server: Cannot place protector in place of immovable object!")
  149. prospector.ptool_mark_single(name, pos, "Blockage")
  150. return
  151. end
  152. local nod
  153. local inv = user:get_inventory()
  154. -- try to take protector from player inventory (block first then logo)
  155. if s then
  156. if inv:contains_item("main", "protector:protect3") then
  157. inv:remove_item("main", "protector:protect3")
  158. nod = "protector:protect3"
  159. elseif inv:contains_item("main", "protector:protect4") then
  160. inv:remove_item("main", "protector:protect4")
  161. nod = "protector:protect4"
  162. end
  163. else
  164. if inv:contains_item("main", "protector:protect") then
  165. inv:remove_item("main", "protector:protect")
  166. nod = "protector:protect"
  167. elseif inv:contains_item("main", "protector:protect2") then
  168. inv:remove_item("main", "protector:protect2")
  169. nod = "protector:protect2"
  170. end
  171. end
  172. -- did we get a protector to use ?
  173. if not nod then
  174. if s then
  175. minetest.chat_send_player(name, "# Server: No basic protectors available to place!")
  176. else
  177. minetest.chat_send_player(name, "# Server: No advanced protectors available to place!")
  178. end
  179. return
  180. end
  181. -- place protector
  182. minetest.set_node(pos, {name = nod, param2 = 1})
  183. -- We are going to execute callbacks.
  184. local protdef = minetest.registered_nodes[nod]
  185. if protdef.on_construct then
  186. protdef.on_construct(pos)
  187. end
  188. if protdef.after_place_node then
  189. -- Assume callback only requires 'pos' and 'user'.
  190. protdef.after_place_node(pos, user)
  191. end
  192. -- Copy members across if holding sneak when using tool.
  193. local members_copied = false
  194. if user:get_player_control().sneak then
  195. local meta = minetest.get_meta(pos)
  196. meta:set_string("members", members)
  197. members_copied = true
  198. else
  199. local meta = minetest.get_meta(pos)
  200. meta:set_string("members", "")
  201. end
  202. ambiance.sound_play(electric_screwdriver.sound, pos, electric_screwdriver.sound_gain, electric_screwdriver.sound_dist)
  203. if members_copied and not s then
  204. minetest.chat_send_player(name, "# Server: Protector placed at " .. rc.pos_to_namestr(pos) .. ". Members copied.")
  205. prospector.ptool_mark_single(name, pos, "Success")
  206. else
  207. minetest.chat_send_player(name, "# Server: Protector placed at " .. rc.pos_to_namestr(pos) .. ".")
  208. prospector.ptool_mark_single(name, pos, "Success")
  209. end
  210. end,
  211. })
  212. -- tool recipe
  213. minetest.register_craft({
  214. output = "protector:tool",
  215. recipe = {
  216. {"protector:protect4"},
  217. {"nodeinspector:nodeinspector"},
  218. }
  219. })
  220. -- This tool is useful if you just want to move a protector without resetting
  221. -- some of its meta, e.g., placement date.
  222. minetest.register_craftitem("protector:tool2", {
  223. description = "Protector Mover Tool\n\nStand near protector, face direction and use.",
  224. inventory_image = "nodeinspector.png^protector_lock.png",
  225. stack_max = 1,
  226. groups = {not_repaired_by_anvil=1},
  227. on_use = function(itemstack, user, pointed_thing)
  228. local name = user:get_player_name()
  229. -- check for protector near player (2 block radius)
  230. local pos = vector_round(user:get_pos())
  231. local pp = minetest.find_nodes_in_area(
  232. vector.subtract(pos, 2), vector.add(pos, 2),
  233. {"protector:protect", "protector:protect2",
  234. "protector:protect3", "protector:protect4"})
  235. if #pp == 0 then return end -- none found
  236. if #pp > 1 then
  237. minetest.chat_send_player(name, "# Server: Too many protectors nearby, choice would be ambiguous.")
  238. return
  239. end
  240. pos = pp[1] -- take position of first protector found
  241. -- get members on protector
  242. local meta = minetest.get_meta(pos)
  243. local members = meta:get_string("members") or ""
  244. local owner = meta:get_string("owner") or ""
  245. local placedate = meta:get_string("placedate") or ""
  246. local protname = minetest.get_node(pos).name
  247. -- require the tool user to be the owner of the initial protector node
  248. if not minetest.check_player_privs(name, {protection_bypass=true}) then
  249. if owner ~= name then
  250. minetest.chat_send_player(name, "# Server: Cannot expand claim from origin, the protector is not yours!")
  251. return
  252. end
  253. end
  254. -- get direction player is facing
  255. local dir = minetest.dir_to_facedir( user:get_look_dir() )
  256. local vec = {x = 0, y = 0, z = 0}
  257. local gap = 1
  258. local pit = user:get_look_vertical()
  259. -- set placement coords
  260. if pit > 1.2 then
  261. vec.y = -gap -- up
  262. elseif pit < -1.2 then
  263. vec.y = gap -- down
  264. elseif dir == 0 then
  265. vec.z = gap -- north
  266. elseif dir == 1 then
  267. vec.x = gap -- east
  268. elseif dir == 2 then
  269. vec.z = -gap -- south
  270. elseif dir == 3 then
  271. vec.x = -gap -- west
  272. end
  273. -- new position (save old position)
  274. local oldpos = {x=pos.x, y=pos.y, z=pos.z}
  275. pos.x = pos.x + vec.x
  276. pos.y = pos.y + vec.y
  277. pos.z = pos.z + vec.z
  278. -- ensure position is within a valid realm
  279. if not rc.is_valid_realm_pos(pos) then
  280. minetest.chat_send_player(name, "# Server: Cannot place protector in the Void!")
  281. return
  282. end
  283. if not minetest.get_node_or_nil(pos) then
  284. minetest.chat_send_player(name, "# Server: Cannot place protector within IGNORE!")
  285. return
  286. end
  287. -- does placing a protector overlap existing area
  288. -- this is the most important check! must not mess this up!
  289. local success, reason = protector.check_overlap_main(protname, owner, pos)
  290. if not success then
  291. if reason == 1 then
  292. minetest.chat_send_player(name, "# Server: Protection bounds overlap into another person's area claim.")
  293. elseif reason == 2 then
  294. minetest.chat_send_player(name, "# Server: You cannot claim this area while someone's fresh corpse is nearby!")
  295. elseif reason == 3 then
  296. minetest.chat_send_player(name, "# Server: You must remove all corpses before you can claim this area.")
  297. else
  298. minetest.chat_send_player(name, "# Server: Cannot place protection for unknown reason.")
  299. end
  300. return
  301. end
  302. -- does a protector already exist ?
  303. if #minetest.find_nodes_in_area(vector.subtract(pos, 0), vector.add(pos, 0),
  304. {"protector:protect", "protector:protect2", "protector:protect3", "protector:protect4"}) > 0 then
  305. minetest.chat_send_player(name, "# Server: Protector already in place!")
  306. return
  307. end
  308. -- do not replace containers with inventory space
  309. if minetest.get_inventory({type = "node", pos = pos}) then
  310. minetest.chat_send_player(name, "# Server: Cannot place protector, container at " .. rc.pos_to_namestr(pos) .. ".")
  311. return
  312. end
  313. -- protection check for other stuff, like bedrock, etc
  314. if minetest.is_protected(pos, owner) then
  315. minetest.chat_send_player(name, "Cannot place protector, already protected at " .. rc.pos_to_namestr(pos) .. ".")
  316. return
  317. end
  318. -- check not replacing an immovable object
  319. local node = minetest.get_node(pos)
  320. if minetest.get_item_group(node.name, "immovable") ~= 0 then
  321. minetest.chat_send_player(name, "# Server: Cannot place protector in place of immovable object!")
  322. return
  323. end
  324. local nod = minetest.get_node(oldpos).name
  325. -- place protector
  326. minetest.set_node(pos, {name = nod, param2 = 1})
  327. minetest.remove_node(oldpos)
  328. -- We are going to execute callbacks.
  329. local protdef = minetest.registered_nodes[nod]
  330. if protdef.on_construct then
  331. protdef.on_construct(pos)
  332. end
  333. if protdef.after_place_node then
  334. -- Assume callback only requires 'pos' and 'user'.
  335. protdef.after_place_node(pos, user)
  336. end
  337. -- set protector metadata
  338. local meta = minetest.get_meta(pos)
  339. local dname = rename.gpn(owner)
  340. -- Restore original placement date and members list.
  341. meta:set_string("placedate", placedate)
  342. meta:set_string("members", members)
  343. ambiance.sound_play(electric_screwdriver.sound, pos, electric_screwdriver.sound_gain, electric_screwdriver.sound_dist)
  344. minetest.chat_send_player(name, "# Server: Protector moved to " .. rc.pos_to_namestr(pos) .. ".")
  345. end,
  346. })
  347. -- tool recipe
  348. minetest.register_craft({
  349. output = "protector:tool2",
  350. recipe = {
  351. {"nodeinspector:nodeinspector"},
  352. {"protector:protect4"},
  353. }
  354. })