using UnityEngine; using UnityEditor; using UnityEditor.AddressableAssets; using UnityEditor.AddressableAssets.Settings; using System.IO; using AppleHills.Core.Settings; namespace AppleHills.Editor { public class SettingsMigrationWindow : EditorWindow { private GameSettings legacySettings; private bool settingsFolderExists; private bool addressablesInstalled; private AddressableAssetSettings addressableSettings; private AddressableAssetGroup settingsGroup; private GUIStyle headerStyle; private GUIStyle successStyle; private Vector2 scrollPosition; private bool migrationCompleted = false; [MenuItem("AppleHills/Migrate Legacy Settings")] public static void ShowWindow() { var window = GetWindow("Settings Migration"); window.minSize = new Vector2(450, 400); } private void OnEnable() { // Check if Settings folder exists settingsFolderExists = AssetDatabase.IsValidFolder("Assets/Settings"); // Check if Addressables package is installed addressablesInstalled = AddressableAssetSettingsDefaultObject.SettingsExists; if (addressablesInstalled) { addressableSettings = AddressableAssetSettingsDefaultObject.Settings; } } private void OnGUI() { if (headerStyle == null) { headerStyle = new GUIStyle(EditorStyles.boldLabel); headerStyle.fontSize = 14; headerStyle.margin = new RectOffset(0, 0, 10, 10); successStyle = new GUIStyle(EditorStyles.label); successStyle.normal.textColor = Color.green; successStyle.fontSize = 12; } scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition); EditorGUILayout.LabelField("Migrate Legacy Settings", headerStyle); EditorGUILayout.Space(10); EditorGUILayout.HelpBox( "This tool will migrate your legacy GameSettings to the new modular settings system. " + "It will create new settings assets in the Assets/Settings folder, mark them as Addressables, " + "and copy values from your legacy settings.", MessageType.Info); EditorGUILayout.Space(10); // Prerequisites section EditorGUILayout.LabelField("Prerequisites", EditorStyles.boldLabel); // Check Addressables GUI.enabled = false; EditorGUILayout.Toggle("Addressables Package Installed", addressablesInstalled); GUI.enabled = true; if (!addressablesInstalled) { EditorGUILayout.HelpBox( "The Addressables package is not installed. Please install it via Window > Package Manager.", MessageType.Error); } EditorGUILayout.Space(5); // Legacy settings field legacySettings = EditorGUILayout.ObjectField("Legacy GameSettings", legacySettings, typeof(GameSettings), false) as GameSettings; if (legacySettings == null) { EditorGUILayout.HelpBox( "Please assign your legacy GameSettings asset to migrate from.", MessageType.Warning); } EditorGUILayout.Space(15); // Migration button GUI.enabled = legacySettings != null && addressablesInstalled; if (GUILayout.Button("Migrate Settings", GUILayout.Height(30))) { MigrateSettings(); } GUI.enabled = true; // Success message if (migrationCompleted) { EditorGUILayout.Space(10); EditorGUILayout.LabelField("Migration completed successfully!", successStyle); EditorGUILayout.HelpBox( "The legacy settings have been migrated to the new system. " + "You can now access these settings through the AppleHills > Settings Editor menu.", MessageType.Info); } EditorGUILayout.EndScrollView(); } private void MigrateSettings() { // Create Settings folder if it doesn't exist if (!settingsFolderExists) { AssetDatabase.CreateFolder("Assets", "Settings"); settingsFolderExists = true; } // Setup Addressables group for settings SetupAddressablesGroup(); // Create and populate the new settings assets CreatePlayerFollowerSettings(); CreateInteractionSettings(); CreateMinigameSettings(); // Save all assets AssetDatabase.SaveAssets(); migrationCompleted = true; } private void SetupAddressablesGroup() { // Find or create a settings group settingsGroup = addressableSettings.FindGroup("Settings"); if (settingsGroup == null) { settingsGroup = addressableSettings.CreateGroup("Settings", false, false, true, null); } } private void AddAssetToAddressables(string assetPath, string address) { // Create entry in addressables var guid = AssetDatabase.AssetPathToGUID(assetPath); var entry = addressableSettings.CreateOrMoveEntry(guid, settingsGroup); // Set the address entry.address = address; Debug.Log($"Added {assetPath} to Addressables with address {address}"); } private void CreatePlayerFollowerSettings() { // Create the settings asset var settings = ScriptableObject.CreateInstance(); // Copy values from legacy settings if (legacySettings != null) { // Player settings var playerSettings = typeof(GameSettings).GetField("moveSpeed"); if (playerSettings != null) settings.GetType().GetField("moveSpeed", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.moveSpeed); var stopDistanceField = typeof(GameSettings).GetField("stopDistance"); if (stopDistanceField != null) settings.GetType().GetField("stopDistance", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.stopDistance); var useRigidbodyField = typeof(GameSettings).GetField("useRigidbody"); if (useRigidbodyField != null) settings.GetType().GetField("useRigidbody", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.useRigidbody); var defaultHoldMovementModeField = typeof(GameSettings).GetField("defaultHoldMovementMode"); if (defaultHoldMovementModeField != null) settings.GetType().GetField("defaultHoldMovementMode", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.defaultHoldMovementMode); // Follower settings var followDistanceField = typeof(GameSettings).GetField("followDistance"); if (followDistanceField != null) settings.GetType().GetField("followDistance", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.followDistance); var manualMoveSmoothField = typeof(GameSettings).GetField("manualMoveSmooth"); if (manualMoveSmoothField != null) settings.GetType().GetField("manualMoveSmooth", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.manualMoveSmooth); var thresholdFarField = typeof(GameSettings).GetField("thresholdFar"); if (thresholdFarField != null) settings.GetType().GetField("thresholdFar", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.thresholdFar); var thresholdNearField = typeof(GameSettings).GetField("thresholdNear"); if (thresholdNearField != null) settings.GetType().GetField("thresholdNear", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.thresholdNear); var stopThresholdField = typeof(GameSettings).GetField("stopThreshold"); if (stopThresholdField != null) settings.GetType().GetField("stopThreshold", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.stopThreshold); // Backend settings var followUpdateIntervalField = typeof(GameSettings).GetField("followUpdateInterval"); if (followUpdateIntervalField != null) settings.GetType().GetField("followUpdateInterval", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.followUpdateInterval); var followerSpeedMultiplierField = typeof(GameSettings).GetField("followerSpeedMultiplier"); if (followerSpeedMultiplierField != null) settings.GetType().GetField("followerSpeedMultiplier", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.followerSpeedMultiplier); var heldIconDisplayHeightField = typeof(GameSettings).GetField("heldIconDisplayHeight"); if (heldIconDisplayHeightField != null) settings.GetType().GetField("heldIconDisplayHeight", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.heldIconDisplayHeight); } // Save the asset string assetPath = "Assets/Settings/PlayerFollowerSettings.asset"; AssetDatabase.CreateAsset(settings, assetPath); // Add to addressables AddAssetToAddressables(assetPath, "Settings/PlayerFollowerSettings"); Debug.Log("Created PlayerFollowerSettings asset"); } private void CreateInteractionSettings() { // Create the settings asset var settings = ScriptableObject.CreateInstance(); // Copy values from legacy settings if (legacySettings != null) { // Interaction settings var playerStopDistanceField = typeof(GameSettings).GetField("playerStopDistance"); if (playerStopDistanceField != null) settings.GetType().GetField("playerStopDistance", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.playerStopDistance); var playerStopDistanceDirectInteractionField = typeof(GameSettings).GetField("playerStopDistanceDirectInteraction"); if (playerStopDistanceDirectInteractionField != null) settings.GetType().GetField("playerStopDistanceDirectInteraction", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.playerStopDistanceDirectInteraction); var followerPickupDelayField = typeof(GameSettings).GetField("followerPickupDelay"); if (followerPickupDelayField != null) settings.GetType().GetField("followerPickupDelay", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.followerPickupDelay); var interactableLayerMaskField = typeof(GameSettings).GetField("interactableLayerMask"); if (interactableLayerMaskField != null) settings.GetType().GetField("interactableLayerMask", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.interactableLayerMask); // Prefabs var basePickupPrefabField = typeof(GameSettings).GetField("basePickupPrefab"); if (basePickupPrefabField != null) settings.GetType().GetField("basePickupPrefab", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.basePickupPrefab); var levelSwitchMenuPrefabField = typeof(GameSettings).GetField("levelSwitchMenuPrefab"); if (levelSwitchMenuPrefabField != null) settings.GetType().GetField("levelSwitchMenuPrefab", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.levelSwitchMenuPrefab); // Item configuration var combinationRulesField = typeof(GameSettings).GetField("combinationRules"); if (combinationRulesField != null) settings.GetType().GetField("combinationRules", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.combinationRules); var slotItemConfigsField = typeof(GameSettings).GetField("slotItemConfigs"); if (slotItemConfigsField != null) settings.GetType().GetField("slotItemConfigs", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.slotItemConfigs); } // Save the asset string assetPath = "Assets/Settings/InteractionSettings.asset"; AssetDatabase.CreateAsset(settings, assetPath); // Add to addressables AddAssetToAddressables(assetPath, "Settings/InteractionSettings"); Debug.Log("Created InteractionSettings asset"); } private void CreateMinigameSettings() { // Create the settings asset var settings = ScriptableObject.CreateInstance(); // Copy values from legacy settings if (legacySettings != null) { // Endless descender settings var endlessDescenderLerpSpeedField = typeof(GameSettings).GetField("endlessDescenderLerpSpeed"); if (endlessDescenderLerpSpeedField != null) settings.GetType().GetField("endlessDescenderLerpSpeed", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.endlessDescenderLerpSpeed); var endlessDescenderMaxOffsetField = typeof(GameSettings).GetField("endlessDescenderMaxOffset"); if (endlessDescenderMaxOffsetField != null) settings.GetType().GetField("endlessDescenderMaxOffset", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.endlessDescenderMaxOffset); var endlessDescenderClampXMinField = typeof(GameSettings).GetField("endlessDescenderClampXMin"); if (endlessDescenderClampXMinField != null) settings.GetType().GetField("endlessDescenderClampXMin", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.endlessDescenderClampXMin); var endlessDescenderClampXMaxField = typeof(GameSettings).GetField("endlessDescenderClampXMax"); if (endlessDescenderClampXMaxField != null) settings.GetType().GetField("endlessDescenderClampXMax", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.endlessDescenderClampXMax); var endlessDescenderSpeedExponentField = typeof(GameSettings).GetField("endlessDescenderSpeedExponent"); if (endlessDescenderSpeedExponentField != null) settings.GetType().GetField("endlessDescenderSpeedExponent", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(settings, legacySettings.endlessDescenderSpeedExponent); } // Save the asset string assetPath = "Assets/Settings/MinigameSettings.asset"; AssetDatabase.CreateAsset(settings, assetPath); // Add to addressables AddAssetToAddressables(assetPath, "Settings/MinigameSettings"); Debug.Log("Created MinigameSettings asset"); } } }