2017-02-19 23:46:22 +02:00
-- Global namespace for functions
mcl_fire = { }
2019-03-08 01:00:09 +02:00
local S = minetest.get_translator ( " mcl_fire " )
2017-02-19 23:46:22 +02:00
--
-- Items
--
-- Flame nodes
2019-03-08 01:00:09 +02:00
local 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. " )
local eternal_fire_help = S ( " Eternal fire is a damaging and destructive block. 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. " )
2019-03-08 22:59:16 +02:00
local fire_death_messages = {
S ( " %s has been cooked crisp. " ) ,
S ( " %s felt the burn. " ) ,
S ( " %s died in the flames. " ) ,
S ( " %s died in a fire. " ) ,
}
2017-03-11 21:54:27 +02:00
2017-02-19 23:46:22 +02:00
minetest.register_node ( " mcl_fire:fire " , {
2019-03-08 01:00:09 +02:00
description = S ( " Fire " ) ,
2017-03-11 21:54:27 +02:00
_doc_items_longdesc = fire_help ,
2017-01-05 05:50:26 +02:00
drawtype = " firelike " ,
2017-02-19 23:46:22 +02:00
tiles = {
{
name = " fire_basic_flame_animated.png " ,
animation = {
type = " vertical_frames " ,
aspect_w = 16 ,
aspect_h = 16 ,
length = 1
} ,
} ,
} ,
2015-06-29 20:55:56 +03:00
inventory_image = " fire_basic_flame.png " ,
2017-02-19 23:46:22 +02:00
paramtype = " light " ,
2017-02-01 23:12:08 +02:00
-- Real light level: 15 (but Minetest caps at 14)
2015-06-29 20:55:56 +03:00
light_source = 14 ,
walkable = false ,
buildable_to = true ,
2017-02-19 23:46:22 +02:00
sunlight_propagates = true ,
damage_per_second = 1 ,
2019-03-08 22:59:16 +02:00
_mcl_node_death_message = fire_death_messages ,
2019-02-01 09:31:17 +02:00
groups = { fire = 1 , dig_immediate = 3 , not_in_creative_inventory = 1 , dig_by_piston = 1 , destroys_items = 1 } ,
2017-08-21 17:37:21 +03:00
floodable = true ,
on_flood = function ( pos , oldnode , newnode )
if minetest.get_item_group ( newnode.name , " water " ) ~= 0 then
minetest.sound_play ( " fire_extinguish_flame " , { pos = pos , gain = 0.25 , max_hear_distance = 16 } )
end
end ,
2017-02-19 23:46:22 +02:00
on_timer = function ( pos )
2017-05-20 03:45:37 +03:00
local airs = minetest.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 " } )
if # airs == 0 then
2017-02-19 23:46:22 +02:00
minetest.remove_node ( pos )
return
end
2017-05-20 03:45:37 +03:00
local burned = false
if math.random ( 1 , 2 ) == 1 then
while # airs > 0 do
local r = math.random ( 1 , # airs )
if minetest.find_node_near ( airs [ r ] , 1 , { " group:flammable " } ) then
minetest.set_node ( airs [ r ] , { name = " mcl_fire:fire " } )
burned = true
break
else
table.remove ( airs , r )
end
end
end
if not burned then
if math.random ( 1 , 3 ) == 1 then
minetest.remove_node ( pos )
return
end
end
2017-02-19 23:46:22 +02:00
-- Restart timer
2017-05-20 03:45:37 +03:00
minetest.get_node_timer ( pos ) : start ( math.random ( 3 , 7 ) )
2017-02-19 23:46:22 +02:00
end ,
drop = " " ,
2017-02-19 23:54:06 +02:00
sounds = { } ,
2017-09-19 16:45:23 +03:00
-- Turn into eternal fire on special blocks, light Nether portal (if possible), start burning timer
2017-02-19 23:46:22 +02:00
on_construct = function ( pos )
2017-11-11 20:52:11 +02:00
local bpos = { x = pos.x , y = pos.y - 1 , z = pos.z }
local under = minetest.get_node ( bpos ) . name
2017-09-19 16:45:23 +03:00
2017-11-24 04:10:02 +02:00
local dim = mcl_worlds.pos_to_dimension ( bpos )
2017-11-11 20:52:11 +02:00
if under == " mcl_nether:magma " or under == " mcl_nether:netherrack " or ( under == " mcl_core:bedrock " and dim == " end " ) then
2017-07-26 23:00:19 +03:00
minetest.swap_node ( pos , { name = " mcl_fire:eternal_fire " } )
end
2017-09-19 16:45:23 +03:00
if minetest.get_modpath ( " mcl_portals " ) then
mcl_portals.light_nether_portal ( pos )
end
2017-05-20 03:45:37 +03:00
minetest.get_node_timer ( pos ) : start ( math.random ( 3 , 7 ) )
2015-06-29 20:55:56 +03:00
end ,
2017-02-22 17:03:59 +02:00
_mcl_blast_resistance = 0 ,
2015-06-29 20:55:56 +03:00
} )
2017-02-19 23:46:22 +02:00
minetest.register_node ( " mcl_fire:eternal_fire " , {
2019-03-08 01:00:09 +02:00
description = S ( " Eternal Fire " ) ,
2017-03-11 21:54:27 +02:00
_doc_items_longdesc = eternal_fire_help ,
2017-02-19 23:46:22 +02: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 " ,
-- Real light level: 15 (but Minetest caps at 14)
light_source = 14 ,
walkable = false ,
buildable_to = true ,
sunlight_propagates = true ,
damage_per_second = 1 ,
2019-03-08 22:59:16 +02:00
_mcl_node_death_message = fire_death_messages ,
2019-02-01 09:31:17 +02:00
groups = { fire = 1 , dig_immediate = 3 , not_in_creative_inventory = 1 , dig_by_piston = 1 , destroys_items = 1 } ,
2017-08-21 17:37:21 +03:00
floodable = true ,
on_flood = function ( pos , oldnode , newnode )
if minetest.get_item_group ( newnode.name , " water " ) ~= 0 then
minetest.sound_play ( " fire_extinguish_flame " , { pos = pos , gain = 0.25 , max_hear_distance = 16 } )
end
end ,
2017-05-20 03:45:37 +03:00
on_timer = function ( pos )
local airs = minetest.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 " } )
while # airs > 0 do
local r = math.random ( 1 , # airs )
if minetest.find_node_near ( airs [ r ] , 1 , { " group:flammable " } ) then
minetest.set_node ( airs [ r ] , { name = " mcl_fire:fire " } )
break
else
table.remove ( airs , r )
end
end
-- Restart timer
minetest.get_node_timer ( pos ) : start ( math.random ( 3 , 7 ) )
end ,
2017-09-19 16:45:23 +03:00
-- Start burning timer and light Nether portal (if possible)
2017-05-20 03:45:37 +03:00
on_construct = function ( pos )
minetest.get_node_timer ( pos ) : start ( math.random ( 3 , 7 ) )
2017-09-19 16:45:23 +03:00
if minetest.get_modpath ( " mcl_portals " ) then
mcl_portals.light_nether_portal ( pos )
end
2017-05-20 03:45:37 +03:00
end ,
2017-02-19 23:54:06 +02:00
sounds = { } ,
2017-02-19 23:46:22 +02:00
drop = " " ,
2017-02-22 17:03:59 +02:00
_mcl_blast_resistance = 0 ,
2017-02-19 23:46:22 +02:00
} )
2017-05-20 04:24:36 +03:00
-- Also make lava set fire to air blocks above
minetest.override_item ( " mcl_core:lava_source " , {
on_timer = function ( pos )
local function try_ignite ( airs )
while # airs > 0 do
local r = math.random ( 1 , # airs )
if minetest.find_node_near ( airs [ r ] , 1 , { " group:flammable " , " group:flammable_lava " } ) then
minetest.set_node ( airs [ r ] , { name = " mcl_fire:fire " } )
return true
else
table.remove ( airs , r )
end
end
return false
end
local airs1 = minetest.find_nodes_in_area ( { x = pos.x - 1 , y = pos.y + 1 , z = pos.z - 1 } , { x = pos.x + 1 , y = pos.y + 1 , z = pos.z + 1 } , { " air " } )
local ok = try_ignite ( airs1 )
if not ok then
local airs2 = minetest.find_nodes_in_area ( { x = pos.x - 2 , y = pos.y + 2 , z = pos.z - 2 } , { x = pos.x + 2 , y = pos.y + 2 , z = pos.z + 2 } , { " air " } )
try_ignite ( airs2 )
end
-- Restart timer
minetest.get_node_timer ( pos ) : start ( math.random ( 5 , 10 ) )
end ,
on_construct = function ( pos )
minetest.get_node_timer ( pos ) : start ( math.random ( 5 , 10 ) )
end
} )
2017-02-19 23:46:22 +02:00
--
-- Sound
--
2017-08-09 17:17:00 +03:00
local flame_sound = minetest.settings : get_bool ( " flame_sound " )
2017-02-19 23:46:22 +02:00
if flame_sound == nil then
-- Enable if no setting present
flame_sound = true
2015-06-29 20:55:56 +03:00
end
2017-02-19 23:46:22 +02:00
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 07:33:07 +02:00
local ppos = player : get_pos ( )
2017-02-19 23:46:22 +02:00
local areamin = vector.subtract ( ppos , radius )
local areamax = vector.add ( ppos , radius )
local fpos , num = minetest.find_nodes_in_area (
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
minetest.sound_stop ( handles [ player_name ] )
handles [ player_name ] = nil
2015-06-29 20:55:56 +03:00
end
2017-02-19 23:46:22 +02: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 20:55:56 +03:00
end
end
2017-02-19 23:46:22 +02:00
-- Cycle for updating players sounds
2015-06-29 20:55:56 +03:00
2017-02-19 23:46:22 +02:00
minetest.register_globalstep ( function ( dtime )
timer = timer + dtime
if timer < cycle then
return
end
2015-06-29 20:55:56 +03:00
2017-02-19 23:46:22 +02:00
timer = 0
local players = minetest.get_connected_players ( )
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 20:55:56 +03:00
2017-02-19 23:46:22 +02: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 20:55:56 +03:00
end
2017-02-19 23:46:22 +02:00
--
-- ABMs
--
2017-05-20 03:45:37 +03:00
-- Extinguish all flames quickly with water and such
2017-02-19 23:46:22 +02:00
2015-06-29 20:55:56 +03:00
minetest.register_abm ( {
2017-02-19 23:46:22 +02:00
label = " Extinguish flame " ,
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 )
minetest.remove_node ( pos )
minetest.sound_play ( " fire_extinguish_flame " ,
{ pos = pos , max_hear_distance = 16 , gain = 0.15 } )
2015-06-29 20:55:56 +03:00
end ,
} )
2017-02-19 23:46:22 +02:00
-- Enable the following ABMs according to 'enable fire' setting
2017-08-09 17:17:00 +03:00
local fire_enabled = minetest.settings : get_bool ( " enable_fire " )
2017-02-19 23:46:22 +02:00
if fire_enabled == nil then
-- New setting not specified, check for old setting.
-- If old setting is also not specified, 'not nil' is true.
2017-08-09 17:17:00 +03:00
fire_enabled = not minetest.settings : get_bool ( " disable_fire " )
2017-02-19 23:46:22 +02:00
end
if not fire_enabled then
2017-05-20 04:24:36 +03:00
-- Remove fire only if fire disabled
2017-02-19 23:46:22 +02:00
minetest.register_abm ( {
label = " Remove disabled fire " ,
2017-05-20 04:24:36 +03:00
nodenames = { " mcl_fire:fire " } ,
2017-02-19 23:46:22 +02:00
interval = 7 ,
chance = 1 ,
catch_up = false ,
action = minetest.remove_node ,
} )
2017-05-20 04:24:36 +03:00
-- Set fire to air nodes (inverse pyramid pattern) above lava source
minetest.register_abm ( {
label = " Ignite fire by lava " ,
nodenames = { " mcl_core:lava_source " } ,
interval = 7 ,
chance = 2 ,
catch_up = false ,
action = function ( pos )
2018-01-26 23:20:15 +02:00
local function try_ignite ( airs )
2017-05-20 04:24:36 +03:00
while # airs > 0 do
local r = math.random ( 1 , # airs )
if minetest.find_node_near ( airs [ r ] , 1 , { " group:flammable " , " group:flammable_lava " } ) then
minetest.set_node ( airs [ r ] , { name = " mcl_fire:fire " } )
return true
else
table.remove ( airs , r )
end
end
return false
end
local airs1 = minetest.find_nodes_in_area ( { x = pos.x - 1 , y = pos.y + 1 , z = pos.z - 1 } , { x = pos.x + 1 , y = pos.y + 1 , z = pos.z + 1 } , { " air " } )
local ok = try_ignite ( airs1 )
if not ok then
local airs2 = minetest.find_nodes_in_area ( { x = pos.x - 2 , y = pos.y + 2 , z = pos.z - 2 } , { x = pos.x + 2 , y = pos.y + 2 , z = pos.z + 2 } , { " air " } )
try_ignite ( airs2 )
end
end ,
} )
2017-02-19 23:46:22 +02:00
else -- Fire enabled
2017-05-20 03:45:37 +03:00
-- Turn flammable nodes around fire into fire
2017-02-19 23:46:22 +02:00
minetest.register_abm ( {
label = " Remove flammable nodes " ,
2017-05-20 03:45:37 +03:00
nodenames = { " group:fire " } ,
neighbors = { " group:flammable " } ,
2017-02-19 23:46:22 +02:00
interval = 5 ,
chance = 18 ,
catch_up = false ,
action = function ( pos , node , active_object_count , active_object_count_wider )
local p = minetest.find_node_near ( pos , 1 , { " group:flammable " } )
2015-06-29 20:55:56 +03:00
if p then
2017-02-19 23:46:22 +02:00
local flammable_node = minetest.get_node ( p )
local def = minetest.registered_nodes [ flammable_node.name ]
if def.on_burn then
def.on_burn ( p )
else
2017-05-20 03:45:37 +03:00
minetest.set_node ( p , { name = " mcl_fire:fire " } )
2017-02-19 23:46:22 +02:00
minetest.check_for_falling ( p )
2015-06-29 20:55:56 +03:00
end
end
2017-02-19 23:46:22 +02:00
end ,
} )
2015-06-29 20:55:56 +03:00
2017-02-19 23:46:22 +02:00
end
2017-02-01 17:43:05 +02:00
2019-02-11 22:27:17 +02:00
-- Set pointed_thing on (normal) fire.
-- * pointed_thing: Pointed thing to ignite
-- * player: Player who sets fire or nil if nobody
mcl_fire.set_fire = function ( pointed_thing , player )
local pname
if player == nil then
pname = " "
else
pname = player : get_player_name ( )
end
2017-02-01 17:43:05 +02:00
local n = minetest.get_node ( pointed_thing.above )
2019-02-11 22:27:17 +02: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
2017-02-19 23:46:22 +02:00
minetest.add_node ( pointed_thing.above , { name = " mcl_fire:fire " } )
2017-02-01 17:43:05 +02:00
end
end
2017-02-19 23:46:22 +02:00
minetest.register_alias ( " mcl_fire:basic_flame " , " mcl_fire:fire " )
minetest.register_alias ( " fire:basic_flame " , " mcl_fire:fire " )
minetest.register_alias ( " fire:permanent_flame " , " mcl_fire:eternal_flame " )
2017-02-01 17:43:05 +02:00
2017-02-01 17:31:27 +02:00
dofile ( minetest.get_modpath ( minetest.get_current_modname ( ) ) .. " /flint_and_steel.lua " )
2017-02-01 17:39:51 +02:00
dofile ( minetest.get_modpath ( minetest.get_current_modname ( ) ) .. " /fire_charge.lua " )