diff --git a/AUTHORS b/AUTHORS index 5713a55016..bf5d0bf93d 100644 --- a/AUTHORS +++ b/AUTHORS @@ -75,6 +75,7 @@ Also thanks to: * Jeff Harris (jeff_1amstudios) * Jes * Joakim Lindberg (booom3) + * John Turner (whinis) * Joppy Furr * Kanar * Kenny Hoxworth (hoxworth) diff --git a/OpenRA.Game/Game.cs b/OpenRA.Game/Game.cs index 9377e34647..844a3db0a1 100644 --- a/OpenRA.Game/Game.cs +++ b/OpenRA.Game/Game.cs @@ -177,8 +177,39 @@ namespace OpenRA public static void RestartGame() { - OrderManager.World.EndGame(); - StartGame(OrderManager.World.Map.Uid, WorldType.Regular); + var replay = OrderManager.Connection as ReplayConnection; + var replayName = replay != null ? replay.Filename : null; + var uid = OrderManager.World.Map.Uid; + var globalSettings = OrderManager.LobbyInfo.GlobalSettings; + + // Disconnect from the current game + Disconnect(); + Ui.ResetAll(); + + // Restart the game with the same replay/mission + if (replay != null) + JoinReplay(replayName); + else + StartMission(uid, globalSettings.GameSpeedType, globalSettings.Difficulty); + } + + public static void StartMission(string mapUID, string gameSpeed, string difficulty, Action onStart = null) + { + OrderManager om = null; + + Action lobbyReady = null; + lobbyReady = () => + { + LobbyInfoChanged -= lobbyReady; + om.IssueOrder(Order.Command("gamespeed {0}".F(gameSpeed))); + om.IssueOrder(Order.Command("difficulty {0}".F(difficulty))); + om.IssueOrder(Order.Command("state {0}".F(Session.ClientState.Ready))); + if (onStart != null) + onStart(); + }; + LobbyInfoChanged += lobbyReady; + + om = JoinServer(IPAddress.Loopback.ToString(), CreateLocalServer(mapUID), ""); } public static bool IsHost diff --git a/OpenRA.Game/Network/ReplayRecorder.cs b/OpenRA.Game/Network/ReplayRecorder.cs index a62d741324..381c2fd4f1 100644 --- a/OpenRA.Game/Network/ReplayRecorder.cs +++ b/OpenRA.Game/Network/ReplayRecorder.cs @@ -69,6 +69,9 @@ namespace OpenRA.Network public void Receive(int clientID, byte[] data) { + if (disposed) // TODO: This can be removed once NetworkConnection is fixed to dispose properly. + return; + if (preStartBuffer != null && IsGameStart(data)) { writer.Flush(); diff --git a/OpenRA.Mods.Common/Widgets/ConfirmationDialogs.cs b/OpenRA.Mods.Common/Widgets/ConfirmationDialogs.cs index 2ce75fd7f3..0958f11b3d 100644 --- a/OpenRA.Mods.Common/Widgets/ConfirmationDialogs.cs +++ b/OpenRA.Mods.Common/Widgets/ConfirmationDialogs.cs @@ -15,28 +15,60 @@ namespace OpenRA.Mods.Common.Widgets { public static class ConfirmationDialogs { - public static void PromptConfirmAction(string title, string text, Action onConfirm, Action onCancel = null, string confirmText = null, string cancelText = null) + public static void PromptConfirmAction( + string title, + string text, + Action onConfirm, + Action onCancel = null, + Action onOther = null, + string confirmText = null, + string cancelText = null, + string otherText = null) { var prompt = Ui.OpenWindow("CONFIRM_PROMPT"); + var confirmButton = prompt.Get("CONFIRM_BUTTON"); + var cancelButton = prompt.GetOrNull("CANCEL_BUTTON"); + var otherButton = prompt.GetOrNull("OTHER_BUTTON"); + prompt.Get("PROMPT_TITLE").GetText = () => title; prompt.Get("PROMPT_TEXT").GetText = () => text; if (!string.IsNullOrEmpty(confirmText)) - prompt.Get("CONFIRM_BUTTON").GetText = () => confirmText; - if (!string.IsNullOrEmpty(cancelText)) - prompt.Get("CANCEL_BUTTON").GetText = () => cancelText; + confirmButton.GetText = () => confirmText; + if (!string.IsNullOrEmpty(otherText) && otherButton != null) + otherButton.GetText = () => otherText; + if (!string.IsNullOrEmpty(cancelText) && cancelButton != null) + cancelButton.GetText = () => cancelText; - prompt.Get("CONFIRM_BUTTON").OnClick = () => + confirmButton.OnClick = () => { Ui.CloseWindow(); onConfirm(); }; - prompt.Get("CANCEL_BUTTON").OnClick = () => + if (onCancel != null && cancelButton != null) { - Ui.CloseWindow(); - if (onCancel != null) - onCancel(); - }; + cancelButton.IsVisible = () => true; + cancelButton.OnClick = () => + { + Ui.CloseWindow(); + if (onCancel != null) + onCancel(); + }; + } + else if (cancelButton != null) + cancelButton.IsVisible = () => false; + + if (onOther != null && otherButton != null) + { + otherButton.IsVisible = () => true; + otherButton.OnClick = () => + { + if (onOther != null) + onOther(); + }; + } + else if (otherButton != null) + otherButton.IsVisible = () => false; } public static void CancelPrompt(string title, string text, Action onCancel = null, string cancelText = null) @@ -55,39 +87,6 @@ namespace OpenRA.Mods.Common.Widgets onCancel(); }; } - /** - * open confirmation dialog for mission / game restart - */ - public static void PromptAbortMission(World world, string title, string text, Action onAbort, Action onCancel = null, Action closeMenu = null) - { - var isMultiplayer = !world.LobbyInfo.IsSinglePlayer && !world.IsReplay; - var prompt = Ui.OpenWindow("ABORT_MISSION_PROMPT"); - prompt.Get("PROMPT_TITLE").GetText = () => title; - prompt.Get("PROMPT_TEXT").GetText = () => text; - prompt.Get("ABORT_BUTTON").OnClick = () => - { - Ui.CloseWindow(); - onAbort(); - }; - - var restartButton = prompt.Get("RESTART_BUTTON"); - restartButton.IsVisible = () => !isMultiplayer; - restartButton.OnClick = () => - { - if (closeMenu != null) - closeMenu(); - - Ui.CloseWindow(); - Game.RestartGame(); - }; - - prompt.Get("CANCEL_BUTTON").OnClick = () => - { - Ui.CloseWindow(); - if (onCancel != null) - onCancel(); - }; - } public static void TextInputPrompt( string title, string prompt, string initialText, diff --git a/OpenRA.Mods.Common/Widgets/Logic/Ingame/IngameMenuLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Ingame/IngameMenuLogic.cs index 864248feda..69366d7708 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Ingame/IngameMenuLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Ingame/IngameMenuLogic.cs @@ -88,14 +88,42 @@ namespace OpenRA.Mods.Common.Widgets.Logic abortMissionButton.OnClick = () => { - if (world.IsGameOver) - { - onQuit(); - return; - } - hideMenu = true; - ConfirmationDialogs.PromptAbortMission(world, "Abort Mission", "Leave this game and return to the menu?", onQuit, showMenu, closeMenu); + + if (world.LocalPlayer == null || (world.LocalPlayer.WinState != WinState.Won && + (!world.IsGameOver || world.Map.Visibility == MapVisibility.MissionSelector))) + { + Action restartAction = null; + if (world.IsReplay || world.Map.Visibility == MapVisibility.MissionSelector) + { + var iop = world.WorldActor.TraitsImplementing().FirstOrDefault(); + var exitDelay = iop != null ? iop.ExitDelay : 0; + restartAction = () => + { + Ui.CloseWindow(); + if (mpe != null) + { + if (Game.IsCurrentWorld(world)) + mpe.Fade(MenuPaletteEffect.EffectType.Black); + exitDelay += 40 * mpe.Info.FadeLength; + } + + Game.RunAfterDelay(exitDelay, Game.RestartGame); + }; + } + + ConfirmationDialogs.PromptConfirmAction( + title: "Leave Mission", + text: "Leave this game and return to the menu?", + onConfirm: onQuit, + onCancel: showMenu, + confirmText: "Leave", + cancelText: "Stay", + otherText: "Restart", + onOther: restartAction); + } + else + onQuit(); }; var exitEditorButton = menu.Get("EXIT_EDITOR"); @@ -103,7 +131,11 @@ namespace OpenRA.Mods.Common.Widgets.Logic exitEditorButton.OnClick = () => { hideMenu = true; - ConfirmationDialogs.PromptConfirmAction("Exit Map Editor", "Exit and lose all unsaved changes?", onQuit, showMenu); + ConfirmationDialogs.PromptConfirmAction( + title: "Exit Map Editor", + text: "Exit and lose all unsaved changes?", + onConfirm: onQuit, + onCancel: showMenu); }; Action onSurrender = () => @@ -117,7 +149,11 @@ namespace OpenRA.Mods.Common.Widgets.Logic surrenderButton.OnClick = () => { hideMenu = true; - ConfirmationDialogs.PromptConfirmAction("Surrender", "Are you sure you want to surrender?", onSurrender, showMenu); + ConfirmationDialogs.PromptConfirmAction( + title: "Surrender", + text: "Are you sure you want to surrender?", + onConfirm: onSurrender, + onCancel: showMenu); }; var saveMapButton = menu.Get("SAVE_MAP"); diff --git a/OpenRA.Mods.Common/Widgets/Logic/MapChooserLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/MapChooserLogic.cs index 72f5f4b3d8..fa205533c8 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/MapChooserLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/MapChooserLogic.cs @@ -309,31 +309,29 @@ namespace OpenRA.Mods.Common.Widgets.Logic void DeleteOneMap(string map, Action after) { ConfirmationDialogs.PromptConfirmAction( - "Delete map", - "Delete the map '{0}'?".F(Game.ModData.MapCache[map].Title), - () => + title: "Delete map", + text: "Delete the map '{0}'?".F(Game.ModData.MapCache[map].Title), + onConfirm: () => { var newUid = DeleteMap(map); if (after != null) after(newUid); }, - null, - "Delete"); + confirmText: "Delete"); } void DeleteAllMaps(string[] maps, Action after) { ConfirmationDialogs.PromptConfirmAction( - "Delete maps", - "Delete all maps on this page?", - () => + title: "Delete maps", + text: "Delete all maps on this page?", + onConfirm: () => { maps.Do(m => DeleteMap(m)); if (after != null) after(WidgetUtils.ChooseInitialMap(null)); }, - null, - "Delete"); + confirmText: "Delete"); } } } diff --git a/OpenRA.Mods.Common/Widgets/Logic/MissionBrowserLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/MissionBrowserLogic.cs index 329eeaaa88..8b32255c31 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/MissionBrowserLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/MissionBrowserLogic.cs @@ -301,29 +301,11 @@ namespace OpenRA.Mods.Common.Widgets.Logic PlayVideo(fsPlayer, gameStartVideo, PlayingVideo.GameStart, () => { StopVideo(fsPlayer); - StartMission(); + Game.StartMission(selectedMapPreview.Uid, gameSpeed, difficulty, onStart); }); } else - StartMission(); - } - - void StartMission() - { - OrderManager om = null; - - Action lobbyReady = null; - lobbyReady = () => - { - om.IssueOrder(Order.Command("gamespeed {0}".F(gameSpeed))); - om.IssueOrder(Order.Command("difficulty {0}".F(difficulty))); - Game.LobbyInfoChanged -= lobbyReady; - onStart(); - om.IssueOrder(Order.Command("state {0}".F(Session.ClientState.Ready))); - }; - Game.LobbyInfoChanged += lobbyReady; - - om = Game.JoinServer(IPAddress.Loopback.ToString(), Game.CreateLocalServer(selectedMapPreview.Uid), ""); + Game.StartMission(selectedMapPreview.Uid, gameSpeed, difficulty, onStart); } class DropDownOption diff --git a/OpenRA.Mods.Common/Widgets/Logic/ReplayBrowserLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/ReplayBrowserLogic.cs index d9f59f403f..da32f254ef 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/ReplayBrowserLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/ReplayBrowserLogic.cs @@ -403,16 +403,15 @@ namespace OpenRA.Mods.Common.Widgets.Logic Action onDeleteReplay = (r, after) => { ConfirmationDialogs.PromptConfirmAction( - "Delete selected replay?", - "Delete replay '{0}'?".F(Path.GetFileNameWithoutExtension(r.FilePath)), - () => + title: "Delete selected replay?", + text: "Delete replay '{0}'?".F(Path.GetFileNameWithoutExtension(r.FilePath)), + onConfirm: () => { DeleteReplay(r); if (after != null) after.Invoke(); }, - null, - "Delete"); + confirmText: "Delete"); }; { @@ -444,16 +443,15 @@ namespace OpenRA.Mods.Common.Widgets.Logic } ConfirmationDialogs.PromptConfirmAction( - "Delete all selected replays?", - "Delete {0} replays?".F(list.Count), - () => + title: "Delete all selected replays?", + text: "Delete {0} replays?".F(list.Count), + onConfirm: () => { list.ForEach(DeleteReplay); if (selectedReplay == null) SelectFirstVisibleReplay(); }, - null, - "Delete All"); + confirmText: "Delete All"); }; } } diff --git a/OpenRA.Mods.Common/Widgets/Logic/SettingsLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/SettingsLogic.cs index 60e95ed2c5..4d201e9784 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/SettingsLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/SettingsLogic.cs @@ -73,12 +73,12 @@ namespace OpenRA.Mods.Common.Widgets.Logic OriginalGraphicsWindowedSize != current.Graphics.WindowedSize || OriginalGraphicsFullscreenSize != current.Graphics.FullscreenSize) ConfirmationDialogs.PromptConfirmAction( - "Restart Now?", - "Some changes will not be applied until\nthe game is restarted. Restart now?", - Game.Restart, - closeAndExit, - "Restart Now", - "Restart Later"); + title: "Restart Now?", + text: "Some changes will not be applied until\nthe game is restarted. Restart now?", + onConfirm: Game.Restart, + onCancel: closeAndExit, + confirmText: "Restart Now", + cancelText: "Restart Later"); else closeAndExit(); }; diff --git a/mods/cnc/chrome/dialogs.yaml b/mods/cnc/chrome/dialogs.yaml index 346aa70923..3c52a68939 100644 --- a/mods/cnc/chrome/dialogs.yaml +++ b/mods/cnc/chrome/dialogs.yaml @@ -136,7 +136,7 @@ ScrollPanel@SPECTATOR_DROPDOWN_TEMPLATE: Container@CONFIRM_PROMPT: X: (WINDOW_RIGHT - WIDTH)/2 Y: (WINDOW_BOTTOM - 90)/2 - Width: 370 + Width: 500 Height: 125 Children: Label@PROMPT_TITLE: @@ -146,7 +146,7 @@ Container@CONFIRM_PROMPT: Contrast: true Align: Center Background@bg: - Width: 370 + Width: 500 Height: 90 Background: panel-black Children: @@ -156,19 +156,26 @@ Container@CONFIRM_PROMPT: Height: 25 Font: Bold Align: Center + Button@CONFIRM_BUTTON: + Key: return + X: 360 + Y: 89 + Width: 140 + Height: 35 + Text: Abort + Button@OTHER_BUTTON: + Key: r + X: 210 + Y: 89 + Width: 140 + Height: 35 + Text: Restart Button@CANCEL_BUTTON: Key: escape Y: 89 Width: 140 Height: 35 Text: Cancel - Button@CONFIRM_BUTTON: - Key: return - X: 230 - Y: 89 - Width: 140 - Height: 35 - Text: Confirm Container@CANCEL_PROMPT: X: (WINDOW_RIGHT - WIDTH)/2 @@ -200,50 +207,6 @@ Container@CANCEL_PROMPT: Height: 35 Text: Cancel -Container@ABORT_MISSION_PROMPT: - X: (WINDOW_RIGHT - WIDTH)/2 - Y: (WINDOW_BOTTOM - 90)/2 - Width: 500 - Height: 125 - Children: - Label@PROMPT_TITLE: - Width: PARENT_RIGHT - Y: 0-25 - Font: BigBold - Contrast: true - Align: Center - Background@bg: - Width: 500 - Height: 90 - Background: panel-black - Children: - Label@PROMPT_TEXT: - Y: (PARENT_BOTTOM-HEIGHT)/2 - Width: PARENT_RIGHT - Height: 25 - Font: Bold - Align: Center - Button@ABORT_BUTTON: - Key: return - X: 360 - Y: 89 - Width: 140 - Height: 35 - Text: Abort - Button@RESTART_BUTTON: - Key: r - X: 180 - Y: 89 - Width: 140 - Height: 35 - Text: Restart - Button@CANCEL_BUTTON: - Key: escape - Y: 89 - Width: 140 - Height: 35 - Text: Cancel - Container@TEXT_INPUT_PROMPT: X: (WINDOW_RIGHT - WIDTH)/2 Y: (WINDOW_BOTTOM - HEIGHT)/2 diff --git a/mods/ra/chrome/confirmation-dialogs.yaml b/mods/ra/chrome/confirmation-dialogs.yaml index 81ebff6341..3fdf96e4ce 100644 --- a/mods/ra/chrome/confirmation-dialogs.yaml +++ b/mods/ra/chrome/confirmation-dialogs.yaml @@ -1,7 +1,7 @@ Background@CONFIRM_PROMPT: X: (WINDOW_RIGHT - WIDTH)/2 Y: (WINDOW_BOTTOM - 90)/2 - Width: 370 + Width: 600 Height: 175 Children: Label@PROMPT_TITLE: @@ -17,41 +17,6 @@ Background@CONFIRM_PROMPT: Height: 65 Align: Center Button@CONFIRM_BUTTON: - X: 20 - Y: PARENT_BOTTOM - 45 - Width: 160 - Height: 25 - Text: Confirm - Font: Bold - Key: return - Button@CANCEL_BUTTON: - X: PARENT_RIGHT - 180 - Y: PARENT_BOTTOM - 45 - Width: 160 - Height: 25 - Text: Cancel - Font: Bold - Key: escape - -Background@ABORT_MISSION_PROMPT: - X: (WINDOW_RIGHT - WIDTH)/2 - Y: (WINDOW_BOTTOM - 90)/2 - Width: 600 - Height: 175 - Children: - Label@PROMPT_TITLE: - Width: PARENT_RIGHT - Y: 20 - Height: 25 - Font: Bold - Align: Center - Label@PROMPT_TEXT: - X: 15 - Y: 50 - Width: PARENT_RIGHT - 30 - Height: 65 - Align: Center - Button@ABORT_BUTTON: X: 20 Y: PARENT_BOTTOM - 45 Width: 160 @@ -59,7 +24,7 @@ Background@ABORT_MISSION_PROMPT: Text: Abort Font: Bold Key: return - Button@RESTART_BUTTON: + Button@OTHER_BUTTON: X: PARENT_RIGHT - 380 Y: PARENT_BOTTOM - 45 Width: 160