From fb0e2c5cc3a70d3cb6a1be04574671ec78b4ad8c Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sat, 1 Aug 2015 19:28:33 +0100 Subject: [PATCH] Introduce background music concept. --- OpenRA.Game/Traits/World/MusicPlaylist.cs | 97 ++++++++++--------- .../Widgets/Logic/MusicPlayerLogic.cs | 28 ++++-- mods/cnc/maps/shellmap/map.yaml | 3 +- mods/d2k/maps/shellmap/map.yaml | 3 +- mods/ts/maps/blank-shellmap/map.yaml | 3 +- 5 files changed, 75 insertions(+), 59 deletions(-) diff --git a/OpenRA.Game/Traits/World/MusicPlaylist.cs b/OpenRA.Game/Traits/World/MusicPlaylist.cs index 7542d22e2b..e79777dba3 100644 --- a/OpenRA.Game/Traits/World/MusicPlaylist.cs +++ b/OpenRA.Game/Traits/World/MusicPlaylist.cs @@ -20,20 +20,15 @@ namespace OpenRA.Traits [Desc("Music to play when the map starts.", "Plays the first song on the playlist when undefined.")] public readonly string StartingMusic = null; - [Desc("Should the starting music loop?")] - public readonly bool LoopStartingMusic = false; - [Desc("Music to play when the game has been won.")] public readonly string VictoryMusic = null; - [Desc("Should the victory music loop?")] - public readonly bool LoopVictoryMusic = false; - [Desc("Music to play when the game has been lost.")] public readonly string DefeatMusic = null; - [Desc("Should the defeat music loop?")] - public readonly bool LoopDefeatMusic = false; + [Desc("This track is played when no other music is playing.", + "It cannot be paused, but can be overridden by selecting a new track.")] + public readonly string BackgroundMusic = null; public object Create(ActorInitializer init) { return new MusicPlaylist(init.World, this); } } @@ -41,18 +36,21 @@ namespace OpenRA.Traits public class MusicPlaylist : INotifyActorDisposing, IGameOver { readonly MusicPlaylistInfo info; + readonly World world; readonly MusicInfo[] random; readonly MusicInfo[] playlist; public readonly bool IsMusicAvailable; + public bool CurrentSongIsBackground { get; private set; } MusicInfo currentSong; - bool repeat; + MusicInfo currentBackgroundSong; public MusicPlaylist(World world, MusicPlaylistInfo info) { this.info = info; + this.world = world; IsMusicAvailable = world.Map.Rules.InstalledMusic.Any(); @@ -63,22 +61,31 @@ namespace OpenRA.Traits random = playlist.Shuffle(Game.CosmeticRandom).ToArray(); - if (!string.IsNullOrEmpty(info.StartingMusic) - && world.Map.Rules.Music.ContainsKey(info.StartingMusic) - && world.Map.Rules.Music[info.StartingMusic].Exists) + if (SongExists(info.StartingMusic)) { currentSong = world.Map.Rules.Music[info.StartingMusic]; - repeat = info.LoopStartingMusic; } - else + else if (SongExists(info.BackgroundMusic)) { - currentSong = Game.Settings.Sound.Shuffle ? random.First() : playlist.First(); - repeat = Game.Settings.Sound.Repeat; + currentSong = currentBackgroundSong = world.Map.Rules.Music[info.BackgroundMusic]; + CurrentSongIsBackground = true; } Play(); } + bool SongExists(string song) + { + return !string.IsNullOrEmpty(song) + && world.Map.Rules.Music.ContainsKey(song) + && world.Map.Rules.Music[song].Exists; + } + + bool SongExists(MusicInfo song) + { + return song != null && song.Exists; + } + public MusicInfo CurrentSong() { return currentSong; @@ -95,43 +102,34 @@ namespace OpenRA.Traits if (!IsMusicAvailable) return; - var playedSong = currentSong; - if (world.LocalPlayer != null && world.LocalPlayer.WinState == WinState.Won) { - if (!string.IsNullOrEmpty(info.VictoryMusic) - && world.Map.Rules.Music.ContainsKey(info.VictoryMusic) - && world.Map.Rules.Music[info.VictoryMusic].Exists) + if (SongExists(info.VictoryMusic)) { - currentSong = world.Map.Rules.Music[info.VictoryMusic]; - repeat = info.LoopVictoryMusic; + currentBackgroundSong = world.Map.Rules.Music[info.VictoryMusic]; + Stop(); } } else { // Most RTS treats observers losing the game, // no need for a special handling involving them here. - if (!string.IsNullOrEmpty(info.DefeatMusic) - && world.Map.Rules.Music.ContainsKey(info.DefeatMusic) - && world.Map.Rules.Music[info.DefeatMusic].Exists) + if (SongExists(info.DefeatMusic)) { - currentSong = world.Map.Rules.Music[info.DefeatMusic]; - repeat = info.LoopDefeatMusic; + currentBackgroundSong = world.Map.Rules.Music[info.DefeatMusic]; + Stop(); } } - - if (playedSong != currentSong) - Play(); } void Play() { - if (currentSong == null || !IsMusicAvailable) + if (!SongExists(currentSong) || !IsMusicAvailable) return; Sound.PlayMusicThen(currentSong, () => { - if (!repeat) + if (!CurrentSongIsBackground && !Game.Settings.Sound.Repeat) currentSong = GetNextSong(); Play(); @@ -144,15 +142,9 @@ namespace OpenRA.Traits return; currentSong = music; - repeat = Game.Settings.Sound.Repeat; + CurrentSongIsBackground = false; - Sound.PlayMusicThen(music, () => - { - if (!repeat) - currentSong = GetNextSong(); - - Play(); - }); + Play(); } public void Play(MusicInfo music, Action onComplete) @@ -161,6 +153,7 @@ namespace OpenRA.Traits return; currentSong = music; + CurrentSongIsBackground = false; Sound.PlayMusicThen(music, onComplete); } @@ -181,22 +174,34 @@ namespace OpenRA.Traits var songs = Game.Settings.Sound.Shuffle ? random : playlist; - return reverse ? songs.SkipWhile(m => m != currentSong) - .Skip(1).FirstOrDefault() ?? songs.FirstOrDefault() : - songs.Reverse().SkipWhile(m => m != currentSong) - .Skip(1).FirstOrDefault() ?? songs.Reverse().FirstOrDefault(); + var next = reverse ? songs.Reverse().SkipWhile(m => m != currentSong) + .Skip(1).FirstOrDefault() ?? songs.Reverse().FirstOrDefault() : + songs.SkipWhile(m => m != currentSong) + .Skip(1).FirstOrDefault() ?? songs.FirstOrDefault(); + + if (SongExists(next)) + return next; + + return null; } public void Stop() { currentSong = null; Sound.StopMusic(); + + if (currentBackgroundSong != null) + { + currentSong = currentBackgroundSong; + CurrentSongIsBackground = true; + Play(); + } } public void Disposing(Actor self) { if (currentSong != null) - Stop(); + Sound.StopMusic(); } } } diff --git a/OpenRA.Mods.Common/Widgets/Logic/MusicPlayerLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/MusicPlayerLogic.cs index 2830512b0e..c1ce802d19 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/MusicPlayerLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/MusicPlayerLogic.cs @@ -35,8 +35,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic BuildMusicTable(); - Func noMusic = () => !musicPlaylist.IsMusicAvailable; - panel.Get("NO_MUSIC_LABEL").IsVisible = noMusic; + Func noMusic = () => !musicPlaylist.IsMusicAvailable || musicPlaylist.CurrentSongIsBackground || currentSong == null; + panel.Get("NO_MUSIC_LABEL").IsVisible = () => !musicPlaylist.IsMusicAvailable; var playButton = panel.Get("BUTTON_PLAY"); playButton.OnClick = Play; @@ -63,14 +63,25 @@ namespace OpenRA.Mods.Common.Widgets.Logic var shuffleCheckbox = panel.Get("SHUFFLE"); shuffleCheckbox.IsChecked = () => Game.Settings.Sound.Shuffle; shuffleCheckbox.OnClick = () => Game.Settings.Sound.Shuffle ^= true; + shuffleCheckbox.IsDisabled = () => musicPlaylist.CurrentSongIsBackground; var repeatCheckbox = panel.Get("REPEAT"); repeatCheckbox.IsChecked = () => Game.Settings.Sound.Repeat; repeatCheckbox.OnClick = () => Game.Settings.Sound.Repeat ^= true; + repeatCheckbox.IsDisabled = () => musicPlaylist.CurrentSongIsBackground; - panel.Get("TIME_LABEL").GetText = () => (currentSong == null) ? "" : - "{0:D2}:{1:D2} / {2:D2}:{3:D2}".F((int)Sound.MusicSeekPosition / 60, (int)Sound.MusicSeekPosition % 60, - currentSong.Length / 60, currentSong.Length % 60); + panel.Get("TIME_LABEL").GetText = () => + { + if (currentSong == null || musicPlaylist.CurrentSongIsBackground) + return ""; + + var minutes = (int)Sound.MusicSeekPosition / 60; + var seconds = (int)Sound.MusicSeekPosition % 60; + var totalMinutes = currentSong.Length / 60; + var totalSeconds = currentSong.Length % 60; + + return "{0:D2}:{1:D2} / {2:D2}:{3:D2}".F(minutes, seconds, totalMinutes, totalSeconds); + }; var musicSlider = panel.Get("MUSIC_SLIDER"); musicSlider.OnChange += x => Sound.MusicVolume = x; @@ -94,7 +105,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic { songWatcher.OnTick = () => { - if (Sound.CurrentMusic == null || currentSong == Sound.CurrentMusic) + if (musicPlaylist.CurrentSongIsBackground && currentSong != null) + currentSong = null; + + if (Sound.CurrentMusic == null || currentSong == Sound.CurrentMusic || musicPlaylist.CurrentSongIsBackground) return; currentSong = Sound.CurrentMusic; @@ -127,7 +141,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic musicList.AddChild(item); } - if (currentSong != null) + if (currentSong != null && !musicPlaylist.CurrentSongIsBackground) musicList.ScrollToItem(currentSong.Filename); } diff --git a/mods/cnc/maps/shellmap/map.yaml b/mods/cnc/maps/shellmap/map.yaml index 2c1f73a826..acf5eb080e 100644 --- a/mods/cnc/maps/shellmap/map.yaml +++ b/mods/cnc/maps/shellmap/map.yaml @@ -995,8 +995,7 @@ Rules: LuaScript: Scripts: shellmap.lua MusicPlaylist: - StartingMusic: map1 - LoopStartingMusic: True + BackgroundMusic: map1 LST: Mobile: Speed: 42 diff --git a/mods/d2k/maps/shellmap/map.yaml b/mods/d2k/maps/shellmap/map.yaml index 31b324a808..ab67831c2a 100644 --- a/mods/d2k/maps/shellmap/map.yaml +++ b/mods/d2k/maps/shellmap/map.yaml @@ -124,8 +124,7 @@ Rules: Minimum: 1 Maximum: 1 MusicPlaylist: - StartingMusic: options - LoopStartingMusic: True + BackgroundMusic: options rockettower: Power: Amount: 100 diff --git a/mods/ts/maps/blank-shellmap/map.yaml b/mods/ts/maps/blank-shellmap/map.yaml index a7bd4c73b0..c25582f018 100644 --- a/mods/ts/maps/blank-shellmap/map.yaml +++ b/mods/ts/maps/blank-shellmap/map.yaml @@ -41,8 +41,7 @@ Rules: -SpawnMPUnits: -MPStartLocations: MusicPlaylist: - StartingMusic: intro - LoopStartingMusic: True + BackgroundMusic: intro Sequences: