From: paramat Date: Sat, 3 Dec 2016 04:54:44 +0000 (+0000) Subject: Fire: Rewrite fire sound code X-Git-Tag: 0.4.15~6 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=fed2151d708b79b86bed7f112c2356966f2b4500;p=oweals%2Fminetest_game.git Fire: Rewrite fire sound code Previous code: Used looped sounds without attaching them to objects or playing direct to players. The looped sounds are not 'stopped' when players leave the area. These may be causing the bug where sounds are heard at extreme distances. Entering a world with already present flames results in silent flames. Sounds are often played at a large number of points in a 6 node lattice. A large fire is reported to cause a high load, disabling the sound code is reported to help this. New code: Optional flame sound to not interfere with ambience mods. Permanent flame now has sound. For multiple flames, sound is positioned at the centre of all flames, and has volume determined by flame number. The original freesound 'large fire' recording was used to create 3 sounds that play at random for a non-repetitive effect. At low volume it is suitable for small fires. Original sound files and sound function (as an empty function) kept temporarily to reduce disruption. Reduce gain of flame extinguish sound. --- diff --git a/minetest.conf.example b/minetest.conf.example index f5e4e85d..3f20eb77 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -24,6 +24,9 @@ # 'permanent flame' nodes will remain with either setting. #enable_fire = true +# Enable flame sound. +#flame_sound = true + # Whether the stuff in initial_stuff should be given to new players #give_initial_stuff = false #initial_stuff = default:pick_steel,default:axe_steel,default:shovel_steel,default:torch 99,default:cobble 99 diff --git a/mods/fire/README.txt b/mods/fire/README.txt index 35affc26..099da1c2 100644 --- a/mods/fire/README.txt +++ b/mods/fire/README.txt @@ -12,17 +12,24 @@ Authors of media (textures and sounds) Everything not listed in here: Copyright (C) 2012 Perttu Ahola (celeron55) (CC BY-SA 3.0) -fire_basic_flame_animated.png: - Muadtralk (CC BY-SA 3.0) +Muadtralk (CC BY-SA 3.0) + fire_basic_flame_animated.png -fire_flint_steel.png - Gambit (CC BY-SA 3.0) +Gambit (CC BY-SA 3.0) + fire_flint_steel.png -fire_small.ogg sampled from: - http://www.freesound.org/people/dobroide/sounds/4211/ (CC BY 3.0) +dobroide (CC BY 3.0) +http://www.freesound.org/people/dobroide/sounds/4211/ + fire_small.ogg -fire_large.ogg sampled from: - http://www.freesound.org/people/Dynamicell/sounds/17548/ (CC BY 3.0) +Dynamicell (CC BY 3.0) +http://www.freesound.org/people/Dynamicell/sounds/17548/ + fire_large.ogg + fire_fire.*.ogg -fire_flint_and_steel.ogg - https://www.freesound.org/people/Benboncan/sounds/66457/ (CC BY 3.0) +fire_small.ogg and fire_large.ogg are unused but kept temporarily to not break +other mods that may use them. + +Benboncan (CC BY 3.0) +https://www.freesound.org/people/Benboncan/sounds/66457/ + fire_flint_and_steel.ogg diff --git a/mods/fire/init.lua b/mods/fire/init.lua index 0dbd7c3e..bee487a5 100644 --- a/mods/fire/init.lua +++ b/mods/fire/init.lua @@ -1,11 +1,13 @@ --- minetest/fire/init.lua - -- Global namespace for functions fire = {} --- Register flame nodes +-- +-- Items +-- + +-- Flame nodes minetest.register_node("fire:basic_flame", { drawtype = "firelike", @@ -34,22 +36,17 @@ minetest.register_node("fire:basic_flame", { minetest.remove_node(pos) return end - -- restart timer + -- 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) + on_blast = function() -- Unaffected by explosions end, - - on_blast = function() - end, -- unaffected by explosions }) minetest.register_node("fire:permanent_flame", { @@ -76,7 +73,7 @@ minetest.register_node("fire:permanent_flame", { groups = {igniter = 2, dig_immediate = 3}, drop = "", - on_blast = function() + on_blast = function() -- Unaffected by explosions end, }) @@ -113,10 +110,10 @@ minetest.register_tool("fire:flint_and_steel", { end end if not minetest.setting_getbool("creative_mode") then - -- wear tool + -- Wear tool local wdef = itemstack:get_definition() itemstack:add_wear(1000) - -- tool break sound + -- Tool break sound if itemstack:get_count() == 0 and wdef.sound and wdef.sound.breaks then minetest.sound_play(wdef.sound.breaks, {pos = pt.above, gain = 0.5}) end @@ -151,72 +148,134 @@ minetest.override_item("default:coalblock", { end, }) --- Get sound area of position -fire.D = 6 -- size of sound areas +-- +-- Sound +-- -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, - } - local p1 = { - x = p0.x + fire.D - 1, - y = p0.y + fire.D - 1, - z = p0.z + fire.D - 1 - } - return p0, p1 +local flame_sound = minetest.setting_getbool("flame_sound") +if flame_sound == nil then + -- Enable if no setting present + flame_sound = true end +if flame_sound then --- Fire sounds table --- key: position hash of low corner of area --- value: {handle=sound handle, name=sound name} -fire.sounds = {} + local handles = {} + local timer = 0 + -- Parameters --- Update fire sounds in sound area of position + local radius = 8 -- Flame node search radius around player + local cycle = 3 -- Cycle time for sound updates -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.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 = 0.7} - elseif #flames_p > 0 then - 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, max_hear_distance = 16, loop = true}), - name = wanted_sound.name, - } + -- Update sound for player + + function fire.update_player_sound(player) + local player_name = player:get_player_name() + -- Search for flame nodes in radius around player + local ppos = player:getpos() + local areamin = vector.subtract(ppos, radius) + local areamax = vector.add(ppos, radius) + local fpos, num = minetest.find_nodes_in_area( + areamin, + areamax, + {"fire:basic_flame", "fire:permanent_flame"} + ) + -- Total number of flames in radius + local flames = (num["fire:basic_flame"] or 0) + + (num["fire:permanent_flame"] or 0) + -- Stop previous sound + if handles[player_name] then + minetest.sound_stop(handles[player_name]) + handles[player_name] = nil end - else - if not wanted_sound then - minetest.sound_stop(sound.handle) - fire.sounds[p0_hash] = nil - elseif sound.name ~= wanted_sound.name then - minetest.sound_stop(sound.handle) - fire.sounds[p0_hash] = { - handle = minetest.sound_play(wanted_sound, - {pos = cp, max_hear_distance = 16, loop = true}), - name = wanted_sound.name, - } + -- 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 end end + + -- Cycle for updating players sounds + + minetest.register_globalstep(function(dtime) + timer = timer + dtime + if timer < cycle then + return + end + + timer = 0 + local players = minetest.get_connected_players() + for n = 1, #players do + fire.update_player_sound(players[n]) + end + end) + + -- Stop sound and clear handle on player leave + + 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) end +-- Deprecated function kept temporarily to avoid crashes if mod fire nodes call it + +function fire.update_sounds_around(pos) +end + + +-- +-- ABMs +-- + -- Extinguish all flames quickly with water, snow, ice minetest.register_abm({ @@ -229,7 +288,7 @@ minetest.register_abm({ 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}) + {pos = pos, max_hear_distance = 16, gain = 0.15}) end, }) @@ -245,7 +304,7 @@ end if not fire_enabled then - -- Remove basic flames only + -- Remove basic flames only if fire disabled minetest.register_abm({ label = "Remove disabled fire", @@ -279,7 +338,7 @@ else -- Fire enabled end, }) - -- Remove flammable nodes + -- Remove flammable nodes around basic flame minetest.register_abm({ label = "Remove flammable nodes", @@ -291,7 +350,6 @@ else -- Fire enabled 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 @@ -305,35 +363,3 @@ else -- Fire enabled }) 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 = 5, - chance = 10, - 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.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 - return - end - local p2 = fire.find_pos_for_flame_around(p) - if p2 then - minetest.set_node(p2, {name = "fire:basic_flame"}) - end - end - end, -}) ---]] diff --git a/mods/fire/sounds/fire_fire.1.ogg b/mods/fire/sounds/fire_fire.1.ogg new file mode 100644 index 00000000..cbfee4c6 Binary files /dev/null and b/mods/fire/sounds/fire_fire.1.ogg differ diff --git a/mods/fire/sounds/fire_fire.2.ogg b/mods/fire/sounds/fire_fire.2.ogg new file mode 100644 index 00000000..e8d0eb13 Binary files /dev/null and b/mods/fire/sounds/fire_fire.2.ogg differ diff --git a/mods/fire/sounds/fire_fire.3.ogg b/mods/fire/sounds/fire_fire.3.ogg new file mode 100644 index 00000000..5cad3d9b Binary files /dev/null and b/mods/fire/sounds/fire_fire.3.ogg differ diff --git a/settingtypes.txt b/settingtypes.txt index 2f91a7b6..eeea0bfc 100644 --- a/settingtypes.txt +++ b/settingtypes.txt @@ -13,6 +13,9 @@ creative_mode (Creative mode) bool false # 'permanent_flame' nodes are unaffected. enable_fire (Fire) bool true +# Enable flame sound. +flame_sound (Flame sound) bool true + # If enabled, steel tools, torches and cobblestone will be given to new # players. give_initial_stuff (Give initial items) bool false