1-- minetest/fire/init.lua 2local weather = minetest.setting_getbool("weather") 3 4minetest.register_node("fire:basic_flame", { 5 description = "Fire", 6 drawtype = "firelike", 7 tiles = {{ 8 name="fire_basic_flame_animated.png", 9 animation={type="vertical_frames", aspect_w=16, aspect_h=16, length=1}, 10 }}, 11 inventory_image = "fire_basic_flame.png", 12 light_source = 14, 13 waving = 1, 14 groups = {igniter=2,dig_immediate=3,hot=200,wield_light=13, drop_by_liquid=1}, 15 drop = '', 16 walkable = false, 17 buildable_to = true, 18 damage_per_second = 4, 19 20 after_place_node = function(pos, placer) 21 fire.on_flame_add_at(pos) 22 end, 23 24 after_dig_node = function(pos, oldnode, oldmetadata, digger) 25 fire.on_flame_remove_at(pos) 26 end, 27}) 28 29fire = {} 30fire.D = 6 31-- key: position hash of low corner of area 32-- value: {handle=sound handle, name=sound name} 33fire.sounds = {} 34 35function fire.get_area_p0p1(pos) 36 local p0 = { 37 x=math.floor(pos.x/fire.D)*fire.D, 38 y=math.floor(pos.y/fire.D)*fire.D, 39 z=math.floor(pos.z/fire.D)*fire.D, 40 } 41 local p1 = { 42 x=p0.x+fire.D-1, 43 y=p0.y+fire.D-1, 44 z=p0.z+fire.D-1 45 } 46 return p0, p1 47end 48 49function fire.update_sounds_around(pos) 50 local p0, p1 = fire.get_area_p0p1(pos) 51 local cp = {x=(p0.x+p1.x)/2, y=(p0.y+p1.y)/2, z=(p0.z+p1.z)/2} 52 local flames_p = minetest.find_nodes_in_area(p0, p1, {"fire:basic_flame"}) 53 --print("number of flames at "..minetest.pos_to_string(p0).."/" 54 -- ..minetest.pos_to_string(p1)..": "..#flames_p) 55 local should_have_sound = (#flames_p > 0) 56 local wanted_sound = nil 57 if #flames_p >= 9 then 58 wanted_sound = {name="fire_large", gain=1.5} 59 elseif #flames_p > 0 then 60 wanted_sound = {name="fire_small", gain=1.5} 61 end 62 local p0_hash = minetest.hash_node_position(p0) 63 local sound = fire.sounds[p0_hash] 64 if not sound then 65 if should_have_sound then 66 fire.sounds[p0_hash] = { 67 handle = minetest.sound_play(wanted_sound, {pos=cp, loop=true}), 68 name = wanted_sound.name, 69 } 70 end 71 else 72 if not wanted_sound then 73 minetest.sound_stop(sound.handle) 74 fire.sounds[p0_hash] = nil 75 elseif sound.name ~= wanted_sound.name then 76 minetest.sound_stop(sound.handle) 77 fire.sounds[p0_hash] = { 78 handle = minetest.sound_play(wanted_sound, {pos=cp, loop=true}), 79 name = wanted_sound.name, 80 } 81 end 82 end 83end 84 85function fire.on_flame_add_at(pos) 86 --print("flame added at "..minetest.pos_to_string(pos)) 87 fire.update_sounds_around(pos) 88end 89 90function fire.on_flame_remove_at(pos) 91 --print("flame removed at "..minetest.pos_to_string(pos)) 92 fire.update_sounds_around(pos) 93end 94 95function fire.find_pos_for_flame_around(pos) 96 return minetest.find_node_near(pos, 1, {"air"}) 97end 98 99function fire.flame_should_extinguish(pos) 100 if minetest.setting_getbool("disable_fire") then return true end 101 if weather then 102 local humidity = core.get_humidity(pos) 103 if humidity > 55 and math.random(55, humidity) >= 60 then 104 return 1 105 end 106 end 107 --return minetest.find_node_near(pos, 1, {"group:puts_out_fire"}) 108 local p0 = {x=pos.x-2, y=pos.y, z=pos.z-2} 109 local p1 = {x=pos.x+2, y=pos.y, z=pos.z+2} 110 local ps = minetest.find_nodes_in_area(p0, p1, {"group:puts_out_fire"}) 111 return (#ps ~= 0) 112end 113 114-- Ignite neighboring nodes 115minetest.register_abm({ 116 nodenames = {"group:flammable"}, 117 neighbors = {"group:igniter"}, 118 interval = 1, 119 chance = 2, 120 action = function(p0, node, _, _) 121 -- If there is water or stuff like that around flame, don't ignite 122 if fire.flame_should_extinguish(p0) then 123 return 124 end 125 local p = fire.find_pos_for_flame_around(p0) 126 if p then 127 minetest.set_node(p, {name="fire:basic_flame"}) 128 fire.on_flame_add_at(p) 129 end 130 end, 131}) 132 133-- Rarely ignite things from far 134minetest.register_abm({ 135 nodenames = {"group:igniter"}, 136 neighbors = {"air"}, 137 interval = 2, 138 chance = 10, 139 action = function(p0, node, _, _) 140 local reg = minetest.registered_nodes[node.name] 141 if not reg or not reg.groups.igniter or reg.groups.igniter < 2 then 142 return 143 end 144 local d = reg.groups.igniter 145 local p = minetest.find_node_near(p0, d, {"group:flammable"}) 146 if p then 147 -- If there is water or stuff like that around flame, don't ignite 148 if fire.flame_should_extinguish(p) then 149 return 150 end 151 local p2 = fire.find_pos_for_flame_around(p) 152 if p2 then 153 minetest.set_node(p2, {name="fire:basic_flame"}) 154 fire.on_flame_add_at(p2) 155 end 156 end 157 end, 158}) 159 160-- Remove flammable nodes and flame 161minetest.register_abm({ 162 nodenames = {"fire:basic_flame"}, 163 interval = 1, 164 chance = 2, 165 action = function(p0, node, _, _) 166 -- If there is water or stuff like that around flame, remove flame 167 if fire.flame_should_extinguish(p0) then 168 minetest.remove_node(p0) 169 fire.on_flame_remove_at(p0) 170 return 171 end 172 --check if fire is permenant or not 173 if core.get_node(p0).param2 == 128 then return end 174 -- Make the following things rarer 175 if math.random(1,3) == 1 then 176 return 177 end 178 -- If there are no flammable nodes around flame, remove flame 179 if not minetest.find_node_near(p0, 1, {"group:flammable"}) then 180 minetest.remove_node(p0) 181 fire.on_flame_remove_at(p0) 182 return 183 end 184 if math.random(1,4) == 1 then 185 -- remove a flammable node around flame 186 local p = minetest.find_node_near(p0, 1, {"group:flammable"}) 187 if p then 188 -- If there is water or stuff like that around flame, don't remove 189 if fire.flame_should_extinguish(p0) then 190 return 191 end 192 minetest.remove_node(p) 193 nodeupdate(p) 194 end 195 else 196 -- remove flame 197 minetest.remove_node(p0) 198 fire.on_flame_remove_at(p0) 199 end 200 end, 201}) 202 203-- too hot 204if weather then 205minetest.register_abm({ 206 nodenames = {"group:flammable"}, 207 interval = 5, 208 chance = 5, 209 action = function(p0, node, _, _) 210 -- If there is water or stuff like that around flame, don't ignite 211 if core.get_heat(p0) < 500 then return end 212 if fire.flame_should_extinguish(p0) then 213 return 214 end 215 local p = fire.find_pos_for_flame_around(p0) 216 if p then 217 minetest.set_node(p, {name="fire:basic_flame"}) 218 fire.on_flame_add_at(p) 219 end 220 end, 221}) 222end 223