#region Copyright & License Information /* * Copyright 2007-2015 The OpenRA Developers (see AUTHORS) * This file is part of OpenRA, which is free software. It is made * available to you under the terms of the GNU General Public License * as published by the Free Software Foundation. For more information, * see COPYING. */ #endregion using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; using OpenRA.Graphics; using OpenRA.Widgets; namespace OpenRA.Mods.Common.Widgets.Logic { public class MissionBrowserLogic : ChromeLogic { enum PlayingVideo { None, Info, Briefing, GameStart } readonly Action onStart; readonly ScrollPanelWidget descriptionPanel; readonly LabelWidget description; readonly SpriteFont descriptionFont; readonly DropDownButtonWidget difficultyButton; readonly DropDownButtonWidget gameSpeedButton; readonly ButtonWidget startBriefingVideoButton; readonly ButtonWidget stopBriefingVideoButton; readonly ButtonWidget startInfoVideoButton; readonly ButtonWidget stopInfoVideoButton; readonly VqaPlayerWidget videoPlayer; readonly BackgroundWidget fullscreenVideoPlayer; readonly ScrollPanelWidget missionList; readonly ScrollItemWidget headerTemplate; readonly ScrollItemWidget template; MapPreview selectedMapPreview; Map selectedMap; PlayingVideo playingVideo; string difficulty; string gameSpeed; [ObjectCreator.UseCtor] public MissionBrowserLogic(Widget widget, World world, Action onStart, Action onExit) { var modData = Game.ModData; this.onStart = onStart; missionList = widget.Get("MISSION_LIST"); headerTemplate = widget.Get("HEADER"); template = widget.Get("TEMPLATE"); var title = widget.GetOrNull("MISSIONBROWSER_TITLE"); if (title != null) 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 = () => playingVideo == PlayingVideo.None; videoPlayer = widget.Get("MISSION_VIDEO"); 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"); description = descriptionPanel.Get("MISSION_DESCRIPTION"); descriptionFont = Game.Renderer.Fonts[description.Font]; difficultyButton = widget.Get("DIFFICULTY_DROPDOWNBUTTON"); gameSpeedButton = widget.GetOrNull("GAMESPEED_DROPDOWNBUTTON"); 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(); // Add a group for each campaign if (modData.Manifest.Missions.Any()) { var yaml = MiniYaml.Merge(modData.Manifest.Missions.Select( m => MiniYaml.FromStream(modData.ModFiles.Open(m)))); foreach (var kv in yaml) { var missionMapPaths = kv.Value.Nodes.Select(n => Path.GetFullPath(n.Key)).ToList(); var maps = modData.MapCache .Where(p => p.Status == MapStatus.Available && missionMapPaths.Contains(Path.GetFullPath(p.Path))) .Select(p => new Map(p.Path)) .OrderBy(m => missionMapPaths.IndexOf(Path.GetFullPath(m.Path))); CreateMissionGroup(kv.Key, maps); allMaps.AddRange(maps); } } // Add an additional group for loose missions var looseMissions = modData.MapCache .Where(p => p.Status == MapStatus.Available && p.Visibility.HasFlag(MapVisibility.MissionSelector) && !allMaps.Any(m => m.Uid == p.Uid)) .Select(p => new Map(p.Path)); if (looseMissions.Any()) { CreateMissionGroup("Missions", looseMissions); allMaps.AddRange(looseMissions); } if (allMaps.Any()) SelectMap(allMaps.First()); var startButton = widget.Get("STARTGAME_BUTTON"); startButton.OnClick = StartMissionClicked; startButton.IsDisabled = () => selectedMap == null || selectedMap.InvalidCustomRules; widget.Get("BACK_BUTTON").OnClick = () => { StopVideo(videoPlayer); Game.Disconnect(); Ui.CloseWindow(); onExit(); }; } void CreateMissionGroup(string title, IEnumerable maps) { var header = ScrollItemWidget.Setup(headerTemplate, () => true, () => { }); header.Get("LABEL").GetText = () => title; missionList.AddChild(header); foreach (var m in maps) { var map = m; var item = ScrollItemWidget.Setup(template, () => selectedMapPreview != null && selectedMapPreview.Uid == map.Uid, () => SelectMap(map), StartMissionClicked); item.Get("TITLE").GetText = () => map.Title; missionList.AddChild(item); } } void SelectMap(Map map) { selectedMap = map; selectedMapPreview = Game.ModData.MapCache[map.Uid]; // Cache the rules on a background thread to avoid jank new Thread(() => selectedMap.PreloadRules()).Start(); var briefingVideo = map.Videos.Briefing; var briefingVideoVisible = briefingVideo != null; var briefingVideoDisabled = !(briefingVideoVisible && Game.ModData.ModFiles.Exists(briefingVideo)); var infoVideo = map.Videos.BackgroundInfo; var infoVideoVisible = infoVideo != null; var infoVideoDisabled = !(infoVideoVisible && Game.ModData.ModFiles.Exists(infoVideo)); 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); description.Text = text; description.Bounds.Height = descriptionFont.Measure(text).Y; descriptionPanel.ScrollToTop(); descriptionPanel.Layout.AdjustChildren(); if (difficultyButton != null) { difficultyButton.IsDisabled = () => !map.Options.Difficulties.Any(); difficulty = map.Options.Difficulties.FirstOrDefault(); difficultyButton.GetText = () => difficulty ?? "Normal"; difficultyButton.OnMouseDown = _ => { var options = map.Options.Difficulties.Select(d => new DropDownOption { Title = d, IsSelected = () => difficulty == d, OnClick = () => difficulty = d }); Func setupItem = (option, template) => { var item = ScrollItemWidget.Setup(template, option.IsSelected, option.OnClick); item.Get("LABEL").GetText = () => option.Title; return item; }; difficultyButton.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", options.Count() * 30, options, setupItem); }; } if (gameSpeedButton != null) { var speeds = Game.ModData.Manifest.Get().Speeds; gameSpeed = "default"; gameSpeedButton.GetText = () => speeds[gameSpeed].Name; gameSpeedButton.OnMouseDown = _ => { var options = speeds.Select(s => new DropDownOption { Title = s.Value.Name, IsSelected = () => gameSpeed == s.Key, OnClick = () => gameSpeed = s.Key }); Func setupItem = (option, template) => { var item = ScrollItemWidget.Setup(template, option.IsSelected, option.OnClick); item.Get("LABEL").GetText = () => option.Title; return item; }; gameSpeedButton.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", options.Count() * 30, options, setupItem); }; } } float cachedSoundVolume; float cachedMusicVolume; void MuteSounds() { cachedSoundVolume = Game.Sound.SoundVolume; cachedMusicVolume = Game.Sound.MusicVolume; Game.Sound.SoundVolume = Game.Sound.MusicVolume = 0; } void UnMuteSounds() { if (cachedSoundVolume > 0) Game.Sound.SoundVolume = cachedSoundVolume; if (cachedMusicVolume > 0) Game.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 (selectedMap.InvalidCustomRules) return; var gameStartVideo = selectedMap.Videos.GameStart; if (gameStartVideo != null && Game.ModData.ModFiles.Exists(gameStartVideo)) { var fsPlayer = fullscreenVideoPlayer.Get("PLAYER"); fullscreenVideoPlayer.Visible = true; PlayVideo(fsPlayer, gameStartVideo, PlayingVideo.GameStart, () => { StopVideo(fsPlayer); Game.StartMission(selectedMapPreview.Uid, gameSpeed, difficulty, onStart); }); } else Game.StartMission(selectedMapPreview.Uid, gameSpeed, difficulty, onStart); } class DropDownOption { public string Title; public Func IsSelected; public Action OnClick; } } }