From c7d8024522cdb399a894e03d62164e98f777717c Mon Sep 17 00:00:00 2001 From: ScottNZ Date: Sun, 11 May 2014 05:28:09 +1200 Subject: [PATCH] Add a news panel to each mod --- OpenRA.Game/FieldLoader.cs | 8 + OpenRA.Game/FieldSaver.cs | 3 + OpenRA.Game/Manifest.cs | 4 + OpenRA.Game/Settings.cs | 3 + .../Widgets/Logic/CncMainMenuLogic.cs | 2 +- OpenRA.Mods.RA/Widgets/Logic/MainMenuLogic.cs | 154 ++++++++++++++++++ OpenRA.Mods.RA/Widgets/Logic/SettingsLogic.cs | 2 + mods/cnc/chrome/mainmenu.yaml | 75 +++++++++ mods/cnc/chrome/settings.yaml | 7 + mods/cnc/mod.yaml | 2 + mods/d2k/chrome/mainmenu.yaml | 73 +++++++++ mods/d2k/mod.yaml | 2 + mods/ra/chrome/mainmenu.yaml | 74 ++++++++- mods/ra/chrome/settings.yaml | 7 + mods/ra/mod.yaml | 2 + mods/ts/mod.yaml | 2 + 16 files changed, 418 insertions(+), 2 deletions(-) diff --git a/OpenRA.Game/FieldLoader.cs b/OpenRA.Game/FieldLoader.cs index ed4f02eea5..860015573c 100644 --- a/OpenRA.Game/FieldLoader.cs +++ b/OpenRA.Game/FieldLoader.cs @@ -356,6 +356,14 @@ namespace OpenRA return fieldType.GetConstructor(new[] { innerType }).Invoke(new[] { innerValue }); } + else if (fieldType == typeof(DateTime)) + { + DateTime dt; + if (DateTime.TryParseExact(value, "yyyy-MM-dd HH-mm-ss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out dt)) + return dt; + return InvalidValueAction(value, fieldType, fieldName); + } + UnknownFieldAction("[Type] {0}".F(value), fieldType); return null; } diff --git a/OpenRA.Game/FieldSaver.cs b/OpenRA.Game/FieldSaver.cs index e2c7048a41..53da76c6b6 100644 --- a/OpenRA.Game/FieldSaver.cs +++ b/OpenRA.Game/FieldSaver.cs @@ -82,6 +82,9 @@ namespace OpenRA return elems.JoinWith(","); } + if (t == typeof(DateTime)) + return ((DateTime)v).ToString("yyyy-MM-dd HH-mm-ss", CultureInfo.InvariantCulture); + return v.ToString(); } diff --git a/OpenRA.Game/Manifest.cs b/OpenRA.Game/Manifest.cs index 3efa61b6ee..dfe79185ee 100644 --- a/OpenRA.Game/Manifest.cs +++ b/OpenRA.Game/Manifest.cs @@ -31,6 +31,7 @@ namespace OpenRA public readonly MiniYaml LobbyDefaults; public readonly Dictionary> Fonts; public readonly Size TileSize = new Size(24, 24); + public readonly string NewsUrl; public Manifest(string mod) { @@ -82,6 +83,9 @@ namespace OpenRA compat.Add(c.Trim()); MapCompatibility = compat.ToArray(); + + if (yaml.ContainsKey("NewsUrl")) + NewsUrl = yaml["NewsUrl"].Value; } static string[] YamlList(Dictionary yaml, string key) diff --git a/OpenRA.Game/Settings.cs b/OpenRA.Game/Settings.cs index 0b6f047a6d..650307bd75 100644 --- a/OpenRA.Game/Settings.cs +++ b/OpenRA.Game/Settings.cs @@ -138,6 +138,9 @@ namespace OpenRA public bool AllowDownloading = true; public string MapRepository = "http://resource.openra.net/map/"; + + public bool FetchNews = true; + public DateTime NewsFetchedDate; } public class KeySettings diff --git a/OpenRA.Mods.Cnc/Widgets/Logic/CncMainMenuLogic.cs b/OpenRA.Mods.Cnc/Widgets/Logic/CncMainMenuLogic.cs index 266515e2b6..43a0429e64 100644 --- a/OpenRA.Mods.Cnc/Widgets/Logic/CncMainMenuLogic.cs +++ b/OpenRA.Mods.Cnc/Widgets/Logic/CncMainMenuLogic.cs @@ -20,7 +20,7 @@ namespace OpenRA.Mods.Cnc.Widgets.Logic : base(widget, world) { var shellmapDecorations = widget.Get("SHELLMAP_DECORATIONS"); - shellmapDecorations.IsVisible = () => menuType != MenuType.None && Game.Settings.Game.ShowShellmap; + shellmapDecorations.IsVisible = () => menuType != MenuType.None && Game.Settings.Game.ShowShellmap && !newsBG.IsVisible(); shellmapDecorations.Get("RECBLOCK").IsVisible = () => world.WorldTick / 25 % 2 == 0; var shellmapDisabledDecorations = widget.Get("SHELLMAP_DISABLED_DECORATIONS"); diff --git a/OpenRA.Mods.RA/Widgets/Logic/MainMenuLogic.cs b/OpenRA.Mods.RA/Widgets/Logic/MainMenuLogic.cs index 8e39d981ca..60106a3b9f 100644 --- a/OpenRA.Mods.RA/Widgets/Logic/MainMenuLogic.cs +++ b/OpenRA.Mods.RA/Widgets/Logic/MainMenuLogic.cs @@ -8,8 +8,12 @@ */ #endregion +using System; +using System.Collections.Generic; +using System.IO; using System.Linq; using System.Net; +using System.Text; using OpenRA.Widgets; namespace OpenRA.Mods.RA.Widgets.Logic @@ -21,6 +25,13 @@ namespace OpenRA.Mods.RA.Widgets.Logic protected MenuType menuType = MenuType.Main; Widget rootMenu; + protected readonly Widget newsBG; + readonly ScrollPanelWidget newsPanel; + readonly Widget newsItemTemplate; + readonly LabelWidget newsStatus; + readonly ButtonWidget showNewsButton; + bool newsExpanded = false; + [ObjectCreator.UseCtor] public MainMenuLogic(Widget widget, World world) { @@ -126,6 +137,149 @@ namespace OpenRA.Mods.RA.Widgets.Logic }; extrasMenu.Get("BACK_BUTTON").OnClick = () => menuType = MenuType.Main; + + newsBG = widget.GetOrNull("NEWS_BG"); + if (newsBG != null) + { + var collapsedNewsBG = widget.Get("COLLAPSED_NEWS_BG"); + + if (!Game.Settings.Game.FetchNews) + collapsedNewsBG.Visible = false; + else + { + newsPanel = widget.Get("NEWS_PANEL"); + newsItemTemplate = widget.Get("NEWS_ITEM_TEMPLATE"); + newsStatus = widget.Get("NEWS_STATUS"); + showNewsButton = widget.Get("SHOW_NEWS_BUTTON"); + + newsPanel.RemoveChildren(); + + newsBG.IsVisible = () => newsExpanded && menuType != MenuType.None; + collapsedNewsBG.IsVisible = () => !newsExpanded && menuType != MenuType.None; + + newsBG.Get("HIDE_NEWS_BUTTON").OnMouseDown = mi => newsExpanded = false; + collapsedNewsBG.Get("SHOW_NEWS_BUTTON").OnMouseDown = mi => + { + showNewsButton.IsHighlighted = () => false; + newsExpanded = true; + }; + + SetNewsStatus("Loading news"); + + if (Game.modData.Manifest.NewsUrl != null) + { + var cacheFile = GetNewsCacheFile(); + var cacheValid = File.Exists(cacheFile) && DateTime.Today.ToUniversalTime() <= Game.Settings.Game.NewsFetchedDate; + + if (cacheValid) + DisplayNews(ReadNews(File.ReadAllBytes(cacheFile))); + else + new Download(Game.modData.Manifest.NewsUrl, e => { }, NewsDownloadComplete); + } + } + } + } + + string GetNewsCacheFile() + { + var cacheDir = Path.Combine(Platform.SupportDir, "cache", Game.modData.Manifest.Mod.Id); + Directory.CreateDirectory(cacheDir); + return Path.Combine(cacheDir, "news.yaml"); + } + + void SetNewsStatus(string message) + { + message = WidgetUtils.WrapText(message, newsStatus.Bounds.Width, Game.Renderer.Fonts[newsStatus.Font]); + newsStatus.GetText = () => message; + } + + class NewsItem + { + public string Title; + public string Author; + public DateTime DateTime; + public string Content; + } + + IEnumerable ReadNews(byte[] bytes) + { + var str = Encoding.UTF8.GetString(bytes); + return MiniYaml.FromString(str).Select(node => new NewsItem + { + Title = node.Value.NodesDict["Title"].Value, + Author = node.Value.NodesDict["Author"].Value, + DateTime = FieldLoader.GetValue("DateTime", node.Key), + Content = node.Value.NodesDict["Content"].Value + }); + } + + void DisplayNews(IEnumerable newsItems) + { + newsPanel.RemoveChildren(); + SetNewsStatus(""); + + foreach (var i in newsItems) + { + var item = i; + + var newsItem = newsItemTemplate.Clone(); + + var titleLabel = newsItem.Get("TITLE"); + titleLabel.GetText = () => item.Title; + + var authorDateTimeLabel = newsItem.Get("AUTHOR_DATETIME"); + var authorDateTime = authorDateTimeLabel.Text.F(item.Author, item.DateTime.ToLocalTime()); + authorDateTimeLabel.GetText = () => authorDateTime; + + var contentLabel = newsItem.Get("CONTENT"); + var content = item.Content.Replace("\\n", "\n"); + content = WidgetUtils.WrapText(content, contentLabel.Bounds.Width, Game.Renderer.Fonts[contentLabel.Font]); + contentLabel.GetText = () => content; + contentLabel.Bounds.Height = Game.Renderer.Fonts[contentLabel.Font].Measure(content).Y; + newsItem.Bounds.Height += contentLabel.Bounds.Height; + + newsPanel.AddChild(newsItem); + newsPanel.Layout.AdjustChildren(); + } + } + + void NewsDownloadComplete(DownloadDataCompletedEventArgs e, bool cancelled) + { + Game.RunAfterTick(() => // run on the main thread + { + if (e.Error != null) + { + SetNewsStatus("Failed to retrieve news: {0}".F(Download.FormatErrorMessage(e.Error))); + return; + } + + IEnumerable newNews; + try + { + newNews = ReadNews(e.Result); + DisplayNews(newNews); + } + catch (Exception ex) + { + SetNewsStatus("Failed to retrieve news: {0}".F(ex.Message)); + return; + } + + Game.Settings.Game.NewsFetchedDate = DateTime.Today.ToUniversalTime(); + Game.Settings.Save(); + + var cacheFile = GetNewsCacheFile(); + if (File.Exists(cacheFile)) + { + var oldNews = ReadNews(File.ReadAllBytes(cacheFile)); + if (newNews.Any(n => !oldNews.Select(c => c.DateTime).Contains(n.DateTime))) + showNewsButton.IsHighlighted = () => Game.LocalTick % 50 < 25; + } + else + showNewsButton.IsHighlighted = () => Game.LocalTick % 50 < 25; + + File.WriteAllBytes(cacheFile, e.Result); + }); } void RemoveShellmapUI() diff --git a/OpenRA.Mods.RA/Widgets/Logic/SettingsLogic.cs b/OpenRA.Mods.RA/Widgets/Logic/SettingsLogic.cs index 3f0ba1076c..e2aef9edaa 100644 --- a/OpenRA.Mods.RA/Widgets/Logic/SettingsLogic.cs +++ b/OpenRA.Mods.RA/Widgets/Logic/SettingsLogic.cs @@ -364,6 +364,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic { var ds = Game.Settings.Debug; var ss = Game.Settings.Server; + var gs = Game.Settings.Game; BindCheckboxPref(panel, "NAT_DISCOVERY", ss, "DiscoverNatDevices"); BindCheckboxPref(panel, "VERBOSE_NAT_CHECKBOX", ss, "VerboseNatDiscovery"); @@ -372,6 +373,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic BindCheckboxPref(panel, "CHECKUNSYNCED_CHECKBOX", ds, "SanityCheckUnsyncedCode"); BindCheckboxPref(panel, "BOTDEBUG_CHECKBOX", ds, "BotDebug"); BindCheckboxPref(panel, "CRASH_DIALOG_CHECKBOX", ds, "ShowFatalErrorDialog"); + BindCheckboxPref(panel, "FETCH_NEWS_CHECKBOX", gs, "FetchNews"); return () => { }; } diff --git a/mods/cnc/chrome/mainmenu.yaml b/mods/cnc/chrome/mainmenu.yaml index ce00d1e8cf..3557241a58 100644 --- a/mods/cnc/chrome/mainmenu.yaml +++ b/mods/cnc/chrome/mainmenu.yaml @@ -186,6 +186,81 @@ Container@MENU_BACKGROUND: Width: 140 Height: 35 Text: Back + Container@COLLAPSED_NEWS_BG: + X: WINDOW_RIGHT - WIDTH - 55 + Y: 136 + Width: 140 + Height: 45 + Children: + DropDownButton@SHOW_NEWS_BUTTON: + X: PARENT_RIGHT - WIDTH + Y: 10 + Width: 120 + Height: 25 + Text: Show News + Font: Bold + Background@NEWS_BG: + X: WINDOW_RIGHT - WIDTH - 55 + Y: 170 + Width: 500 + Height: 265 + Background: panel-black + Children: + Label@NEWS_TITLE: + X: 0 + Y: 0 - 40 + Width: PARENT_RIGHT + Height: 30 + Text: News + Align: Center + Font: BigBold + Contrast: True + DropDownButton@HIDE_NEWS_BUTTON: + X: PARENT_RIGHT - WIDTH + Y: 0 - 24 + Width: 120 + Height: 25 + Text: Hide News + Font: Bold + ScrollPanel@NEWS_PANEL: + X: 15 + Y: 15 + Width: PARENT_RIGHT - 30 + Height: 235 + ItemSpacing: 5 + Children: + Container@NEWS_ITEM_TEMPLATE: + X: 5 + Y: 5 + Width: PARENT_RIGHT - 30 + Height: 45 + Children: + Label@TITLE: + X: 0 + Y: 0 + Width: PARENT_RIGHT + Height: 25 + Align: Center + Font: Bold + Label@AUTHOR_DATETIME: + X: 0 + Y: 25 + Width: PARENT_RIGHT + Height: 15 + Align: Center + Text: by {0} at {1} + Font: TinyBold + Label@CONTENT: + X: 0 + Y: 45 + Width: PARENT_RIGHT + Label@NEWS_STATUS: + X: 80 + Y: 0 + Width: PARENT_RIGHT - 80 - 80 - 24 + Height: PARENT_BOTTOM + Align: Center + VAlign: Middle Container@PERFORMANCE_INFO: Logic: PerfDebugLogic Children: diff --git a/mods/cnc/chrome/settings.yaml b/mods/cnc/chrome/settings.yaml index 4b95dbf60c..d9f653322a 100644 --- a/mods/cnc/chrome/settings.yaml +++ b/mods/cnc/chrome/settings.yaml @@ -415,6 +415,13 @@ Container@SETTINGS_PANEL: Height: 20 Font: Regular Text: Show Performance Graph + Checkbox@FETCH_NEWS_CHECKBOX: + X: 15 + Y: 100 + Width: 300 + Height: 20 + Font: Regular + Text: Fetch Community News Label@DEBUG_TITLE: Y: 140 Width: PARENT_RIGHT diff --git a/mods/cnc/mod.yaml b/mods/cnc/mod.yaml index 035b32a7bb..a984034f55 100644 --- a/mods/cnc/mod.yaml +++ b/mods/cnc/mod.yaml @@ -190,3 +190,5 @@ Missions: mods/cnc/missions.yaml SupportsMapsFrom: cnc + +NewsUrl: https://raw.githubusercontent.com/OpenRA/OpenRAWeb/master/content/news/ingame-news.yaml \ No newline at end of file diff --git a/mods/d2k/chrome/mainmenu.yaml b/mods/d2k/chrome/mainmenu.yaml index 2eaa717d14..220d20aa60 100644 --- a/mods/d2k/chrome/mainmenu.yaml +++ b/mods/d2k/chrome/mainmenu.yaml @@ -149,6 +149,79 @@ Container@MAINMENU: Height: 30 Text: Back Font: Bold + Background@COLLAPSED_NEWS_BG: + X: WINDOW_RIGHT - WIDTH - 20 + Y: 20 + Width: 140 + Height: 45 + Children: + DropDownButton@SHOW_NEWS_BUTTON: + X: PARENT_RIGHT - WIDTH - 10 + Y: 10 + Width: 120 + Height: 25 + Text: Show News + Font: Bold + Background@NEWS_BG: + X: WINDOW_RIGHT - WIDTH - 20 + Y: 20 + Width: 500 + Height: 300 + Children: + Label@NEWS_TITLE: + X: 0 + Y: 20 + Width: PARENT_RIGHT + Height: 30 + Text: News + Align: Center + Font: Bold + DropDownButton@HIDE_NEWS_BUTTON: + X: PARENT_RIGHT - WIDTH - 10 + Y: 10 + Width: 120 + Height: 25 + Text: Hide News + Font: Bold + ScrollPanel@NEWS_PANEL: + X: 15 + Y: 50 + Width: PARENT_RIGHT - 30 + Height: 235 + ItemSpacing: 5 + Children: + Container@NEWS_ITEM_TEMPLATE: + X: 5 + Y: 5 + Width: PARENT_RIGHT - 30 + Height: 45 + Children: + Label@TITLE: + X: 0 + Y: 0 + Width: PARENT_RIGHT + Height: 25 + Align: Center + Font: Bold + Label@AUTHOR_DATETIME: + X: 0 + Y: 25 + Width: PARENT_RIGHT + Height: 15 + Align: Center + Text: by {0} at {1} + Font: TinyBold + Label@CONTENT: + X: 0 + Y: 45 + Width: PARENT_RIGHT + Label@NEWS_STATUS: + X: 80 + Y: 0 + Width: PARENT_RIGHT - 80 - 80 - 24 + Height: PARENT_BOTTOM + Align: Center + VAlign: Middle Container@PERFORMANCE_INFO: Logic: PerfDebugLogic Children: diff --git a/mods/d2k/mod.yaml b/mods/d2k/mod.yaml index 4fb04f678e..6f4ebedb6c 100644 --- a/mods/d2k/mod.yaml +++ b/mods/d2k/mod.yaml @@ -169,3 +169,5 @@ LuaScripts: mods/common/lua/facing.lua SupportsMapsFrom: d2k + +NewsUrl: https://raw.githubusercontent.com/OpenRA/OpenRAWeb/master/content/news/ingame-news.yaml \ No newline at end of file diff --git a/mods/ra/chrome/mainmenu.yaml b/mods/ra/chrome/mainmenu.yaml index 8f9f0df508..a970371d22 100644 --- a/mods/ra/chrome/mainmenu.yaml +++ b/mods/ra/chrome/mainmenu.yaml @@ -184,4 +184,76 @@ Container@MAINMENU: Y: 5 Width: 200 Height: 200 - + Background@COLLAPSED_NEWS_BG: + X: WINDOW_RIGHT - WIDTH - 100 + Y: 320 + Width: 140 + Height: 45 + Children: + DropDownButton@SHOW_NEWS_BUTTON: + X: PARENT_RIGHT - WIDTH - 10 + Y: 10 + Width: 120 + Height: 25 + Text: Show News + Font: Bold + Background@NEWS_BG: + X: WINDOW_RIGHT - WIDTH - 100 + Y: 320 + Width: 500 + Height: 300 + Children: + Label@NEWS_TITLE: + X: 0 + Y: 20 + Width: PARENT_RIGHT + Height: 30 + Text: News + Align: Center + Font: Bold + DropDownButton@HIDE_NEWS_BUTTON: + X: PARENT_RIGHT - WIDTH - 10 + Y: 10 + Width: 120 + Height: 25 + Text: Hide News + Font: Bold + ScrollPanel@NEWS_PANEL: + X: 15 + Y: 50 + Width: PARENT_RIGHT - 30 + Height: 235 + ItemSpacing: 5 + Children: + Container@NEWS_ITEM_TEMPLATE: + X: 5 + Y: 5 + Width: PARENT_RIGHT - 30 + Height: 45 + Children: + Label@TITLE: + X: 0 + Y: 0 + Width: PARENT_RIGHT + Height: 25 + Align: Center + Font: Bold + Label@AUTHOR_DATETIME: + X: 0 + Y: 25 + Width: PARENT_RIGHT + Height: 15 + Align: Center + Text: by {0} at {1} + Font: TinyBold + Label@CONTENT: + X: 0 + Y: 45 + Width: PARENT_RIGHT + Label@NEWS_STATUS: + X: 80 + Y: 0 + Width: PARENT_RIGHT - 80 - 80 - 24 + Height: PARENT_BOTTOM + Align: Center + VAlign: Middle diff --git a/mods/ra/chrome/settings.yaml b/mods/ra/chrome/settings.yaml index 07ce211ac4..191bff3d4e 100644 --- a/mods/ra/chrome/settings.yaml +++ b/mods/ra/chrome/settings.yaml @@ -415,6 +415,13 @@ Background@SETTINGS_PANEL: Height: 20 Font: Regular Text: Show Performance Graph + Checkbox@FETCH_NEWS_CHECKBOX: + X: 15 + Y: 100 + Width: 300 + Height: 20 + Font: Regular + Text: Fetch Community News Label@DEBUG_TITLE: Y: 140 Width: PARENT_RIGHT diff --git a/mods/ra/mod.yaml b/mods/ra/mod.yaml index f6d8ab90a9..84323c6aed 100644 --- a/mods/ra/mod.yaml +++ b/mods/ra/mod.yaml @@ -188,3 +188,5 @@ Missions: mods/ra/missions.yaml SupportsMapsFrom: ra + +NewsUrl: https://raw.githubusercontent.com/OpenRA/OpenRAWeb/master/content/news/ingame-news.yaml \ No newline at end of file diff --git a/mods/ts/mod.yaml b/mods/ts/mod.yaml index f981ff4f00..313607952e 100644 --- a/mods/ts/mod.yaml +++ b/mods/ts/mod.yaml @@ -209,3 +209,5 @@ LuaScripts: mods/common/lua/facing.lua SupportsMapsFrom: ts + +NewsUrl: https://raw.githubusercontent.com/OpenRA/OpenRAWeb/master/content/news/ingame-news.yaml \ No newline at end of file