diff --git a/Nautilus/Initializer.cs b/Nautilus/Initializer.cs index 0b7b4ac7..3ff1461b 100644 --- a/Nautilus/Initializer.cs +++ b/Nautilus/Initializer.cs @@ -85,5 +85,6 @@ static Initializer() WaterParkPatcher.Patch(_harmony); ModMessageSystem.Patch(); BiomePatcher.Patch(_harmony); + DependencyWarningPatcher.Patch(_harmony); } } diff --git a/Nautilus/MonoBehaviours/PluginLoadErrorMenu.cs b/Nautilus/MonoBehaviours/PluginLoadErrorMenu.cs new file mode 100644 index 00000000..aa337d07 --- /dev/null +++ b/Nautilus/MonoBehaviours/PluginLoadErrorMenu.cs @@ -0,0 +1,9 @@ +using UnityEngine; + +namespace Nautilus.MonoBehaviours; + +internal class PluginLoadErrorMenu : MonoBehaviour +{ + public void OnButtonOpen() => gameObject.SetActive(true); + public void OnButtonClose() => gameObject.SetActive(false); +} \ No newline at end of file diff --git a/Nautilus/Patchers/DependencyWarningPatcher.cs b/Nautilus/Patchers/DependencyWarningPatcher.cs new file mode 100644 index 00000000..1175ed57 --- /dev/null +++ b/Nautilus/Patchers/DependencyWarningPatcher.cs @@ -0,0 +1,202 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using BepInEx.Bootstrap; +using HarmonyLib; +using Nautilus.MonoBehaviours; +using Nautilus.Utility; +using TMPro; +using UnityEngine; +using UnityEngine.UI; +using Object = UnityEngine.Object; + +namespace Nautilus.Patchers; + +internal static class DependencyWarningPatcher +{ + internal static void Patch(Harmony harmony) + { + harmony.PatchAll(typeof(DependencyWarningPatcher)); + } + + [HarmonyPostfix] + [HarmonyPatch(typeof(uGUI_MainMenu), nameof(uGUI_MainMenu.Start))] + private static void MainMenuStartPostfix(uGUI_MainMenu __instance) + { + try + { + CreateDependencyWarningUI(__instance); + } + catch (Exception e) + { + InternalLogger.Error("Failed to create main menu dependency warning UI! Exception thrown: " + e); + } + } + + private static void CreateDependencyWarningUI(uGUI_MainMenu mainMenu) + { + var dependencyErrors = Chainloader.DependencyErrors; + if (dependencyErrors.Count == 0) + return; + + var missingDependencies = GetMissingDependencies(); + var formattedMissingDependencies = FormatMissingDependencies(missingDependencies); + + // -- Add warning text -- + var textReference = mainMenu.transform.Find("Panel/MainMenu/PlayerName").gameObject; + var textObject = Object.Instantiate(textReference, textReference.transform.parent, true); + textObject.name = "NautilusDependencyWarningText"; + Object.DestroyImmediate(textObject.GetComponent()); + textObject.SetActive(true); + var transform = textObject.GetComponent(); + transform.anchorMin = new Vector2(0, 1); + transform.anchorMax = new Vector2(1, 1); + transform.pivot = new Vector2(0.5f, 0); + transform.sizeDelta = new Vector2(1, 40); + transform.offsetMin = new Vector2(-0.5f, 0); + transform.offsetMax = new Vector2(-0.5f, 570); + var text = textObject.GetComponentInChildren(); + text.overflowMode = TextOverflowModes.Ellipsis; + text.alignment = TextAlignmentOptions.Center; + text.color = Color.red; + text.richText = true; + text.geometrySortingOrder = VertexSortingOrder.Reverse; + text.fontStyle = FontStyles.Bold; + text.text = "Some mods failed to load! Please view the mod load errors for more details.\n" + + formattedMissingDependencies + ""; + + // -- Add mod load errors window -- + var optionsWindowReference = mainMenu.transform.Find("Panel/Options"); + var errorsMenuPanel = + Object.Instantiate(optionsWindowReference.gameObject, optionsWindowReference.parent, true); + errorsMenuPanel.name = "NautilusModErrorsWindow"; + var menuBehaviour = errorsMenuPanel.AddComponent(); + errorsMenuPanel.SetActive(false); + var optionsPanel = errorsMenuPanel.GetComponentInChildren(); + var panePrefab = optionsPanel.panePrefab; + Object.DestroyImmediate(optionsPanel); + var errorsMenuHeader = errorsMenuPanel.transform.Find("Header"); + errorsMenuHeader.gameObject.GetComponent().text = "BepInEx plugin load errors"; + Object.DestroyImmediate(errorsMenuHeader.GetComponent()); + errorsMenuPanel.transform.Find("Middle/TabsHolder").gameObject.SetActive(false); + var panesHolder = errorsMenuPanel.transform.Find("Middle/PanesHolder"); + foreach (Transform child in panesHolder) + { + Object.Destroy(child.gameObject); + } + var mainPaneTransform = Object.Instantiate(panePrefab, panesHolder).transform; + var mainPaneContent = mainPaneTransform.Find("Viewport/Content"); +#if BELOWZERO + var layoutGroup = mainPaneContent.gameObject.GetComponent(); + layoutGroup.spacing = 65; + layoutGroup.padding = new RectOffset(15, 15, 40, 15); +#endif + // Create a list of all error messages that should be displayed + var errorsToDisplay = new List + { + $"{formattedMissingDependencies}", + "Mod load errors:" + }; + errorsToDisplay.AddRange(dependencyErrors.Where(ShouldDisplayError)); + // Add error messages to menu + foreach (var error in errorsToDisplay) + { + var errorEntryTextObj = Object.Instantiate(textReference, mainPaneContent, true); + Object.DestroyImmediate(errorEntryTextObj.GetComponent()); + var errorEntryText = errorEntryTextObj.GetComponent(); + errorEntryText.text = error; + errorEntryText.enableWordWrapping = true; + errorEntryText.fontSize = 25; + errorEntryText.richText = true; + errorEntryTextObj.SetActive(true); + errorEntryTextObj.transform.localScale = Vector3.one; + } + + // Fix up buttons and add close window button + var closeWindowButton = errorsMenuPanel.transform.Find("Bottom/ButtonBack").gameObject; + Object.DestroyImmediate(closeWindowButton.GetComponentInChildren()); + closeWindowButton.GetComponentInChildren().text = "Close"; + var closeWindowButtonComponent = closeWindowButton.GetComponent