#region Copyright & License Information /* * Copyright 2007-2020 The OpenRA Developers (see AUTHORS) * This file is part of OpenRA, which is free software. It is made * available to you under the terms of the GNU General Public License * as published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version. For more * information, see COPYING. */ #endregion using System.Collections.Generic; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits.Sound { [Desc("Plays a looping audio file at the actor position. Attach this to the `World` actor to cover the whole map.")] class AmbientSoundInfo : ConditionalTraitInfo { [FieldLoader.Require] public readonly string[] SoundFiles = null; [Desc("Initial delay (in ticks) before playing the sound for the first time.", "Two values indicate a random delay range.")] public readonly int[] Delay = { 0 }; [Desc("Interval between playing the sound (in ticks).", "Two values indicate a random delay range.")] public readonly int[] Interval = { 0 }; public override object Create(ActorInitializer init) { return new AmbientSound(init.Self, this); } } class AmbientSound : ConditionalTrait, ITick, INotifyRemovedFromWorld { readonly bool loop; HashSet currentSounds = new HashSet(); WPos cachedPosition; int delay; public AmbientSound(Actor self, AmbientSoundInfo info) : base(info) { delay = Util.RandomDelay(self.World, info.Delay); loop = Info.Interval.Length == 0 || (Info.Interval.Length == 1 && Info.Interval[0] == 0); } void ITick.Tick(Actor self) { if (IsTraitDisabled) return; currentSounds.RemoveWhere(s => s == null || s.Complete); if (self.OccupiesSpace != null) { var pos = self.CenterPosition; if (pos != cachedPosition) { foreach (var s in currentSounds) s.SetPosition(pos); cachedPosition = pos; } } if (delay < 0) return; if (--delay < 0) { StartSound(self); if (!loop) delay = Util.RandomDelay(self.World, Info.Interval); } } void StartSound(Actor self) { var sound = Info.SoundFiles.RandomOrDefault(Game.CosmeticRandom); ISound s; if (self.OccupiesSpace != null) { cachedPosition = self.CenterPosition; s = loop ? Game.Sound.PlayLooped(SoundType.World, sound, cachedPosition) : Game.Sound.Play(SoundType.World, sound, self.CenterPosition); } else s = loop ? Game.Sound.PlayLooped(SoundType.World, sound) : Game.Sound.Play(SoundType.World, sound); currentSounds.Add(s); } void StopSound() { foreach (var s in currentSounds) Game.Sound.StopSound(s); currentSounds.Clear(); } protected override void TraitEnabled(Actor self) { delay = Util.RandomDelay(self.World, Info.Delay); } protected override void TraitDisabled(Actor self) { StopSound(); } void INotifyRemovedFromWorld.RemovedFromWorld(Actor self) { StopSound(); } } }