init.lua 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. -- minetest/fire/init.lua
  2. -- Global namespace for functions
  3. fire = {}
  4. -- Register flame nodes
  5. minetest.register_node("fire:basic_flame", {
  6. drawtype = "firelike",
  7. tiles = {
  8. {
  9. name = "fire_basic_flame_animated.png",
  10. animation = {
  11. type = "vertical_frames",
  12. aspect_w = 16,
  13. aspect_h = 16,
  14. length = 1
  15. },
  16. },
  17. },
  18. inventory_image = "fire_basic_flame.png",
  19. paramtype = "light",
  20. light_source = 14,
  21. walkable = false,
  22. buildable_to = true,
  23. sunlight_propagates = true,
  24. damage_per_second = 4,
  25. groups = {igniter = 2, dig_immediate = 3, not_in_creative_inventory = 1},
  26. on_timer = function(pos)
  27. local f = minetest.find_node_near(pos, 1, {"group:flammable"})
  28. if not f then
  29. minetest.remove_node(pos)
  30. return
  31. end
  32. -- restart timer
  33. return true
  34. end,
  35. drop = "",
  36. on_construct = function(pos)
  37. minetest.get_node_timer(pos):start(math.random(30, 60))
  38. minetest.after(0, fire.update_sounds_around, pos)
  39. end,
  40. on_destruct = function(pos)
  41. minetest.after(0, fire.update_sounds_around, pos)
  42. end,
  43. on_blast = function()
  44. end, -- unaffected by explosions
  45. })
  46. minetest.register_node("fire:permanent_flame", {
  47. description = "Permanent Flame",
  48. drawtype = "firelike",
  49. tiles = {
  50. {
  51. name = "fire_basic_flame_animated.png",
  52. animation = {
  53. type = "vertical_frames",
  54. aspect_w = 16,
  55. aspect_h = 16,
  56. length = 1
  57. },
  58. },
  59. },
  60. inventory_image = "fire_basic_flame.png",
  61. paramtype = "light",
  62. light_source = 14,
  63. walkable = false,
  64. buildable_to = true,
  65. sunlight_propagates = true,
  66. damage_per_second = 4,
  67. groups = {igniter = 2, dig_immediate = 3},
  68. drop = "",
  69. on_blast = function()
  70. end,
  71. })
  72. -- Flint and steel
  73. minetest.register_tool("fire:flint_and_steel", {
  74. description = "Flint and Steel",
  75. inventory_image = "fire_flint_steel.png",
  76. on_use = function(itemstack, user, pointed_thing)
  77. itemstack:add_wear(1000)
  78. local pt = pointed_thing
  79. if pt.type == "node" then
  80. local node_under = minetest.get_node(pt.under).name
  81. local is_coalblock = node_under == "default:coalblock"
  82. local is_tnt = node_under == "tnt:tnt"
  83. local is_gunpowder = node_under == "tnt:gunpowder"
  84. if minetest.get_item_group(node_under, "flammable") >= 1 or
  85. is_coalblock or is_tnt or is_gunpowder then
  86. local flame_pos = pt.above
  87. if is_coalblock then
  88. flame_pos = {x = pt.under.x, y = pt.under.y + 1, z = pt.under.z}
  89. elseif is_tnt or is_gunpowder then
  90. flame_pos = pt.under
  91. end
  92. if minetest.get_node(flame_pos).name == "air" or
  93. is_tnt or is_gunpowder then
  94. local player_name = user:get_player_name()
  95. if not minetest.is_protected(flame_pos, player_name) then
  96. if is_coalblock then
  97. minetest.set_node(flame_pos,
  98. {name = "fire:permanent_flame"})
  99. elseif is_tnt then
  100. minetest.set_node(flame_pos,
  101. {name = "tnt:tnt_burning"})
  102. elseif is_gunpowder then
  103. minetest.set_node(flame_pos,
  104. {name = "tnt:gunpowder_burning"})
  105. else
  106. minetest.set_node(flame_pos,
  107. {name = "fire:basic_flame"})
  108. end
  109. else
  110. minetest.chat_send_player(player_name, "This area is protected")
  111. end
  112. end
  113. end
  114. end
  115. if not minetest.setting_getbool("creative_mode") then
  116. return itemstack
  117. end
  118. end
  119. })
  120. minetest.register_craft({
  121. output = "fire:flint_and_steel",
  122. recipe = {
  123. {"default:flint", "default:steel_ingot"}
  124. }
  125. })
  126. -- Override coalblock to enable permanent flame above
  127. -- Coalblock is non-flammable to avoid unwanted basic_flame nodes
  128. minetest.override_item("default:coalblock", {
  129. after_destruct = function(pos, oldnode)
  130. pos.y = pos.y + 1
  131. if minetest.get_node(pos).name == "fire:permanent_flame" then
  132. minetest.remove_node(pos)
  133. end
  134. end,
  135. })
  136. -- Get sound area of position
  137. fire.D = 6 -- size of sound areas
  138. function fire.get_area_p0p1(pos)
  139. local p0 = {
  140. x = math.floor(pos.x / fire.D) * fire.D,
  141. y = math.floor(pos.y / fire.D) * fire.D,
  142. z = math.floor(pos.z / fire.D) * fire.D,
  143. }
  144. local p1 = {
  145. x = p0.x + fire.D - 1,
  146. y = p0.y + fire.D - 1,
  147. z = p0.z + fire.D - 1
  148. }
  149. return p0, p1
  150. end
  151. -- Fire sounds table
  152. -- key: position hash of low corner of area
  153. -- value: {handle=sound handle, name=sound name}
  154. fire.sounds = {}
  155. -- Update fire sounds in sound area of position
  156. function fire.update_sounds_around(pos)
  157. local p0, p1 = fire.get_area_p0p1(pos)
  158. local cp = {x = (p0.x + p1.x) / 2, y = (p0.y + p1.y) / 2, z = (p0.z + p1.z) / 2}
  159. local flames_p = minetest.find_nodes_in_area(p0, p1, {"fire:basic_flame"})
  160. --print("number of flames at "..minetest.pos_to_string(p0).."/"
  161. -- ..minetest.pos_to_string(p1)..": "..#flames_p)
  162. local should_have_sound = (#flames_p > 0)
  163. local wanted_sound = nil
  164. if #flames_p >= 9 then
  165. wanted_sound = {name = "fire_large", gain = 0.7}
  166. elseif #flames_p > 0 then
  167. wanted_sound = {name = "fire_small", gain = 0.9}
  168. end
  169. local p0_hash = minetest.hash_node_position(p0)
  170. local sound = fire.sounds[p0_hash]
  171. if not sound then
  172. if should_have_sound then
  173. fire.sounds[p0_hash] = {
  174. handle = minetest.sound_play(wanted_sound,
  175. {pos = cp, max_hear_distance = 16, loop = true}),
  176. name = wanted_sound.name,
  177. }
  178. end
  179. else
  180. if not wanted_sound then
  181. minetest.sound_stop(sound.handle)
  182. fire.sounds[p0_hash] = nil
  183. elseif sound.name ~= wanted_sound.name then
  184. minetest.sound_stop(sound.handle)
  185. fire.sounds[p0_hash] = {
  186. handle = minetest.sound_play(wanted_sound,
  187. {pos = cp, max_hear_distance = 16, loop = true}),
  188. name = wanted_sound.name,
  189. }
  190. end
  191. end
  192. end
  193. -- Extinguish all flames quickly with water, snow, ice
  194. minetest.register_abm({
  195. label = "Extinguish flame",
  196. nodenames = {"fire:basic_flame", "fire:permanent_flame"},
  197. neighbors = {"group:puts_out_fire"},
  198. interval = 3,
  199. chance = 1,
  200. catch_up = false,
  201. action = function(pos, node, active_object_count, active_object_count_wider)
  202. minetest.remove_node(pos)
  203. minetest.sound_play("fire_extinguish_flame",
  204. {pos = pos, max_hear_distance = 16, gain = 0.25})
  205. end,
  206. })
  207. -- Enable the following ABMs according to 'enable fire' setting
  208. local fire_enabled = minetest.setting_getbool("enable_fire")
  209. if fire_enabled == nil then
  210. -- New setting not specified, check for old setting.
  211. -- If old setting is also not specified, 'not nil' is true.
  212. fire_enabled = not minetest.setting_getbool("disable_fire")
  213. end
  214. if not fire_enabled then
  215. -- Remove basic flames only
  216. minetest.register_abm({
  217. label = "Remove disabled fire",
  218. nodenames = {"fire:basic_flame"},
  219. interval = 7,
  220. chance = 1,
  221. catch_up = false,
  222. action = minetest.remove_node,
  223. })
  224. else -- Fire enabled
  225. -- Ignite neighboring nodes, add basic flames
  226. minetest.register_abm({
  227. label = "Ignite flame",
  228. nodenames = {"group:flammable"},
  229. neighbors = {"group:igniter"},
  230. interval = 7,
  231. chance = 12,
  232. catch_up = false,
  233. action = function(pos, node, active_object_count, active_object_count_wider)
  234. -- If there is water or stuff like that around node, don't ignite
  235. if minetest.find_node_near(pos, 1, {"group:puts_out_fire"}) then
  236. return
  237. end
  238. local p = minetest.find_node_near(pos, 1, {"air"})
  239. if p then
  240. minetest.set_node(p, {name = "fire:basic_flame"})
  241. end
  242. end,
  243. })
  244. -- Remove flammable nodes
  245. minetest.register_abm({
  246. label = "Remove flammable nodes",
  247. nodenames = {"fire:basic_flame"},
  248. neighbors = "group:flammable",
  249. interval = 5,
  250. chance = 18,
  251. catch_up = false,
  252. action = function(pos, node, active_object_count, active_object_count_wider)
  253. local p = minetest.find_node_near(pos, 1, {"group:flammable"})
  254. if p then
  255. -- remove flammable nodes around flame
  256. local flammable_node = minetest.get_node(p)
  257. local def = minetest.registered_nodes[flammable_node.name]
  258. if def.on_burn then
  259. def.on_burn(p)
  260. else
  261. minetest.remove_node(p)
  262. nodeupdate(p)
  263. end
  264. end
  265. end,
  266. })
  267. end
  268. -- Rarely ignite things from far
  269. --[[ Currently disabled to reduce the chance of uncontrollable spreading
  270. fires that disrupt servers. Also for less lua processing load.
  271. minetest.register_abm({
  272. nodenames = {"group:igniter"},
  273. neighbors = {"air"},
  274. interval = 5,
  275. chance = 10,
  276. action = function(pos, node, active_object_count, active_object_count_wider)
  277. local reg = minetest.registered_nodes[node.name]
  278. if not reg or not reg.groups.igniter or reg.groups.igniter < 2 then
  279. return
  280. end
  281. local d = reg.groups.igniter
  282. local p = minetest.find_node_near(pos, d, {"group:flammable"})
  283. if p then
  284. -- If there is water or stuff like that around flame, don't ignite
  285. if fire.flame_should_extinguish(p) then
  286. return
  287. end
  288. local p2 = fire.find_pos_for_flame_around(p)
  289. if p2 then
  290. minetest.set_node(p2, {name = "fire:basic_flame"})
  291. end
  292. end
  293. end,
  294. })
  295. --]]