rockdrill.lua 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. if not minetest.global_exists("rockdrill") then rockdrill = {} end
  2. rockdrill.modpath = minetest.get_modpath("silicon")
  3. rockdrill.image = "rockdrill_rockdrill.png"
  4. rockdrill.sound = "rockdrill"
  5. rockdrill.name = "rockdrill:rockdrill"
  6. rockdrill.description = "Rock Drill\n\nUses stored energy to blast stone.\nWon't function in protected areas.\nMust be charged to use."
  7. rockdrill.range = 4
  8. -- Localize for performance.
  9. local math_floor = math.floor
  10. local math_random = math.random
  11. -- This is how many nodes the tool can blast.
  12. rockdrill.uses = math_floor(65535/2500)
  13. -- Find all blastable nodes in a small radius.
  14. function rockdrill.find_stone(sp, wear)
  15. local traversal = {}
  16. local queue = {}
  17. local output = {}
  18. local curpos, hash, exists, name, found, norm, cb, depth
  19. local get_node_hash = minetest.hash_node_position
  20. local get_node = minetest.get_node
  21. local is_blastable = rockdrill.is_blastable
  22. local is_protected = minetest.test_protection
  23. queue[#queue+1] = {x=sp.x, y=sp.y, z=sp.z, d=1}
  24. ::continue::
  25. curpos = queue[#queue]
  26. queue[#queue] = nil
  27. depth = curpos.d
  28. curpos.d = nil
  29. hash = get_node_hash(curpos)
  30. exists = false
  31. if traversal[hash] then
  32. exists = true
  33. if depth >= traversal[hash] then
  34. goto next
  35. end
  36. end
  37. if depth >= rockdrill.range then
  38. goto next
  39. end
  40. if wear > math_floor(65535-rockdrill.uses) then
  41. goto next
  42. end
  43. name = get_node(curpos).name
  44. found = false
  45. if is_blastable(name) then
  46. if not is_protected(curpos, "") then
  47. found = true
  48. end
  49. end
  50. if not found then
  51. goto next
  52. end
  53. traversal[hash] = depth
  54. if not exists then
  55. output[#output+1] = vector.new(curpos)
  56. wear = wear + rockdrill.uses
  57. end
  58. queue[#queue+1] = {x=curpos.x+1, y=curpos.y, z=curpos.z, d=depth+1}
  59. queue[#queue+1] = {x=curpos.x-1, y=curpos.y, z=curpos.z, d=depth+1}
  60. queue[#queue+1] = {x=curpos.x, y=curpos.y+1, z=curpos.z, d=depth+1}
  61. queue[#queue+1] = {x=curpos.x, y=curpos.y-1, z=curpos.z, d=depth+1}
  62. queue[#queue+1] = {x=curpos.x, y=curpos.y, z=curpos.z+1, d=depth+1}
  63. queue[#queue+1] = {x=curpos.x, y=curpos.y, z=curpos.z-1, d=depth+1}
  64. ::next::
  65. if #queue > 0 then
  66. goto continue
  67. end
  68. return output, wear
  69. end
  70. function rockdrill.is_blastable(name)
  71. -- Air is not blastable, and therefor not obtainable.
  72. if name == "air" then
  73. return
  74. end
  75. -- Check node def.
  76. local def = minetest.reg_ns_nodes[name] or minetest.registered_nodes[name]
  77. if def and def.groups then
  78. local lg = (def.groups.immovable or 0)
  79. local pg = (def.groups.protector or 0)
  80. if lg > 0 or pg > 0 then
  81. return
  82. end
  83. if def.liquidtype ~= "none" then
  84. return
  85. end
  86. end
  87. return true
  88. end
  89. local function effect(pos, amount, texture, min_size, max_size, radius, gravity, glow)
  90. radius = radius or 2
  91. min_size = min_size or 2.0
  92. max_size = max_size or 6.0
  93. gravity = gravity or -10
  94. glow = glow or 0
  95. minetest.add_particlespawner({
  96. amount = amount,
  97. time = 0.25,
  98. minpos = pos,
  99. maxpos = pos,
  100. minvel = {x = -radius, y = -radius, z = -radius},
  101. maxvel = {x = radius, y = radius, z = radius},
  102. minacc = {x = 0, y = gravity, z = 0},
  103. maxacc = {x = 0, y = gravity, z = 0},
  104. minexptime = 0.1,
  105. maxexptime = 1,
  106. minsize = min_size,
  107. maxsize = max_size,
  108. texture = texture,
  109. glow = glow,
  110. })
  111. end
  112. function rockdrill.handle_node_drops(pos, user)
  113. ---[[
  114. local node = minetest.get_node(pos)
  115. if node.name == "air" then
  116. return
  117. end
  118. local def = minetest.registered_nodes[node.name]
  119. if def and def.groups then
  120. local ig = (def.groups.immovable or 0)
  121. if ig > 0 then
  122. return
  123. end
  124. end
  125. local inv = user:get_inventory()
  126. if not inv then
  127. return
  128. end
  129. -- This function takes both nodetables and nodenames.
  130. -- Pass nodenames, because passing a nodetable gives wrong results.
  131. local drops = minetest.get_node_drops(node.name, "")
  132. for _, item in pairs(drops) do
  133. local stack = ItemStack(item) -- Itemstring to itemstack.
  134. local remain = inv:add_item("main", stack)
  135. if not remain:is_empty() then
  136. local p = {
  137. x = pos.x + math_random()/2 - 0.25,
  138. y = pos.y + math_random()/2 - 0.25,
  139. z = pos.z + math_random()/2 - 0.25,
  140. }
  141. minetest.add_item(p, remain)
  142. end
  143. end
  144. if math.random(1, 5) == 1 then
  145. effect(pos, math_random(2, 5), "tnt_smoke.png")
  146. end
  147. minetest.remove_node(pos)
  148. --]]
  149. --_nodeupdate.drop_node_as_entity(pos)
  150. end
  151. function rockdrill.on_use(itemstack, user, pt)
  152. if not user or not user:is_player() then
  153. return
  154. end
  155. if pt.type ~= "node" then
  156. return
  157. end
  158. local wear = itemstack:get_wear()
  159. if wear == 0 then
  160. -- Tool isn't charged!
  161. -- Once it is charged the first time, wear should never be 0 again.
  162. return
  163. end
  164. if wear > math_floor(65535-rockdrill.uses) then
  165. -- Tool has no charge left.
  166. return
  167. end
  168. local under = pt.under
  169. local blasted, newwear = rockdrill.find_stone(under, wear)
  170. if #blasted == 0 then
  171. return
  172. end
  173. ambiance.sound_play(rockdrill.sound, under, 1.0, 40)
  174. for k, v in ipairs(blasted) do
  175. local node = minetest.get_node(v)
  176. local def = minetest.registered_nodes[node.name]
  177. if def and def.on_blast then
  178. -- Behave as if blasted by TNT.
  179. local drops = def.on_blast(v, 1.0)
  180. if drops and type(drops) == "table" then
  181. for k, j in ipairs(drops) do
  182. minetest.add_item(v, j)
  183. end
  184. end
  185. else
  186. -- No on_blast function? Destroy node normally.
  187. rockdrill.handle_node_drops(v, user)
  188. end
  189. minetest.check_for_falling(v)
  190. end
  191. wear = newwear
  192. -- Don't let wear reach max or tool will be destroyed.
  193. if wear >= 65535 then
  194. wear = 65534
  195. end
  196. itemstack:set_wear(wear)
  197. return itemstack
  198. end
  199. if not rockdrill.run_once then
  200. minetest.register_tool(":" .. rockdrill.name, {
  201. description = rockdrill.description,
  202. inventory_image = rockdrill.image,
  203. wear_represents = "eu_charge",
  204. groups = {not_repaired_by_anvil = 1, disable_repair = 1},
  205. on_use = function(...)
  206. return rockdrill.on_use(...)
  207. end,
  208. })
  209. ---[[
  210. minetest.register_craft({
  211. output = rockdrill.name,
  212. recipe = {
  213. {'moreores:tin_ingot', 'gem_cutter:blade', 'moreores:tin_ingot'},
  214. {'stainless_steel:ingot', 'techcrafts:electric_motor', 'stainless_steel:ingot'},
  215. {'', 'battery:battery', 'default:copper_ingot'},
  216. }
  217. })
  218. --]]
  219. local c = "rockdrill:core"
  220. local f = rockdrill.modpath .. "/rockdrill.lua"
  221. reload.register_file(c, f, false)
  222. rockdrill.run_once = true
  223. end