#region Copyright & License Information /* * Copyright (c) The OpenRA Developers and Contributors * 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; using System.Collections.Frozen; using System.Collections.Generic; using System.Collections.Immutable; namespace OpenRA.GameRules { public class SoundInfo { public readonly FrozenDictionary> Variants = FrozenDictionary>.Empty; public readonly FrozenDictionary> Prefixes = FrozenDictionary>.Empty; public readonly FrozenDictionary> Voices = FrozenDictionary>.Empty; public readonly FrozenDictionary> Notifications = FrozenDictionary>.Empty; public readonly string DefaultVariant = ".aud"; public readonly string DefaultPrefix = ""; public readonly FrozenSet DisableVariants = FrozenSet.Empty; public readonly FrozenSet DisablePrefixes = FrozenSet.Empty; public readonly Lazy> VoicePools; public readonly Lazy> NotificationsPools; public SoundInfo(MiniYaml y) { FieldLoader.Load(this, y); VoicePools = Exts.Lazy(() => Voices.ToFrozenDictionary(a => a.Key, a => new SoundPool(1f, SoundPool.DefaultInterruptType, a.Value))); NotificationsPools = Exts.Lazy(() => ParseSoundPool(y, "Notifications")); } static FrozenDictionary ParseSoundPool(MiniYaml y, string key) { var classifiction = y.NodeWithKey(key); var ret = new Dictionary(classifiction.Value.Nodes.Length); foreach (var t in classifiction.Value.Nodes) { var volumeModifier = 1f; var volumeModifierNode = t.Value.NodeWithKeyOrDefault(nameof(SoundPool.VolumeModifier)); if (volumeModifierNode != null) volumeModifier = FieldLoader.GetValue(volumeModifierNode.Key, volumeModifierNode.Value.Value); var interruptType = SoundPool.DefaultInterruptType; var interruptTypeNode = t.Value.NodeWithKeyOrDefault(nameof(SoundPool.InterruptType)); if (interruptTypeNode != null) interruptType = FieldLoader.GetValue(interruptTypeNode.Key, interruptTypeNode.Value.Value); var names = FieldLoader.GetValue>(t.Key, t.Value.Value); var sp = new SoundPool(volumeModifier, interruptType, names); ret.Add(t.Key, sp); } return ret.ToFrozenDictionary(); } } public class SoundPool { public enum InterruptType { DoNotPlay, Interrupt, Overlap } public const InterruptType DefaultInterruptType = InterruptType.DoNotPlay; public readonly float VolumeModifier; public readonly InterruptType Type; readonly ImmutableArray clips; readonly List liveclips = []; public SoundPool(float volumeModifier, InterruptType interruptType, ImmutableArray clips) { VolumeModifier = volumeModifier; Type = interruptType; this.clips = clips; } public string GetNext() { if (liveclips.Count == 0) liveclips.AddRange(clips); // Avoid crashing if there's no clips at all if (liveclips.Count == 0) return null; var i = Game.CosmeticRandom.Next(liveclips.Count); var s = liveclips[i]; liveclips.RemoveAt(i); return s; } } }