1 -- Global namespace for functions
10 -- Flood flame function
12 local function flood_flame(pos, oldnode, newnode)
13 -- Play flame extinguish sound if liquid is not an 'igniter'
14 local nodedef = minetest.registered_items[newnode.name]
15 if not (nodedef and nodedef.groups and
16 nodedef.groups.igniter and nodedef.groups.igniter > 0) then
17 minetest.sound_play("fire_extinguish_flame",
18 {pos = pos, max_hear_distance = 16, gain = 0.15})
26 minetest.register_node("fire:basic_flame", {
27 drawtype = "firelike",
30 name = "fire_basic_flame_animated.png",
32 type = "vertical_frames",
39 inventory_image = "fire_basic_flame.png",
44 sunlight_propagates = true,
46 damage_per_second = 4,
47 groups = {igniter = 2, dig_immediate = 3, not_in_creative_inventory = 1},
50 on_timer = function(pos)
51 local f = minetest.find_node_near(pos, 1, {"group:flammable"})
53 minetest.remove_node(pos)
60 on_construct = function(pos)
61 minetest.get_node_timer(pos):start(math.random(30, 60))
64 on_flood = flood_flame,
67 minetest.register_node("fire:permanent_flame", {
68 description = "Permanent Flame",
69 drawtype = "firelike",
72 name = "fire_basic_flame_animated.png",
74 type = "vertical_frames",
81 inventory_image = "fire_basic_flame.png",
86 sunlight_propagates = true,
88 damage_per_second = 4,
89 groups = {igniter = 2, dig_immediate = 3},
92 on_flood = flood_flame,
98 minetest.register_tool("fire:flint_and_steel", {
99 description = "Flint and Steel",
100 inventory_image = "fire_flint_steel.png",
101 sound = {breaks = "default_tool_breaks"},
103 on_use = function(itemstack, user, pointed_thing)
104 local sound_pos = pointed_thing.above or user:get_pos()
106 "fire_flint_and_steel",
107 {pos = sound_pos, gain = 0.5, max_hear_distance = 8}
109 local player_name = user:get_player_name()
110 if pointed_thing.type == "node" then
111 local node_under = minetest.get_node(pointed_thing.under).name
112 local nodedef = minetest.registered_nodes[node_under]
116 if minetest.is_protected(pointed_thing.under, player_name) then
117 minetest.chat_send_player(player_name, "This area is protected")
120 if nodedef.on_ignite then
121 nodedef.on_ignite(pointed_thing.under, user)
122 elseif minetest.get_item_group(node_under, "flammable") >= 1
123 and minetest.get_node(pointed_thing.above).name == "air" then
124 minetest.set_node(pointed_thing.above, {name = "fire:basic_flame"})
127 if not (creative and creative.is_enabled_for
128 and creative.is_enabled_for(player_name)) then
130 local wdef = itemstack:get_definition()
131 itemstack:add_wear(1000)
133 if itemstack:get_count() == 0 and wdef.sound and wdef.sound.breaks then
134 minetest.sound_play(wdef.sound.breaks, {pos = sound_pos, gain = 0.5})
141 minetest.register_craft({
142 output = "fire:flint_and_steel",
144 {"default:flint", "default:steel_ingot"}
149 -- Override coalblock to enable permanent flame above
150 -- Coalblock is non-flammable to avoid unwanted basic_flame nodes
152 minetest.override_item("default:coalblock", {
153 after_destruct = function(pos, oldnode)
155 if minetest.get_node(pos).name == "fire:permanent_flame" then
156 minetest.remove_node(pos)
159 on_ignite = function(pos, igniter)
160 local flame_pos = {x = pos.x, y = pos.y + 1, z = pos.z}
161 if minetest.get_node(flame_pos).name == "air" then
162 minetest.set_node(flame_pos, {name = "fire:permanent_flame"})
172 local flame_sound = minetest.settings:get_bool("flame_sound")
173 if flame_sound == nil then
174 -- Enable if no setting present
185 local radius = 8 -- Flame node search radius around player
186 local cycle = 3 -- Cycle time for sound updates
188 -- Update sound for player
190 function fire.update_player_sound(player)
191 local player_name = player:get_player_name()
192 -- Search for flame nodes in radius around player
193 local ppos = player:get_pos()
194 local areamin = vector.subtract(ppos, radius)
195 local areamax = vector.add(ppos, radius)
196 local fpos, num = minetest.find_nodes_in_area(
199 {"fire:basic_flame", "fire:permanent_flame"}
201 -- Total number of flames in radius
202 local flames = (num["fire:basic_flame"] or 0) +
203 (num["fire:permanent_flame"] or 0)
204 -- Stop previous sound
205 if handles[player_name] then
206 minetest.sound_stop(handles[player_name])
207 handles[player_name] = nil
211 -- Find centre of flame positions
212 local fposmid = fpos[1]
213 -- If more than 1 flame
215 local fposmin = areamax
216 local fposmax = areamin
218 local fposi = fpos[i]
219 if fposi.x > fposmax.x then
222 if fposi.y > fposmax.y then
225 if fposi.z > fposmax.z then
228 if fposi.x < fposmin.x then
231 if fposi.y < fposmin.y then
234 if fposi.z < fposmin.z then
238 fposmid = vector.divide(vector.add(fposmin, fposmax), 2)
241 local handle = minetest.sound_play(
245 to_player = player_name,
246 gain = math.min(0.06 * (1 + flames * 0.125), 0.18),
247 max_hear_distance = 32,
248 loop = true, -- In case of lag
251 -- Store sound handle for this player
253 handles[player_name] = handle
258 -- Cycle for updating players sounds
260 minetest.register_globalstep(function(dtime)
261 timer = timer + dtime
262 if timer < cycle then
267 local players = minetest.get_connected_players()
268 for n = 1, #players do
269 fire.update_player_sound(players[n])
273 -- Stop sound and clear handle on player leave
275 minetest.register_on_leaveplayer(function(player)
276 local player_name = player:get_player_name()
277 if handles[player_name] then
278 minetest.sound_stop(handles[player_name])
279 handles[player_name] = nil
285 -- Deprecated function kept temporarily to avoid crashes if mod fire nodes call it
287 function fire.update_sounds_around(pos)
295 -- Enable the following ABMs according to 'enable fire' setting
297 local fire_enabled = minetest.settings:get_bool("enable_fire")
298 if fire_enabled == nil then
299 -- enable_fire setting not specified, check for disable_fire
300 local fire_disabled = minetest.settings:get_bool("disable_fire")
301 if fire_disabled == nil then
302 -- Neither setting specified, check whether singleplayer
303 fire_enabled = minetest.is_singleplayer()
305 fire_enabled = not fire_disabled
309 if not fire_enabled then
311 -- Remove basic flames only if fire disabled
313 minetest.register_abm({
314 label = "Remove disabled fire",
315 nodenames = {"fire:basic_flame"},
319 action = minetest.remove_node,
324 -- Ignite neighboring nodes, add basic flames
326 minetest.register_abm({
327 label = "Ignite flame",
328 nodenames = {"group:flammable"},
329 neighbors = {"group:igniter"},
333 action = function(pos, node, active_object_count, active_object_count_wider)
334 local p = minetest.find_node_near(pos, 1, {"air"})
336 minetest.set_node(p, {name = "fire:basic_flame"})
341 -- Remove flammable nodes around basic flame
343 minetest.register_abm({
344 label = "Remove flammable nodes",
345 nodenames = {"fire:basic_flame"},
346 neighbors = "group:flammable",
350 action = function(pos, node, active_object_count, active_object_count_wider)
351 local p = minetest.find_node_near(pos, 1, {"group:flammable"})
353 local flammable_node = minetest.get_node(p)
354 local def = minetest.registered_nodes[flammable_node.name]
358 minetest.remove_node(p)
359 minetest.check_for_falling(p)