diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index 2d0ac08622..55fd98b7be 100644 --- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj +++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj @@ -634,6 +634,7 @@ + diff --git a/OpenRA.Mods.Common/Traits/PaletteEffects/MenuPaletteEffect.cs b/OpenRA.Mods.Common/Traits/PaletteEffects/MenuPaletteEffect.cs index 5ae9c3066e..82525b11c7 100644 --- a/OpenRA.Mods.Common/Traits/PaletteEffects/MenuPaletteEffect.cs +++ b/OpenRA.Mods.Common/Traits/PaletteEffects/MenuPaletteEffect.cs @@ -31,7 +31,7 @@ namespace OpenRA.Mods.Common.Traits public object Create(ActorInitializer init) { return new MenuPaletteEffect(this); } } - public class MenuPaletteEffect : IPaletteModifier, IRender, IWorldLoaded + public class MenuPaletteEffect : IPaletteModifier, IRender, IWorldLoaded, INotifyGameLoaded { public enum EffectType { None, Black, Desaturated } public readonly MenuPaletteEffectInfo Info; @@ -110,9 +110,20 @@ namespace OpenRA.Mods.Common.Traits } } - public void WorldLoaded(World w, WorldRenderer wr) + void IWorldLoaded.WorldLoaded(World w, WorldRenderer wr) { - Fade(Info.Effect); + // HACK: Defer fade-in until the GameLoaded notification for game saves + if (!w.IsLoadingGameSave) + Fade(Info.Effect); + } + + void INotifyGameLoaded.GameLoaded(World world) + { + // HACK: Let the menu opening trigger the fade for game saves + // to avoid glitches resulting from trying to trigger both + // the standard and menu fades at the same time + if (world.IsReplay) + Fade(Info.Effect); } } } diff --git a/OpenRA.Mods.Common/Traits/World/LoadWidgetAtGameStart.cs b/OpenRA.Mods.Common/Traits/World/LoadWidgetAtGameStart.cs index 47edffe59f..33373e5341 100644 --- a/OpenRA.Mods.Common/Traits/World/LoadWidgetAtGameStart.cs +++ b/OpenRA.Mods.Common/Traits/World/LoadWidgetAtGameStart.cs @@ -10,6 +10,7 @@ #endregion using OpenRA.Graphics; +using OpenRA.Mods.Common.Widgets; using OpenRA.Traits; using OpenRA.Widgets; @@ -26,31 +27,65 @@ namespace OpenRA.Mods.Common.Traits [Desc("The widget tree to open when the map editor is loaded.")] public readonly string EditorRoot = "EDITOR_ROOT"; + [Desc("The widget tree to open (in addition to INGAME_ROOT) while loading a saved game.")] + public readonly string GameSaveLoadingRoot = "GAMESAVE_LOADING_SCREEN"; + [Desc("Remove any existing UI when a map is loaded.")] public readonly bool ClearRoot = true; public object Create(ActorInitializer init) { return new LoadWidgetAtGameStart(this); } } - public class LoadWidgetAtGameStart : IWorldLoaded + public class LoadWidgetAtGameStart : IWorldLoaded, INotifyGameLoading, INotifyGameLoaded { readonly LoadWidgetAtGameStartInfo info; + Widget root; public LoadWidgetAtGameStart(LoadWidgetAtGameStartInfo info) { this.info = info; } - public void WorldLoaded(World world, WorldRenderer wr) + void INotifyGameLoading.GameLoading(World world) { // Clear any existing widget state if (info.ClearRoot) Ui.ResetAll(); + Ui.OpenWindow(info.GameSaveLoadingRoot, new WidgetArgs() + { + { "world", world } + }); + } + + void IWorldLoaded.WorldLoaded(World world, WorldRenderer wr) + { + if (!world.IsLoadingGameSave && info.ClearRoot) + Ui.ResetAll(); + var widget = world.Type == WorldType.Shellmap ? info.ShellmapRoot : world.Type == WorldType.Editor ? info.EditorRoot : info.IngameRoot; - Game.LoadWidget(world, widget, Ui.Root, new WidgetArgs()); + root = Game.LoadWidget(world, widget, Ui.Root, new WidgetArgs()); + + // The Lua API requires the UI to available, so hide it instead + if (world.IsLoadingGameSave) + root.IsVisible = () => false; + } + + void INotifyGameLoaded.GameLoaded(World world) + { + Ui.CloseWindow(); + root.IsVisible = () => true; + + // Open the options menu + if (!world.IsReplay) + { + var optionsButton = root.GetOrNull("OPTIONS_BUTTON"); + world.SetPauseState(false); + if (optionsButton != null) + Sync.RunUnsynced(Game.Settings.Debug.SyncCheckUnsyncedCode, world, optionsButton.OnClick); + } } } } diff --git a/OpenRA.Mods.Common/Traits/World/MusicPlaylist.cs b/OpenRA.Mods.Common/Traits/World/MusicPlaylist.cs index 65e6cf126e..c6b2d526f1 100644 --- a/OpenRA.Mods.Common/Traits/World/MusicPlaylist.cs +++ b/OpenRA.Mods.Common/Traits/World/MusicPlaylist.cs @@ -12,6 +12,7 @@ using System; using System.Linq; using OpenRA.GameRules; +using OpenRA.Graphics; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits @@ -38,7 +39,7 @@ namespace OpenRA.Mods.Common.Traits public object Create(ActorInitializer init) { return new MusicPlaylist(init.World, this); } } - public class MusicPlaylist : INotifyActorDisposing, IGameOver + public class MusicPlaylist : INotifyActorDisposing, IGameOver, IWorldLoaded, INotifyGameLoaded { readonly MusicPlaylistInfo info; readonly World world; @@ -89,7 +90,16 @@ namespace OpenRA.Mods.Common.Traits currentSong = world.Map.Rules.Music[info.StartingMusic]; CurrentSongIsBackground = false; } + } + void IWorldLoaded.WorldLoaded(World world, WorldRenderer wr) + { + if (!world.IsLoadingGameSave) + Play(); + } + + void INotifyGameLoaded.GameLoaded(World world) + { Play(); } diff --git a/OpenRA.Mods.Common/Traits/World/StartGameNotification.cs b/OpenRA.Mods.Common/Traits/World/StartGameNotification.cs index aa588211ec..ab325a4100 100644 --- a/OpenRA.Mods.Common/Traits/World/StartGameNotification.cs +++ b/OpenRA.Mods.Common/Traits/World/StartGameNotification.cs @@ -30,9 +30,10 @@ namespace OpenRA.Mods.Common.Traits this.info = info; } - public void WorldLoaded(World world, WorldRenderer wr) + void IWorldLoaded.WorldLoaded(World world, WorldRenderer wr) { - Game.Sound.PlayNotification(world.Map.Rules, null, "Speech", info.Notification, world.RenderPlayer == null ? null : world.RenderPlayer.Faction.InternalName); + if (!world.IsLoadingGameSave) + Game.Sound.PlayNotification(world.Map.Rules, null, "Speech", info.Notification, world.RenderPlayer == null ? null : world.RenderPlayer.Faction.InternalName); } } } diff --git a/OpenRA.Mods.Common/Widgets/Logic/Ingame/GameSaveLoadingLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Ingame/GameSaveLoadingLogic.cs new file mode 100644 index 0000000000..877c7de2d0 --- /dev/null +++ b/OpenRA.Mods.Common/Widgets/Logic/Ingame/GameSaveLoadingLogic.cs @@ -0,0 +1,49 @@ +#region Copyright & License Information +/* + * Copyright 2007-2019 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, either version 3 of + * the License, or (at your option) any later version. For more + * information, see COPYING. + */ +#endregion + +using OpenRA.Widgets; + +namespace OpenRA.Mods.Common.Widgets.Logic +{ + public class GameSaveLoadingLogic : ChromeLogic + { + [ObjectCreator.UseCtor] + public GameSaveLoadingLogic(Widget widget, ModData modData, World world) + { + widget.Get("PROGRESS").GetPercentage = () => world.GameSaveLoadingPercentage; + + var versionLabel = widget.GetOrNull("VERSION_LABEL"); + if (versionLabel != null) + versionLabel.Text = modData.Manifest.Metadata.Version; + + var keyhandler = widget.Get("CANCEL_HANDLER"); + keyhandler.AddHandler(e => + { + if (e.Event == KeyInputEvent.Down && e.Key == Keycode.ESCAPE) + { + Game.Disconnect(); + Ui.ResetAll(); + Game.LoadShellMap(); + return true; + } + + return false; + }); + + Game.HideCursor = true; + } + + protected override void Dispose(bool disposing) + { + Game.HideCursor = false; + } + } +} diff --git a/mods/cnc/chrome/gamesave-loading.yaml b/mods/cnc/chrome/gamesave-loading.yaml new file mode 100644 index 0000000000..6e0ac415dc --- /dev/null +++ b/mods/cnc/chrome/gamesave-loading.yaml @@ -0,0 +1,52 @@ +Container@GAMESAVE_LOADING_SCREEN: + Logic: GameSaveLoadingLogic + Width: WINDOW_RIGHT + Height: WINDOW_BOTTOM + Children: + LogicKeyListener@CANCEL_HANDLER: + Image@NOD: + X: WINDOW_RIGHT / 2 - 384 + Y: (WINDOW_BOTTOM - 256) / 2 + ImageCollection: logos + ImageName: nod-load + Image@GDI: + X: WINDOW_RIGHT / 2 + 128 + Y: (WINDOW_BOTTOM - 256) / 2 + ImageCollection: logos + ImageName: gdi-load + Image@EVA: + X: WINDOW_RIGHT - 128 - 43 + Y: 43 + Width: 128 + Height: 64 + ImageCollection: logos + ImageName: eva + Label@VERSION_LABEL: + X: WINDOW_RIGHT - 128 - 43 + Y: 115 + Width: 128 + Align: Center + Shadow: true + Background@BORDER: + Width: WINDOW_RIGHT + Height: WINDOW_BOTTOM + Background: shellmapborder + Label@TITLE: + Width: WINDOW_RIGHT + Y: 3 * WINDOW_BOTTOM / 4 - 30 + Height: 25 + Font: Bold + Align: Center + Text: Loading Saved Game + ProgressBar@PROGRESS: + X: (WINDOW_RIGHT - 500) / 2 + Y: 3 * WINDOW_BOTTOM / 4 + Width: 500 + Height: 20 + Label@DESC: + Width: WINDOW_RIGHT + Y: 3 * WINDOW_BOTTOM / 4 + 20 + Height: 25 + Font: Regular + Align: Center + Text: Press Escape to cancel loading and return to the main menu diff --git a/mods/cnc/mod.yaml b/mods/cnc/mod.yaml index 51da16f769..3fa975c9f6 100644 --- a/mods/cnc/mod.yaml +++ b/mods/cnc/mod.yaml @@ -107,6 +107,7 @@ ChromeLayout: cnc|chrome/color-picker.yaml cnc|chrome/mapchooser.yaml cnc|chrome/replaybrowser.yaml + cnc|chrome/gamesave-loading.yaml cnc|chrome/ingame.yaml cnc|chrome/ingame-chat.yaml cnc|chrome/ingame-menu.yaml diff --git a/mods/common/chrome/gamesave-loading.yaml b/mods/common/chrome/gamesave-loading.yaml new file mode 100644 index 0000000000..ab8864e7a4 --- /dev/null +++ b/mods/common/chrome/gamesave-loading.yaml @@ -0,0 +1,35 @@ +Container@GAMESAVE_LOADING_SCREEN: + Logic: GameSaveLoadingLogic + Width: WINDOW_RIGHT + Height: WINDOW_BOTTOM + Children: + LogicKeyListener@CANCEL_HANDLER: + Background@STRIPE: + Y: (WINDOW_BOTTOM - HEIGHT) / 2 + Width: WINDOW_RIGHT + Height: 256 + Background: loadscreen-stripe + Image@LOGO: + X: (WINDOW_RIGHT - 256) / 2 + Y: (WINDOW_BOTTOM - 256) / 2 + ImageCollection: logos + ImageName: logo + Label@TITLE: + Width: WINDOW_RIGHT + Y: 3 * WINDOW_BOTTOM / 4 - 30 + Height: 25 + Font: Bold + Align: Center + Text: Loading Saved Game + ProgressBar@PROGRESS: + X: (WINDOW_RIGHT - 500) / 2 + Y: 3 * WINDOW_BOTTOM / 4 + Width: 500 + Height: 20 + Label@DESC: + Width: WINDOW_RIGHT + Y: 3 * WINDOW_BOTTOM / 4 + 20 + Height: 25 + Font: Regular + Align: Center + Text: Press Escape to cancel loading and return to the main menu diff --git a/mods/d2k/chrome.yaml b/mods/d2k/chrome.yaml index 11e6294900..49baa2612e 100644 --- a/mods/d2k/chrome.yaml +++ b/mods/d2k/chrome.yaml @@ -638,4 +638,10 @@ scrollheader-selected: dialog.png corner-br: 639,127,1,1 dropdown: dialog.png - separator: 512,1,1,19 + separator: 512,1,1,19 + +logos: loadscreen.png + logo: 0,0,256,256 + +loadscreen-stripe: loadscreen.png + background: 256,0,256,256 diff --git a/mods/d2k/mod.yaml b/mods/d2k/mod.yaml index e7a60b4851..3f065b3bd6 100644 --- a/mods/d2k/mod.yaml +++ b/mods/d2k/mod.yaml @@ -107,6 +107,7 @@ ChromeLayout: common|chrome/confirmation-dialogs.yaml common|chrome/editor.yaml common|chrome/replaybrowser.yaml + common|chrome/gamesave-loading.yaml Weapons: d2k|weapons/debris.yaml diff --git a/mods/ra/chrome.yaml b/mods/ra/chrome.yaml index 1e3847b415..cfad39e401 100644 --- a/mods/ra/chrome.yaml +++ b/mods/ra/chrome.yaml @@ -1046,6 +1046,9 @@ scrollheader-selected: dialog.png logos: loadscreen.png logo: 0,0,256,256 +loadscreen-stripe: loadscreen.png + background: 256,0,256,256 + mainmenu-border: dialog.png border-r: 728,427,40,40 border-l: 648,427,40,40 diff --git a/mods/ra/chrome/gamesave-loading.yaml b/mods/ra/chrome/gamesave-loading.yaml new file mode 100644 index 0000000000..76b1414c4b --- /dev/null +++ b/mods/ra/chrome/gamesave-loading.yaml @@ -0,0 +1,37 @@ +Container@GAMESAVE_LOADING_SCREEN: + Logic: GameSaveLoadingLogic + Width: WINDOW_RIGHT + Height: WINDOW_BOTTOM + Children: + LogicKeyListener@CANCEL_HANDLER: + Background@STRIPE: + Y: (WINDOW_BOTTOM - HEIGHT) / 2 + Width: WINDOW_RIGHT + Height: 256 + Background: loadscreen-stripe + Image@LOGO: + X: (WINDOW_RIGHT - 256) / 2 + Y: (WINDOW_BOTTOM - 256) / 2 + ImageCollection: logos + ImageName: logo + Label@TITLE: + Width: WINDOW_RIGHT + Y: 3 * WINDOW_BOTTOM / 4 - 30 + Height: 25 + Font: Bold + Align: Center + Text: Loading Saved Game + ProgressBar@PROGRESS: + X: (WINDOW_RIGHT - 500) / 2 + Y: 3 * WINDOW_BOTTOM / 4 + Width: 500 + Height: 20 + Background: observer-scrollpanel-button-pressed + Bar: observer-scrollpanel-button + Label@DESC: + Width: WINDOW_RIGHT + Y: 3 * WINDOW_BOTTOM / 4 + 20 + Height: 25 + Font: Regular + Align: Center + Text: Press Escape to cancel loading and return to the main menu diff --git a/mods/ra/mod.yaml b/mods/ra/mod.yaml index 3642abdd69..0e68a5cc6a 100644 --- a/mods/ra/mod.yaml +++ b/mods/ra/mod.yaml @@ -114,6 +114,7 @@ ChromeLayout: common|chrome/multiplayer-directconnect.yaml common|chrome/connection.yaml common|chrome/replaybrowser.yaml + ra|chrome/gamesave-loading.yaml common|chrome/dropdowns.yaml common|chrome/musicplayer.yaml common|chrome/tooltips.yaml diff --git a/mods/ts/chrome.yaml b/mods/ts/chrome.yaml index a7ea27b868..0270527952 100644 --- a/mods/ts/chrome.yaml +++ b/mods/ts/chrome.yaml @@ -932,3 +932,9 @@ mainmenu-border: dialog.png dropdown: dialog.png separator: 512,1,1,19 + +logos: loadscreen.png + logo: 0,0,256,256 + +loadscreen-stripe: loadscreen.png + background: 256,0,256,256 diff --git a/mods/ts/mod.yaml b/mods/ts/mod.yaml index 83387298be..b674ee09e5 100644 --- a/mods/ts/mod.yaml +++ b/mods/ts/mod.yaml @@ -163,6 +163,7 @@ ChromeLayout: common|chrome/multiplayer-directconnect.yaml common|chrome/connection.yaml common|chrome/replaybrowser.yaml + common|chrome/gamesave-loading.yaml ts|chrome/dropdowns.yaml common|chrome/musicplayer.yaml common|chrome/tooltips.yaml