tool.lua 13 KB

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