From 8e8ccddb68434e4dc6f4047ab9215a052c259a8e Mon Sep 17 00:00:00 2001 From: cora Date: Fri, 14 Jan 2022 22:15:44 +0100 Subject: [PATCH] Replace MineClone2 fire with Minetest Game ABMs ABMs have better performance than the laggy timer-based implementation. --- mods/ITEMS/mcl_fire/init.lua | 185 ++++++++++++----------------------- 1 file changed, 63 insertions(+), 122 deletions(-) diff --git a/mods/ITEMS/mcl_fire/init.lua b/mods/ITEMS/mcl_fire/init.lua index 50303e3b..b29e60ef 100644 --- a/mods/ITEMS/mcl_fire/init.lua +++ b/mods/ITEMS/mcl_fire/init.lua @@ -124,82 +124,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 @@ -255,29 +179,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,6 +304,31 @@ if flame_sound then end +-- Set pointed_thing on (normal) fire. +-- * pointed_thing: Pointed thing to ignite +-- * player: Player who sets fire or nil if nobody +-- * allow_on_fire: If false, can't ignite fire on fire (default: true) +mcl_fire.set_fire = function(pointed_thing, player, allow_on_fire) + local pname + if player == nil then + pname = "" + else + pname = player:get_player_name() + end + local n = minetest.get_node(pointed_thing.above) + local nu = minetest.get_node(pointed_thing.under) + if allow_on_fire == false and minetest.get_item_group(nu.name, "fire") ~= 0 then + return + end + if minetest.is_protected(pointed_thing.above, pname) then + minetest.record_protection_violation(pointed_thing.above, pname) + return + end + if n.name == "air" then + minetest.add_node(pointed_thing.above, {name="mcl_fire:fire"}) + end +end + -- -- ABMs -- @@ -508,31 +435,45 @@ else -- Fire enabled end, }) -end + -- Ignite neighboring nodes, add basic flames + minetest.register_abm({ + label = "Ignite flame", + nodenames = {"group:flammable"}, + neighbors = {"mcl_fire:fire","mcl_fire:eternal_fire","mcl_core:lava_source","mcl_core:lava_flowing","mcl_nether:nether_lava_source","mcl_nether:nether_lava_flowing",}, + interval = 7, + chance = 12, + catch_up = false, + action = function(pos) + local p = minetest.find_node_near(pos, 1, {"air"}) + if p then + minetest.set_node(p, {name = "mcl_fire:fire"}) + end + end + }) --- Set pointed_thing on (normal) fire. --- * pointed_thing: Pointed thing to ignite --- * player: Player who sets fire or nil if nobody --- * allow_on_fire: If false, can't ignite fire on fire (default: true) -mcl_fire.set_fire = function(pointed_thing, player, allow_on_fire) - local pname - if player == nil then - pname = "" - else - pname = player:get_player_name() - end - local n = minetest.get_node(pointed_thing.above) - local nu = minetest.get_node(pointed_thing.under) - if allow_on_fire == false and minetest.get_item_group(nu.name, "fire") ~= 0 then - return - end - if minetest.is_protected(pointed_thing.above, pname) then - minetest.record_protection_violation(pointed_thing.above, pname) - return - end - if n.name == "air" then - minetest.add_node(pointed_thing.above, {name="mcl_fire:fire"}) - end + -- Remove flammable nodes around basic flame + minetest.register_abm({ + label = "Remove flammable nodes", + nodenames = {"mc_fire:fire"}, + neighbors = "group:flammable", + interval = 5, + chance = 18, + catch_up = false, + action = function(pos) + local p = minetest.find_node_near(pos, 1, {"group:flammable"}) + if not p then + return + end + 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 + minetest.remove_node(p) + minetest.check_for_falling(p) + end + end + }) end minetest.register_lbm({