mirror of
https://git.minetest.land/VoxeLibre/VoxeLibre.git
synced 2024-11-28 21:41:06 +01:00
Add crash guards against corrupt map data in mcl_core/functions.lua, update fire spread algorithm to limit spread while still burning things down, make burn spread dependent on area's humidity (more humid areas have less spread)
This commit is contained in:
parent
5ee1bd0373
commit
44ae70d498
1 changed files with 67 additions and 45 deletions
|
@ -44,8 +44,8 @@ shuffle_table(adjacents)
|
||||||
local function has_flammable(pos)
|
local function has_flammable(pos)
|
||||||
for k,v in pairs(adjacents) do
|
for k,v in pairs(adjacents) do
|
||||||
local p=vector.add(pos,v)
|
local p=vector.add(pos,v)
|
||||||
local n=minetest.get_node_or_nil(p)
|
local n=get_node_or_nil(p)
|
||||||
if n and minetest.get_item_group(n.name, "flammable") ~= 0 then
|
if n and get_item_group(n.name, "flammable") ~= 0 then
|
||||||
return p
|
return p
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -63,8 +63,8 @@ local function get_adjacent_fire_age(pos)
|
||||||
local lowest_age = nil
|
local lowest_age = nil
|
||||||
for k,v in pairs(all_adjacents) do
|
for k,v in pairs(all_adjacents) do
|
||||||
local p = vector.add(pos, v)
|
local p = vector.add(pos, v)
|
||||||
local node = minetest.get_node_or_nil(p)
|
local node = get_node_or_nil(p)
|
||||||
if node and minetest.get_item_group(node.name, "fire") ~= 0 then
|
if node and get_item_group(node.name, "fire") ~= 0 then
|
||||||
if not lowest_age or node.param2 < lowest_age then
|
if not lowest_age or node.param2 < lowest_age then
|
||||||
lowest_age = node.param2
|
lowest_age = node.param2
|
||||||
end
|
end
|
||||||
|
@ -112,23 +112,37 @@ 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.")
|
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
|
end
|
||||||
|
|
||||||
local function spawn_fire(pos, age)
|
-- exponential constant is such that at age=255, p=0.5%
|
||||||
|
local K1 = ( math.log(0.005) / math.log(10) ) / 255
|
||||||
|
local function spawn_fire(pos, age, force)
|
||||||
if not age then
|
if not age then
|
||||||
|
minetest.log("warning","No age specified at "..debug.traceback())
|
||||||
|
|
||||||
-- Get adjacent age
|
-- Get adjacent age
|
||||||
local adjacent_age = get_adjacent_fire_age(pos)
|
local adjacent_age = get_adjacent_fire_age(pos)
|
||||||
|
|
||||||
-- Don't create new fire if we can't find adjacent fire from this position
|
-- Don't create new fire if we can't find adjacent fire from this position
|
||||||
if not adjacent_age then return end
|
if not adjacent_age then return end
|
||||||
|
|
||||||
age = math.random(1,5) + adjacent_age
|
age = adjacent_age + math.ceil(minetest.get_humidity(pos)/10) + math.random(5)
|
||||||
end
|
end
|
||||||
if age <= 1 then
|
if age <= 1 then
|
||||||
minetest.log("warning","new flash point at "..vector.to_string(pos).." age="..tostring(age)..",backtrace = "..debug.traceback())
|
minetest.log("warning","new flash point at "..vector.to_string(pos).." age="..tostring(age)..",backtrace = "..debug.traceback())
|
||||||
end
|
end
|
||||||
|
if age >= 255 then
|
||||||
|
age = 255
|
||||||
|
end
|
||||||
|
|
||||||
|
local node = get_node(pos)
|
||||||
|
local node_is_flammable = get_item_group(node.name, "flammable")
|
||||||
|
|
||||||
-- Limit fire spread
|
-- Limit fire spread
|
||||||
local probability = math.pow(10,-1.176e-2 * age) -- exponential constant is such that at age=255, p=0.1%
|
local probability_age = age
|
||||||
if math.random(65536)/65536 >= probability then
|
if node_is_flammable then
|
||||||
|
probability_age = probability_age * 0.80
|
||||||
|
end
|
||||||
|
local probability = math.pow(10,K1 * probability_age)
|
||||||
|
if not force and math.random(65536)/65536 >= probability then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -399,32 +413,47 @@ if not fire_enabled then
|
||||||
|
|
||||||
else -- Fire enabled
|
else -- Fire enabled
|
||||||
|
|
||||||
|
-- Extinguish parameters
|
||||||
|
local C2 = math.log(1/20) / math.log(10)
|
||||||
|
local K2 = -C2 / 255
|
||||||
|
|
||||||
-- Fire Spread
|
-- Fire Spread
|
||||||
minetest.register_abm({
|
minetest.register_abm({
|
||||||
label = "Ignite flame",
|
label = "Ignite flame",
|
||||||
nodenames ={"mcl_fire:fire","mcl_fire:eternal_fire"},
|
nodenames ={"mcl_fire:fire","mcl_fire:eternal_fire"},
|
||||||
interval = 7,
|
interval = 7,
|
||||||
chance = 12,
|
chance = 5,
|
||||||
catch_up = false,
|
catch_up = false,
|
||||||
action = function(pos)
|
action = function(pos)
|
||||||
local node = minetest.get_node(pos)
|
local node = get_node(pos)
|
||||||
local age = node.param2
|
local age = node.param2
|
||||||
|
|
||||||
|
-- Always age the source fire
|
||||||
|
age = age + math.ceil(minetest.get_humidity(pos)/10) + math.random(5)
|
||||||
|
if age > 255 then age = 255 end
|
||||||
|
node.param2 = age
|
||||||
|
|
||||||
local p = get_ignitable(pos)
|
local p = get_ignitable(pos)
|
||||||
if p then
|
if p then
|
||||||
-- Spawn new fire with an age based on this node's age
|
-- Spawn new fire with an age based on this node's age
|
||||||
spawn_fire(p, age+math.random(3,7))
|
spawn_fire(p, age + math.ceil(minetest.get_humidity(p)/10) + math.random(5))
|
||||||
shuffle_table(adjacents)
|
shuffle_table(adjacents)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Always age the source fire
|
if node.name ~= "mcl_fire:eternal_fire" then
|
||||||
age = age + math.random(2,5)
|
-- Randomly extinguish fires with increasing probability the older they are
|
||||||
node.param2 = age
|
local extinguish_probability = math.pow(10,K2 * age + C2)
|
||||||
if age >= 255 then
|
if math.random(65536)/65536 <= extinguish_probability then
|
||||||
node.name = "air"
|
node.name = "air"
|
||||||
node.param2 = 0
|
node.param2 = 0
|
||||||
|
|
||||||
|
-- Extinguish fires not adjacent to flammable materials
|
||||||
|
elseif not has_flammable(pos) then
|
||||||
|
node.name = "air"
|
||||||
|
node.param2 = 0
|
||||||
|
end
|
||||||
end
|
end
|
||||||
minetest.set_node(pos, node)
|
set_node(pos, node)
|
||||||
end
|
end
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -439,26 +468,7 @@ else -- Fire enabled
|
||||||
action = function(pos)
|
action = function(pos)
|
||||||
local p=get_ignitable_by_lava(pos)
|
local p=get_ignitable_by_lava(pos)
|
||||||
if p then
|
if p then
|
||||||
spawn_fire(p)
|
spawn_fire(p, 0)
|
||||||
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
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
@ -477,15 +487,27 @@ else -- Fire enabled
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local nn = minetest.get_node(p).name
|
local node = get_node(p)
|
||||||
local def = minetest.registered_nodes[nn]
|
local node_name = node.name
|
||||||
local fgroup = minetest.get_item_group(nn, "flammable")
|
local def = minetest.registered_nodes[node_name]
|
||||||
|
local fgroup = minetest.get_item_group(node_name, "flammable")
|
||||||
|
|
||||||
if def and def._on_burn then
|
if def and def._on_burn then
|
||||||
def._on_burn(p)
|
def._on_burn(p)
|
||||||
elseif fgroup ~= -1 then
|
elseif fgroup ~= -1 then
|
||||||
spawn_fire(p)
|
local source_node = get_node(pos)
|
||||||
|
local age = source_node.param2
|
||||||
|
|
||||||
|
spawn_fire(p, age + math.ceil(minetest.get_humidity(p)/10) + math.random(5), true)
|
||||||
minetest.check_for_falling(p)
|
minetest.check_for_falling(p)
|
||||||
|
|
||||||
|
if source_node.name == "mcl_fire:fire" then
|
||||||
|
-- Always age the source fire
|
||||||
|
age = age + math.ceil(minetest.get_humidity(pos)/10) + math.random(5)
|
||||||
|
if age > 255 then age = 255 end
|
||||||
|
source_node.param2 = age
|
||||||
|
set_node(pos, source_node)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
})
|
})
|
||||||
|
@ -508,17 +530,17 @@ function mcl_fire.set_fire(pointed_thing, player, allow_on_fire)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local n_pointed = minetest.get_node(pointed_thing.under)
|
local n_pointed = get_node(pointed_thing.under)
|
||||||
if allow_on_fire == false and get_item_group(n_pointed.name, "fire") ~= 0 then
|
if allow_on_fire == false and get_item_group(n_pointed.name, "fire") ~= 0 then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local n_fire_pos = minetest.get_node(pointed_thing.above)
|
local n_fire_pos = get_node(pointed_thing.above)
|
||||||
if n_fire_pos.name ~= "air" then
|
if n_fire_pos.name ~= "air" then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local n_below = minetest.get_node(vector.offset(pointed_thing.above, 0, -1, 0))
|
local n_below = get_node(vector.offset(pointed_thing.above, 0, -1, 0))
|
||||||
if minetest.get_item_group(n_below.name, "water") ~= 0 then
|
if minetest.get_item_group(n_below.name, "water") ~= 0 then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue