diff --git a/MiraAPI/GameOptions/IModdedOption.cs b/MiraAPI/GameOptions/IModdedOption.cs index ecffc1a4..95173fb2 100644 --- a/MiraAPI/GameOptions/IModdedOption.cs +++ b/MiraAPI/GameOptions/IModdedOption.cs @@ -90,4 +90,16 @@ public interface IModdedOption /// The ConfigFile representing the preset configuration. /// Indicates whether to save the default value instead of the current value. void SaveToPreset(ConfigFile presetConfig, bool saveDefault = false); + + /// + /// Binds the option to a configuration file. + /// + /// The ConfigFile to bind the option to. + void Bind(ConfigFile config); + + /// + /// Loads the option from a preset configuration file, applying the values to the option's configuration. + /// + /// The ConfigFile representing the preset configuration. + void LoadFromPreset(ConfigFile presetConfig); } diff --git a/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs b/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs index e4484d3f..cf5e9004 100644 --- a/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs +++ b/MiraAPI/GameOptions/OptionTypes/ModdedOption.cs @@ -136,6 +136,25 @@ public abstract class ModdedOption : IModdedOption presetConfig[ConfigDefinition].BoxedValue = value; } + /// + public void Bind(ConfigFile config) + { + config.Bind(ConfigDefinition, DefaultValue); + } + + /// + public void LoadFromPreset(ConfigFile presetConfig) + { + if (presetConfig.TryGetEntry(ConfigDefinition, out ConfigEntry entry)) + { + SetValue(entry.Value); + } + else + { + Logger.Error($"Attempted to load {Title} from preset, but ConfigDefinition is not found in preset."); + } + } + /// public abstract float GetFloatData(); diff --git a/MiraAPI/Patches/Options/GamePresetsTabPatches.cs b/MiraAPI/Patches/Options/GamePresetsTabPatches.cs index 308978dc..5d0ad567 100644 --- a/MiraAPI/Patches/Options/GamePresetsTabPatches.cs +++ b/MiraAPI/Patches/Options/GamePresetsTabPatches.cs @@ -3,7 +3,6 @@ using System.IO; using System.Linq; using BepInEx.Configuration; using HarmonyLib; -using MiraAPI.Hud; using MiraAPI.PluginLoading; using MiraAPI.Presets; using MiraAPI.Utilities.Assets; @@ -26,164 +25,10 @@ internal static class GamePresetsTabPatches private static GameObject _presetHolder = null!; private static GridArrange _arrange = null!; - [HarmonyPatch(typeof(GamePresetsTab), nameof(GamePresetsTab.Start))] - public static class GamePresetsTabStartPatch - { - // ReSharper disable once InconsistentNaming - public static void Postfix(GamePresetsTab __instance) - { - Logger.Error("GamePresetsTab Start called"); - var prefab = GameSettingMenu.Instance.GameSettingsButton; - if (prefab == null) - { - Logger.Error("GameSettingsButton prefab is null"); - return; - } + private static FileSystemWatcher? _watcher; - __instance.PresetDescriptionText.gameObject.GetComponent().Destroy(); + private static readonly object Lock = new(); - var oldDiv = __instance.transform.FindChild("DividerImage"); - _newDivider = Object.Instantiate(oldDiv.gameObject, oldDiv.transform.parent); - _newDivider.transform.localPosition = new Vector3(1.85f, 0.13f, 0f); - _newDivider.transform.localScale = new Vector3(1.13f, 1, 1); - _newDivider.transform.localRotation = Quaternion.Euler(new Vector3(0, 0, 90)); - - // create the save button - var saveButton = Object.Instantiate(prefab, __instance.transform); - _saveButton = saveButton.gameObject; - _saveButton.gameObject.name = "SaveButton"; - _saveButton.transform.localPosition = new Vector3(3.2f, 1.7f, -2); - - // set the button text and alignment - var saveText = saveButton.buttonText; - saveText.text = "Save"; - saveText.GetComponent().Destroy(); - saveText.alignment = TextAlignmentOptions.Center; - saveText.transform.parent.localPosition = new Vector3( - -.525f, - saveText.transform.parent.localPosition.y, - saveText.transform.parent.localPosition.z); - - DivideSize(saveButton.gameObject, 2f); - - // Create refresh button - var refreshButton = Object.Instantiate(saveButton, __instance.transform); - _refreshButton = refreshButton.gameObject; - _refreshButton.name = "RefreshButton"; - _refreshButton.transform.localPosition = new Vector3(2.85f, 1.1f, -2); - - var icon = new GameObject("Sprite"); - icon.transform.SetParent(refreshButton.transform); - icon.transform.localPosition = new Vector3(-0.47f, -0.08f, -5f); - icon.transform.localScale = new Vector3(0.5f, 0.5f, 1); - icon.layer = refreshButton.gameObject.layer; - - var iconSpriteRend = icon.AddComponent(); - iconSpriteRend.sprite = MiraAssets.RefreshIcon.LoadAsset(); - - DivideSize(refreshButton.gameObject, 2f); - refreshButton.buttonText.gameObject.Destroy(); - - var openFolderButton = Object.Instantiate(refreshButton, __instance.transform); - _folderButton = openFolderButton.gameObject; - _folderButton.name = "OpenFolderButton"; - _folderButton.transform.localPosition = new Vector3(3.55f, 1.1f, -2); - - var folderRend = openFolderButton.transform.FindChild("Sprite").gameObject.GetComponent(); - folderRend.sprite = MiraAssets.FolderIcon.LoadAsset(); - folderRend.transform.localScale = new Vector3(0.4f, 0.4f, 1); - - openFolderButton.OnClick = new Button.ButtonClickedEvent(); - openFolderButton.OnClick.AddListener( - (UnityAction)(() => - { - var directory = Path.Join(PresetManager.PresetDirectory, GameSettingMenuPatches.SelectedMod?.PluginId ?? string.Empty); - Directory.CreateDirectory(directory); - Process.Start( - new ProcessStartInfo - { - FileName = directory, - UseShellExecute = true, - Verb = "open", - }); - })); - - // add the click event to save the preset - saveButton.OnClick = new Button.ButtonClickedEvent(); - saveButton.OnClick.AddListener( - (UnityAction)(() => - { - if (GameSettingMenuPatches.SelectedModIdx == 0) - { - return; - } - - SavePresetPopup.CreatePopup(name => - { - if (GameSettingMenuPatches.SelectedMod == null) - { - return; - } - - if (name == string.Empty) - { - return; - } - - var presetFile = new ConfigFile( - Path.Join(PresetManager.PresetDirectory, GameSettingMenuPatches.SelectedMod.PluginId, $"{name}.cfg"), - false); - foreach (var option in GameSettingMenuPatches.SelectedMod.InternalOptions.Where(x => x.IncludeInPreset)) - { - option.SaveToPreset(presetFile); - } - - presetFile.Save(); - Refresh(); - }); - })); - - // add the click event to refresh the presets - refreshButton.OnClick = new Button.ButtonClickedEvent(); - refreshButton.OnClick.AddListener((UnityAction)Refresh); - - _saveButton.SetActive(false); - _refreshButton.SetActive(false); - _folderButton.SetActive(false); - _newDivider.SetActive(false); - - _presetHolder = new GameObject("PresetHolder"); - _presetHolder.transform.SetParent(__instance.transform, false); - _presetHolder.transform.localScale = new Vector3(0.9f, 0.9f, 1); - _presetHolder.transform.localPosition = new Vector3(-1.9f, 1.7f, 0); - _arrange = _presetHolder.AddComponent(); - _arrange.Alignment = GridArrange.StartAlign.Right; - _arrange.CellSize = new Vector2(2.15f, -.55f); - _arrange.MaxColumns = 2; - - var watcher = new FileSystemWatcher - { - Path = PresetManager.PresetDirectory, - NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName, - Filter = "*.cfg", - IncludeSubdirectories = true, - EnableRaisingEvents = true, - }; - watcher.Changed += (_, _) => { Refresh(); }; - } - - private static void Refresh() - { - Logger.Error("Refreshing presets"); - foreach (var mod in MiraPluginManager.Instance.RegisteredPlugins) - { - PresetManager.LoadPresets(mod); - } - - GameSettingMenu.Instance.ChangeTab(0, false); // refresh the tab - } - } - public static void DivideSize(GameObject obj, float amount) { foreach (var collider in obj.GetComponentsInChildren(true)) @@ -206,7 +51,13 @@ internal static class GamePresetsTabPatches // ReSharper disable once InconsistentNaming public static void Postfix(GamePresetsTab __instance) { - Logger.Error("OnEnable called"); + if (!GameSettingMenu.Instance) + { + return; + } + + CreatePresetMenu(__instance); + __instance.StandardPresetButton.gameObject.SetActive(GameSettingMenuPatches.SelectedModIdx == 0); __instance.SecondPresetButton.gameObject.SetActive(GameSettingMenuPatches.SelectedModIdx == 0); @@ -232,7 +83,6 @@ internal static class GamePresetsTabPatches { _folderButton.SetActive(GameSettingMenuPatches.SelectedModIdx != 0); } - if (_newDivider) { _newDivider.SetActive(GameSettingMenuPatches.SelectedModIdx != 0); @@ -255,7 +105,6 @@ internal static class GamePresetsTabPatches button.OnClick.AddListener( (UnityAction)(() => { - Logger.Error($"Loading preset {preset.Name}"); preset.LoadPreset(); })); button.gameObject.SetActive(false); @@ -264,7 +113,7 @@ internal static class GamePresetsTabPatches foreach (var mod in MiraPluginManager.Instance.RegisteredPlugins) { - foreach (var button in mod.Presets.Select(x=>x.PresetButton)) + foreach (var button in mod.Presets.Select(x => x.PresetButton)) { if (button != null) { @@ -272,6 +121,7 @@ internal static class GamePresetsTabPatches } } } + _arrange.Start(); _arrange.ArrangeChilds(); } @@ -285,7 +135,6 @@ internal static class GamePresetsTabPatches { Logger.Error("OnDisable called"); - // Hide the save and refresh buttons if (_saveButton) { _saveButton.SetActive(false); @@ -299,9 +148,14 @@ internal static class GamePresetsTabPatches _folderButton.SetActive(false); } + if (_newDivider) + { + _newDivider.SetActive(false); + } + foreach (var mod in MiraPluginManager.Instance.RegisteredPlugins) { - foreach (var button in mod.Presets.Select(x=>x.PresetButton)) + foreach (var button in mod.Presets.Select(x => x.PresetButton)) { if (button != null) { @@ -311,4 +165,176 @@ internal static class GamePresetsTabPatches } } } + + public static void CreatePresetMenu(GamePresetsTab presetTab) + { + var prefab = GameSettingMenu.Instance.GameSettingsButton; + if (prefab == null) + { + Logger.Error("GameSettingsButton prefab is null"); + return; + } + + var tmpTranslator = presetTab.PresetDescriptionText.gameObject.GetComponent(); + if (tmpTranslator) + { + tmpTranslator.Destroy(); + } + + if (!_newDivider) + { + var oldDiv = presetTab.transform.FindChild("DividerImage"); + _newDivider = Object.Instantiate(oldDiv.gameObject, oldDiv.transform.parent); + _newDivider.transform.localPosition = new Vector3(1.85f, 0.13f, 0f); + _newDivider.transform.localScale = new Vector3(1.13f, 1, 1); + _newDivider.transform.localRotation = Quaternion.Euler(new Vector3(0, 0, 90)); + } + + PassiveButton saveButton; + if (!_saveButton) + { + saveButton = Object.Instantiate(prefab, presetTab.transform); + _saveButton = saveButton.gameObject; + _saveButton.gameObject.name = "SaveButton"; + _saveButton.transform.localPosition = new Vector3(3.2f, 1.7f, -2); + + // set the button text and alignment + var saveText = saveButton.buttonText; + saveText.text = "Save"; + saveText.GetComponent().Destroy(); + saveText.alignment = TextAlignmentOptions.Center; + saveText.transform.parent.localPosition = new Vector3( + -.525f, + saveText.transform.parent.localPosition.y, + saveText.transform.parent.localPosition.z); + + DivideSize(saveButton.gameObject, 2f); + + saveButton.OnClick = new Button.ButtonClickedEvent(); + saveButton.OnClick.AddListener( + (UnityAction)(() => + { + if (GameSettingMenuPatches.SelectedModIdx == 0) + { + return; + } + + SavePresetPopup.CreatePopup( + name => + { + if (GameSettingMenuPatches.SelectedMod == null) + { + return; + } + + if (name == string.Empty) + { + return; + } + + var presetFile = new ConfigFile( + Path.Combine( + PresetManager.PresetDirectory, + GameSettingMenuPatches.SelectedMod.PluginId, + $"{name}.cfg"), + false); + foreach (var option in GameSettingMenuPatches.SelectedMod.InternalOptions.Where( + x => x.IncludeInPreset)) + { + option.SaveToPreset(presetFile); + } + + presetFile.Save(); + Refresh(); + }); + })); + } + + saveButton = _saveButton.GetComponent(); + + PassiveButton refreshButton; + if (!_refreshButton) + { + refreshButton = Object.Instantiate(saveButton, presetTab.transform); + _refreshButton = refreshButton.gameObject; + _refreshButton.name = "RefreshButton"; + _refreshButton.transform.localPosition = new Vector3(2.85f, 1.1f, -2); + + var icon = new GameObject("Sprite"); + icon.transform.SetParent(refreshButton.transform); + icon.transform.localPosition = new Vector3(-0.47f, -0.08f, -5f); + icon.transform.localScale = new Vector3(0.5f, 0.5f, 1); + icon.layer = refreshButton.gameObject.layer; + + var iconSpriteRend = icon.AddComponent(); + iconSpriteRend.sprite = MiraAssets.RefreshIcon.LoadAsset(); + + DivideSize(refreshButton.gameObject, 2f); + refreshButton.buttonText.gameObject.Destroy(); + + refreshButton.OnClick = new Button.ButtonClickedEvent(); + refreshButton.OnClick.AddListener((UnityAction)Refresh); + } + refreshButton = _refreshButton.GetComponent(); + + if (!_folderButton) + { + var openFolderButton = Object.Instantiate(refreshButton, presetTab.transform); + _folderButton = openFolderButton.gameObject; + _folderButton.name = "OpenFolderButton"; + _folderButton.transform.localPosition = new Vector3(3.55f, 1.1f, -2); + + var folderRend = openFolderButton.transform.FindChild("Sprite").gameObject.GetComponent(); + folderRend.sprite = MiraAssets.FolderIcon.LoadAsset(); + folderRend.transform.localScale = new Vector3(0.4f, 0.4f, 1); + + openFolderButton.OnClick = new Button.ButtonClickedEvent(); + openFolderButton.OnClick.AddListener( + (UnityAction)(() => + { + var directory = Path.Combine( + PresetManager.PresetDirectory, + GameSettingMenuPatches.SelectedMod?.PluginId ?? string.Empty); + Directory.CreateDirectory(directory); + Process.Start( + new ProcessStartInfo + { + FileName = directory, + UseShellExecute = true, + Verb = "open", + }); + })); + } + + _saveButton.SetActive(false); + _refreshButton.SetActive(false); + _folderButton.SetActive(false); + _newDivider.SetActive(false); + + if (!_presetHolder) + { + _presetHolder = new GameObject("PresetHolder"); + _presetHolder.transform.SetParent(presetTab.transform, false); + _presetHolder.transform.localScale = new Vector3(0.9f, 0.9f, 1); + _presetHolder.transform.localPosition = new Vector3(-1.9f, 1.7f, 0); + _arrange = _presetHolder.AddComponent(); + _arrange.Alignment = GridArrange.StartAlign.Right; + _arrange.CellSize = new Vector2(2.15f, -.55f); + _arrange.MaxColumns = 2; + } + } + + private static void Refresh() + { + lock (Lock) + { + Logger.Error("Refreshing presets"); + foreach (var mod in MiraPluginManager.Instance.RegisteredPlugins) + { + PresetManager.LoadPresets(mod); + } + + GameSettingMenu.Instance.ChangeTab(0, false); // refresh the tab + } + } } diff --git a/MiraAPI/Presets/OptionPreset.cs b/MiraAPI/Presets/OptionPreset.cs index 7ba935ee..3a551a4b 100644 --- a/MiraAPI/Presets/OptionPreset.cs +++ b/MiraAPI/Presets/OptionPreset.cs @@ -1,5 +1,4 @@ -using System.Linq; -using BepInEx.Configuration; +using BepInEx.Configuration; using MiraAPI.PluginLoading; using UnityEngine; @@ -54,9 +53,10 @@ public class OptionPreset /// public void LoadPreset() { - foreach (var option in PresetConfig.Where(x=>PluginConfig.ContainsKey(x.Key))) + PresetConfig.Reload(); + foreach (var option in Plugin.InternalOptions) { - PluginConfig[option.Key].BoxedValue = option.Value.BoxedValue; + option.LoadFromPreset(PresetConfig); } } } diff --git a/MiraAPI/Presets/PresetManager.cs b/MiraAPI/Presets/PresetManager.cs index 033d8e71..732b602e 100644 --- a/MiraAPI/Presets/PresetManager.cs +++ b/MiraAPI/Presets/PresetManager.cs @@ -2,6 +2,7 @@ using System.Linq; using BepInEx.Configuration; using MiraAPI.PluginLoading; +using Reactor.Utilities; using UnityEngine; namespace MiraAPI.Presets; @@ -14,17 +15,17 @@ public static class PresetManager /// /// Gets the directory where the presets are stored. /// - public static string PresetDirectory { get; } = Path.Join(Application.persistentDataPath, "mira_presets"); + public static string PresetDirectory { get; } = Path.GetFullPath("mira_presets", Application.persistentDataPath); internal static void CreateDefaultPreset(MiraPluginInfo plugin) { - var presetPath = Path.Join(PresetManager.PresetDirectory, plugin.PluginId); + var presetPath = Path.Combine(PresetDirectory, plugin.PluginId); if (!Directory.Exists(presetPath)) { Directory.CreateDirectory(presetPath); } - var presetConfig = new ConfigFile(Path.Join(presetPath, "Default.cfg"), true); + var presetConfig = new ConfigFile(Path.Combine(presetPath, "Default.cfg"), false); foreach (var option in plugin.InternalOptions.Where(x => x.IncludeInPreset)) { @@ -54,7 +55,7 @@ public static class PresetManager Directory.CreateDirectory(PresetDirectory); } - var pluginPresetPath = Path.Join(PresetDirectory, plugin.PluginId); + var pluginPresetPath = Path.Combine(PresetDirectory, plugin.PluginId); if (!Directory.Exists(pluginPresetPath)) { Directory.CreateDirectory(pluginPresetPath); @@ -62,8 +63,13 @@ public static class PresetManager foreach (var file in Directory.GetFiles(pluginPresetPath, "*.cfg")) { + Logger.Info($"Loading preset file {file}"); var presetName = Path.GetFileNameWithoutExtension(file); var presetConfig = new ConfigFile(file, true); + foreach (var option in plugin.InternalOptions.Where(x=>x.IncludeInPreset)) + { + option.Bind(presetConfig); + } plugin.InternalPresets.Add(new OptionPreset(presetName, plugin, presetConfig)); } plugin.Presets = [.. plugin.InternalPresets];