breath.lua 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. breath = breath or {}
  2. breath.modpath = minetest.get_modpath("hb4")
  3. -- Localize for performance.
  4. local vector_round = vector.round
  5. local math_random = math.random
  6. function breath.time()
  7. return math_random(20, 200)/10
  8. end
  9. -- Recursive algorithm.
  10. local function floodfill(startpos, maxdepth)
  11. local traversal = {}
  12. local queue = {}
  13. local output = {}
  14. local curpos, hash, exists, name, found, norm, cb, depth
  15. local maxlength = 1
  16. local get_node_hash = minetest.hash_node_position
  17. local get_node = minetest.get_node
  18. startpos.d = 1
  19. queue[#queue+1] = startpos
  20. ::continue::
  21. curpos = queue[#queue]
  22. queue[#queue] = nil
  23. depth = curpos.d
  24. curpos.d = nil
  25. hash = get_node_hash(curpos)
  26. exists = false
  27. if traversal[hash] then
  28. exists = true
  29. if depth >= traversal[hash] then
  30. goto next
  31. end
  32. end
  33. if depth >= maxdepth then
  34. goto next
  35. end
  36. name = get_node(curpos).name
  37. found = false
  38. if name == 'air' then
  39. found = true
  40. end
  41. if not found then
  42. goto next
  43. end
  44. traversal[hash] = depth
  45. if not exists then
  46. output[#output+1] = vector.new(curpos)
  47. end
  48. queue[#queue+1] = {x=curpos.x+1, y=curpos.y, z=curpos.z, d=depth+1}
  49. queue[#queue+1] = {x=curpos.x-1, y=curpos.y, z=curpos.z, d=depth+1}
  50. queue[#queue+1] = {x=curpos.x, y=curpos.y+1, z=curpos.z, d=depth+1}
  51. queue[#queue+1] = {x=curpos.x, y=curpos.y-1, z=curpos.z, d=depth+1}
  52. queue[#queue+1] = {x=curpos.x, y=curpos.y, z=curpos.z+1, d=depth+1}
  53. queue[#queue+1] = {x=curpos.x, y=curpos.y, z=curpos.z-1, d=depth+1}
  54. if #queue > maxlength then
  55. maxlength = #queue
  56. end
  57. ::next::
  58. if #queue > 0 then
  59. goto continue
  60. end
  61. --minetest.chat_send_all("# Server: Array size: " .. maxlength)
  62. return output
  63. end
  64. function breath.on_construct(pos)
  65. end
  66. function breath.on_destruct(pos)
  67. end
  68. function breath.on_timer(pos, elapsed)
  69. end
  70. function breath.ignite_nearby_gas(pos)
  71. --minetest.chat_send_player("MustTest", "# Server: Igniting gas @ " .. minetest.pos_to_string(pos) .. "!")
  72. pos = vector_round(pos)
  73. local gas = minetest.find_node_near(pos, 2, {"group:gas"})
  74. if gas then
  75. minetest.set_node(gas, {name="fire:basic_flame"})
  76. end
  77. end
  78. function breath.extinguish_torches_around(v)
  79. -- Find nearby torches.
  80. local min = {x=v.x-1, y=v.y-1, z=v.z-1}
  81. local max = {x=v.x+1, y=v.y+1, z=v.z+1}
  82. local torches = minetest.find_nodes_in_area(min, max, {"group:torch", "group:fire"})
  83. -- Replace nearby torches or fire with gas.
  84. for i = 1, #torches, 1 do
  85. local nn = minetest.get_node(torches[i]).name
  86. minetest.after(math_random(1, 10), function()
  87. -- We delayed a bit, we must ensure node has not changed.
  88. local n2 = minetest.get_node(torches[i]).name
  89. if n2 == nn then
  90. _nodeupdate.drop_node_as_entity(torches[i])
  91. local node = minetest.get_node(torches[i])
  92. if node.name == "air" then
  93. node.name = "gas:poison"
  94. minetest.set_node(torches[i], node)
  95. end
  96. end
  97. end)
  98. end
  99. end
  100. function breath.spawn_gas(pos)
  101. pos = vector_round(pos)
  102. ambiance.sound_play("tnt_ignite", pos, 1.0, 60)
  103. local positions = floodfill(pos, math_random(10, 30))
  104. local set_node = minetest.set_node
  105. for k, v in ipairs(positions) do
  106. set_node(v, {name="gas:poison"})
  107. breath.extinguish_torches_around(v)
  108. end
  109. end
  110. if not breath.run_once then
  111. -- 8 levels of gas, now depreciated.
  112. for i = 1, 8, 1 do
  113. minetest.register_alias("gas:poison_" .. i, "gas:poison")
  114. end
  115. minetest.register_node(":gas:poison", {
  116. drawtype = "airlike",
  117. --tiles = {"default_gold_block.png"},
  118. description = "Poison Gas",
  119. paramtype = "light",
  120. sunlight_propagates = true,
  121. walkable = false,
  122. pointable = false,
  123. climbable = false,
  124. buildable_to = true,
  125. floodable = true,
  126. drop = "",
  127. post_effect_color = {a = 80, r = 127, g = 127, b = 127},
  128. drowning = 1,
  129. groups = {immovable=1, gas=1, flammable=3},
  130. on_construct = function(...)
  131. return breath.on_construct(...)
  132. end,
  133. on_destruct = function(...)
  134. return breath.on_destruct(...)
  135. end,
  136. on_timer = function(...)
  137. return breath.on_timer(...)
  138. end,
  139. -- Player should not be able to obtain node.
  140. on_collapse_to_entity = function(pos, node)
  141. -- Do nothing.
  142. end,
  143. -- Player should not be able to obtain node.
  144. on_finish_collapse = function(pos, node)
  145. minetest.remove_node(pos)
  146. end,
  147. })
  148. local c = "breath:core"
  149. local f = breath.modpath .. "/breath.lua"
  150. reload.register_file(c, f, false)
  151. breath.run_once = true
  152. end