Add yaml-defined FMV playback to GUI elements

This commit is contained in:
Oliver Brakmann
2014-12-05 00:31:32 +01:00
parent 3bb3eebeb9
commit 04bd4627d4
5 changed files with 197 additions and 68 deletions

View File

@@ -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 public class Map
{ {
[FieldLoader.Ignore] public IFolder Container; [FieldLoader.Ignore] public IFolder Container;
@@ -102,7 +111,6 @@ namespace OpenRA
public string Title; public string Title;
public string Type = "Conquest"; public string Type = "Conquest";
public string PreviewVideo;
public string Description; public string Description;
public string Author; public string Author;
public string Tileset; public string Tileset;
@@ -131,6 +139,19 @@ namespace OpenRA
return options; 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<Dictionary<string, ActorReference>> Actors; [FieldLoader.Ignore] public Lazy<Dictionary<string, ActorReference>> Actors;
public int PlayerCount { get { return Players.Count(p => p.Value.Playable); } } public int PlayerCount { get { return Players.Count(p => p.Value.Playable); } }
@@ -372,7 +393,6 @@ namespace OpenRA
"Title", "Title",
"Description", "Description",
"Author", "Author",
"PreviewVideo",
"Tileset", "Tileset",
"MapSize", "MapSize",
"Bounds", "Bounds",

View File

@@ -21,7 +21,7 @@ namespace OpenRA.Mods.RA.Widgets
{ {
class LeaveMapLogic class LeaveMapLogic
{ {
enum Tab { Objectives, Chat }; enum Tab { Objectives, Chat }
Tab currentTab; Tab currentTab;
OrderManager orderManager; OrderManager orderManager;
@@ -125,6 +125,13 @@ namespace OpenRA.Mods.RA.Widgets
var objectivesContainer = dialog.Get<ContainerWidget>("OBJECTIVES_PANEL"); var objectivesContainer = dialog.Get<ContainerWidget>("OBJECTIVES_PANEL");
Game.LoadWidget(world, panel, objectivesContainer, new WidgetArgs()); Game.LoadWidget(world, panel, objectivesContainer, new WidgetArgs());
objectivesContainer.IsVisible = () => currentTab == Tab.Objectives; 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) if (isMultiplayer)

View File

@@ -23,14 +23,19 @@ namespace OpenRA.Mods.RA.Widgets.Logic
{ {
public class MissionBrowserLogic public class MissionBrowserLogic
{ {
enum PlayingVideo { None, Info, Briefing, GameStart }
readonly Action onStart; readonly Action onStart;
readonly ScrollPanelWidget descriptionPanel; readonly ScrollPanelWidget descriptionPanel;
readonly LabelWidget description; readonly LabelWidget description;
readonly SpriteFont descriptionFont; readonly SpriteFont descriptionFont;
readonly DropDownButtonWidget difficultyButton; readonly DropDownButtonWidget difficultyButton;
readonly ButtonWidget startVideoButton; readonly ButtonWidget startBriefingVideoButton;
readonly ButtonWidget stopVideoButton; readonly ButtonWidget stopBriefingVideoButton;
readonly ButtonWidget startInfoVideoButton;
readonly ButtonWidget stopInfoVideoButton;
readonly VqaPlayerWidget videoPlayer; readonly VqaPlayerWidget videoPlayer;
readonly BackgroundWidget fullscreenVideoPlayer;
readonly ScrollPanelWidget missionList; readonly ScrollPanelWidget missionList;
readonly ScrollItemWidget headerTemplate; readonly ScrollItemWidget headerTemplate;
@@ -38,12 +43,12 @@ namespace OpenRA.Mods.RA.Widgets.Logic
MapPreview selectedMapPreview; MapPreview selectedMapPreview;
bool showVideoPlayer; PlayingVideo playingVideo;
string difficulty; string difficulty;
[ObjectCreator.UseCtor] [ObjectCreator.UseCtor]
public MissionBrowserLogic(Widget widget, Action onStart, Action onExit) public MissionBrowserLogic(Widget widget, World world, Action onStart, Action onExit)
{ {
this.onStart = onStart; this.onStart = onStart;
@@ -54,16 +59,17 @@ namespace OpenRA.Mods.RA.Widgets.Logic
var title = widget.GetOrNull<LabelWidget>("MISSIONBROWSER_TITLE"); var title = widget.GetOrNull<LabelWidget>("MISSIONBROWSER_TITLE");
if (title != null) 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; widget.Get("MISSION_INFO").IsVisible = () => selectedMapPreview != null;
var previewWidget = widget.Get<MapPreviewWidget>("MISSION_PREVIEW"); var previewWidget = widget.Get<MapPreviewWidget>("MISSION_PREVIEW");
previewWidget.Preview = () => selectedMapPreview; previewWidget.Preview = () => selectedMapPreview;
previewWidget.IsVisible = () => !showVideoPlayer; previewWidget.IsVisible = () => playingVideo == PlayingVideo.None;
videoPlayer = widget.Get<VqaPlayerWidget>("MISSION_VIDEO"); videoPlayer = widget.Get<VqaPlayerWidget>("MISSION_VIDEO");
widget.Get("MISSION_BIN").IsVisible = () => showVideoPlayer; widget.Get("MISSION_BIN").IsVisible = () => playingVideo != PlayingVideo.None;
fullscreenVideoPlayer = Ui.LoadWidget<BackgroundWidget>("FULLSCREEN_PLAYER", Ui.Root, new WidgetArgs { { "world", world } });
descriptionPanel = widget.Get<ScrollPanelWidget>("MISSION_DESCRIPTION_PANEL"); descriptionPanel = widget.Get<ScrollPanelWidget>("MISSION_DESCRIPTION_PANEL");
@@ -72,10 +78,15 @@ namespace OpenRA.Mods.RA.Widgets.Logic
difficultyButton = widget.Get<DropDownButtonWidget>("DIFFICULTY_DROPDOWNBUTTON"); difficultyButton = widget.Get<DropDownButtonWidget>("DIFFICULTY_DROPDOWNBUTTON");
startVideoButton = widget.Get<ButtonWidget>("START_VIDEO_BUTTON"); startBriefingVideoButton = widget.Get<ButtonWidget>("START_BRIEFING_VIDEO_BUTTON");
stopVideoButton = widget.Get<ButtonWidget>("STOP_VIDEO_BUTTON"); stopBriefingVideoButton = widget.Get<ButtonWidget>("STOP_BRIEFING_VIDEO_BUTTON");
stopVideoButton.IsVisible = () => showVideoPlayer; stopBriefingVideoButton.IsVisible = () => playingVideo == PlayingVideo.Briefing;
stopVideoButton.OnClick = StopVideo; stopBriefingVideoButton.OnClick = () => StopVideo(videoPlayer);
startInfoVideoButton = widget.Get<ButtonWidget>("START_INFO_VIDEO_BUTTON");
stopInfoVideoButton = widget.Get<ButtonWidget>("STOP_INFO_VIDEO_BUTTON");
stopInfoVideoButton.IsVisible = () => playingVideo == PlayingVideo.Info;
stopInfoVideoButton.OnClick = () => StopVideo(videoPlayer);
var allMaps = new List<Map>(); var allMaps = new List<Map>();
missionList.RemoveChildren(); missionList.RemoveChildren();
@@ -114,12 +125,12 @@ namespace OpenRA.Mods.RA.Widgets.Logic
SelectMap(allMaps.First()); SelectMap(allMaps.First());
var startButton = widget.Get<ButtonWidget>("STARTGAME_BUTTON"); var startButton = widget.Get<ButtonWidget>("STARTGAME_BUTTON");
startButton.OnClick = StartMission; startButton.OnClick = StartMissionClicked;
startButton.IsDisabled = () => selectedMapPreview.RuleStatus != MapRuleStatus.Cached; startButton.IsDisabled = () => selectedMapPreview.RuleStatus != MapRuleStatus.Cached;
widget.Get<ButtonWidget>("BACK_BUTTON").OnClick = () => widget.Get<ButtonWidget>("BACK_BUTTON").OnClick = () =>
{ {
StopVideo(); StopVideo(videoPlayer);
Game.Disconnect(); Game.Disconnect();
Ui.CloseWindow(); Ui.CloseWindow();
onExit(); onExit();
@@ -128,7 +139,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic
void CreateMissionGroup(string title, IEnumerable<Map> maps) void CreateMissionGroup(string title, IEnumerable<Map> maps)
{ {
var header = ScrollItemWidget.Setup(headerTemplate, () => true, () => {}); var header = ScrollItemWidget.Setup(headerTemplate, () => true, () => { });
header.Get<LabelWidget>("LABEL").GetText = () => title; header.Get<LabelWidget>("LABEL").GetText = () => title;
missionList.AddChild(header); missionList.AddChild(header);
@@ -139,41 +150,35 @@ namespace OpenRA.Mods.RA.Widgets.Logic
var item = ScrollItemWidget.Setup(template, var item = ScrollItemWidget.Setup(template,
() => selectedMapPreview != null && selectedMapPreview.Uid == map.Uid, () => selectedMapPreview != null && selectedMapPreview.Uid == map.Uid,
() => SelectMap(map), () => SelectMap(map),
StartMission); StartMissionClicked);
item.Get<LabelWidget>("TITLE").GetText = () => map.Title; item.Get<LabelWidget>("TITLE").GetText = () => map.Title;
missionList.AddChild(item); missionList.AddChild(item);
} }
} }
float cachedSoundVolume;
float cachedMusicVolume;
void SelectMap(Map map) void SelectMap(Map map)
{ {
StopVideo();
selectedMapPreview = Game.modData.MapCache[map.Uid]; selectedMapPreview = Game.modData.MapCache[map.Uid];
// Cache the rules on a background thread to avoid jank // Cache the rules on a background thread to avoid jank
new Thread(selectedMapPreview.CacheRules).Start(); new Thread(selectedMapPreview.CacheRules).Start();
var video = selectedMapPreview.Map.PreviewVideo; var briefingVideo = selectedMapPreview.Map.Videos.Briefing;
var videoVisible = video != null; var briefingVideoVisible = briefingVideo != null;
var videoDisabled = !(videoVisible && GlobalFileSystem.Exists(video)); var briefingVideoDisabled = !(briefingVideoVisible && GlobalFileSystem.Exists(briefingVideo));
startVideoButton.IsVisible = () => videoVisible && !showVideoPlayer; var infoVideo = selectedMapPreview.Map.Videos.BackgroundInfo;
startVideoButton.IsDisabled = () => videoDisabled; var infoVideoVisible = infoVideo != null;
startVideoButton.OnClick = () => var infoVideoDisabled = !(infoVideoVisible && GlobalFileSystem.Exists(infoVideo));
{
showVideoPlayer = true;
videoPlayer.Load(video);
videoPlayer.PlayThen(StopVideo);
// Mute other distracting sounds startBriefingVideoButton.IsVisible = () => briefingVideoVisible && playingVideo != PlayingVideo.Briefing;
cachedSoundVolume = Sound.SoundVolume; startBriefingVideoButton.IsDisabled = () => briefingVideoDisabled || playingVideo != PlayingVideo.None;
cachedMusicVolume = Sound.MusicVolume; startBriefingVideoButton.OnClick = () => PlayVideo(videoPlayer, briefingVideo, PlayingVideo.Briefing, () => StopVideo(videoPlayer));
Sound.SoundVolume = Sound.MusicVolume = 0;
}; 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") : ""; var text = map.Description != null ? map.Description.Replace("\\n", "\n") : "";
text = WidgetUtils.WrapText(text, description.Bounds.Width, descriptionFont); 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) cachedSoundVolume = Sound.SoundVolume;
return; cachedMusicVolume = Sound.MusicVolume;
Sound.SoundVolume = Sound.MusicVolume = 0;
Sound.SoundVolume = cachedSoundVolume;
Sound.MusicVolume = cachedMusicVolume;
videoPlayer.Stop();
showVideoPlayer = false;
} }
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) if (selectedMapPreview.RuleStatus != MapRuleStatus.Cached)
return; return;
var gameStartVideo = selectedMapPreview.Map.Videos.GameStart;
if (gameStartVideo != null && GlobalFileSystem.Exists(gameStartVideo))
{
var fsPlayer = fullscreenVideoPlayer.Get<VqaPlayerWidget>("PLAYER");
fullscreenVideoPlayer.Visible = true;
PlayVideo(fsPlayer, gameStartVideo, PlayingVideo.GameStart, () =>
{
StopVideo(fsPlayer);
StartMission();
});
}
else
StartMission();
}
void StartMission()
{
OrderManager om = null; OrderManager om = null;
Action lobbyReady = null; Action lobbyReady = null;

View File

@@ -21,7 +21,7 @@ Container@MISSIONBROWSER_PANEL:
X: 15 X: 15
Y: 15 Y: 15
Width: 239 Width: 239
Height: 347 Height: 307
Children: Children:
ScrollItem@HEADER: ScrollItem@HEADER:
Width: PARENT_RIGHT-27 Width: PARENT_RIGHT-27
@@ -74,6 +74,13 @@ Container@MISSIONBROWSER_PANEL:
Width: PARENT_RIGHT - 32 Width: PARENT_RIGHT - 32
VAlign: Top VAlign: Top
Font: Small Font: Small
DropDownButton@DIFFICULTY_DROPDOWNBUTTON:
X: 15
Y: 337
Width: 239
Height: 25
Text: Difficulty
Font: Regular
Button@BACK_BUTTON: Button@BACK_BUTTON:
Y: 376 Y: 376
Width: 140 Width: 140
@@ -81,20 +88,34 @@ Container@MISSIONBROWSER_PANEL:
Text: Back Text: Back
Font: Bold Font: Bold
Key: escape Key: escape
Button@START_VIDEO_BUTTON: Button@START_BRIEFING_VIDEO_BUTTON:
X: PARENT_RIGHT - 290 X: PARENT_RIGHT - 290
Y: 376 Y: 376
Width: 140 Width: 140
Height: 35 Height: 35
Text: View Briefing Text: Watch Briefing
Font: Bold Font: Bold
Button@STOP_VIDEO_BUTTON: Button@STOP_BRIEFING_VIDEO_BUTTON:
X: PARENT_RIGHT - 290 X: PARENT_RIGHT - 290
Y: 376 Y: 376
Width: 140 Width: 140
Height: 35 Height: 35
Text: Stop Briefing Text: Stop Briefing
Font: Bold 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: Button@STARTGAME_BUTTON:
X: PARENT_RIGHT - 140 X: PARENT_RIGHT - 140
Y: 376 Y: 376
@@ -102,13 +123,6 @@ Container@MISSIONBROWSER_PANEL:
Height: 35 Height: 35
Text: Play Text: Play
Font: Bold Font: Bold
DropDownButton@DIFFICULTY_DROPDOWNBUTTON:
X: PARENT_RIGHT - 290 - 150
Y: 376
Width: 140
Height: 35
Text: Difficulty
Font: Bold
Container@MISSION_BIN: Container@MISSION_BIN:
Children: Children:
VqaPlayer@MISSION_VIDEO: VqaPlayer@MISSION_VIDEO:
@@ -116,3 +130,14 @@ Container@MISSIONBROWSER_PANEL:
Y: 1 Y: 1
Width: 640 Width: 640
Height: 375 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

View File

@@ -16,7 +16,7 @@ Background@MISSIONBROWSER_PANEL:
X: 20 X: 20
Y: 50 Y: 50
Width: 270 Width: 270
Height: 377 Height: 332
Children: Children:
ScrollItem@HEADER: ScrollItem@HEADER:
BaseName: scrollheader BaseName: scrollheader
@@ -69,20 +69,34 @@ Background@MISSIONBROWSER_PANEL:
Width: PARENT_RIGHT - 32 Width: PARENT_RIGHT - 32
VAlign: Top VAlign: Top
Font: Small Font: Small
Button@START_VIDEO_BUTTON: Button@START_BRIEFING_VIDEO_BUTTON:
X: 20 X: 20
Y: PARENT_BOTTOM - 45 Y: PARENT_BOTTOM - 45
Width: 120 Width: 130
Height: 25 Height: 25
Text: Play Briefing Text: Watch Briefing
Font: Bold Font: Bold
Button@STOP_VIDEO_BUTTON: Button@STOP_BRIEFING_VIDEO_BUTTON:
X: 20 X: 20
Y: PARENT_BOTTOM - 45 Y: PARENT_BOTTOM - 45
Width: 120 Width: 130
Height: 25 Height: 25
Text: Stop Briefing Text: Stop Briefing
Font: Bold 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: Button@STARTGAME_BUTTON:
X: PARENT_RIGHT - 140 - 130 X: PARENT_RIGHT - 140 - 130
Y: PARENT_BOTTOM - 45 Y: PARENT_BOTTOM - 45
@@ -99,9 +113,9 @@ Background@MISSIONBROWSER_PANEL:
Font: Bold Font: Bold
Key: escape Key: escape
DropDownButton@DIFFICULTY_DROPDOWNBUTTON: DropDownButton@DIFFICULTY_DROPDOWNBUTTON:
X: PARENT_RIGHT - 140 - 130 - 150 X: 20
Y: PARENT_BOTTOM - 45 Y: 427 - HEIGHT
Width: 140 Width: 270
Height: 25 Height: 25
Text: Difficulty Text: Difficulty
Font: Bold Font: Bold
@@ -117,3 +131,14 @@ Background@MISSIONBROWSER_PANEL:
Y: 1 Y: 1
Width: 640 Width: 640
Height: 375 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