# Ambience

So I have started coding a simple ambience system. I have ran into a slight problem. I have an idea on how to make the ambience’s play after eachother but no idea how to code it. My idea is to declare how many seconds one ambience mp3 file is. Then once that length of time has passed, play a new random one. Help is appreciated. Here is my code:
Oh and, I may have some errors in here (its untested) so if you see any please point them out. Thanks.

[lua]
local toggled = false

AmbienceIndex = {
“colzdragon/ambience/1.mp3”,
“colzdragon/ambience/2.mp3”,
“colzdragon/ambience/3.mp3”,
“colzdragon/ambience/4.mp3”
}

for k,v in pairs(AmbienceIndex) do
for c,d in pairs(AmbienceIndex[k]) do
util.PrecacheSound(d)
end
end

function PlayAmbience( ply )
if toggled then
ply:ConCommand( “play colzdragon/ambience/”…math.random(1,4)…".wav" )
ply:SetNWBool( “colz_ambience”, true )
else
ply:SetNWBool( “colz_ambience”, false )
end
end

function ToggleAmbience( ply )
toggled = !toggled
PlayAmbience( ply )
end
[/lua]

[lua]
local playing = false

local AmbienceIndex = {
{ song = “colzdragon/ambience/1.mp3”, length = 1234 },
{ song = “colzdragon/ambience/2.mp3”, length = 2345 },
{ song = “colzdragon/ambience/3.mp3”, length = 3456 },
{ song = “colzdragon/ambience/4.mp3”, length = 4567 }
}

for k,v in pairs(AmbienceIndex) do
util.PrecacheSound( v.song )
end

function PlayAmbience( ply, play )
if play then
local index = math.random( 1,4 )
ply:ConCommand( "play " … AmbienceIndex[index].song )
ply:SetNWBool( “colz_ambience”, true )
timer.Simple( AmbienceIndex[index].length, function()
if ply and ply:IsValid() then
PlayAmbience( ply, true )
end
end )
else
ply:SetNWBool( “colz_ambience”, false )
end
playing = play
end

function ToggleAmbience( ply )
PlayAmbience( ply, !playing )
end
[/lua]

Instead of manually getting the length, you can use SoundDuration to get the length of the sound file.

Here’s the ambience script I made for my gamemode:

[lua]local ambience = {
[“dronescape”] = {
Sound(“ta/ambient1.wav”),
Sound(“ta/ambient2.wav”),
Sound(“ta/ambient3.wav”),
Sound(“ta/ambient4.wav”),
Sound(“ta/ambient5.wav”),
Sound(“ta/ambient6.wav”),
Sound(“ta/ambient7.wav”),
Sound(“ta/ambient8.wav”),
Sound(“ta/ambient9.wav”),
},
[“battle”] = {
Sound(“ambient/explosions/battle_loop1.wav”),
Sound(“ambient/explosions/battle_loop2.wav”),
Sound(“ambient/levels/streetwar/strider_distant_walk1.wav”),
Sound(“ambient/levels/streetwar/city_riot2.wav”),
Sound(“ambient/levels/streetwar/heli_distant1.wav”),
Sound(“ambient/levels/streetwar/apc_distant1.wav”),
Sound(“ambient/levels/streetwar/apc_distant3.wav”),
Sound(“ambient/levels/streetwar/city_battle4.wav”),
},
[“wind”] = {
Sound(“ambient/wind/windgust_strong.wav”),
Sound(“ambient/wind/windgust.wav”),
Sound(“ambient/wind/windgust_med1.wav”),
Sound(“ambient/wind/windgust_snippet5.wav”),
Sound(“ambient/wind/wasteland_wind.wav”),
},
[“lake”] = {
Sound(“ambient/water/lake_water.wav”),
},
[“klaxon_alarm”] = { Sound(“ambient/alarms/klaxon1.wav”) },
[“bank_alarm”] = { Sound(“ambient/alarms/combine_bank_alarm_loop4.wav”) },
[“apc_alarm”] = {Sound(“ambient/alarms/apc_alarm_loop1.wav”) },
}

local cur_sound = nil
function LoopAmbience(snd)

``````if cur_sound then cur_sound:Stop() end
cur_sound = CreateSound(LocalPlayer(),snd)
cur_sound:PlayEx(0.1,100)
cur_sound:SetSoundLevel(1.25)

local tab = ""
if not ambience[GetGlobalString("ta_ambience")] then tab = "dronescape" else tab = GetGlobalString("ta_ambience") end
timer.Simple(SoundDuration(snd),function() LoopAmbience(table.Random(ambience[tab])) end)
``````

end
timer.Simple(10,function() LoopAmbience(ambience[“dronescape”][1]) end)[/lua]

[lua]
local toggled = false

AmbienceIndex = {
“colzdragon/ambience/1.mp3”,
“colzdragon/ambience/2.mp3”,
“colzdragon/ambience/3.mp3”,
“colzdragon/ambience/4.mp3”
}

for k,v in pairs(AmbienceIndex) do
for c,d in pairs(AmbienceIndex) do
util.PrecacheSound(d)
end
end

function PlayAmbience( ply )
if not ValidEntity(ply) then return end --exit out of the function if the player left or something
if ply.ambtoggled then
local whichtoplay = math.random(1, 4)
ply:ConCommand( "play " … AmbienceIndex[whichtoplay] )
ply:SetNWBool( “colz_ambience”, true )
timer.Create(“colz_amb”…ply:UniqueID(), SoundDuration(AmbienceIndex[whichtoplay]), 1, PlayAmbience, ply)
else
timer.Remove(“colz_amb”…ply:UniqueID())
ply:SetNWBool( “colz_ambience”, false )
end
end

function ToggleAmbience( ply )
ply.ambtoggled = !ply.ambtoggled or true --switch the player’s ambience toggled value between true and false, or set it to true if it doesn’t exist yet
PlayAmbience( ply )
end
[/lua]
So what it does is it sets up a timer, named uniquely for the player, to start a new random ambience when the timer is up. If you put the command in again to toggle it off it destroys the timer. It also checks to see if the player left since then, and quits out of the function if he’s not a fully valid player entity we can work with. Also when you use pairs, just put the name of the table.

What’s the point of using it on the server? Why use useless networked bools, unnecessary concommands, and oddly named timers when you can just do it on the client with a sound patch?

I agree actually. The only ‘advantage’ is that all players hear the same ambience, which isn’t really important because it’s ambience. It’s supposed to be in the background, something you don’t really focus on.

[lua]if SERVER then AddCSLuaFile( “ambient.lua” ) return end

local playing = false
local curSong

local songs = {
Sound “colzdragon/ambience/1.mp3”,
Sound “colzdragon/ambience/2.mp3”,
Sound “colzdragon/ambience/3.mp3”,
Sound “colzdragon/ambience/4.mp3”
}

function PlayAmbience( play )
if play then
local song = math.random( 1,#songs )
curSong = CreateSound( LocalPlayer(), songs[song] )
curSong:PlayEx( 0.1, 100 )
curSong:SetSoundLevel(1.25)
timer.Simple( SoundDuration( songs[song] ), function()
PlayAmbience( playing ) --Play the next song only if it was already playing
end )
else
curSong:Stop()
end
playing = play
end

function ToggleAmbience()
PlayAmbience( !playing )
end