diff --git a/OpenRA.Game/Traits/TraitsInterfaces.cs b/OpenRA.Game/Traits/TraitsInterfaces.cs index f6845d7187..d5856d24c9 100644 --- a/OpenRA.Game/Traits/TraitsInterfaces.cs +++ b/OpenRA.Game/Traits/TraitsInterfaces.cs @@ -352,6 +352,8 @@ namespace OpenRA.Traits void OnObjectiveFailed(Player player, int objectiveID); } + public interface IGameOver { void GameOver(World world); } + public interface IWarhead { int Delay { get; } diff --git a/OpenRA.Game/Traits/World/MusicPlaylist.cs b/OpenRA.Game/Traits/World/MusicPlaylist.cs index 143bae11a0..cf275ba64a 100644 --- a/OpenRA.Game/Traits/World/MusicPlaylist.cs +++ b/OpenRA.Game/Traits/World/MusicPlaylist.cs @@ -17,14 +17,31 @@ namespace OpenRA.Traits [Desc("Trait for music handling. Attach this to the world actor.")] public class MusicPlaylistInfo : ITraitInfo { + [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; + public object Create(ActorInitializer init) { return new MusicPlaylist(init.World, this); } } - public class MusicPlaylist : INotifyActorDisposing + public class MusicPlaylist : INotifyActorDisposing, IGameOver { + readonly MusicPlaylistInfo info; + readonly MusicInfo[] random; readonly MusicInfo[] playlist; @@ -35,6 +52,8 @@ namespace OpenRA.Traits public MusicPlaylist(World world, MusicPlaylistInfo info) { + this.info = info; + IsMusicAvailable = world.Map.Rules.InstalledMusic.Any(); playlist = world.Map.Rules.InstalledMusic.Select(a => a.Value).ToArray(); @@ -71,6 +90,40 @@ namespace OpenRA.Traits return playlist; } + public void GameOver(World world) + { + if (!IsMusicAvailable) + return; + + var playedSong = currentSong; + + if (world.LocalPlayer.WinState == WinState.Won) + { + if (!string.IsNullOrEmpty(info.VictoryMusic) + && world.Map.Rules.Music.ContainsKey(info.VictoryMusic) + && world.Map.Rules.Music[info.VictoryMusic].Exists) + { + currentSong = world.Map.Rules.Music[info.VictoryMusic]; + repeat = info.LoopVictoryMusic; + } + } + 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) + { + currentSong = world.Map.Rules.Music[info.DefeatMusic]; + repeat = info.LoopDefeatMusic; + } + } + + if (playedSong != currentSong) + Play(); + } + void Play() { if (currentSong == null || !IsMusicAvailable) diff --git a/OpenRA.Game/World.cs b/OpenRA.Game/World.cs index 2413c73913..eeb224692a 100644 --- a/OpenRA.Game/World.cs +++ b/OpenRA.Game/World.cs @@ -58,6 +58,10 @@ namespace OpenRA if (!gameOver) { gameOver = true; + + foreach (var t in WorldActor.TraitsImplementing()) + t.GameOver(this); + GameOver(); } } diff --git a/mods/cnc/audio/music.yaml b/mods/cnc/audio/music.yaml index 08bf4facc5..aca9f24d8f 100644 --- a/mods/cnc/audio/music.yaml +++ b/mods/cnc/audio/music.yaml @@ -37,9 +37,15 @@ trouble2: In Trouble (Voiced) Filename: trouble Extension: var justdoit: Just Do it Up +justdoit2: Just Do it Up (Voiced) + Filename: justdoit + Extension: var map1: Map Theme march: March to Doom nomercy: No Mercy +nomercy2: No Mercy (Voiced) + Filename: nomercy + Extension: var otp: On the Prowl prp: Prepare for Battle radio: Radio @@ -52,3 +58,6 @@ target: Target (Mechanical Man) j1: Untamed Land valkyrie: Ride of the Valkyries voic226m: Voice Rhythm +outtakes: Outtakes +nod_map1: Nod Map Theme +nod_win1: Nod Win Theme \ No newline at end of file diff --git a/mods/cnc/maps/gdi01/gdi01.lua b/mods/cnc/maps/gdi01/gdi01.lua index 24c4265c0c..490e72f695 100644 --- a/mods/cnc/maps/gdi01/gdi01.lua +++ b/mods/cnc/maps/gdi01/gdi01.lua @@ -72,9 +72,6 @@ WorldLoaded = function() Trigger.OnPlayerWon(player, function() Media.PlaySpeechNotification(player, "Win") - Trigger.AfterDelay(DateTime.Seconds(1), function() - Media.PlayMusic("win1") - end) end) Trigger.OnPlayerLost(player, function() diff --git a/mods/cnc/maps/nod01/map.yaml b/mods/cnc/maps/nod01/map.yaml index afd8124a7b..c31cd8885a 100644 --- a/mods/cnc/maps/nod01/map.yaml +++ b/mods/cnc/maps/nod01/map.yaml @@ -291,6 +291,7 @@ Rules: PanelName: MISSION_OBJECTIVES MusicPlaylist: StartingMusic: nomercy + VictoryMusic: nod_win1 C10: Tooltip: Name: Nikoomba diff --git a/mods/cnc/maps/nod02a/map.yaml b/mods/cnc/maps/nod02a/map.yaml index bf42e40551..e4ad3d67c0 100644 --- a/mods/cnc/maps/nod02a/map.yaml +++ b/mods/cnc/maps/nod02a/map.yaml @@ -228,6 +228,7 @@ Rules: Scripts: nod02a.lua MusicPlaylist: StartingMusic: ind2 + VictoryMusic: nod_win1 ^Vehicle: Tooltip: GenericVisibility: Enemy diff --git a/mods/cnc/maps/nod02b/map.yaml b/mods/cnc/maps/nod02b/map.yaml index a2e61a2735..0a1fe599aa 100644 --- a/mods/cnc/maps/nod02b/map.yaml +++ b/mods/cnc/maps/nod02b/map.yaml @@ -270,6 +270,7 @@ Rules: Scripts: nod02b.lua MusicPlaylist: StartingMusic: ind2 + VictoryMusic: nod_win1 ^Vehicle: Tooltip: GenericVisibility: Enemy diff --git a/mods/cnc/maps/nod03a/map.yaml b/mods/cnc/maps/nod03a/map.yaml index cbc36d8d04..0d7f550c0e 100644 --- a/mods/cnc/maps/nod03a/map.yaml +++ b/mods/cnc/maps/nod03a/map.yaml @@ -469,6 +469,7 @@ Rules: PanelName: MISSION_OBJECTIVES MusicPlaylist: StartingMusic: chrg226m + VictoryMusic: nod_win1 ^Vehicle: Tooltip: GenericVisibility: Enemy diff --git a/mods/cnc/maps/nod03b/map.yaml b/mods/cnc/maps/nod03b/map.yaml index 527c88c00d..502dce0af4 100644 --- a/mods/cnc/maps/nod03b/map.yaml +++ b/mods/cnc/maps/nod03b/map.yaml @@ -513,6 +513,7 @@ Rules: PanelName: MISSION_OBJECTIVES MusicPlaylist: StartingMusic: chrg226m + VictoryMusic: nod_win1 ^Vehicle: Tooltip: GenericVisibility: Enemy diff --git a/mods/cnc/maps/nod04a/map.yaml b/mods/cnc/maps/nod04a/map.yaml index c8fc9ab427..0f49bf9141 100644 --- a/mods/cnc/maps/nod04a/map.yaml +++ b/mods/cnc/maps/nod04a/map.yaml @@ -570,6 +570,7 @@ Rules: PanelName: MISSION_OBJECTIVES MusicPlaylist: StartingMusic: valkyrie + VictoryMusic: nod_win1 ^Vehicle: Tooltip: GenericVisibility: Enemy diff --git a/mods/cnc/maps/nod04b/map.yaml b/mods/cnc/maps/nod04b/map.yaml index 616d2df796..88b788e9de 100644 --- a/mods/cnc/maps/nod04b/map.yaml +++ b/mods/cnc/maps/nod04b/map.yaml @@ -509,6 +509,7 @@ Rules: Scripts: nod04b.lua MusicPlaylist: StartingMusic: warfare + VictoryMusic: nod_win1 ^Vehicle: Tooltip: GenericVisibility: Enemy diff --git a/mods/cnc/maps/nod05/map.yaml b/mods/cnc/maps/nod05/map.yaml index e5ec9568cd..348a4d1058 100644 --- a/mods/cnc/maps/nod05/map.yaml +++ b/mods/cnc/maps/nod05/map.yaml @@ -404,6 +404,7 @@ Rules: Scripts: nod05.lua MusicPlaylist: StartingMusic: airstrik + VictoryMusic: nod_win1 ^Vehicle: Tooltip: GenericVisibility: Enemy diff --git a/mods/cnc/maps/nod06a/map.yaml b/mods/cnc/maps/nod06a/map.yaml index dcf76654f8..3b7e726ffa 100644 --- a/mods/cnc/maps/nod06a/map.yaml +++ b/mods/cnc/maps/nod06a/map.yaml @@ -677,6 +677,7 @@ Rules: Scripts: nod06a.lua MusicPlaylist: StartingMusic: rout + VictoryMusic: nod_win1 ^Vehicle: Tooltip: GenericVisibility: Enemy diff --git a/mods/cnc/maps/nod06b/map.yaml b/mods/cnc/maps/nod06b/map.yaml index 630c6548fd..74d7601728 100644 --- a/mods/cnc/maps/nod06b/map.yaml +++ b/mods/cnc/maps/nod06b/map.yaml @@ -619,6 +619,7 @@ Rules: Scripts: nod06b.lua MusicPlaylist: StartingMusic: rout + VictoryMusic: nod_win1 ^Vehicle: Tooltip: GenericVisibility: Enemy diff --git a/mods/cnc/maps/nod06c/map.yaml b/mods/cnc/maps/nod06c/map.yaml index 5707b73468..bcd786271f 100644 --- a/mods/cnc/maps/nod06c/map.yaml +++ b/mods/cnc/maps/nod06c/map.yaml @@ -505,6 +505,7 @@ Rules: Scripts: nod06c.lua MusicPlaylist: StartingMusic: rout + VictoryMusic: nod_win1 ^Vehicle: Tooltip: GenericVisibility: Enemy diff --git a/mods/cnc/rules/world.yaml b/mods/cnc/rules/world.yaml index 5a4c6f174a..3cd793916c 100644 --- a/mods/cnc/rules/world.yaml +++ b/mods/cnc/rules/world.yaml @@ -4,6 +4,8 @@ ScreenMap: ActorMap: MusicPlaylist: + VictoryMusic: win1 + DefeatMusic: nod_map1 TerrainGeometryOverlay: ShroudRenderer: ShroudVariants: typea, typeb, typec, typed diff --git a/mods/d2k/maps/shellmap/map.yaml b/mods/d2k/maps/shellmap/map.yaml index fd42ee8e29..2bb6499885 100644 --- a/mods/d2k/maps/shellmap/map.yaml +++ b/mods/d2k/maps/shellmap/map.yaml @@ -126,7 +126,7 @@ Rules: LuaScript: Scripts: shellmap.lua MusicPlaylist: - StartingMusic: score + StartingMusic: waitgame GlobalLightingPaletteEffect: rockettower: Power: diff --git a/mods/d2k/rules/world.yaml b/mods/d2k/rules/world.yaml index b90ee115b6..443a8b2e56 100644 --- a/mods/d2k/rules/world.yaml +++ b/mods/d2k/rules/world.yaml @@ -4,6 +4,8 @@ ScreenMap: ActorMap: MusicPlaylist: + VictoryMusic: score + DefeatMusic: options TerrainGeometryOverlay: ShroudRenderer: ShroudVariants: typea, typeb, typec, typed diff --git a/mods/ra/rules/world.yaml b/mods/ra/rules/world.yaml index 2a1d469c82..d359a4326f 100644 --- a/mods/ra/rules/world.yaml +++ b/mods/ra/rules/world.yaml @@ -4,6 +4,8 @@ ActorMap: ScreenMap: MusicPlaylist: + VictoryMusic: score + DefeatMusic: map TerrainGeometryOverlay: LoadWidgetAtGameStart: ShroudRenderer: diff --git a/mods/ts/rules/world.yaml b/mods/ts/rules/world.yaml index c5a588430a..60747a2f31 100644 --- a/mods/ts/rules/world.yaml +++ b/mods/ts/rules/world.yaml @@ -4,6 +4,8 @@ ScreenMap: ActorMap: MusicPlaylist: + VictoryMusic: score + DefeatMusic: maps LoadWidgetAtGameStart: ShroudRenderer: Index: 255, 16, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192, 208, 224, 240, 20, 40, 56, 65, 97, 130, 148, 194, 24, 33, 66, 132, 28, 41, 67, 134, 1, 2, 4, 8, 3, 6, 12, 9, 7, 14, 13, 11, 5, 10, 15, 255