diff --git a/OpenRA.Game/GameRules/SoundInfo.cs b/OpenRA.Game/GameRules/SoundInfo.cs index 555af9052dd4..954a3b42c6fb 100644 --- a/OpenRA.Game/GameRules/SoundInfo.cs +++ b/OpenRA.Game/GameRules/SoundInfo.cs @@ -21,6 +21,7 @@ public class SoundInfo public readonly Dictionary Prefixes = new Dictionary(); public readonly Dictionary Voices = new Dictionary(); public readonly Dictionary Notifications = new Dictionary(); + public readonly Dictionary NotificationRateLimits = new Dictionary(); public readonly string DefaultVariant = ".aud"; public readonly string DefaultPrefix = ""; public readonly HashSet DisableVariants = new HashSet(); @@ -33,19 +34,26 @@ public SoundInfo(MiniYaml y) { FieldLoader.Load(this, y); - VoicePools = Exts.Lazy(() => Voices.ToDictionary(a => a.Key, a => new SoundPool(a.Value))); - NotificationsPools = Exts.Lazy(() => Notifications.ToDictionary(a => a.Key, a => new SoundPool(a.Value))); + VoicePools = Exts.Lazy(() => Voices.ToDictionary(a => a.Key, + a => new SoundPool(NotificationRateLimits.ContainsKey(a.Key) ? NotificationRateLimits[a.Key] : 0, a.Value))); + NotificationsPools = Exts.Lazy(() => Notifications.ToDictionary(a => a.Key, + a => new SoundPool(NotificationRateLimits.ContainsKey(a.Key) ? NotificationRateLimits[a.Key] : 0, a.Value))); } } public class SoundPool { readonly string[] clips; + readonly int rateLimit; readonly List liveclips = new List(); + readonly TimeSpan rateSpan; + DateTime lastPlayed = DateTime.MinValue; - public SoundPool(params string[] clips) + public SoundPool(int rateLimit, params string[] clips) { this.clips = clips; + this.rateLimit = rateLimit; + this.rateSpan = new TimeSpan(0, 0, 0, 0, rateLimit); } public string GetNext() @@ -54,7 +62,16 @@ public string GetNext() liveclips.AddRange(clips); if (liveclips.Count == 0) - return null; /* avoid crashing if there's no clips at all */ + return null; /* avoid crashing if there's no clips at all */ + + // Perform rate limiting if necessary + if (rateLimit != 0) + { + var now = DateTime.Now; + if (lastPlayed != DateTime.MinValue && now < lastPlayed + rateSpan) + return null; + lastPlayed = now; + } var i = Game.CosmeticRandom.Next(liveclips.Count); var s = liveclips[i]; diff --git a/OpenRA.Game/Sound/Sound.cs b/OpenRA.Game/Sound/Sound.cs index 91374715439b..2e9f9f6fd160 100644 --- a/OpenRA.Game/Sound/Sound.cs +++ b/OpenRA.Game/Sound/Sound.cs @@ -12,9 +12,11 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using OpenRA.FileSystem; using OpenRA.GameRules; using OpenRA.Primitives; +using OpenRA.Traits; namespace OpenRA { diff --git a/mods/cnc/audio/notifications.yaml b/mods/cnc/audio/notifications.yaml index 7a4cd1fcdae9..46c48e86112b 100644 --- a/mods/cnc/audio/notifications.yaml +++ b/mods/cnc/audio/notifications.yaml @@ -46,6 +46,11 @@ Speech: UnitReady: unitredy Win: accom1 DisablePrefixes: AbilityInsufficientPower, BaseAttack, Building, BuildingCannotPlaceAudio, BuildingInProgress, BuildingLost, Cancelled, CivilianBuildingCaptured, CivilianKilled, ConstructionComplete, EnemyUnitsApproaching, EnemyStructureDestroyed, EnemyPlanesApproaching, HarvesterAttack, InsufficientPower, IonCannonCharging, IonCannonReady, Leave, Lose, LowPower, MissionAccomplished, MissionFailed, NewOptions, NoBuild, NodStructureDestroyed, NotReady, NuclearWarheadApproaching, NuclearWeaponAvailable, NuclearWeaponLaunched, OnHold, PrimaryBuildingSelected, Reinforce, Repairing, SelectTarget, SilosNeeded, Training, UnitLost, UnitReady, Win + NotificationRateLimits: + Building: 600 + Training: 800 + UnitReady: 900 + Cancelled: 500 Sounds: Notifications: diff --git a/mods/d2k/audio/notifications.yaml b/mods/d2k/audio/notifications.yaml index d742b91c2b74..aacab8ecd457 100644 --- a/mods/d2k/audio/notifications.yaml +++ b/mods/d2k/audio/notifications.yaml @@ -58,6 +58,11 @@ Speech: Win: MWIN WormAttack: WATTK WormSign: WSIGN + NotificationRateLimits: + Building: 600 + Training: 800 + UnitReady: 900 + Cancelled: 500 Sounds: DefaultVariant: .WAV diff --git a/mods/ra/audio/notifications.yaml b/mods/ra/audio/notifications.yaml index f85f23655357..1b9af4818452 100644 --- a/mods/ra/audio/notifications.yaml +++ b/mods/ra/audio/notifications.yaml @@ -113,6 +113,11 @@ Speech: WarningFourMinutesRemaining: 4minr WarningFiveMinutesRemaining: 5minr Win: misnwon1 + NotificationRateLimits: + Building: 600 + Training: 800 + UnitReady: 900 + Cancelled: 500 Sounds: Notifications: diff --git a/mods/ts/audio/speech-generic.yaml b/mods/ts/audio/speech-generic.yaml index b616dd172d4f..b5f0a8ff879c 100644 --- a/mods/ts/audio/speech-generic.yaml +++ b/mods/ts/audio/speech-generic.yaml @@ -81,3 +81,8 @@ Speech: WarningInboundTacticalNukeDetected: 00-n098 Win: 00-i284 YouHaveResigned: 00-i288 + NotificationRateLimits: + Building: 600 + Training: 800 + UnitReady: 900 + Cancelled: 500