diff --git a/OpenRA.Game/Map/Map.cs b/OpenRA.Game/Map/Map.cs index d2ac405aaa..ea3adcfc22 100644 --- a/OpenRA.Game/Map/Map.cs +++ b/OpenRA.Game/Map/Map.cs @@ -88,6 +88,15 @@ namespace OpenRA } } + public class MapVideos + { + public string BackgroundInfo; + public string Briefing; + public string GameStart; + public string GameWon; + public string GameLost; + } + public class Map { [FieldLoader.Ignore] public IFolder Container; @@ -102,7 +111,6 @@ namespace OpenRA public string Title; public string Type = "Conquest"; - public string PreviewVideo; public string Description; public string Author; public string Tileset; @@ -131,6 +139,19 @@ namespace OpenRA return options; } + [FieldLoader.LoadUsing("LoadVideos")] + public MapVideos Videos; + + static object LoadVideos(MiniYaml y) + { + var videos = new MapVideos(); + var nodesDict = y.ToDictionary(); + if (nodesDict.ContainsKey("Videos")) + FieldLoader.Load(videos, nodesDict["Videos"]); + + return videos; + } + [FieldLoader.Ignore] public Lazy> Actors; public int PlayerCount { get { return Players.Count(p => p.Value.Playable); } } @@ -372,7 +393,6 @@ namespace OpenRA "Title", "Description", "Author", - "PreviewVideo", "Tileset", "MapSize", "Bounds", diff --git a/OpenRA.Mods.RA/Widgets/Logic/Ingame/LeaveMapLogic.cs b/OpenRA.Mods.RA/Widgets/Logic/Ingame/LeaveMapLogic.cs index d030dd697c..5bd38258a6 100644 --- a/OpenRA.Mods.RA/Widgets/Logic/Ingame/LeaveMapLogic.cs +++ b/OpenRA.Mods.RA/Widgets/Logic/Ingame/LeaveMapLogic.cs @@ -21,7 +21,7 @@ namespace OpenRA.Mods.RA.Widgets { class LeaveMapLogic { - enum Tab { Objectives, Chat }; + enum Tab { Objectives, Chat } Tab currentTab; OrderManager orderManager; @@ -125,6 +125,13 @@ namespace OpenRA.Mods.RA.Widgets var objectivesContainer = dialog.Get("OBJECTIVES_PANEL"); Game.LoadWidget(world, panel, objectivesContainer, new WidgetArgs()); objectivesContainer.IsVisible = () => currentTab == Tab.Objectives; + + string video = null; + if (world.LocalPlayer.WinState != WinState.Undefined) + video = world.LocalPlayer.WinState == WinState.Won ? world.Map.Videos.GameWon : world.Map.Videos.GameLost; + + if (!string.IsNullOrEmpty(video)) + Media.PlayFMVFullscreen(world, video, () => { }); } if (isMultiplayer) diff --git a/OpenRA.Mods.RA/Widgets/Logic/MissionBrowserLogic.cs b/OpenRA.Mods.RA/Widgets/Logic/MissionBrowserLogic.cs index 46edd5bb62..6bb79416ea 100644 --- a/OpenRA.Mods.RA/Widgets/Logic/MissionBrowserLogic.cs +++ b/OpenRA.Mods.RA/Widgets/Logic/MissionBrowserLogic.cs @@ -23,14 +23,19 @@ namespace OpenRA.Mods.RA.Widgets.Logic { public class MissionBrowserLogic { + enum PlayingVideo { None, Info, Briefing, GameStart } + readonly Action onStart; readonly ScrollPanelWidget descriptionPanel; readonly LabelWidget description; readonly SpriteFont descriptionFont; readonly DropDownButtonWidget difficultyButton; - readonly ButtonWidget startVideoButton; - readonly ButtonWidget stopVideoButton; + readonly ButtonWidget startBriefingVideoButton; + readonly ButtonWidget stopBriefingVideoButton; + readonly ButtonWidget startInfoVideoButton; + readonly ButtonWidget stopInfoVideoButton; readonly VqaPlayerWidget videoPlayer; + readonly BackgroundWidget fullscreenVideoPlayer; readonly ScrollPanelWidget missionList; readonly ScrollItemWidget headerTemplate; @@ -38,12 +43,12 @@ namespace OpenRA.Mods.RA.Widgets.Logic MapPreview selectedMapPreview; - bool showVideoPlayer; + PlayingVideo playingVideo; string difficulty; [ObjectCreator.UseCtor] - public MissionBrowserLogic(Widget widget, Action onStart, Action onExit) + public MissionBrowserLogic(Widget widget, World world, Action onStart, Action onExit) { this.onStart = onStart; @@ -54,16 +59,17 @@ namespace OpenRA.Mods.RA.Widgets.Logic var title = widget.GetOrNull("MISSIONBROWSER_TITLE"); if (title != null) - title.GetText = () => showVideoPlayer ? selectedMapPreview.Title : title.Text; + title.GetText = () => playingVideo != PlayingVideo.None ? selectedMapPreview.Title : title.Text; widget.Get("MISSION_INFO").IsVisible = () => selectedMapPreview != null; var previewWidget = widget.Get("MISSION_PREVIEW"); previewWidget.Preview = () => selectedMapPreview; - previewWidget.IsVisible = () => !showVideoPlayer; + previewWidget.IsVisible = () => playingVideo == PlayingVideo.None; videoPlayer = widget.Get("MISSION_VIDEO"); - widget.Get("MISSION_BIN").IsVisible = () => showVideoPlayer; + widget.Get("MISSION_BIN").IsVisible = () => playingVideo != PlayingVideo.None; + fullscreenVideoPlayer = Ui.LoadWidget("FULLSCREEN_PLAYER", Ui.Root, new WidgetArgs { { "world", world } }); descriptionPanel = widget.Get("MISSION_DESCRIPTION_PANEL"); @@ -72,10 +78,15 @@ namespace OpenRA.Mods.RA.Widgets.Logic difficultyButton = widget.Get("DIFFICULTY_DROPDOWNBUTTON"); - startVideoButton = widget.Get("START_VIDEO_BUTTON"); - stopVideoButton = widget.Get("STOP_VIDEO_BUTTON"); - stopVideoButton.IsVisible = () => showVideoPlayer; - stopVideoButton.OnClick = StopVideo; + startBriefingVideoButton = widget.Get("START_BRIEFING_VIDEO_BUTTON"); + stopBriefingVideoButton = widget.Get("STOP_BRIEFING_VIDEO_BUTTON"); + stopBriefingVideoButton.IsVisible = () => playingVideo == PlayingVideo.Briefing; + stopBriefingVideoButton.OnClick = () => StopVideo(videoPlayer); + + startInfoVideoButton = widget.Get("START_INFO_VIDEO_BUTTON"); + stopInfoVideoButton = widget.Get("STOP_INFO_VIDEO_BUTTON"); + stopInfoVideoButton.IsVisible = () => playingVideo == PlayingVideo.Info; + stopInfoVideoButton.OnClick = () => StopVideo(videoPlayer); var allMaps = new List(); missionList.RemoveChildren(); @@ -114,12 +125,12 @@ namespace OpenRA.Mods.RA.Widgets.Logic SelectMap(allMaps.First()); var startButton = widget.Get("STARTGAME_BUTTON"); - startButton.OnClick = StartMission; + startButton.OnClick = StartMissionClicked; startButton.IsDisabled = () => selectedMapPreview.RuleStatus != MapRuleStatus.Cached; widget.Get("BACK_BUTTON").OnClick = () => { - StopVideo(); + StopVideo(videoPlayer); Game.Disconnect(); Ui.CloseWindow(); onExit(); @@ -128,7 +139,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic void CreateMissionGroup(string title, IEnumerable maps) { - var header = ScrollItemWidget.Setup(headerTemplate, () => true, () => {}); + var header = ScrollItemWidget.Setup(headerTemplate, () => true, () => { }); header.Get("LABEL").GetText = () => title; missionList.AddChild(header); @@ -139,41 +150,35 @@ namespace OpenRA.Mods.RA.Widgets.Logic var item = ScrollItemWidget.Setup(template, () => selectedMapPreview != null && selectedMapPreview.Uid == map.Uid, () => SelectMap(map), - StartMission); + StartMissionClicked); item.Get("TITLE").GetText = () => map.Title; missionList.AddChild(item); } } - float cachedSoundVolume; - float cachedMusicVolume; void SelectMap(Map map) { - StopVideo(); - selectedMapPreview = Game.modData.MapCache[map.Uid]; // Cache the rules on a background thread to avoid jank new Thread(selectedMapPreview.CacheRules).Start(); - var video = selectedMapPreview.Map.PreviewVideo; - var videoVisible = video != null; - var videoDisabled = !(videoVisible && GlobalFileSystem.Exists(video)); + var briefingVideo = selectedMapPreview.Map.Videos.Briefing; + var briefingVideoVisible = briefingVideo != null; + var briefingVideoDisabled = !(briefingVideoVisible && GlobalFileSystem.Exists(briefingVideo)); - startVideoButton.IsVisible = () => videoVisible && !showVideoPlayer; - startVideoButton.IsDisabled = () => videoDisabled; - startVideoButton.OnClick = () => - { - showVideoPlayer = true; - videoPlayer.Load(video); - videoPlayer.PlayThen(StopVideo); + var infoVideo = selectedMapPreview.Map.Videos.BackgroundInfo; + var infoVideoVisible = infoVideo != null; + var infoVideoDisabled = !(infoVideoVisible && GlobalFileSystem.Exists(infoVideo)); - // Mute other distracting sounds - cachedSoundVolume = Sound.SoundVolume; - cachedMusicVolume = Sound.MusicVolume; - Sound.SoundVolume = Sound.MusicVolume = 0; - }; + startBriefingVideoButton.IsVisible = () => briefingVideoVisible && playingVideo != PlayingVideo.Briefing; + startBriefingVideoButton.IsDisabled = () => briefingVideoDisabled || playingVideo != PlayingVideo.None; + startBriefingVideoButton.OnClick = () => PlayVideo(videoPlayer, briefingVideo, PlayingVideo.Briefing, () => StopVideo(videoPlayer)); + + startInfoVideoButton.IsVisible = () => infoVideoVisible && playingVideo != PlayingVideo.Info; + startInfoVideoButton.IsDisabled = () => infoVideoDisabled || playingVideo != PlayingVideo.None; + startInfoVideoButton.OnClick = () => PlayVideo(videoPlayer, infoVideo, PlayingVideo.Info, () => StopVideo(videoPlayer)); var text = map.Description != null ? map.Description.Replace("\\n", "\n") : ""; text = WidgetUtils.WrapText(text, description.Bounds.Width, descriptionFont); @@ -205,25 +210,72 @@ namespace OpenRA.Mods.RA.Widgets.Logic }; } - void StopVideo() + float cachedSoundVolume; + float cachedMusicVolume; + void MuteSounds() { - if (!showVideoPlayer) - return; - - Sound.SoundVolume = cachedSoundVolume; - Sound.MusicVolume = cachedMusicVolume; - - videoPlayer.Stop(); - showVideoPlayer = false; + cachedSoundVolume = Sound.SoundVolume; + cachedMusicVolume = Sound.MusicVolume; + Sound.SoundVolume = Sound.MusicVolume = 0; } - void StartMission() + void UnMuteSounds() { - StopVideo(); + if (cachedSoundVolume > 0) + Sound.SoundVolume = cachedSoundVolume; + + if (cachedMusicVolume > 0) + Sound.MusicVolume = cachedMusicVolume; + } + + void PlayVideo(VqaPlayerWidget player, string video, PlayingVideo pv, Action onComplete) + { + StopVideo(player); + + playingVideo = pv; + player.Load(video); + + // video playback runs asynchronously + player.PlayThen(onComplete); + + // Mute other distracting sounds + MuteSounds(); + } + + void StopVideo(VqaPlayerWidget player) + { + if (playingVideo == PlayingVideo.None) + return; + + UnMuteSounds(); + player.Stop(); + playingVideo = PlayingVideo.None; + } + + void StartMissionClicked() + { + StopVideo(videoPlayer); if (selectedMapPreview.RuleStatus != MapRuleStatus.Cached) return; + var gameStartVideo = selectedMapPreview.Map.Videos.GameStart; + if (gameStartVideo != null && GlobalFileSystem.Exists(gameStartVideo)) + { + var fsPlayer = fullscreenVideoPlayer.Get("PLAYER"); + fullscreenVideoPlayer.Visible = true; + PlayVideo(fsPlayer, gameStartVideo, PlayingVideo.GameStart, () => + { + StopVideo(fsPlayer); + StartMission(); + }); + } + else + StartMission(); + } + + void StartMission() + { OrderManager om = null; Action lobbyReady = null; diff --git a/mods/cnc/chrome/missionbrowser.yaml b/mods/cnc/chrome/missionbrowser.yaml index 992e2613d9..b38435c7e2 100644 --- a/mods/cnc/chrome/missionbrowser.yaml +++ b/mods/cnc/chrome/missionbrowser.yaml @@ -21,7 +21,7 @@ Container@MISSIONBROWSER_PANEL: X: 15 Y: 15 Width: 239 - Height: 347 + Height: 307 Children: ScrollItem@HEADER: Width: PARENT_RIGHT-27 @@ -74,6 +74,13 @@ Container@MISSIONBROWSER_PANEL: Width: PARENT_RIGHT - 32 VAlign: Top Font: Small + DropDownButton@DIFFICULTY_DROPDOWNBUTTON: + X: 15 + Y: 337 + Width: 239 + Height: 25 + Text: Difficulty + Font: Regular Button@BACK_BUTTON: Y: 376 Width: 140 @@ -81,20 +88,34 @@ Container@MISSIONBROWSER_PANEL: Text: Back Font: Bold Key: escape - Button@START_VIDEO_BUTTON: + Button@START_BRIEFING_VIDEO_BUTTON: X: PARENT_RIGHT - 290 Y: 376 Width: 140 Height: 35 - Text: View Briefing + Text: Watch Briefing Font: Bold - Button@STOP_VIDEO_BUTTON: + Button@STOP_BRIEFING_VIDEO_BUTTON: X: PARENT_RIGHT - 290 Y: 376 Width: 140 Height: 35 Text: Stop Briefing Font: Bold + Button@START_INFO_VIDEO_BUTTON: + X: PARENT_RIGHT - 440 + Y: 376 + Width: 140 + Height: 35 + Text: Watch Info Video + Font: Bold + Button@STOP_INFO_VIDEO_BUTTON: + X: PARENT_RIGHT - 440 + Y: 376 + Width: 140 + Height: 35 + Text: Stop Info Video + Font: Bold Button@STARTGAME_BUTTON: X: PARENT_RIGHT - 140 Y: 376 @@ -102,13 +123,6 @@ Container@MISSIONBROWSER_PANEL: Height: 35 Text: Play Font: Bold - DropDownButton@DIFFICULTY_DROPDOWNBUTTON: - X: PARENT_RIGHT - 290 - 150 - Y: 376 - Width: 140 - Height: 35 - Text: Difficulty - Font: Bold Container@MISSION_BIN: Children: VqaPlayer@MISSION_VIDEO: @@ -116,3 +130,14 @@ Container@MISSIONBROWSER_PANEL: Y: 1 Width: 640 Height: 375 +Background@FULLSCREEN_PLAYER: + Width: WINDOW_RIGHT + Height: WINDOW_BOTTOM + Background: panel-allblack + Visible: False + Children: + VqaPlayer@PLAYER: + X: 0 + Y: 0 + Width: WINDOW_RIGHT + Height: WINDOW_BOTTOM diff --git a/mods/ra/chrome/missionbrowser.yaml b/mods/ra/chrome/missionbrowser.yaml index 1200ec72d3..573016812a 100644 --- a/mods/ra/chrome/missionbrowser.yaml +++ b/mods/ra/chrome/missionbrowser.yaml @@ -16,7 +16,7 @@ Background@MISSIONBROWSER_PANEL: X: 20 Y: 50 Width: 270 - Height: 377 + Height: 332 Children: ScrollItem@HEADER: BaseName: scrollheader @@ -69,20 +69,34 @@ Background@MISSIONBROWSER_PANEL: Width: PARENT_RIGHT - 32 VAlign: Top Font: Small - Button@START_VIDEO_BUTTON: + Button@START_BRIEFING_VIDEO_BUTTON: X: 20 Y: PARENT_BOTTOM - 45 - Width: 120 + Width: 130 Height: 25 - Text: Play Briefing + Text: Watch Briefing Font: Bold - Button@STOP_VIDEO_BUTTON: + Button@STOP_BRIEFING_VIDEO_BUTTON: X: 20 Y: PARENT_BOTTOM - 45 - Width: 120 + Width: 130 Height: 25 Text: Stop Briefing Font: Bold + Button@START_INFO_VIDEO_BUTTON: + X: 160 + Y: PARENT_BOTTOM - 45 + Width: 130 + Height: 25 + Text: Watch Info Video + Font: Bold + Button@STOP_INFO_VIDEO_BUTTON: + X: 160 + Y: PARENT_BOTTOM - 45 + Width: 130 + Height: 25 + Text: Stop Info Video + Font: Bold Button@STARTGAME_BUTTON: X: PARENT_RIGHT - 140 - 130 Y: PARENT_BOTTOM - 45 @@ -99,9 +113,9 @@ Background@MISSIONBROWSER_PANEL: Font: Bold Key: escape DropDownButton@DIFFICULTY_DROPDOWNBUTTON: - X: PARENT_RIGHT - 140 - 130 - 150 - Y: PARENT_BOTTOM - 45 - Width: 140 + X: 20 + Y: 427 - HEIGHT + Width: 270 Height: 25 Text: Difficulty Font: Bold @@ -117,3 +131,14 @@ Background@MISSIONBROWSER_PANEL: Y: 1 Width: 640 Height: 375 +Background@FULLSCREEN_PLAYER: + Width: WINDOW_RIGHT + Height: WINDOW_BOTTOM + Background: dialog5 + Visible: False + Children: + VqaPlayer@PLAYER: + X: 0 + Y: 0 + Width: WINDOW_RIGHT + Height: WINDOW_BOTTOM