init.lua 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. if not minetest.global_exists("handholds") then handholds = {} end
  2. handholds.modpath = minetest.get_modpath("handholds")
  3. handholds.players = handholds.players or {}
  4. -- Localize for performance.
  5. local math_random = math.random
  6. -- function to safely remove climbable air
  7. local function remove_air(pos, oldnode)
  8. local dir = minetest.facedir_to_dir(oldnode.param2)
  9. local airpos = vector.subtract(pos, dir)
  10. local north_node = minetest.get_node({x = airpos.x, y = airpos.y, z = airpos.z+1})
  11. local south_node = minetest.get_node({x = airpos.x, y = airpos.y, z = airpos.z-1})
  12. local east_node = minetest.get_node({x = airpos.x+1, y = airpos.y, z = airpos.z})
  13. local west_node = minetest.get_node({x = airpos.x-1, y = airpos.y, z = airpos.z})
  14. local keep_air =
  15. (minetest.get_item_group(north_node.name, "handholds") == 1 and
  16. north_node.param2 == 0) or
  17. (minetest.get_item_group(south_node.name, "handholds") == 1 and
  18. south_node.param2 == 2) or
  19. (minetest.get_item_group(east_node.name, "handholds") == 1 and
  20. east_node.param2 == 1) or
  21. (minetest.get_item_group(west_node.name, "handholds") == 1 and
  22. west_node.param2 == 3)
  23. if not keep_air then
  24. minetest.add_node(airpos, {name = "air"})
  25. end
  26. end
  27. -- remove handholds from nodes buried under falling nodes
  28. local function remove_handholds(pos)
  29. local north_pos = {x = pos.x, y = pos.y, z = pos.z+1}
  30. local south_pos = {x = pos.x, y = pos.y, z = pos.z-1}
  31. local east_pos = {x = pos.x+1, y = pos.y, z = pos.z}
  32. local west_pos = {x = pos.x-1, y = pos.y, z = pos.z}
  33. local north_node = minetest.get_node(north_pos)
  34. local south_node = minetest.get_node(south_pos)
  35. local east_node = minetest.get_node(east_pos)
  36. local west_node = minetest.get_node(west_pos)
  37. local node_pos
  38. if minetest.get_item_group(north_node.name, "handholds") == 1 and
  39. north_node.param2 == 0 then
  40. node_pos = north_pos
  41. elseif minetest.get_item_group(south_node.name, "handholds") == 1 and
  42. south_node.param2 == 2 then
  43. node_pos = south_pos
  44. elseif minetest.get_item_group(east_node.name, "handholds") == 1 and
  45. east_node.param2 == 1 then
  46. node_pos = east_pos
  47. elseif minetest.get_item_group(west_node.name, "handholds") == 1 and
  48. west_node.param2 == 3 then
  49. node_pos = west_pos
  50. end
  51. if node_pos then
  52. --local handholds_node = string.split(minetest.get_node(node_pos).name, ":")
  53. local nn = minetest.get_node(node_pos).name
  54. local def = minetest.registered_items[nn]
  55. if def and def._handholds_original then
  56. minetest.add_node(node_pos, {name = def._handholds_original})
  57. end
  58. end
  59. end
  60. function handholds.on_use(itemstack, player, pointed_thing)
  61. local pname = player:get_player_name()
  62. -- Ensure pointing at a SIDE face, not top or bottom.
  63. if not pointed_thing or
  64. pointed_thing.type ~= "node" or
  65. pointed_thing.under.y + 1 == pointed_thing.above.y or
  66. pointed_thing.under.y - 1 == pointed_thing.above.y then
  67. return
  68. end
  69. local control = player:get_player_control()
  70. if control.up and control.jump then
  71. local last = handholds.players[pname] or {time=0}
  72. local timenow = os.time()
  73. -- Limit to 1/sec to disallow climbing shear walls by spamclicking.
  74. if timenow > last.time then
  75. local dir = player:get_look_dir()
  76. ambiance.sound_play("climbing_pick_strike", pointed_thing.above, 0.5, 30)
  77. player:add_velocity({x=dir.x, y=8.0, z=dir.z})
  78. handholds.players[pname] = {
  79. time = os.time(),
  80. }
  81. local wdef = itemstack:get_definition()
  82. itemstack:add_wear(10)
  83. if itemstack:get_count() == 0 and wdef.sound and wdef.sound.breaks then
  84. minetest.sound_play(wdef.sound.breaks, {pos = pointed_thing.above, gain = 0.5}, true)
  85. end
  86. return itemstack
  87. end
  88. return
  89. end
  90. -- Can't put handholds in protected surfaces.
  91. if minetest.is_protected(pointed_thing.under, pname) or minetest.is_protected(pointed_thing.above, pname) then
  92. return
  93. end
  94. local node_def =
  95. minetest.reg_ns_nodes[minetest.get_node(pointed_thing.above).name]
  96. if not node_def or not node_def.buildable_to then
  97. return
  98. end
  99. local node_name = minetest.get_node(pointed_thing.under).name
  100. local rotation = minetest.dir_to_facedir(
  101. vector.subtract(pointed_thing.under, pointed_thing.above))
  102. if node_name == "default:stone" then
  103. minetest.add_node(pointed_thing.under,
  104. {name = "handholds:stone", param2 = rotation})
  105. elseif node_name == "default:desert_stone" then
  106. minetest.add_node(pointed_thing.under,
  107. {name = "handholds:desert_stone", param2 = rotation})
  108. elseif node_name == "default:sandstone" then
  109. minetest.add_node(pointed_thing.under,
  110. {name = "handholds:sandstone", param2 = rotation})
  111. elseif node_name == "default:ice" then
  112. minetest.add_node(pointed_thing.under,
  113. {name = "handholds:ice", param2 = rotation})
  114. elseif node_name == "rackstone:rackstone" or node_name == "rackstone:mg_rackstone" then
  115. minetest.add_node(pointed_thing.under,
  116. {name = "handholds:rackstone", param2 = rotation})
  117. elseif node_name == "rackstone:redrack" or node_name == "rackstone:mg_redrack" then
  118. minetest.add_node(pointed_thing.under,
  119. {name = "handholds:redrack", param2 = rotation})
  120. else
  121. return
  122. end
  123. minetest.add_node(pointed_thing.above, {name = "handholds:climbable_air"})
  124. ambiance.sound_play("default_dig_cracky", pointed_thing.above, 0.5, 30)
  125. local wdef = itemstack:get_definition()
  126. itemstack:add_wear(256)
  127. if itemstack:get_count() == 0 and wdef.sound and wdef.sound.breaks then
  128. minetest.sound_play(wdef.sound.breaks, {pos = pointed_thing.above, gain = 0.5}, true)
  129. end
  130. return itemstack
  131. end
  132. -- climbable air!
  133. minetest.register_node("handholds:climbable_air", {
  134. description = "Air!",
  135. drawtype = "airlike",
  136. paramtype = "light",
  137. sunlight_propagates = true,
  138. walkable = false,
  139. pointable = false,
  140. diggable = false,
  141. climbable = true,
  142. buildable_to = true,
  143. floodable = true,
  144. drop = "",
  145. groups = {not_in_creative_inventory = 1},
  146. sounds = default.node_sound_stone_defaults(),
  147. on_destruct = function(pos)
  148. remove_handholds(pos)
  149. end,
  150. on_flood = function(pos)
  151. remove_handholds(pos)
  152. end,
  153. -- Player should not be able to obtain node.
  154. on_finish_collapse = function(pos, node)
  155. minetest.remove_node(pos)
  156. end,
  157. on_collapse_to_entity = function(pos, node)
  158. -- Do nothing.
  159. end,
  160. })
  161. -- handholds nodes
  162. minetest.register_node("handholds:stone", {
  163. description = "Stone Handholds",
  164. tiles = {
  165. "default_stone.png", "default_stone.png",
  166. "default_stone.png", "default_stone.png",
  167. "default_stone.png", "default_stone.png^handholds_holds.png"
  168. },
  169. paramtype2 = "facedir",
  170. on_rotate = function()
  171. return false
  172. end,
  173. groups = utility.dig_groups("stone", {not_in_creative_inventory = 1, handholds = 1}),
  174. drop = 'default:cobble',
  175. sounds = default.node_sound_stone_defaults(),
  176. after_destruct = function(pos, oldnode)
  177. remove_air(pos, oldnode)
  178. end,
  179. _handholds_original = "default:stone",
  180. })
  181. minetest.register_node("handholds:desert_stone", {
  182. description = "Desert Stone Handholds",
  183. tiles = {
  184. "default_desert_stone.png", "default_desert_stone.png",
  185. "default_desert_stone.png", "default_desert_stone.png",
  186. "default_desert_stone.png", "default_desert_stone.png^handholds_holds.png"
  187. },
  188. paramtype2 = "facedir",
  189. on_rotate = function()
  190. return false
  191. end,
  192. groups = utility.dig_groups("stone", {not_in_creative_inventory = 1, handholds = 1}),
  193. drop = 'default:desert_cobble2',
  194. sounds = default.node_sound_stone_defaults(),
  195. after_destruct = function(pos, oldnode)
  196. remove_air(pos, oldnode)
  197. end,
  198. _handholds_original = "default:desert_stone",
  199. })
  200. minetest.register_node("handholds:sandstone", {
  201. description = "Sandstone Handholds",
  202. tiles = {
  203. "default_sandstone.png", "default_sandstone.png",
  204. "default_sandstone.png", "default_sandstone.png",
  205. "default_sandstone.png", "default_sandstone.png^handholds_holds.png"
  206. },
  207. paramtype2 = "facedir",
  208. on_rotate = function()
  209. return false
  210. end,
  211. groups = utility.dig_groups("softstone", {not_in_creative_inventory = 1, handholds = 1}),
  212. drop = 'default:sandstone',
  213. sounds = default.node_sound_stone_defaults(),
  214. after_destruct = function(pos, oldnode)
  215. remove_air(pos, oldnode)
  216. end,
  217. _handholds_original = "default:sandstone",
  218. })
  219. minetest.register_node("handholds:ice", {
  220. description = "Ice Handholds",
  221. tiles = {
  222. "default_ice.png", "default_ice.png",
  223. "default_ice.png", "default_ice.png",
  224. "default_ice.png", "default_ice.png^handholds_holds.png"
  225. },
  226. paramtype2 = "facedir",
  227. on_rotate = function()
  228. return false
  229. end,
  230. groups = utility.dig_groups("ice", {
  231. puts_out_fire = 1, cools_lava = 1,
  232. not_in_creative_inventory = 1, handholds = 1
  233. }),
  234. drop = 'default:ice',
  235. sounds = default.node_sound_glass_defaults(),
  236. after_destruct = function(pos, oldnode)
  237. remove_air(pos, oldnode)
  238. end,
  239. _handholds_original = "default:ice",
  240. on_construct = function(pos)
  241. if rc.ice_melts_at_pos(pos) then
  242. minetest.get_node_timer(pos):start(math_random(ice.minmax_time()))
  243. end
  244. end,
  245. on_timer = function(pos, elapsed)
  246. if rc.ice_melts_at_pos(pos) then
  247. minetest.add_node(pos, {name="default:water_flowing"})
  248. end
  249. end,
  250. })
  251. minetest.register_node("handholds:rackstone", {
  252. description = "Rackstone Handholds",
  253. tiles = {
  254. "rackstone_rackstone.png", "rackstone_rackstone.png",
  255. "rackstone_rackstone.png", "rackstone_rackstone.png",
  256. "rackstone_rackstone.png", "rackstone_rackstone.png^handholds_holds_dark.png"
  257. },
  258. paramtype2 = "facedir",
  259. groups = utility.dig_groups("stone", {handholds=1}),
  260. sounds = default.node_sound_stone_defaults(),
  261. drop = 'rackstone:rackstone',
  262. on_rotate = function()
  263. return false
  264. end,
  265. after_destruct = function(pos, oldnode)
  266. remove_air(pos, oldnode)
  267. rackstone.destabilize_dauthsand(pos)
  268. end,
  269. _handholds_original = "rackstone:rackstone",
  270. })
  271. minetest.register_node("handholds:redrack", {
  272. description = "Netherack Handholds",
  273. tiles = {
  274. "rackstone_redrack.png", "rackstone_redrack.png",
  275. "rackstone_redrack.png", "rackstone_redrack.png",
  276. "rackstone_redrack.png", "rackstone_redrack.png^handholds_holds_very_dark.png"
  277. },
  278. paramtype2 = "facedir",
  279. groups = utility.dig_groups("netherack", {handholds=1}),
  280. sounds = rackstone.rackstone_sounds(),
  281. drop = 'rackstone:redrack',
  282. on_rotate = function()
  283. return false
  284. end,
  285. after_destruct = function(pos, oldnode)
  286. remove_air(pos, oldnode)
  287. rackstone.after_redrack_remove(pos, oldnode)
  288. rackstone.destabilize_dauthsand(pos, oldnode)
  289. end,
  290. on_construct = function(pos)
  291. rackstone.on_redrack_place(pos)
  292. end,
  293. _handholds_original = "rackstone:redrack",
  294. })
  295. -- handholds tool
  296. minetest.register_tool("handholds:climbing_pick", {
  297. description = "Climbing Pick",
  298. inventory_image = "handholds_tool.png",
  299. sound = {breaks = "default_tool_breaks"},
  300. on_use = function(...) return handholds.on_use(...) end,
  301. })
  302. minetest.register_craft({
  303. output = "handholds:climbing_pick",
  304. recipe = {
  305. {'default:diamond', 'default:diamond', 'default:diamond'},
  306. {'group:stick', '', ''},
  307. {'group:stick', '', ''},
  308. },
  309. })