tool.lua 12 KB

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