mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-16 21:33:46 +03:00
275 lines
8.9 KiB
Lua
275 lines
8.9 KiB
Lua
--[[
|
|
| This file was obtained through the combined efforts
|
|
| of Madbluntz & Plymouth Antiquarian Society.
|
|
|
|
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
|
| Maloy, DrPepper10 @ RIP, Atle!
|
|
|
|
|
| Visit for more: https://plymouth.thetwilightzone.ru/
|
|
--]]
|
|
|
|
SF_AMB_SND = SF_AMB_SND or {}
|
|
SF_AMB_CHANNEL = SF_AMB_CHANNEL or {} -- [snd]{station, target_vol, current_vol}
|
|
|
|
StormFox2.Ambience = {}
|
|
|
|
--[[
|
|
- Outside Constant
|
|
- Near Outside 3D
|
|
- Near Window By Distance to nearest
|
|
- Roof By Distance to nearest
|
|
- Glass Roof (Like window) By Distance to nearest
|
|
- Metal Roof By Distance to nearest
|
|
]]
|
|
|
|
--[[ Enums
|
|
SF_AMB_CONSTANT = 0
|
|
SF_AMB_DISTANCE = 1
|
|
SF_AMB_FAKE3D = 2 -- Pans the sound towards the point
|
|
SF_AMB_USE3D = 3
|
|
]]
|
|
|
|
SF_AMB_OUTSIDE = 0 -- CONSTANT VOLUME
|
|
SF_AMB_NEAR_OUTSIDE = 1 -- DISTANCE VOLUME
|
|
SF_AMB_WINDOW = 2 -- DISTANCE VOLUME
|
|
SF_AMB_UNDER_WATER = 3 -- CONSTANT VOLUME
|
|
SF_AMB_UNDER_WATER_Z = 4 -- Z-DISTANCE VOLUME (The distance to surface)
|
|
SF_AMB_ROOF_ANY = 5 -- Z-DISTANCE (SF_AMB_ROOF_CONCRETE and SF_AMB_ROOF_GROUND will be ignored)
|
|
SF_AMB_ROOF_GLASS = 6 -- Z-DISTANCE
|
|
SF_AMB_ROOF_METAL = 7 -- Z-DISTANCE
|
|
SF_AMB_ROOF_WOOD = 8 -- Z-DISTANCE
|
|
SF_AMB_ROOF_CONCRETE = 9 -- Z-DISTANCE
|
|
SF_AMB_ROOF_GROUND = 10-- Z-DISTANCE (Default roof)
|
|
SF_AMB_ROOF_WATER = 11-- Z-DISTANCE
|
|
|
|
-- Smooth the volume of SF_AMB_CHANNEL
|
|
hook.Add("Think", "StormFox2.Ambiences.Smooth", function()
|
|
for snd,t in pairs( SF_AMB_CHANNEL ) do
|
|
if not IsValid( t[1] ) then -- In case something goes wrong. Delete the channel
|
|
SF_AMB_CHANNEL[snd] = nil
|
|
continue
|
|
end
|
|
-- Calc the new volume
|
|
local c_vol = t[3]
|
|
local newvol = math.Approach( c_vol, t[2], FrameTime() )
|
|
if c_vol == newvol then continue end
|
|
if newvol <= 0 then
|
|
-- Stop the sound and remove channel
|
|
t[1]:Stop()
|
|
SF_AMB_CHANNEL[snd] = nil
|
|
else
|
|
if system.HasFocus() then -- We don't want sound playing when gmod is unfocused.
|
|
t[1]:SetVolume( newvol )
|
|
else
|
|
t[1]:SetVolume( 0 )
|
|
end
|
|
SF_AMB_CHANNEL[snd][3] = newvol
|
|
end
|
|
end
|
|
end)
|
|
|
|
local AMB_LOAD = {}
|
|
-- Handles the sound-channel.
|
|
local function RequestChannel( snd )
|
|
if AMB_LOAD[snd] then return end -- Already loading, or error
|
|
if SF_AMB_CHANNEL[snd] then return end -- Already loaded
|
|
AMB_LOAD[snd] = true
|
|
sound.PlayFile( snd, "noblock noplay", function( station, errCode, errStr )
|
|
if ( IsValid( station ) ) then
|
|
SF_AMB_CHANNEL[snd] = {station, 0.1, 0}
|
|
station:SetVolume( 0 )
|
|
station:EnableLooping( true )
|
|
station:Play()
|
|
AMB_LOAD[snd] = nil -- Allow it to be loaded again
|
|
else
|
|
if errCode == 1 then
|
|
StormFox2.Warning("Sound Error! [1] Memory error.")
|
|
elseif errCode == 2 then
|
|
StormFox2.Warning("Sound Error! [2] Unable to locate or open: " .. snd .. ".")
|
|
else
|
|
StormFox2.Warning("Sound Error! [" .. errCode .. "] " .. errStr .. ".")
|
|
end
|
|
end
|
|
end)
|
|
end
|
|
|
|
local snd_meta = {}
|
|
snd_meta.__index = snd_meta
|
|
|
|
---Creates an ambience sound and returns a sound-object.
|
|
---@param snd string
|
|
---@param SF_AMB_TYPE number
|
|
---@param vol_scale? number
|
|
---@param min? number
|
|
---@param max? number
|
|
---@param playrate? number
|
|
---@return table
|
|
---@client
|
|
function StormFox2.Ambience.CreateAmbienceSnd( snd, SF_AMB_TYPE, vol_scale, min, max, playrate )
|
|
local t = {}
|
|
t.snd = "sound/" .. snd
|
|
t.m_vol = vol_scale or 1
|
|
t.min = min or 60
|
|
t.max = max or 300
|
|
t.SF_AMB_TYPE = SF_AMB_TYPE or SF_AMB_OUTSIDE
|
|
t.playbackrate = playrate or 1
|
|
setmetatable( t , snd_meta )
|
|
return t
|
|
end
|
|
|
|
---Returns the current sound channels / data.
|
|
---@return table
|
|
---@client
|
|
function StormFox2.Ambience.DebugList()
|
|
return SF_AMB_CHANNEL
|
|
end
|
|
-- Sets the scale of the sound
|
|
function snd_meta:SetVolume( num )
|
|
self.m_vol = math.Clamp(num, 0, 2) -- Just in case
|
|
end
|
|
-- Doesn't work on sounds with SF_AMB_OUTSIDE or SF_AMB_UNDER_WATER
|
|
function snd_meta:SetFadeDistance( min, max )
|
|
self.min = min
|
|
self.max = max
|
|
end
|
|
-- Set playback rate.
|
|
function snd_meta:SetPlaybackRate( n )
|
|
self.playbackrate = n or 1
|
|
end
|
|
-- Adds ambience for weather
|
|
hook.Add("stormfox2.preloadweather", "StormFox2.Amb.Create", function( w_meta )
|
|
function w_meta:AddAmbience( amb_object )
|
|
if not self.ambience_tab then self.ambience_tab = {} end
|
|
table.insert(self.ambience_tab, amb_object)
|
|
end
|
|
function w_meta:ClearAmbience()
|
|
self.ambience_tab = {}
|
|
end
|
|
hook.Remove("stormfox2.preloadweather", "StormFox2.Amb.Create")
|
|
end)
|
|
-- Applies the ambience sound
|
|
local function check(SF_AMB_TYPE, env)
|
|
if SF_AMB_TYPE == SF_AMB_NEAR_OUTSIDE and env.nearest_outside then return env.nearest_outside end
|
|
if SF_AMB_TYPE == SF_AMB_WINDOW and env.nearest_window then return env.nearest_window end
|
|
end
|
|
|
|
local p_br = {}
|
|
-- Forces a sound to play
|
|
local fP
|
|
|
|
---Insers ambience sound and forces it to play.
|
|
---@param snd string
|
|
---@param nVolume number
|
|
---@param playbackSpeed number
|
|
---@client
|
|
function StormFox2.Ambience.ForcePlay( snd, nVolume, playbackSpeed )
|
|
if string.sub(snd, 0, 6) ~= "sound/" then
|
|
snd = "sound/" .. snd
|
|
end
|
|
fP[snd] = nVolume
|
|
p_br[snd] = playbackSpeed or 1
|
|
end
|
|
hook.Add("Think", "StormFox2.Ambiences.Logic", function()
|
|
if not StormFox2 or not StormFox2.Weather or not StormFox2.Weather.GetCurrent then return end
|
|
local c = StormFox2.Weather.GetCurrent()
|
|
local v_pos = StormFox2.util.GetCalcView().pos
|
|
local env = StormFox2.Environment.Get()
|
|
-- Set all target volume to 0
|
|
for _,t2 in pairs( SF_AMB_CHANNEL ) do
|
|
t2[2] = 0
|
|
end
|
|
-- Generate a list of all sounds the client should hear. And set the the volume
|
|
local t = {}
|
|
if c.ambience_tab and StormFox2.Setting.SFEnabled() then
|
|
for _,amb_object in ipairs( c.ambience_tab ) do
|
|
local c_vol = t[amb_object.snd] or 0
|
|
-- WATER
|
|
if env.in_water then -- All sounds gets ignored in water. Exp SF_AMB_INWATER
|
|
if amb_object.SF_AMB_TYPE == SF_AMB_INWATER then
|
|
if c_vol > amb_object.m_vol then
|
|
continue
|
|
end
|
|
c_vol = amb_object.m_vol
|
|
elseif env.outside and amb_object.SF_AMB_TYPE == SF_AMB_UNDER_WATER_Z then
|
|
local dis = env.in_water - v_pos.z
|
|
local vol = math.min(1 - ( dis - amb_object.min ) / ( amb_object.max - amb_object.min ) , 1) * amb_object.m_vol
|
|
if c_vol > vol then
|
|
continue
|
|
end
|
|
c_vol = vol
|
|
end
|
|
-- OUTSIDE
|
|
elseif amb_object.SF_AMB_TYPE == SF_AMB_OUTSIDE and env.outside then
|
|
if c_vol > amb_object.m_vol then
|
|
continue
|
|
end
|
|
c_vol = amb_object.m_vol -- Outside is a constant volume
|
|
-- ROOFS
|
|
elseif amb_object.SF_AMB_TYPE >= SF_AMB_ROOF_ANY and amb_object.SF_AMB_TYPE <= SF_AMB_ROOF_WATER then
|
|
if amb_object.SF_AMB_TYPE == SF_AMB_ROOF_ANY and env.roof_z then
|
|
if env.roof_type ~= SF_AMB_ROOF_CONCRETE and env.roof_type ~= SF_AMB_ROOF_GROUND then
|
|
local dis = env.roof_z - v_pos.z
|
|
local vol = math.min(1 - ( dis - amb_object.min ) / ( amb_object.max - amb_object.min ) , 1) * amb_object.m_vol
|
|
if c_vol > vol then
|
|
continue
|
|
end
|
|
c_vol = vol
|
|
end
|
|
elseif env.roof_z and env.roof_type then
|
|
if amb_object.SF_AMB_TYPE == SF_AMB_ROOF_GROUND and env.roof_type == SF_DOWNFALL_HIT_GROUND then
|
|
elseif amb_object.SF_AMB_TYPE == SF_AMB_ROOF_GLASS and env.roof_type == SF_DOWNFALL_HIT_GLASS then
|
|
elseif amb_object.SF_AMB_TYPE == SF_AMB_ROOF_METAL and env.roof_type == SF_DOWNFALL_HIT_METAL then
|
|
elseif amb_object.SF_AMB_TYPE == SF_AMB_ROOF_WOOD and env.roof_type == SF_DOWNFALL_HIT_WOOD then
|
|
elseif amb_object.SF_AMB_TYPE == SF_AMB_ROOF_CONCRETE and env.roof_type == SF_DOWNFALL_HIT_CONCRETE then
|
|
elseif amb_object.SF_AMB_TYPE == SF_AMB_ROOF_WATER and env.roof_type == SF_DOWNFALL_HIT_WATER then
|
|
else
|
|
continue
|
|
end
|
|
local dis = env.roof_z - v_pos.z
|
|
local vol = math.min(1 - ( dis - amb_object.min ) / ( amb_object.max - amb_object.min ) , 1) * amb_object.m_vol
|
|
if c_vol > vol then
|
|
continue
|
|
end
|
|
c_vol = vol
|
|
end
|
|
else
|
|
local pos = check( amb_object.SF_AMB_TYPE, env )
|
|
if not pos then continue end
|
|
local dis = pos:Distance( v_pos )
|
|
--if amb_object.SF_AMB_TYPE == SF_AMB_WINDOW and env.nearest_outside then
|
|
-- dis = math.max(dis, 250 - env.nearest_outside:Distance(v_pos))
|
|
|
|
--end
|
|
if dis > amb_object.max then continue end -- Too far away
|
|
local vol = math.min(1 - ( dis - amb_object.min ) / ( amb_object.max - amb_object.min ) , 1) * amb_object.m_vol
|
|
if vol <= 0 then continue end -- Vol too low
|
|
if c_vol > vol then
|
|
continue
|
|
end
|
|
c_vol = vol
|
|
end
|
|
if c_vol > 0 then
|
|
t[amb_object.snd] = c_vol
|
|
p_br[amb_object.snd] = amb_object.playbackrate
|
|
end
|
|
end
|
|
end
|
|
fP = t
|
|
hook.Run("StormFox2.Ambiences.OnSound")
|
|
-- Set the target volume
|
|
for snd, vol in pairs( t ) do
|
|
if not SF_AMB_CHANNEL[snd] then -- Request to create the sound channel
|
|
RequestChannel( snd )
|
|
else
|
|
SF_AMB_CHANNEL[snd][2] = vol -- Set the target volume
|
|
if IsValid( SF_AMB_CHANNEL[snd][1] ) then
|
|
if SF_AMB_CHANNEL[snd][1]:GetState() == 0 then -- Somehow stopped
|
|
SF_AMB_CHANNEL[snd][1]:Play()
|
|
end
|
|
if SF_AMB_CHANNEL[snd][1]:GetPlaybackRate() ~= p_br[snd] then
|
|
SF_AMB_CHANNEL[snd][1]:SetPlaybackRate(p_br[snd])
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end) |