Introduce background music concept.
This commit is contained in:
@@ -20,20 +20,15 @@ namespace OpenRA.Traits
|
|||||||
[Desc("Music to play when the map starts.", "Plays the first song on the playlist when undefined.")]
|
[Desc("Music to play when the map starts.", "Plays the first song on the playlist when undefined.")]
|
||||||
public readonly string StartingMusic = null;
|
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.")]
|
[Desc("Music to play when the game has been won.")]
|
||||||
public readonly string VictoryMusic = null;
|
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.")]
|
[Desc("Music to play when the game has been lost.")]
|
||||||
public readonly string DefeatMusic = null;
|
public readonly string DefeatMusic = null;
|
||||||
|
|
||||||
[Desc("Should the defeat music loop?")]
|
[Desc("This track is played when no other music is playing.",
|
||||||
public readonly bool LoopDefeatMusic = false;
|
"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); }
|
public object Create(ActorInitializer init) { return new MusicPlaylist(init.World, this); }
|
||||||
}
|
}
|
||||||
@@ -41,18 +36,21 @@ namespace OpenRA.Traits
|
|||||||
public class MusicPlaylist : INotifyActorDisposing, IGameOver
|
public class MusicPlaylist : INotifyActorDisposing, IGameOver
|
||||||
{
|
{
|
||||||
readonly MusicPlaylistInfo info;
|
readonly MusicPlaylistInfo info;
|
||||||
|
readonly World world;
|
||||||
|
|
||||||
readonly MusicInfo[] random;
|
readonly MusicInfo[] random;
|
||||||
readonly MusicInfo[] playlist;
|
readonly MusicInfo[] playlist;
|
||||||
|
|
||||||
public readonly bool IsMusicAvailable;
|
public readonly bool IsMusicAvailable;
|
||||||
|
public bool CurrentSongIsBackground { get; private set; }
|
||||||
|
|
||||||
MusicInfo currentSong;
|
MusicInfo currentSong;
|
||||||
bool repeat;
|
MusicInfo currentBackgroundSong;
|
||||||
|
|
||||||
public MusicPlaylist(World world, MusicPlaylistInfo info)
|
public MusicPlaylist(World world, MusicPlaylistInfo info)
|
||||||
{
|
{
|
||||||
this.info = info;
|
this.info = info;
|
||||||
|
this.world = world;
|
||||||
|
|
||||||
IsMusicAvailable = world.Map.Rules.InstalledMusic.Any();
|
IsMusicAvailable = world.Map.Rules.InstalledMusic.Any();
|
||||||
|
|
||||||
@@ -63,22 +61,31 @@ namespace OpenRA.Traits
|
|||||||
|
|
||||||
random = playlist.Shuffle(Game.CosmeticRandom).ToArray();
|
random = playlist.Shuffle(Game.CosmeticRandom).ToArray();
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(info.StartingMusic)
|
if (SongExists(info.StartingMusic))
|
||||||
&& world.Map.Rules.Music.ContainsKey(info.StartingMusic)
|
|
||||||
&& world.Map.Rules.Music[info.StartingMusic].Exists)
|
|
||||||
{
|
{
|
||||||
currentSong = world.Map.Rules.Music[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();
|
currentSong = currentBackgroundSong = world.Map.Rules.Music[info.BackgroundMusic];
|
||||||
repeat = Game.Settings.Sound.Repeat;
|
CurrentSongIsBackground = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Play();
|
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()
|
public MusicInfo CurrentSong()
|
||||||
{
|
{
|
||||||
return currentSong;
|
return currentSong;
|
||||||
@@ -95,43 +102,34 @@ namespace OpenRA.Traits
|
|||||||
if (!IsMusicAvailable)
|
if (!IsMusicAvailable)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var playedSong = currentSong;
|
|
||||||
|
|
||||||
if (world.LocalPlayer != null && world.LocalPlayer.WinState == WinState.Won)
|
if (world.LocalPlayer != null && world.LocalPlayer.WinState == WinState.Won)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(info.VictoryMusic)
|
if (SongExists(info.VictoryMusic))
|
||||||
&& world.Map.Rules.Music.ContainsKey(info.VictoryMusic)
|
|
||||||
&& world.Map.Rules.Music[info.VictoryMusic].Exists)
|
|
||||||
{
|
{
|
||||||
currentSong = world.Map.Rules.Music[info.VictoryMusic];
|
currentBackgroundSong = world.Map.Rules.Music[info.VictoryMusic];
|
||||||
repeat = info.LoopVictoryMusic;
|
Stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Most RTS treats observers losing the game,
|
// Most RTS treats observers losing the game,
|
||||||
// no need for a special handling involving them here.
|
// no need for a special handling involving them here.
|
||||||
if (!string.IsNullOrEmpty(info.DefeatMusic)
|
if (SongExists(info.DefeatMusic))
|
||||||
&& world.Map.Rules.Music.ContainsKey(info.DefeatMusic)
|
|
||||||
&& world.Map.Rules.Music[info.DefeatMusic].Exists)
|
|
||||||
{
|
{
|
||||||
currentSong = world.Map.Rules.Music[info.DefeatMusic];
|
currentBackgroundSong = world.Map.Rules.Music[info.DefeatMusic];
|
||||||
repeat = info.LoopDefeatMusic;
|
Stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (playedSong != currentSong)
|
|
||||||
Play();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Play()
|
void Play()
|
||||||
{
|
{
|
||||||
if (currentSong == null || !IsMusicAvailable)
|
if (!SongExists(currentSong) || !IsMusicAvailable)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Sound.PlayMusicThen(currentSong, () =>
|
Sound.PlayMusicThen(currentSong, () =>
|
||||||
{
|
{
|
||||||
if (!repeat)
|
if (!CurrentSongIsBackground && !Game.Settings.Sound.Repeat)
|
||||||
currentSong = GetNextSong();
|
currentSong = GetNextSong();
|
||||||
|
|
||||||
Play();
|
Play();
|
||||||
@@ -144,15 +142,9 @@ namespace OpenRA.Traits
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
currentSong = music;
|
currentSong = music;
|
||||||
repeat = Game.Settings.Sound.Repeat;
|
CurrentSongIsBackground = false;
|
||||||
|
|
||||||
Sound.PlayMusicThen(music, () =>
|
Play();
|
||||||
{
|
|
||||||
if (!repeat)
|
|
||||||
currentSong = GetNextSong();
|
|
||||||
|
|
||||||
Play();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Play(MusicInfo music, Action onComplete)
|
public void Play(MusicInfo music, Action onComplete)
|
||||||
@@ -161,6 +153,7 @@ namespace OpenRA.Traits
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
currentSong = music;
|
currentSong = music;
|
||||||
|
CurrentSongIsBackground = false;
|
||||||
Sound.PlayMusicThen(music, onComplete);
|
Sound.PlayMusicThen(music, onComplete);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,22 +174,34 @@ namespace OpenRA.Traits
|
|||||||
|
|
||||||
var songs = Game.Settings.Sound.Shuffle ? random : playlist;
|
var songs = Game.Settings.Sound.Shuffle ? random : playlist;
|
||||||
|
|
||||||
return reverse ? songs.SkipWhile(m => m != currentSong)
|
var next = reverse ? songs.Reverse().SkipWhile(m => m != currentSong)
|
||||||
.Skip(1).FirstOrDefault() ?? songs.FirstOrDefault() :
|
.Skip(1).FirstOrDefault() ?? songs.Reverse().FirstOrDefault() :
|
||||||
songs.Reverse().SkipWhile(m => m != currentSong)
|
songs.SkipWhile(m => m != currentSong)
|
||||||
.Skip(1).FirstOrDefault() ?? songs.Reverse().FirstOrDefault();
|
.Skip(1).FirstOrDefault() ?? songs.FirstOrDefault();
|
||||||
|
|
||||||
|
if (SongExists(next))
|
||||||
|
return next;
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Stop()
|
public void Stop()
|
||||||
{
|
{
|
||||||
currentSong = null;
|
currentSong = null;
|
||||||
Sound.StopMusic();
|
Sound.StopMusic();
|
||||||
|
|
||||||
|
if (currentBackgroundSong != null)
|
||||||
|
{
|
||||||
|
currentSong = currentBackgroundSong;
|
||||||
|
CurrentSongIsBackground = true;
|
||||||
|
Play();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Disposing(Actor self)
|
public void Disposing(Actor self)
|
||||||
{
|
{
|
||||||
if (currentSong != null)
|
if (currentSong != null)
|
||||||
Stop();
|
Sound.StopMusic();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
|||||||
|
|
||||||
BuildMusicTable();
|
BuildMusicTable();
|
||||||
|
|
||||||
Func<bool> noMusic = () => !musicPlaylist.IsMusicAvailable;
|
Func<bool> noMusic = () => !musicPlaylist.IsMusicAvailable || musicPlaylist.CurrentSongIsBackground || currentSong == null;
|
||||||
panel.Get("NO_MUSIC_LABEL").IsVisible = noMusic;
|
panel.Get("NO_MUSIC_LABEL").IsVisible = () => !musicPlaylist.IsMusicAvailable;
|
||||||
|
|
||||||
var playButton = panel.Get<ButtonWidget>("BUTTON_PLAY");
|
var playButton = panel.Get<ButtonWidget>("BUTTON_PLAY");
|
||||||
playButton.OnClick = Play;
|
playButton.OnClick = Play;
|
||||||
@@ -63,14 +63,25 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
|||||||
var shuffleCheckbox = panel.Get<CheckboxWidget>("SHUFFLE");
|
var shuffleCheckbox = panel.Get<CheckboxWidget>("SHUFFLE");
|
||||||
shuffleCheckbox.IsChecked = () => Game.Settings.Sound.Shuffle;
|
shuffleCheckbox.IsChecked = () => Game.Settings.Sound.Shuffle;
|
||||||
shuffleCheckbox.OnClick = () => Game.Settings.Sound.Shuffle ^= true;
|
shuffleCheckbox.OnClick = () => Game.Settings.Sound.Shuffle ^= true;
|
||||||
|
shuffleCheckbox.IsDisabled = () => musicPlaylist.CurrentSongIsBackground;
|
||||||
|
|
||||||
var repeatCheckbox = panel.Get<CheckboxWidget>("REPEAT");
|
var repeatCheckbox = panel.Get<CheckboxWidget>("REPEAT");
|
||||||
repeatCheckbox.IsChecked = () => Game.Settings.Sound.Repeat;
|
repeatCheckbox.IsChecked = () => Game.Settings.Sound.Repeat;
|
||||||
repeatCheckbox.OnClick = () => Game.Settings.Sound.Repeat ^= true;
|
repeatCheckbox.OnClick = () => Game.Settings.Sound.Repeat ^= true;
|
||||||
|
repeatCheckbox.IsDisabled = () => musicPlaylist.CurrentSongIsBackground;
|
||||||
|
|
||||||
panel.Get<LabelWidget>("TIME_LABEL").GetText = () => (currentSong == null) ? "" :
|
panel.Get<LabelWidget>("TIME_LABEL").GetText = () =>
|
||||||
"{0:D2}:{1:D2} / {2:D2}:{3:D2}".F((int)Sound.MusicSeekPosition / 60, (int)Sound.MusicSeekPosition % 60,
|
{
|
||||||
currentSong.Length / 60, currentSong.Length % 60);
|
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<SliderWidget>("MUSIC_SLIDER");
|
var musicSlider = panel.Get<SliderWidget>("MUSIC_SLIDER");
|
||||||
musicSlider.OnChange += x => Sound.MusicVolume = x;
|
musicSlider.OnChange += x => Sound.MusicVolume = x;
|
||||||
@@ -94,7 +105,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
|||||||
{
|
{
|
||||||
songWatcher.OnTick = () =>
|
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;
|
return;
|
||||||
|
|
||||||
currentSong = Sound.CurrentMusic;
|
currentSong = Sound.CurrentMusic;
|
||||||
@@ -127,7 +141,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
|||||||
musicList.AddChild(item);
|
musicList.AddChild(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentSong != null)
|
if (currentSong != null && !musicPlaylist.CurrentSongIsBackground)
|
||||||
musicList.ScrollToItem(currentSong.Filename);
|
musicList.ScrollToItem(currentSong.Filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -995,8 +995,7 @@ Rules:
|
|||||||
LuaScript:
|
LuaScript:
|
||||||
Scripts: shellmap.lua
|
Scripts: shellmap.lua
|
||||||
MusicPlaylist:
|
MusicPlaylist:
|
||||||
StartingMusic: map1
|
BackgroundMusic: map1
|
||||||
LoopStartingMusic: True
|
|
||||||
LST:
|
LST:
|
||||||
Mobile:
|
Mobile:
|
||||||
Speed: 42
|
Speed: 42
|
||||||
|
|||||||
@@ -124,8 +124,7 @@ Rules:
|
|||||||
Minimum: 1
|
Minimum: 1
|
||||||
Maximum: 1
|
Maximum: 1
|
||||||
MusicPlaylist:
|
MusicPlaylist:
|
||||||
StartingMusic: options
|
BackgroundMusic: options
|
||||||
LoopStartingMusic: True
|
|
||||||
rockettower:
|
rockettower:
|
||||||
Power:
|
Power:
|
||||||
Amount: 100
|
Amount: 100
|
||||||
|
|||||||
@@ -41,8 +41,7 @@ Rules:
|
|||||||
-SpawnMPUnits:
|
-SpawnMPUnits:
|
||||||
-MPStartLocations:
|
-MPStartLocations:
|
||||||
MusicPlaylist:
|
MusicPlaylist:
|
||||||
StartingMusic: intro
|
BackgroundMusic: intro
|
||||||
LoopStartingMusic: True
|
|
||||||
|
|
||||||
Sequences:
|
Sequences:
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user