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.
This commit is contained in:
parent
58038a7941
commit
fed2151d70
|
@ -24,6 +24,9 @@
|
||||||
# 'permanent flame' nodes will remain with either setting.
|
# 'permanent flame' nodes will remain with either setting.
|
||||||
#enable_fire = true
|
#enable_fire = true
|
||||||
|
|
||||||
|
# Enable flame sound.
|
||||||
|
#flame_sound = true
|
||||||
|
|
||||||
# Whether the stuff in initial_stuff should be given to new players
|
# Whether the stuff in initial_stuff should be given to new players
|
||||||
#give_initial_stuff = false
|
#give_initial_stuff = false
|
||||||
#initial_stuff = default:pick_steel,default:axe_steel,default:shovel_steel,default:torch 99,default:cobble 99
|
#initial_stuff = default:pick_steel,default:axe_steel,default:shovel_steel,default:torch 99,default:cobble 99
|
||||||
|
|
|
@ -12,17 +12,24 @@ Authors of media (textures and sounds)
|
||||||
Everything not listed in here:
|
Everything not listed in here:
|
||||||
Copyright (C) 2012 Perttu Ahola (celeron55) <celeron55@gmail.com> (CC BY-SA 3.0)
|
Copyright (C) 2012 Perttu Ahola (celeron55) <celeron55@gmail.com> (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:
|
dobroide (CC BY 3.0)
|
||||||
http://www.freesound.org/people/dobroide/sounds/4211/ (CC BY 3.0)
|
http://www.freesound.org/people/dobroide/sounds/4211/
|
||||||
|
fire_small.ogg
|
||||||
|
|
||||||
fire_large.ogg sampled from:
|
Dynamicell (CC BY 3.0)
|
||||||
http://www.freesound.org/people/Dynamicell/sounds/17548/ (CC BY 3.0)
|
http://www.freesound.org/people/Dynamicell/sounds/17548/
|
||||||
|
fire_large.ogg
|
||||||
|
fire_fire.*.ogg
|
||||||
|
|
||||||
fire_flint_and_steel.ogg
|
fire_small.ogg and fire_large.ogg are unused but kept temporarily to not break
|
||||||
https://www.freesound.org/people/Benboncan/sounds/66457/ (CC BY 3.0)
|
other mods that may use them.
|
||||||
|
|
||||||
|
Benboncan (CC BY 3.0)
|
||||||
|
https://www.freesound.org/people/Benboncan/sounds/66457/
|
||||||
|
fire_flint_and_steel.ogg
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
-- minetest/fire/init.lua
|
|
||||||
|
|
||||||
-- Global namespace for functions
|
-- Global namespace for functions
|
||||||
|
|
||||||
fire = {}
|
fire = {}
|
||||||
|
|
||||||
|
|
||||||
-- Register flame nodes
|
--
|
||||||
|
-- Items
|
||||||
|
--
|
||||||
|
|
||||||
|
-- Flame nodes
|
||||||
|
|
||||||
minetest.register_node("fire:basic_flame", {
|
minetest.register_node("fire:basic_flame", {
|
||||||
drawtype = "firelike",
|
drawtype = "firelike",
|
||||||
|
@ -34,22 +36,17 @@ minetest.register_node("fire:basic_flame", {
|
||||||
minetest.remove_node(pos)
|
minetest.remove_node(pos)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
-- restart timer
|
-- Restart timer
|
||||||
return true
|
return true
|
||||||
end,
|
end,
|
||||||
drop = "",
|
drop = "",
|
||||||
|
|
||||||
on_construct = function(pos)
|
on_construct = function(pos)
|
||||||
minetest.get_node_timer(pos):start(math.random(30, 60))
|
minetest.get_node_timer(pos):start(math.random(30, 60))
|
||||||
minetest.after(0, fire.update_sounds_around, pos)
|
|
||||||
end,
|
end,
|
||||||
|
|
||||||
on_destruct = function(pos)
|
on_blast = function() -- Unaffected by explosions
|
||||||
minetest.after(0, fire.update_sounds_around, pos)
|
|
||||||
end,
|
end,
|
||||||
|
|
||||||
on_blast = function()
|
|
||||||
end, -- unaffected by explosions
|
|
||||||
})
|
})
|
||||||
|
|
||||||
minetest.register_node("fire:permanent_flame", {
|
minetest.register_node("fire:permanent_flame", {
|
||||||
|
@ -76,7 +73,7 @@ minetest.register_node("fire:permanent_flame", {
|
||||||
groups = {igniter = 2, dig_immediate = 3},
|
groups = {igniter = 2, dig_immediate = 3},
|
||||||
drop = "",
|
drop = "",
|
||||||
|
|
||||||
on_blast = function()
|
on_blast = function() -- Unaffected by explosions
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -113,10 +110,10 @@ minetest.register_tool("fire:flint_and_steel", {
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if not minetest.setting_getbool("creative_mode") then
|
if not minetest.setting_getbool("creative_mode") then
|
||||||
-- wear tool
|
-- Wear tool
|
||||||
local wdef = itemstack:get_definition()
|
local wdef = itemstack:get_definition()
|
||||||
itemstack:add_wear(1000)
|
itemstack:add_wear(1000)
|
||||||
-- tool break sound
|
-- Tool break sound
|
||||||
if itemstack:get_count() == 0 and wdef.sound and wdef.sound.breaks then
|
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})
|
minetest.sound_play(wdef.sound.breaks, {pos = pt.above, gain = 0.5})
|
||||||
end
|
end
|
||||||
|
@ -151,72 +148,134 @@ minetest.override_item("default:coalblock", {
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
-- Get sound area of position
|
|
||||||
|
|
||||||
fire.D = 6 -- size of sound areas
|
--
|
||||||
|
-- Sound
|
||||||
|
--
|
||||||
|
|
||||||
function fire.get_area_p0p1(pos)
|
local flame_sound = minetest.setting_getbool("flame_sound")
|
||||||
local p0 = {
|
if flame_sound == nil then
|
||||||
x = math.floor(pos.x / fire.D) * fire.D,
|
-- Enable if no setting present
|
||||||
y = math.floor(pos.y / fire.D) * fire.D,
|
flame_sound = true
|
||||||
z = math.floor(pos.z / fire.D) * fire.D,
|
end
|
||||||
}
|
|
||||||
local p1 = {
|
if flame_sound then
|
||||||
x = p0.x + fire.D - 1,
|
|
||||||
y = p0.y + fire.D - 1,
|
local handles = {}
|
||||||
z = p0.z + fire.D - 1
|
local timer = 0
|
||||||
}
|
|
||||||
return p0, p1
|
-- Parameters
|
||||||
|
|
||||||
|
local radius = 8 -- Flame node search radius around player
|
||||||
|
local cycle = 3 -- Cycle time for sound updates
|
||||||
|
|
||||||
|
-- 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
|
||||||
|
-- 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
|
end
|
||||||
|
|
||||||
|
|
||||||
-- Fire sounds table
|
-- Deprecated function kept temporarily to avoid crashes if mod fire nodes call it
|
||||||
-- 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)
|
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,
|
|
||||||
}
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- ABMs
|
||||||
|
--
|
||||||
|
|
||||||
-- Extinguish all flames quickly with water, snow, ice
|
-- Extinguish all flames quickly with water, snow, ice
|
||||||
|
|
||||||
minetest.register_abm({
|
minetest.register_abm({
|
||||||
|
@ -229,7 +288,7 @@ minetest.register_abm({
|
||||||
action = function(pos, node, active_object_count, active_object_count_wider)
|
action = function(pos, node, active_object_count, active_object_count_wider)
|
||||||
minetest.remove_node(pos)
|
minetest.remove_node(pos)
|
||||||
minetest.sound_play("fire_extinguish_flame",
|
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,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -245,7 +304,7 @@ end
|
||||||
|
|
||||||
if not fire_enabled then
|
if not fire_enabled then
|
||||||
|
|
||||||
-- Remove basic flames only
|
-- Remove basic flames only if fire disabled
|
||||||
|
|
||||||
minetest.register_abm({
|
minetest.register_abm({
|
||||||
label = "Remove disabled fire",
|
label = "Remove disabled fire",
|
||||||
|
@ -279,7 +338,7 @@ else -- Fire enabled
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
-- Remove flammable nodes
|
-- Remove flammable nodes around basic flame
|
||||||
|
|
||||||
minetest.register_abm({
|
minetest.register_abm({
|
||||||
label = "Remove flammable nodes",
|
label = "Remove flammable nodes",
|
||||||
|
@ -291,7 +350,6 @@ else -- Fire enabled
|
||||||
action = function(pos, node, active_object_count, active_object_count_wider)
|
action = function(pos, node, active_object_count, active_object_count_wider)
|
||||||
local p = minetest.find_node_near(pos, 1, {"group:flammable"})
|
local p = minetest.find_node_near(pos, 1, {"group:flammable"})
|
||||||
if p then
|
if p then
|
||||||
-- remove flammable nodes around flame
|
|
||||||
local flammable_node = minetest.get_node(p)
|
local flammable_node = minetest.get_node(p)
|
||||||
local def = minetest.registered_nodes[flammable_node.name]
|
local def = minetest.registered_nodes[flammable_node.name]
|
||||||
if def.on_burn then
|
if def.on_burn then
|
||||||
|
@ -305,35 +363,3 @@ else -- Fire enabled
|
||||||
})
|
})
|
||||||
|
|
||||||
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 = 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,
|
|
||||||
})
|
|
||||||
--]]
|
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -13,6 +13,9 @@ creative_mode (Creative mode) bool false
|
||||||
# 'permanent_flame' nodes are unaffected.
|
# 'permanent_flame' nodes are unaffected.
|
||||||
enable_fire (Fire) bool true
|
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
|
# If enabled, steel tools, torches and cobblestone will be given to new
|
||||||
# players.
|
# players.
|
||||||
give_initial_stuff (Give initial items) bool false
|
give_initial_stuff (Give initial items) bool false
|
||||||
|
|
Loading…
Reference in New Issue