2017-02-19 21:46:22 +00:00
-- Global namespace for functions
mcl_fire = { }
2021-03-15 00:10:33 +00:00
local modpath = minetest.get_modpath ( minetest.get_current_modname ( ) )
2017-02-19 21:46:22 +00:00
2019-03-07 23:00:09 +00:00
local S = minetest.get_translator ( " mcl_fire " )
2019-03-15 06:10:18 +00:00
local N = function ( s ) return s end
2017-02-19 21:46:22 +00:00
2021-03-15 00:10:33 +00:00
local has_mcl_portals = minetest.get_modpath ( " mcl_portals " )
local set_node = minetest.set_node
local get_node = minetest.get_node
local add_node = minetest.add_node
local remove_node = minetest.remove_node
local swap_node = minetest.swap_node
local get_node_or_nil = minetest.get_node_or_nil
local find_nodes_in_area = minetest.find_nodes_in_area
local find_node_near = minetest.find_node_near
local get_item_group = minetest.get_item_group
local get_connected_players = minetest.get_connected_players
local vector = vector
local math = math
2020-12-14 20:27:20 +00:00
-- inverse pyramid pattern above lava source, floor 1 of 2:
local lava_fire =
{
{ x =- 1 , y = 1 , z =- 1 } ,
{ x =- 1 , y = 1 , z = 0 } ,
{ x =- 1 , y = 1 , z = 1 } ,
{ x = 0 , y = 1 , z =- 1 } ,
{ x = 0 , y = 1 , z = 0 } ,
{ x = 0 , y = 1 , z = 1 } ,
{ x = 1 , y = 1 , z =- 1 } ,
{ x = 1 , y = 1 , z = 0 } ,
{ x = 1 , y = 1 , z = 1 }
}
local alldirs =
{
{ x =- 1 , y = 0 , z = 0 } ,
{ x = 1 , y = 0 , z = 0 } ,
{ x = 0 , y =- 1 , z = 0 } ,
{ x = 0 , y = 1 , z = 0 } ,
{ x = 0 , y = 0 , z =- 1 } ,
{ x = 0 , y = 0 , z = 1 }
}
2021-03-14 18:57:13 +00:00
-- 3 exptime variants because the animation is not tied to particle expiration time.
2021-03-15 02:04:47 +00:00
-- 3 colorized variants to imitate minecraft's
2021-03-14 18:57:13 +00:00
local smoke_pdef_base = {
2021-03-20 01:17:43 +00:00
amount = 0.001 ,
time = 0 ,
-- minpos = vector.add(pos, { x = -0.45, y = -0.45, z = -0.45 }),
-- maxpos = vector.add(pos, { x = 0.45, y = 0.45, z = 0.45 }),
minvel = { x = - 0.1 , y = 0.3 , z = - 0.1 } ,
maxvel = { x = 0.1 , y = 1.6 , z = 0.1 } ,
-- minexptime = 3 exptime variants,
-- maxexptime = 3 exptime variants
minsize = 4.0 ,
maxsize = 4.5 ,
-- texture = "mcl_particles_smoke_anim.png^[colorize:#000000:(3 colourize variants)",
animation = {
type = " vertical_frames " ,
aspect_w = 8 ,
aspect_h = 8 ,
-- length = 3 exptime variants
} ,
collisiondetection = true ,
2021-03-14 18:57:13 +00:00
}
local smoke_pdef_cached = { }
2020-08-19 17:27:59 +00:00
local spawn_smoke = function ( pos )
2021-03-20 01:17:43 +00:00
local min = math.min
local new_minpos = vector.add ( pos , { x = - 0.45 , y = - 0.45 , z = - 0.45 } )
local new_maxpos = vector.add ( pos , { x = 0.45 , y = 0.45 , z = 0.45 } )
-- populate the cache
if not next ( smoke_pdef_cached ) then
-- the last frame plays for 1/8 * N seconds, so we can take advantage of it
-- to have varying exptime for each variant.
local exptimes = { 0.75 , 1.5 , 4.0 }
local colorizes = { " 199 " , " 209 " , " 243 " } -- round(78%, 82%, 90% of 256) - 1
local id = 1
for _ , exptime in ipairs ( exptimes ) do
for _ , colorize in ipairs ( colorizes ) do
smoke_pdef_base.minpos = new_minpos
smoke_pdef_base.maxpos = new_maxpos
smoke_pdef_base.maxexptime = exptime
smoke_pdef_base.animation . length = exptime + 0.1
-- minexptime must be set such that the last frame is actully rendered,
-- even if its very short. Larger exptime -> larger range
smoke_pdef_base.minexptime = min ( exptime , ( 7.0 / 8.0 * ( exptime + 0.1 ) + 0.1 ) )
smoke_pdef_base.texture = " mcl_particles_smoke_anim.png^[colorize:#000000: " .. colorize
smoke_pdef_cached [ id ] = table.copy ( smoke_pdef_base )
mcl_particles.add_node_particlespawner ( pos , smoke_pdef_cached [ id ] , " high " )
id = id + 1
end
end
-- cache already populated
else
for i , smoke_pdef in ipairs ( smoke_pdef_cached ) do
smoke_pdef.minpos = new_minpos
smoke_pdef.maxpos = new_maxpos
mcl_particles.add_node_particlespawner ( pos , smoke_pdef , " high " )
end
end
2021-03-14 18:57:13 +00:00
--[[ Old smoke pdef
2021-03-20 01:17:43 +00:00
local spawn_smoke = function ( pos )
2020-08-19 17:27:59 +00:00
mcl_particles.add_node_particlespawner ( pos , {
amount = 0.1 ,
time = 0 ,
minpos = vector.add ( pos , { x = - 0.45 , y = - 0.45 , z = - 0.45 } ) ,
maxpos = vector.add ( pos , { x = 0.45 , y = 0.45 , z = 0.45 } ) ,
minvel = { x = 0 , y = 0.5 , z = 0 } ,
maxvel = { x = 0 , y = 0.6 , z = 0 } ,
minexptime = 2.0 ,
maxexptime = 2.0 ,
minsize = 3.0 ,
maxsize = 4.0 ,
texture = " mcl_particles_smoke_anim.png^[colorize:#000000:127 " ,
animation = {
type = " vertical_frames " ,
aspect_w = 8 ,
aspect_h = 8 ,
2020-08-19 18:44:12 +00:00
length = 2.1 ,
2020-08-19 17:27:59 +00:00
} ,
2020-08-19 18:39:05 +00:00
} , " high " )
2021-03-20 01:17:43 +00:00
-- ]]
2021-03-14 18:57:13 +00:00
2020-08-19 17:27:59 +00:00
end
2017-02-19 21:46:22 +00:00
--
-- Items
--
-- Flame nodes
2019-03-15 03:25:54 +00:00
-- Fire settings
-- When enabled, fire destroys other blocks.
2019-04-09 13:25:27 +00:00
local fire_enabled = minetest.settings : get_bool ( " enable_fire " , true )
2019-03-15 03:25:54 +00:00
-- Enable sound
2019-04-09 13:25:27 +00:00
local flame_sound = minetest.settings : get_bool ( " flame_sound " , true )
2019-03-15 03:25:54 +00:00
-- Help texts
2019-04-09 13:25:27 +00:00
local fire_help , eternal_fire_help
2019-03-15 03:25:54 +00:00
if fire_enabled then
fire_help = S ( " Fire is a damaging and destructive but short-lived kind of block. It will destroy and spread towards near flammable blocks, but fire will disappear when there is nothing to burn left. It will be extinguished by nearby water and rain. Fire can be destroyed safely by punching it, but it is hurtful if you stand directly in it. If a fire is started above netherrack or a magma block, it will immediately turn into an eternal fire. " )
else
2019-03-15 03:36:17 +00:00
fire_help = S ( " Fire is a damaging but non-destructive short-lived kind of block. It will disappear when there is no flammable block around. Fire does not destroy blocks, at least not in this world. It will be extinguished by nearby water and rain. Fire can be destroyed safely by punching it, but it is hurtful if you stand directly in it. If a fire is started above netherrack or a magma block, it will immediately turn into an eternal fire. " )
2019-03-15 03:25:54 +00:00
end
2019-04-09 13:25:27 +00:00
if fire_enabled then
eternal_fire_help = S ( " Eternal fire is a damaging block that might create more fire. It will create fire around it when flammable blocks are nearby. Eternal fire can be extinguished by punches and nearby water blocks. Other than (normal) fire, eternal fire does not get extinguished on its own and also continues to burn under rain. Punching eternal fire is safe, but it hurts if you stand inside. " )
else
eternal_fire_help = S ( " Eternal fire is a damaging block. Eternal fire can be extinguished by punches and nearby water blocks. Other than (normal) fire, eternal fire does not get extinguished on its own and also continues to burn under rain. Punching eternal fire is safe, but it hurts if you stand inside. " )
end
2019-03-15 03:25:54 +00:00
2019-03-08 20:59:16 +00:00
local fire_death_messages = {
2019-03-15 06:10:18 +00:00
N ( " @1 has been cooked crisp. " ) ,
N ( " @1 felt the burn. " ) ,
N ( " @1 died in the flames. " ) ,
N ( " @1 died in a fire. " ) ,
2019-03-08 20:59:16 +00:00
}
2017-03-11 19:54:27 +00:00
2020-05-09 15:41:51 +00:00
local fire_timer = function ( pos )
minetest.get_node_timer ( pos ) : start ( math.random ( 3 , 7 ) )
end
2020-05-09 15:53:32 +00:00
local spawn_fire = function ( pos , age )
2021-03-15 00:10:33 +00:00
set_node ( pos , { name = " mcl_fire:fire " , param2 = age } )
2020-05-09 15:53:32 +00:00
minetest.check_single_for_falling ( { x = pos.x , y = pos.y + 1 , z = pos.z } )
end
2017-02-19 21:46:22 +00:00
minetest.register_node ( " mcl_fire:fire " , {
2019-03-07 23:00:09 +00:00
description = S ( " Fire " ) ,
2017-03-11 19:54:27 +00:00
_doc_items_longdesc = fire_help ,
2017-01-05 03:50:26 +00:00
drawtype = " firelike " ,
2017-02-19 21:46:22 +00:00
tiles = {
{
name = " fire_basic_flame_animated.png " ,
animation = {
type = " vertical_frames " ,
aspect_w = 16 ,
aspect_h = 16 ,
length = 1
} ,
} ,
} ,
2015-06-29 17:55:56 +00:00
inventory_image = " fire_basic_flame.png " ,
2017-02-19 21:46:22 +00:00
paramtype = " light " ,
2019-12-14 17:57:17 +00:00
light_source = minetest.LIGHT_MAX ,
2015-06-29 17:55:56 +00:00
walkable = false ,
buildable_to = true ,
2017-02-19 21:46:22 +00:00
sunlight_propagates = true ,
damage_per_second = 1 ,
2019-03-08 20:59:16 +00:00
_mcl_node_death_message = fire_death_messages ,
2021-04-14 13:46:52 +00:00
groups = { fire = 1 , dig_immediate = 3 , not_in_creative_inventory = 1 , dig_by_piston = 1 , destroys_items = 1 , set_on_fire = 8 , fire_damage = 1 } ,
2017-08-21 14:37:21 +00:00
floodable = true ,
on_flood = function ( pos , oldnode , newnode )
2021-03-15 00:10:33 +00:00
if get_item_group ( newnode.name , " water " ) ~= 0 then
2020-04-06 22:55:45 +00:00
minetest.sound_play ( " fire_extinguish_flame " , { pos = pos , gain = 0.25 , max_hear_distance = 16 } , true )
2017-08-21 14:37:21 +00:00
end
end ,
2017-02-19 21:46:22 +00:00
on_timer = function ( pos )
2021-03-15 00:10:33 +00:00
local node = get_node ( pos )
2020-05-09 15:41:51 +00:00
-- Age is a number from 0 to 15 and is increased every timer step.
-- "old" fire is more likely to be extinguished
local age = node.param2
2021-03-15 00:10:33 +00:00
local flammables = find_nodes_in_area ( { x = pos.x - 1 , y = pos.y - 1 , z = pos.z - 1 } , { x = pos.x + 1 , y = pos.y + 4 , z = pos.z + 1 } , { " group:flammable " } )
local below = get_node ( { x = pos.x , y = pos.z - 1 , z = pos.z } )
local below_is_flammable = get_item_group ( below.name , " flammable " ) > 0
2020-05-09 15:41:51 +00:00
-- Extinguish fire
if ( not fire_enabled ) and ( math.random ( 1 , 3 ) == 1 ) then
2021-03-15 00:10:33 +00:00
remove_node ( pos )
2020-05-09 15:41:51 +00:00
return
end
if age == 15 and not below_is_flammable then
2021-03-15 00:10:33 +00:00
remove_node ( pos )
2020-05-09 15:41:51 +00:00
return
elseif age > 3 and # flammables == 0 and not below_is_flammable and math.random ( 1 , 4 ) == 1 then
2021-03-15 00:10:33 +00:00
remove_node ( pos )
2017-02-19 21:46:22 +00:00
return
end
2020-05-09 15:41:51 +00:00
local age_add = 1
-- If fire spread is disabled, we have to skip the "destructive" code
2019-04-09 13:25:27 +00:00
if ( not fire_enabled ) then
2020-05-09 15:41:51 +00:00
if age + age_add <= 15 then
node.param2 = age + age_add
2021-03-15 00:10:33 +00:00
set_node ( pos , node )
2020-05-09 15:41:51 +00:00
end
2019-04-09 13:25:27 +00:00
-- Restart timer
2020-05-09 15:41:51 +00:00
fire_timer ( pos )
2019-04-09 13:25:27 +00:00
return
end
2020-05-09 15:41:51 +00:00
-- Spawn fire to nearby flammable nodes
2021-03-15 00:10:33 +00:00
local is_next_to_flammable = find_node_near ( pos , 2 , { " group:flammable " } ) ~= nil
2020-05-09 15:41:51 +00:00
if is_next_to_flammable and math.random ( 1 , 2 ) == 1 then
-- The fire we spawn copies the age of this fire.
-- This prevents fire from spreading infinitely far as the fire fire dies off
-- quicker the further it has spreaded.
local age_next = math.min ( 15 , age + math.random ( 0 , 1 ) )
-- Select random type of fire spread
local burntype = math.random ( 1 , 2 )
if burntype == 1 then
-- Spawn fire in air
2021-03-15 00:10:33 +00:00
local nodes = find_nodes_in_area ( { x = pos.x - 1 , y = pos.y - 1 , z = pos.z - 1 } , { x = pos.x + 1 , y = pos.y + 4 , z = pos.z + 1 } , { " air " } )
2020-05-09 15:41:51 +00:00
while # nodes > 0 do
local r = math.random ( 1 , # nodes )
2021-03-15 00:10:33 +00:00
if find_node_near ( nodes [ r ] , 1 , { " group:flammable " } ) then
2020-05-09 15:53:32 +00:00
spawn_fire ( nodes [ r ] , age_next )
2020-05-09 15:41:51 +00:00
break
else
table.remove ( nodes , r )
end
end
else
2020-05-09 16:52:03 +00:00
-- Burn flammable block
2021-03-15 00:10:33 +00:00
local nodes = find_nodes_in_area ( { x = pos.x - 1 , y = pos.y - 1 , z = pos.z - 1 } , { x = pos.x + 1 , y = pos.y + 4 , z = pos.z + 1 } , { " group:flammable " } )
2020-05-09 15:41:51 +00:00
if # nodes > 0 then
local r = math.random ( 1 , # nodes )
2021-03-15 00:10:33 +00:00
local nn = get_node ( nodes [ r ] ) . name
2020-05-09 16:55:50 +00:00
local ndef = minetest.registered_nodes [ nn ]
2021-03-15 00:10:33 +00:00
local fgroup = get_item_group ( nn , " flammable " )
2020-05-09 16:52:03 +00:00
if ndef and ndef._on_burn then
ndef._on_burn ( nodes [ r ] )
2020-05-09 16:55:50 +00:00
elseif fgroup ~= - 1 then
2020-05-09 16:52:03 +00:00
spawn_fire ( nodes [ r ] , age_next )
end
2017-05-20 00:45:37 +00:00
end
end
end
2020-05-09 15:41:51 +00:00
-- Regular age increase
if age + age_add <= 15 then
node.param2 = age + age_add
2021-03-15 00:10:33 +00:00
set_node ( pos , node )
2017-05-20 00:45:37 +00:00
end
2017-02-19 21:46:22 +00:00
-- Restart timer
2020-05-09 15:41:51 +00:00
fire_timer ( pos )
2017-02-19 21:46:22 +00:00
end ,
drop = " " ,
2017-02-19 21:54:06 +00:00
sounds = { } ,
2017-09-19 13:45:23 +00:00
-- Turn into eternal fire on special blocks, light Nether portal (if possible), start burning timer
2017-02-19 21:46:22 +00:00
on_construct = function ( pos )
2017-11-11 18:52:11 +00:00
local bpos = { x = pos.x , y = pos.y - 1 , z = pos.z }
2021-03-15 00:10:33 +00:00
local under = get_node ( bpos ) . name
2017-09-19 13:45:23 +00:00
2017-11-24 02:10:02 +00:00
local dim = mcl_worlds.pos_to_dimension ( bpos )
2017-11-11 18:52:11 +00:00
if under == " mcl_nether:magma " or under == " mcl_nether:netherrack " or ( under == " mcl_core:bedrock " and dim == " end " ) then
2021-03-15 00:10:33 +00:00
swap_node ( pos , { name = " mcl_fire:eternal_fire " } )
2017-07-26 20:00:19 +00:00
end
2017-09-19 13:45:23 +00:00
2021-03-15 00:10:33 +00:00
if has_mcl_portals then
2017-09-19 13:45:23 +00:00
mcl_portals.light_nether_portal ( pos )
end
2020-05-09 15:41:51 +00:00
fire_timer ( pos )
2020-08-19 17:27:59 +00:00
spawn_smoke ( pos )
end ,
on_destruct = function ( pos )
mcl_particles.delete_node_particlespawners ( pos )
2015-06-29 17:55:56 +00:00
end ,
2017-02-22 15:03:59 +00:00
_mcl_blast_resistance = 0 ,
2015-06-29 17:55:56 +00:00
} )
2017-02-19 21:46:22 +00:00
minetest.register_node ( " mcl_fire:eternal_fire " , {
2019-03-07 23:00:09 +00:00
description = S ( " Eternal Fire " ) ,
2017-03-11 19:54:27 +00:00
_doc_items_longdesc = eternal_fire_help ,
2017-02-19 21:46:22 +00:00
drawtype = " firelike " ,
tiles = {
{
name = " fire_basic_flame_animated.png " ,
animation = {
type = " vertical_frames " ,
aspect_w = 16 ,
aspect_h = 16 ,
length = 1
} ,
} ,
} ,
inventory_image = " fire_basic_flame.png " ,
paramtype = " light " ,
2019-12-14 17:57:17 +00:00
light_source = minetest.LIGHT_MAX ,
2017-02-19 21:46:22 +00:00
walkable = false ,
buildable_to = true ,
sunlight_propagates = true ,
damage_per_second = 1 ,
2019-03-08 20:59:16 +00:00
_mcl_node_death_message = fire_death_messages ,
2021-04-14 13:46:52 +00:00
groups = { fire = 1 , dig_immediate = 3 , not_in_creative_inventory = 1 , dig_by_piston = 1 , destroys_items = 1 , set_on_fire = 8 , fire_damage = 1 } ,
2017-08-21 14:37:21 +00:00
floodable = true ,
on_flood = function ( pos , oldnode , newnode )
2021-03-15 00:10:33 +00:00
if get_item_group ( newnode.name , " water " ) ~= 0 then
2020-04-06 22:55:45 +00:00
minetest.sound_play ( " fire_extinguish_flame " , { pos = pos , gain = 0.25 , max_hear_distance = 16 } , true )
2017-08-21 14:37:21 +00:00
end
end ,
2017-05-20 00:45:37 +00:00
on_timer = function ( pos )
2019-04-09 13:25:27 +00:00
if fire_enabled then
2021-03-15 00:10:33 +00:00
local airs = find_nodes_in_area ( { x = pos.x - 1 , y = pos.y - 1 , z = pos.z - 1 } , { x = pos.x + 1 , y = pos.y + 4 , z = pos.z + 1 } , { " air " } )
2019-04-09 13:25:27 +00:00
while # airs > 0 do
local r = math.random ( 1 , # airs )
2021-03-15 00:10:33 +00:00
if find_node_near ( airs [ r ] , 1 , { " group:flammable " } ) then
local node = get_node ( airs [ r ] )
2020-08-05 16:04:37 +00:00
local age = node.param2
local age_next = math.min ( 15 , age + math.random ( 0 , 1 ) )
2020-05-09 15:53:32 +00:00
spawn_fire ( airs [ r ] , age_next )
2019-04-09 13:25:27 +00:00
break
else
table.remove ( airs , r )
end
2017-05-20 00:45:37 +00:00
end
end
-- Restart timer
2020-05-09 15:41:51 +00:00
fire_timer ( pos )
2017-05-20 00:45:37 +00:00
end ,
2017-09-19 13:45:23 +00:00
-- Start burning timer and light Nether portal (if possible)
2017-05-20 00:45:37 +00:00
on_construct = function ( pos )
2020-05-09 15:41:51 +00:00
fire_timer ( pos )
2017-09-19 13:45:23 +00:00
2021-03-15 00:10:33 +00:00
if has_mcl_portals then --Calling directly minetest.get_modpath consumes 4x more compute time
2017-09-19 13:45:23 +00:00
mcl_portals.light_nether_portal ( pos )
end
2020-08-19 17:27:59 +00:00
spawn_smoke ( pos )
end ,
on_destruct = function ( pos )
mcl_particles.delete_node_particlespawners ( pos )
2017-05-20 00:45:37 +00:00
end ,
2017-02-19 21:54:06 +00:00
sounds = { } ,
2017-02-19 21:46:22 +00:00
drop = " " ,
2017-02-22 15:03:59 +00:00
_mcl_blast_resistance = 0 ,
2017-02-19 21:46:22 +00:00
} )
--
-- Sound
--
if flame_sound then
local handles = { }
local timer = 0
-- Parameters
local radius = 8 -- Flame node search radius around player
local cycle = 3 -- Cycle time for sound updates
-- Update sound for player
function mcl_fire . update_player_sound ( player )
local player_name = player : get_player_name ( )
-- Search for flame nodes in radius around player
2019-02-01 05:33:07 +00:00
local ppos = player : get_pos ( )
2017-02-19 21:46:22 +00:00
local areamin = vector.subtract ( ppos , radius )
local areamax = vector.add ( ppos , radius )
2021-03-15 00:10:33 +00:00
local fpos , num = find_nodes_in_area (
2017-02-19 21:46:22 +00:00
areamin ,
areamax ,
{ " mcl_fire:fire " , " mcl_fire:eternal_fire " }
)
-- Total number of flames in radius
local flames = ( num [ " mcl_fire:fire " ] or 0 ) +
( num [ " mcl_fire:eternal_fire " ] or 0 )
-- Stop previous sound
if handles [ player_name ] then
2019-09-04 22:07:32 +00:00
minetest.sound_fade ( handles [ player_name ] , - 0.4 , 0.0 )
2017-02-19 21:46:22 +00:00
handles [ player_name ] = nil
2015-06-29 17:55:56 +00:00
end
2017-02-19 21:46:22 +00:00
-- If flames
if flames > 0 then
-- Find centre of flame positions
local fposmid = fpos [ 1 ]
-- If more than 1 flame
if # fpos > 1 then
local fposmin = areamax
local fposmax = areamin
for i = 1 , # fpos do
local fposi = fpos [ i ]
if fposi.x > fposmax.x then
fposmax.x = fposi.x
end
if fposi.y > fposmax.y then
fposmax.y = fposi.y
end
if fposi.z > fposmax.z then
fposmax.z = fposi.z
end
if fposi.x < fposmin.x then
fposmin.x = fposi.x
end
if fposi.y < fposmin.y then
fposmin.y = fposi.y
end
if fposi.z < fposmin.z then
fposmin.z = fposi.z
end
end
fposmid = vector.divide ( vector.add ( fposmin , fposmax ) , 2 )
end
-- Play sound
local handle = minetest.sound_play (
" fire_fire " ,
{
pos = fposmid ,
to_player = player_name ,
gain = math.min ( 0.06 * ( 1 + flames * 0.125 ) , 0.18 ) ,
max_hear_distance = 32 ,
loop = true , -- In case of lag
}
)
-- Store sound handle for this player
if handle then
handles [ player_name ] = handle
end
2015-06-29 17:55:56 +00:00
end
end
2017-02-19 21:46:22 +00:00
-- Cycle for updating players sounds
2015-06-29 17:55:56 +00:00
2017-02-19 21:46:22 +00:00
minetest.register_globalstep ( function ( dtime )
timer = timer + dtime
if timer < cycle then
return
end
2015-06-29 17:55:56 +00:00
2017-02-19 21:46:22 +00:00
timer = 0
2021-03-15 00:10:33 +00:00
local players = get_connected_players ( )
2017-02-19 21:46:22 +00:00
for n = 1 , # players do
mcl_fire.update_player_sound ( players [ n ] )
end
end )
-- Stop sound and clear handle on player leave
2015-06-29 17:55:56 +00:00
2017-02-19 21:46:22 +00:00
minetest.register_on_leaveplayer ( function ( player )
local player_name = player : get_player_name ( )
if handles [ player_name ] then
minetest.sound_stop ( handles [ player_name ] )
handles [ player_name ] = nil
end
end )
2015-06-29 17:55:56 +00:00
end
2017-02-19 21:46:22 +00:00
--
-- ABMs
--
2017-05-20 00:45:37 +00:00
-- Extinguish all flames quickly with water and such
2017-02-19 21:46:22 +00:00
2015-06-29 17:55:56 +00:00
minetest.register_abm ( {
2020-04-21 23:31:30 +00:00
label = " Extinguish fire " ,
2017-02-19 21:46:22 +00:00
nodenames = { " mcl_fire:fire " , " mcl_fire:eternal_fire " } ,
neighbors = { " group:puts_out_fire " } ,
interval = 3 ,
chance = 1 ,
catch_up = false ,
action = function ( pos , node , active_object_count , active_object_count_wider )
2021-03-15 00:10:33 +00:00
remove_node ( pos )
2017-02-19 21:46:22 +00:00
minetest.sound_play ( " fire_extinguish_flame " ,
2020-04-06 22:55:45 +00:00
{ pos = pos , max_hear_distance = 16 , gain = 0.15 } , true )
2015-06-29 17:55:56 +00:00
end ,
} )
2017-02-19 21:46:22 +00:00
-- Enable the following ABMs according to 'enable fire' setting
2020-12-14 20:27:20 +00:00
local function has_flammable ( pos )
local npos , node
for n , v in ipairs ( alldirs ) do
npos = vector.add ( pos , v )
2021-03-15 00:10:33 +00:00
node = get_node_or_nil ( npos )
if node and node.name and get_item_group ( node.name , " flammable " ) ~= 0 then
2020-12-14 20:27:20 +00:00
return npos
end
end
return false
end
2017-02-19 21:46:22 +00:00
if not fire_enabled then
2019-04-09 13:25:27 +00:00
-- Occasionally remove fire if fire disabled
-- NOTE: Fire is normally extinguished in timer function
2017-02-19 21:46:22 +00:00
minetest.register_abm ( {
label = " Remove disabled fire " ,
2017-05-20 01:24:36 +00:00
nodenames = { " mcl_fire:fire " } ,
2019-04-09 13:25:27 +00:00
interval = 10 ,
chance = 10 ,
2017-02-19 21:46:22 +00:00
catch_up = false ,
2021-03-15 00:10:33 +00:00
action = remove_node ,
2017-02-19 21:46:22 +00:00
} )
2019-04-09 13:25:27 +00:00
else -- Fire enabled
2020-12-14 20:27:20 +00:00
-- Set fire to air nodes
2017-05-20 01:24:36 +00:00
minetest.register_abm ( {
label = " Ignite fire by lava " ,
2019-04-09 13:25:27 +00:00
nodenames = { " group:lava " } ,
2020-12-14 20:27:20 +00:00
neighbors = { " air " } ,
2017-05-20 01:24:36 +00:00
interval = 7 ,
2020-12-14 20:27:20 +00:00
chance = 3 ,
2017-05-20 01:24:36 +00:00
catch_up = false ,
action = function ( pos )
2020-12-14 20:27:20 +00:00
local i , dir , target , node , i2 , f
i = math.random ( 1 , 9 )
dir = lava_fire [ i ]
target = { x = pos.x + dir.x , y = pos.y + dir.y , z = pos.z + dir.z }
2021-03-15 00:10:33 +00:00
node = get_node ( target )
2020-12-14 20:27:20 +00:00
if not node or node.name ~= " air " then
i = ( ( i + math.random ( 0 , 7 ) ) % 9 ) + 1
dir = lava_fire [ i ]
target = { x = pos.x + dir.x , y = pos.y + dir.y , z = pos.z + dir.z }
2021-03-15 00:10:33 +00:00
node = get_node ( target )
2020-12-14 20:27:20 +00:00
if not node or node.name ~= " air " then
return
end
2019-04-09 13:25:27 +00:00
end
2020-12-14 20:27:20 +00:00
i2 = math.random ( 1 , 15 )
if i2 < 10 then
local dir2 , target2 , node2
dir2 = lava_fire [ i2 ]
target2 = { x = target.x + dir2.x , y = target.y + dir2.y , z = target.z + dir2.z }
2021-03-15 00:10:33 +00:00
node2 = get_node ( target2 )
2020-12-14 20:27:20 +00:00
if node2 and node2.name == " air " then
f = has_flammable ( target2 )
if f then
minetest.after ( 1 , spawn_fire , { x = target2.x , y = target2.y , z = target2.z } )
minetest.add_particle ( {
pos = vector.new ( { x = pos.x , y = pos.y + 0.5 , z = pos.z } ) ,
velocity = { x = f.x - pos.x , y = math.max ( f.y - pos.y , 0.7 ) , z = f.z - pos.z } ,
expirationtime = 1 , size = 1.5 , collisiondetection = false ,
glow = minetest.LIGHT_MAX , texture = " mcl_particles_flame.png "
} )
return
2017-05-20 01:24:36 +00:00
end
end
end
2020-12-14 20:27:20 +00:00
f = has_flammable ( target )
if f then
minetest.after ( 1 , spawn_fire , { x = target.x , y = target.y , z = target.z } )
minetest.add_particle ( {
pos = vector.new ( { x = pos.x , y = pos.y + 0.5 , z = pos.z } ) ,
velocity = { x = f.x - pos.x , y = math.max ( f.y - pos.y , 0.25 ) , z = f.z - pos.z } ,
expirationtime = 1 , size = 1 , collisiondetection = false ,
glow = minetest.LIGHT_MAX , texture = " mcl_particles_flame.png "
} )
2017-05-20 01:24:36 +00:00
end
end ,
} )
2017-02-19 21:46:22 +00:00
end
2017-02-01 15:43:05 +00:00
2019-02-11 20:27:17 +00:00
-- Set pointed_thing on (normal) fire.
-- * pointed_thing: Pointed thing to ignite
-- * player: Player who sets fire or nil if nobody
2020-03-24 18:53:08 +00:00
-- * allow_on_fire: If false, can't ignite fire on fire (default: true)
mcl_fire.set_fire = function ( pointed_thing , player , allow_on_fire )
2019-02-11 20:27:17 +00:00
local pname
if player == nil then
pname = " "
else
pname = player : get_player_name ( )
end
2021-03-15 00:10:33 +00:00
local n = get_node ( pointed_thing.above )
local nu = get_node ( pointed_thing.under )
if allow_on_fire == false and get_item_group ( nu.name , " fire " ) ~= 0 then
2020-03-24 18:53:08 +00:00
return
end
2019-02-11 20:27:17 +00:00
if minetest.is_protected ( pointed_thing.above , pname ) then
minetest.record_protection_violation ( pointed_thing.above , pname )
return
end
if n.name == " air " then
2021-03-15 00:10:33 +00:00
add_node ( pointed_thing.above , { name = " mcl_fire:fire " } )
2017-02-01 15:43:05 +00:00
end
end
2020-08-19 17:27:59 +00:00
minetest.register_lbm ( {
label = " Smoke particles from fire " ,
name = " mcl_fire:smoke " ,
nodenames = { " group:fire " } ,
run_at_every_load = true ,
action = function ( pos , node )
spawn_smoke ( pos )
end ,
} )
2017-02-19 21:46:22 +00:00
minetest.register_alias ( " mcl_fire:basic_flame " , " mcl_fire:fire " )
minetest.register_alias ( " fire:basic_flame " , " mcl_fire:fire " )
2020-09-19 23:38:31 +00:00
minetest.register_alias ( " fire:permanent_flame " , " mcl_fire:eternal_fire " )
2017-02-01 15:43:05 +00:00
2021-03-15 00:10:33 +00:00
dofile ( modpath .. " /flint_and_steel.lua " )
dofile ( modpath .. " /fire_charge.lua " )