diff --git a/mods/ITEMS/mcl_beds/api.lua b/mods/ITEMS/mcl_beds/api.lua index c274a29a..f72b20be 100644 --- a/mods/ITEMS/mcl_beds/api.lua +++ b/mods/ITEMS/mcl_beds/api.lua @@ -81,7 +81,7 @@ function mcl_beds.register_bed(name, def) paramtype2 = "facedir", is_ground_content = false, stack_max = 1, - groups = {handy=1, flammable = 3, bed = 1, dig_by_piston=1, bouncy=66, fall_damage_add_percent=-50, deco_block = 1, flammable=-1}, + groups = {handy=1, bed = 1, dig_by_piston=1, bouncy=66, fall_damage_add_percent=-50, deco_block = 1, flammable=-1}, _mcl_hardness = 0.2, _mcl_blast_resistance = 1, sounds = def.sounds or default_sounds, @@ -204,7 +204,7 @@ function mcl_beds.register_bed(name, def) paramtype2 = "facedir", is_ground_content = false, -- FIXME: Should be bouncy=66, but this would be a higher bounciness than slime blocks! - groups = {handy = 1, flammable = 3, bed = 2, dig_by_piston=1, bouncy=33, fall_damage_add_percent=-50, not_in_creative_inventory = 1}, + groups = {handy = 1, flammable = -1, bed = 2, dig_by_piston=1, bouncy=33, fall_damage_add_percent=-50, not_in_creative_inventory = 1}, _mcl_hardness = 0.2, _mcl_blast_resistance = 1, sounds = def.sounds or default_sounds, diff --git a/mods/ITEMS/mcl_fire/init.lua b/mods/ITEMS/mcl_fire/init.lua index 50303e3b..2eed0843 100644 --- a/mods/ITEMS/mcl_fire/init.lua +++ b/mods/ITEMS/mcl_fire/init.lua @@ -5,29 +5,6 @@ mcl_fire = {} local S = minetest.get_translator("mcl_fire") local N = function(s) return s end --- 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} -} - local spawn_smoke = function(pos) mcl_particles.add_node_particlespawner(pos, { amount = 0.1, @@ -50,6 +27,34 @@ local spawn_smoke = function(pos) }, "high") end +local adjacents = { + { 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 }, +} + +math.randomseed(os.time()) +local function shuffle_table(t) + for i = #t, 1, -1 do + local r = math.random(i) + t[i], t[r] = t[r], t[i] + end +end +shuffle_table(adjacents) + +local function has_flammable(pos) + for k,v in pairs(adjacents) do + local p=vector.add(pos,v) + local n=minetest.get_node_or_nil(p) + if n and minetest.get_item_group(n.name, "flammable") ~= 0 then + return p + end + end +end + -- -- Items -- @@ -85,10 +90,6 @@ local fire_death_messages = { N("@1 died in a fire."), } -local fire_timer = function(pos) - minetest.get_node_timer(pos):start(math.random(3, 7)) -end - local spawn_fire = function(pos, age) minetest.set_node(pos, {name="mcl_fire:fire", param2 = age}) minetest.check_single_for_falling({x=pos.x, y=pos.y+1, z=pos.z}) @@ -124,82 +125,6 @@ minetest.register_node("mcl_fire:fire", { minetest.sound_play("fire_extinguish_flame", {pos = pos, gain = 0.25, max_hear_distance = 16}, true) end end, - on_timer = function(pos) - local node = minetest.get_node(pos) - -- 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 - local flammables = 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}, {"group:flammable"}) - local below = minetest.get_node({x=pos.x, y=pos.z-1, z=pos.z}) - local below_is_flammable = minetest.get_item_group(below.name, "flammable") > 0 - -- Extinguish fire - if (not fire_enabled) and (math.random(1,3) == 1) then - minetest.remove_node(pos) - return - end - if age == 15 and not below_is_flammable then - minetest.remove_node(pos) - return - elseif age > 3 and #flammables == 0 and not below_is_flammable and math.random(1,4) == 1 then - minetest.remove_node(pos) - return - end - local age_add = 1 - -- If fire spread is disabled, we have to skip the "destructive" code - if (not fire_enabled) then - if age + age_add <= 15 then - node.param2 = age + age_add - minetest.set_node(pos, node) - end - -- Restart timer - fire_timer(pos) - return - end - -- Spawn fire to nearby flammable nodes - local is_next_to_flammable = minetest.find_node_near(pos, 2, {"group:flammable"}) ~= nil - 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 - local nodes = 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 #nodes > 0 do - local r = math.random(1, #nodes) - if minetest.find_node_near(nodes[r], 1, {"group:flammable"}) then - spawn_fire(nodes[r], age_next) - break - else - table.remove(nodes, r) - end - end - else - -- Burn flammable block - local nodes = 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}, {"group:flammable"}) - if #nodes > 0 then - local r = math.random(1, #nodes) - local nn = minetest.get_node(nodes[r]).name - local ndef = minetest.registered_nodes[nn] - local fgroup = minetest.get_item_group(nn, "flammable") - if ndef and ndef._on_burn then - ndef._on_burn(nodes[r]) - elseif fgroup ~= -1 then - spawn_fire(nodes[r], age_next) - end - end - end - end - -- Regular age increase - if age + age_add <= 15 then - node.param2 = age + age_add - minetest.set_node(pos, node) - end - -- Restart timer - fire_timer(pos) - end, drop = "", sounds = {}, -- Turn into eternal fire on special blocks, light Nether portal (if possible), start burning timer @@ -209,14 +134,12 @@ minetest.register_node("mcl_fire:fire", { local dim = mcl_worlds.pos_to_dimension(bpos) if under == "mcl_nether:magma" or under == "mcl_nether:netherrack" or (under == "mcl_core:bedrock" and dim == "end") then - minetest.swap_node(pos, {name = "mcl_fire:eternal_fire"}) + minetest.set_node(pos, {name="mcl_fire:eternal_fire"}) end if minetest.get_modpath("mcl_portals") then mcl_portals.light_nether_portal(pos) end - - fire_timer(pos) spawn_smoke(pos) end, on_destruct = function(pos) @@ -255,29 +178,7 @@ minetest.register_node("mcl_fire:eternal_fire", { minetest.sound_play("fire_extinguish_flame", {pos = pos, gain = 0.25, max_hear_distance = 16}, true) end end, - on_timer = function(pos) - if fire_enabled then - 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 - local node = minetest.get_node(airs[r]) - local age = node.param2 - local age_next = math.min(15, age + math.random(0, 1)) - spawn_fire(airs[r], age_next) - break - else - table.remove(airs, r) - end - end - end - -- Restart timer - fire_timer(pos) - end, - -- Start burning timer and light Nether portal (if possible) on_construct = function(pos) - fire_timer(pos) - if minetest.get_modpath("mcl_portals") then mcl_portals.light_nether_portal(pos) end @@ -402,114 +303,6 @@ if flame_sound then end --- --- ABMs --- - --- Extinguish all flames quickly with water and such - -minetest.register_abm({ - label = "Extinguish fire", - 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}, true) - end, -}) - - --- Enable the following ABMs according to 'enable fire' setting - -local function has_flammable(pos) - local npos, node - for n, v in ipairs(alldirs) do - npos = vector.add(pos, v) - node = minetest.get_node_or_nil(npos) - if node and node.name and minetest.get_item_group(node.name, "flammable") ~= 0 then - return npos - end - end - return false -end - -if not fire_enabled then - - -- Occasionally remove fire if fire disabled - -- NOTE: Fire is normally extinguished in timer function - minetest.register_abm({ - label = "Remove disabled fire", - nodenames = {"mcl_fire:fire"}, - interval = 10, - chance = 10, - catch_up = false, - action = minetest.remove_node, - }) - -else -- Fire enabled - - -- Set fire to air nodes - minetest.register_abm({ - label = "Ignite fire by lava", - nodenames = {"group:lava"}, - neighbors = {"air"}, - interval = 7, - chance = 3, - catch_up = false, - action = function(pos) - 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} - node = minetest.get_node(target) - 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} - node = minetest.get_node(target) - if not node or node.name ~= "air" then - return - end - end - 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} - node2 = minetest.get_node(target2) - 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 - end - end - end - 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" - }) - end - end, - }) - -end - -- Set pointed_thing on (normal) fire. -- * pointed_thing: Pointed thing to ignite -- * player: Player who sets fire or nil if nobody @@ -535,6 +328,144 @@ mcl_fire.set_fire = function(pointed_thing, player, allow_on_fire) end end +-- +-- ABMs +-- + +-- Extinguish all flames quickly with water and such + +minetest.register_abm({ + label = "Extinguish fire", + 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}, true) + end, +}) + + +-- Enable the following ABMs according to 'enable fire' setting + +-- [...]a fire that is not adjacent to any flammable block does not spread, even to another flammable block within the normal range. +-- https://minecraft.fandom.com/wiki/Fire#Spread + +local function check_aircube(p1,p2) + local nds=minetest.find_nodes_in_area(p1,p2,{"air"}) + shuffle_table(nds) + for k,v in pairs(nds) do + if has_flammable(v) then return v end + end +end + + +-- [...] a fire block can turn any air block that is adjacent to a flammable block into a fire block. This can happen at a distance of up to one block downward, one block sideways (including diagonals), and four blocks upward of the original fire block (not the block the fire is on/next to). +local function get_ignitable(pos) + return check_aircube(vector.add(pos,vector.new(-1,-1,-1)),vector.add(pos,vector.new(1,4,1))) +end +-- Fire spreads from a still lava block similarly: any air block one above and up to one block sideways (including diagonals) or two above and two blocks sideways (including diagonals) that is adjacent to a flammable block may be turned into a fire block. +local function get_ignitable_by_lava(pos) + return check_aircube(vector.add(pos,vector.new(-1,1,-1)),vector.add(pos,vector.new(1,1,1))) or check_aircube(vector.add(pos,vector.new(-2,2,-2)),vector.add(pos,vector.new(2,2,2))) or nil +end + +if not fire_enabled then + + -- Occasionally remove fire if fire disabled + -- NOTE: Fire is normally extinguished in timer function + minetest.register_abm({ + label = "Remove disabled fire", + nodenames = {"mcl_fire:fire"}, + interval = 10, + chance = 10, + catch_up = false, + action = minetest.remove_node, + }) + +else -- Fire enabled + + -- Fire Spread + minetest.register_abm({ + label = "Ignite flame", + nodenames ={"mcl_fire:fire","mcl_fire:eternal_fire"}, + interval = 7, + chance = 12, + catch_up = false, + action = function(pos) + local p = get_ignitable(pos) + if p then + spawn_fire(p) + shuffle_table(adjacents) + end + end + }) + + --lava fire spread + minetest.register_abm({ + label = "Ignite fire by lava", + nodenames = {"mcl_core:lava_source","mcl_nether:nether_lava_source"}, + neighbors = {"air","group:flammable"}, + interval = 7, + chance = 3, + catch_up = false, + action = function(pos) + local p=get_ignitable_by_lava(pos) + if p then + spawn_fire(p) + end + end, + }) + + minetest.register_abm({ + label = "Remove fires", + nodenames = {"mcl_fire:fire"}, + interval = 7, + chance = 3, + catch_up = false, + action = function(pos) + local p=has_flammable(pos) + if p then + local n=minetest.get_node_or_nil(p) + if n and minetest.get_item_group(n.name, "flammable") < 1 then + minetest.remove_node(pos) + end + else + minetest.remove_node(pos) + end + end, + }) + + -- Remove flammable nodes around basic flame + minetest.register_abm({ + label = "Remove flammable nodes", + nodenames = {"mcl_fire:fire","mcl_fire:eternal_fire"}, + neighbors = {"group:flammable"}, + interval = 5, + chance = 18, + catch_up = false, + action = function(pos) + local p = has_flammable(pos) + if not p then + return + end + + local nn = minetest.get_node(p).name + local def = minetest.registered_nodes[nn] + local fgroup = minetest.get_item_group(nn, "flammable") + + if def and def._on_burn then + def._on_burn(p) + elseif fgroup ~= -1 then + spawn_fire(p) + minetest.check_for_falling(p) + end + end + }) +end + minetest.register_lbm({ label = "Smoke particles from fire", name = "mcl_fire:smoke",