diff --git a/Winch/Assets/Localization/en.json b/Winch/Assets/Localization/en.json index d2980a36..6f5b6e76 100644 --- a/Winch/Assets/Localization/en.json +++ b/Winch/Assets/Localization/en.json @@ -1,5 +1,6 @@ { "menu.mods": "Mods", + "prompt.incompatible-mods": "This save file was last used with modded items that are no longer available due either to uninstallation or an update.\n\nIf you load this save file, those items will disappear the next time you save.\n\nAre you sure you wish to continue?", "popup.confirm-restore-mod-default-settings": "Are you sure you want to restore this mod's settings back to default values?\n\nThis action cannot be undone.", "settings.tab.mods": "Mods", "settings.mods.footer.list": "List", diff --git a/Winch/Data/ExtendedSaveData.cs b/Winch/Data/ExtendedSaveData.cs index cf637f76..df882674 100644 --- a/Winch/Data/ExtendedSaveData.cs +++ b/Winch/Data/ExtendedSaveData.cs @@ -3,6 +3,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; +using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; @@ -10,6 +11,7 @@ using System.Text; using System.Threading.Tasks; using UnityEngine; +using UnityEngine.UI; using Winch.Config; using Winch.Core; using Winch.Util; @@ -314,6 +316,73 @@ public void SetSaveUsesMod(string modGUID, bool value) public void SetSaveUsesMod(ModAssembly mod, bool value) => SetSaveUsesMod(mod.GUID, value); + public static IEnumerator ShowLoadFailedWithIssueDialog(TitleController titleController, UnityEngine.UI.Selectable selectable, Action callback) + { + string localizationKey = "prompt.incompatible-mods"; + yield return ShowLoadFailedWithIssueDialog(titleController, selectable, localizationKey, callback); + } + + public static IEnumerator ShowLoadFailedWithIssueDialog(TitleController titleController, Selectable s, string localizationKey, Action callback) + { + WinchCore.Log.Debug($"[ExtendedSaveData] ShowLoadFailedWithIssueDialog({localizationKey})"); + titleController.DisableMenuCanvas(); + titleController.controllerFocusGrabber.enabled = false; + DialogButtonOptions cancel = new DialogButtonOptions + { + buttonString = "prompt.cancel", + id = 0, + hideOnButtonPress = true, + isBackOption = true + }; + DialogButtonOptions confirm = new DialogButtonOptions + { + buttonString = "prompt.confirm", + id = 1, + hideOnButtonPress = true + }; + DialogButtonOptions[] buttonOptions = new DialogButtonOptions[2] { cancel, confirm }; + DialogOptions dialogOptions = new DialogOptions + { + text = localizationKey, + buttonOptions = buttonOptions + }; + bool responded = false; + bool result = false; + GameManager.Instance.DialogManager.ShowDialog(dialogOptions, (DialogButtonOptions options) => + { + result = options.id == confirm.id; + WinchCore.Log.Debug($"[ExtendedSaveData] ShowLoadFailedWithIssueDialog({localizationKey}) closed: {options.id}"); + titleController.controllerFocusGrabber.enabled = true; + titleController.controllerFocusGrabber.selectThis = s; + titleController.EnableMenuCanvas(); + titleController.controllerFocusGrabber.SelectSelectable(); + responded = true; + callback(result); + }); + yield return new WaitUntil(() => responded); + } + + public bool IsSaveNotAllowedToBeLoaded() + { + foreach (var gridByKey in saveData.grids) + { + var key = gridByKey.Key; + var grid = gridByKey.Value; + if (EnumUtil.IsDefined(key) && grid.spatialItems.Concat(grid.spatialUnderlayItems).Any(WinchExtensions.DoesItemDataNotExist)) + { + return true; + } + } + foreach (var crabPotPOI in saveData.serializedCrabPotPOIs) + { + if (crabPotPOI.DoesItemDataExist() && crabPotPOI.grid.spatialItems.Concat(crabPotPOI.grid.spatialUnderlayItems).Any(WinchExtensions.DoesItemDataNotExist)) + { + return true; + } + } + return false; + } + /// /// A participant of the saveData system. /// diff --git a/Winch/Patches/SaveManagerPatcher.cs b/Winch/Patches/SaveManagerPatcher.cs index 652f3c1e..6c92a4cc 100644 --- a/Winch/Patches/SaveManagerPatcher.cs +++ b/Winch/Patches/SaveManagerPatcher.cs @@ -1,7 +1,11 @@ using HarmonyLib; +using System; +using System.Collections; +using UnityEngine.UI; using Winch.Core; using Winch.Data; using Winch.Util; +using static ContinueOrNewButton; namespace Winch.Patches { @@ -125,5 +129,46 @@ public static void DeleteSaveFile(SaveManager __instance, int slot) WinchCore.Log.Error(ex); } } + + [HarmonyPrefix] + [HarmonyPatch(typeof(ContinueOrNewButton), nameof(ContinueOrNewButton.OnClick))] + public static bool OnContinueOrNewButtonClicked(ContinueOrNewButton __instance) + { + if (__instance.hasBeenClicked) + { + return false; + } + __instance.hasBeenClicked = true; + if (__instance.currentMode == StartButtonMode.CONTINUE) + { + __instance.CheckIsSaveAllowedToBeLoaded(); + } + else if (__instance.currentMode == StartButtonMode.NEW) + { + GameManager.Instance.Loader.LoadGameFromTitle(); + } + return false; + } + + [HarmonyPrefix] + [HarmonyPatch(typeof(SaveSlotUI), nameof(SaveSlotUI.OnClicked))] + public static bool OnSaveSlotLoadButtonClicked(SaveSlotUI __instance) + { + if (__instance.hasBeenClicked || __instance.hasDeleteBeenClicked) + { + return false; + } + __instance.hasBeenClicked = true; + __instance.saveData = GameManager.Instance.SaveManager.LoadIntoMemory(__instance.slotNum); + if (__instance.hasSaveFile) + { + __instance.CheckIsSaveAllowedToBeLoaded(); + } + else + { + __instance.DoContinueOrNew(canCreateNew: true); + } + return false; + } } } diff --git a/Winch/Util/WinchExtensions.cs b/Winch/Util/WinchExtensions.cs index 7d7d01e5..26c53bbc 100644 --- a/Winch/Util/WinchExtensions.cs +++ b/Winch/Util/WinchExtensions.cs @@ -23,6 +23,7 @@ using Winch.Components; using Winch.Config; using Winch.Core; +using Winch.Data; using Winch.Data.Abilities; using Winch.Data.Character; using Winch.Data.Item; @@ -695,6 +696,65 @@ public static SerializedCrabPotPOIData MakeIdentical(this SerializedCrabPotPOIDa partialCrabPot.grid = new SerializableGrid(); return partialCrabPot; } + + public static IEnumerator CheckIsSaveAllowedToBeLoaded(this TitleController titleController, int slotNum, SaveData s, Selectable selectable, Action callback) + { + WinchCore.Log.Debug("[TitleController] CheckIsSaveAllowedToBeLoaded()"); + bool result = true; + if (s == null) + { + result = false; + string localizationKey = "popup.corrupt-save-identified"; + yield return titleController.ShowLoadFailedWithIssueDialog(selectable, localizationKey); + } + else if (((s.GetSaveUsesEntitlement(Entitlement.DLC_1) && !GameManager.Instance.EntitlementManager.GetHasEntitlement(Entitlement.DLC_1)) || (s.GetSaveUsesEntitlement(Entitlement.DLC_2) && !GameManager.Instance.EntitlementManager.GetHasEntitlement(Entitlement.DLC_2)))) + { + result = false; + string localizationKey = "popup.incompatible-dlc"; + yield return titleController.ShowLoadFailedWithIssueDialog(selectable, localizationKey); + } + else if (SaveUtil.GetInMemorySaveDataForSlot(slotNum).IsSaveNotAllowedToBeLoaded()) + { + result = false; + yield return ExtendedSaveData.ShowLoadFailedWithIssueDialog(titleController, selectable, callback); + yield break; + } + callback(result); + } + + public static void CheckIsSaveAllowedToBeLoaded(this ContinueOrNewButton continueOrNewButton) + { + var slotNum = GameManager.Instance.SaveManager.ActiveSettingsData.lastSaveSlot; + SaveData s = GameManager.Instance.SaveManager.LoadIntoMemory(slotNum); + continueOrNewButton.StartCoroutine(continueOrNewButton.titleController.CheckIsSaveAllowedToBeLoaded(slotNum, s, continueOrNewButton.selectable, (bool result) => + { + if (result) + { + GameManager.Instance.Loader.LoadGameFromTitle(canCreateNew: false); + } + else + { + continueOrNewButton.hasBeenClicked = false; + GameManager.Instance.Input.SetActiveActionLayer(ActionLayer.POPUP_WINDOW); + } + })); + } + + public static void CheckIsSaveAllowedToBeLoaded(this SaveSlotUI saveSlotUI) + { + saveSlotUI.StartCoroutine(saveSlotUI.titleController.CheckIsSaveAllowedToBeLoaded(saveSlotUI.slotNum, saveSlotUI.saveData, saveSlotUI.selectable, (bool result) => + { + if (result) + { + saveSlotUI.DoContinueOrNew(canCreateNew: false); + } + else + { + saveSlotUI.hasBeenClicked = false; + GameManager.Instance.Input.SetActiveActionLayer(ActionLayer.POPUP_WINDOW); + } + })); + } #endregion #region Reflection