X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=mods%2Ffire%2Finit.lua;h=a3457f649091266f003009f0cff29bd41f2146eb;hb=bca0142e7f0318a9a17bf8c263af40ad2a83c7fa;hp=c0d95a4339e0266d5676598fa51fc8770e1428a0;hpb=bed848f68a8940276cf48e9a0adfff21897ba3fd;p=oweals%2Fminetest_game.git diff --git a/mods/fire/init.lua b/mods/fire/init.lua index c0d95a43..a3457f64 100644 --- a/mods/fire/init.lua +++ b/mods/fire/init.lua @@ -1,59 +1,208 @@ -- minetest/fire/init.lua +-- Global namespace for functions + +fire = {} + + +-- Register flame nodes + minetest.register_node("fire:basic_flame", { - description = "Fire", - drawtype = "plantlike", - tiles = {{ - name="fire_basic_flame_animated.png", - animation={type="vertical_frames", aspect_w=16, aspect_h=16, length=1}, - }}, + 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", light_source = 14, - groups = {igniter=2,dig_immediate=3}, - drop = '', walkable = false, + buildable_to = true, + sunlight_propagates = true, damage_per_second = 4, + groups = {igniter = 2, dig_immediate = 3, not_in_creative_inventory = 1}, + on_timer = function(pos) + local f = minetest.find_node_near(pos, 1, {"group:flammable"}) + if not f then + minetest.remove_node(pos) + return + end + -- restart timer + return true + end, + drop = "", + + on_construct = function(pos) + minetest.get_node_timer(pos):start(math.random(30, 60)) + minetest.after(0, fire.update_sounds_around, pos) + end, + + on_destruct = function(pos) + minetest.after(0, fire.update_sounds_around, pos) + end, + + on_blast = function() + end, -- unaffected by explosions }) -local fire = {} -fire.D = 6 --- key: position hash of low corner of area --- value: {handle=sound handle, name=sound name} -fire.sounds = {} +minetest.register_node("fire:permanent_flame", { + description = "Permanent Flame", + 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", + light_source = 14, + walkable = false, + buildable_to = true, + sunlight_propagates = true, + damage_per_second = 4, + groups = {igniter = 2, dig_immediate = 3}, + drop = "", + + on_blast = function() + end, +}) + + +-- Flint and steel + +minetest.register_tool("fire:flint_and_steel", { + description = "Flint and Steel", + inventory_image = "fire_flint_steel.png", + on_use = function(itemstack, user, pointed_thing) + local pt = pointed_thing + minetest.sound_play( + "fire_flint_and_steel", + {pos = pt.above, gain = 0.6, max_hear_distance = 8} + ) + itemstack:add_wear(1000) + if pt.type == "node" then + local node_under = minetest.get_node(pt.under).name + local is_coalblock = node_under == "default:coalblock" + local is_tnt = node_under == "tnt:tnt" + local is_gunpowder = node_under == "tnt:gunpowder" + if minetest.get_item_group(node_under, "flammable") >= 1 or + is_coalblock or is_tnt or is_gunpowder then + local flame_pos = pt.above + if is_coalblock then + flame_pos = {x = pt.under.x, y = pt.under.y + 1, z = pt.under.z} + elseif is_tnt or is_gunpowder then + flame_pos = pt.under + end + if minetest.get_node(flame_pos).name == "air" or + is_tnt or is_gunpowder then + local player_name = user:get_player_name() + if not minetest.is_protected(flame_pos, player_name) then + if is_coalblock then + minetest.set_node(flame_pos, + {name = "fire:permanent_flame"}) + elseif is_tnt then + minetest.set_node(flame_pos, + {name = "tnt:tnt_burning"}) + elseif is_gunpowder then + minetest.set_node(flame_pos, + {name = "tnt:gunpowder_burning"}) + else + minetest.set_node(flame_pos, + {name = "fire:basic_flame"}) + end + else + minetest.chat_send_player(player_name, "This area is protected") + end + end + end + end + if not minetest.setting_getbool("creative_mode") then + return itemstack + end + end +}) + +minetest.register_craft({ + output = "fire:flint_and_steel", + recipe = { + {"default:flint", "default:steel_ingot"} + } +}) + + +-- Override coalblock to enable permanent flame above +-- Coalblock is non-flammable to avoid unwanted basic_flame nodes + +minetest.override_item("default:coalblock", { + after_destruct = function(pos, oldnode) + pos.y = pos.y + 1 + if minetest.get_node(pos).name == "fire:permanent_flame" then + minetest.remove_node(pos) + end + end, +}) + + +-- Get sound area of position + +fire.D = 6 -- size of sound areas function fire.get_area_p0p1(pos) local p0 = { - x=math.floor(pos.x/fire.D)*fire.D, - y=math.floor(pos.y/fire.D)*fire.D, - z=math.floor(pos.z/fire.D)*fire.D, + x = math.floor(pos.x / fire.D) * fire.D, + y = math.floor(pos.y / fire.D) * fire.D, + z = math.floor(pos.z / fire.D) * fire.D, } local p1 = { - x=p0.x+fire.D-1, - y=p0.y+fire.D-1, - z=p0.z+fire.D-1 + x = p0.x + fire.D - 1, + y = p0.y + fire.D - 1, + z = p0.z + fire.D - 1 } return p0, p1 end + +-- Fire sounds table +-- key: position hash of low corner of area +-- value: {handle=sound handle, name=sound name} +fire.sounds = {} + + +-- Update fire sounds in sound area of position + function fire.update_sounds_around(pos) local p0, p1 = fire.get_area_p0p1(pos) - local cp = {x=(p0.x+p1.x)/2, y=(p0.y+p1.y)/2, z=(p0.z+p1.z)/2} - local flames_p = minetest.env:find_nodes_in_area(p0, p1, {"fire:basic_flame"}) + local cp = {x = (p0.x + p1.x) / 2, y = (p0.y + p1.y) / 2, z = (p0.z + p1.z) / 2} + local flames_p = minetest.find_nodes_in_area(p0, p1, {"fire:basic_flame"}) --print("number of flames at "..minetest.pos_to_string(p0).."/" -- ..minetest.pos_to_string(p1)..": "..#flames_p) local should_have_sound = (#flames_p > 0) local wanted_sound = nil if #flames_p >= 9 then - wanted_sound = {name="fire_large", gain=1.5} + wanted_sound = {name = "fire_large", gain = 0.7} elseif #flames_p > 0 then - wanted_sound = {name="fire_small", gain=1.5} + wanted_sound = {name = "fire_small", gain = 0.9} end local p0_hash = minetest.hash_node_position(p0) local sound = fire.sounds[p0_hash] if not sound then if should_have_sound then fire.sounds[p0_hash] = { - handle = minetest.sound_play(wanted_sound, {pos=cp, loop=true}), + handle = minetest.sound_play(wanted_sound, + {pos = cp, max_hear_distance = 16, loop = true}), name = wanted_sound.name, } end @@ -64,79 +213,122 @@ function fire.update_sounds_around(pos) elseif sound.name ~= wanted_sound.name then minetest.sound_stop(sound.handle) fire.sounds[p0_hash] = { - handle = minetest.sound_play(wanted_sound, {pos=cp, loop=true}), + handle = minetest.sound_play(wanted_sound, + {pos = cp, max_hear_distance = 16, loop = true}), name = wanted_sound.name, } end end end -function fire.on_flame_add_at(pos) - --print("flame added at "..minetest.pos_to_string(pos)) - fire.update_sounds_around(pos) -end -function fire.on_flame_remove_at(pos) - --print("flame removed at "..minetest.pos_to_string(pos)) - fire.update_sounds_around(pos) -end +-- Extinguish all flames quickly with water, snow, ice -function fire.find_pos_for_flame_around(pos) - return minetest.env:find_node_near(pos, 1, {"air"}) -end +minetest.register_abm({ + label = "Extinguish flame", + nodenames = {"fire:basic_flame", "fire:permanent_flame"}, + 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.25}) + end, +}) -function fire.flame_should_extinguish(pos) - --return minetest.env:find_node_near(pos, 1, {"group:puts_out_fire"}) - local p0 = {x=pos.x-2, y=pos.y, z=pos.z-2} - local p1 = {x=pos.x+2, y=pos.y, z=pos.z+2} - local ps = minetest.env:find_nodes_in_area(p0, p1, {"group:puts_out_fire"}) - return (#ps ~= 0) + +-- Enable the following ABMs according to 'enable fire' setting + +local fire_enabled = minetest.setting_getbool("enable_fire") +if fire_enabled == nil then + -- New setting not specified, check for old setting. + -- If old setting is also not specified, 'not nil' is true. + fire_enabled = not minetest.setting_getbool("disable_fire") end -minetest.register_on_placenode(function(pos, newnode, placer) - if newnode.name == "fire:basic_flame" then - fire.on_flame_add_at(pos) - end -end) +if not fire_enabled then -minetest.register_on_dignode(function(pos, oldnode, digger) - if oldnode.name == "fire:basic_flame" then - fire.on_flame_remove_at(pos) - end -end) + -- Remove basic flames only + + minetest.register_abm({ + label = "Remove disabled fire", + nodenames = {"fire:basic_flame"}, + interval = 7, + chance = 1, + catch_up = false, + action = minetest.remove_node, + }) + +else -- Fire enabled + + -- Ignite neighboring nodes, add basic flames + + minetest.register_abm({ + label = "Ignite flame", + nodenames = {"group:flammable"}, + neighbors = {"group:igniter"}, + interval = 7, + chance = 12, + catch_up = false, + action = function(pos, node, active_object_count, active_object_count_wider) + -- If there is water or stuff like that around node, don't ignite + if minetest.find_node_near(pos, 1, {"group:puts_out_fire"}) then + return + end + local p = minetest.find_node_near(pos, 1, {"air"}) + if p then + minetest.set_node(p, {name = "fire:basic_flame"}) + end + end, + }) + + -- Remove flammable nodes + + minetest.register_abm({ + label = "Remove flammable nodes", + nodenames = {"fire:basic_flame"}, + neighbors = "group:flammable", + 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"}) + if p then + -- remove flammable nodes around flame + 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) + nodeupdate(p) + end + end + end, + }) + +end --- Ignite neighboring nodes -minetest.register_abm({ - nodenames = {"group:flammable"}, - neighbors = {"group:igniter"}, - interval = 1, - chance = 2, - action = function(p0, node, _, _) - -- If there is water or stuff like that around flame, don't ignite - if fire.flame_should_extinguish(p0) then - return - end - local p = fire.find_pos_for_flame_around(p0) - if p then - minetest.env:set_node(p, {name="fire:basic_flame"}) - fire.on_flame_add_at(p) - end - end, -}) -- Rarely ignite things from far + +--[[ Currently disabled to reduce the chance of uncontrollable spreading + fires that disrupt servers. Also for less lua processing load. + minetest.register_abm({ nodenames = {"group:igniter"}, neighbors = {"air"}, - interval = 2, + interval = 5, chance = 10, - action = function(p0, node, _, _) + action = function(pos, node, active_object_count, active_object_count_wider) local reg = minetest.registered_nodes[node.name] if not reg or not reg.groups.igniter or reg.groups.igniter < 2 then return end local d = reg.groups.igniter - local p = minetest.env:find_node_near(p0, d, {"group:flammable"}) + local p = minetest.find_node_near(pos, d, {"group:flammable"}) if p then -- If there is water or stuff like that around flame, don't ignite if fire.flame_should_extinguish(p) then @@ -144,50 +336,9 @@ minetest.register_abm({ end local p2 = fire.find_pos_for_flame_around(p) if p2 then - minetest.env:set_node(p2, {name="fire:basic_flame"}) - fire.on_flame_add_at(p2) + minetest.set_node(p2, {name = "fire:basic_flame"}) end end end, }) - --- Remove flammable nodes and flame -minetest.register_abm({ - nodenames = {"fire:basic_flame"}, - interval = 1, - chance = 2, - action = function(p0, node, _, _) - -- If there is water or stuff like that around flame, remove flame - if fire.flame_should_extinguish(p0) then - minetest.env:remove_node(p0) - fire.on_flame_remove_at(p0) - return - end - -- Make the following things rarer - if math.random(1,3) == 1 then - return - end - -- If there are no flammable nodes around flame, remove flame - if not minetest.env:find_node_near(p0, 1, {"group:flammable"}) then - minetest.env:remove_node(p0) - fire.on_flame_remove_at(p0) - return - end - if math.random(1,4) == 1 then - -- remove a flammable node around flame - local p = minetest.env:find_node_near(p0, 1, {"group:flammable"}) - if p then - -- If there is water or stuff like that around flame, don't remove - if fire.flame_should_extinguish(p0) then - return - end - minetest.env:remove_node(p) - end - else - -- remove flame - minetest.env:remove_node(p0) - fire.on_flame_remove_at(p0) - end - end, -}) - +--]]