using System; using AppleHills.Core.Settings; using Core; using Input; using Interactions; using System.Threading.Tasks; using PuzzleS; using UnityEngine; namespace Levels { /// /// Saveable data for MinigameSwitch state /// [Serializable] public class MinigameSwitchSaveData { public bool isUnlocked; } /// /// Handles switching into minigame levels when interacted with. Applies switch data and triggers scene transitions. /// public class MinigameSwitch : SaveableInteractable { /// /// Data for this level switch (target scene, icon, etc). /// public LevelSwitchData switchData; /// /// If true, skip save/load logic and start the game active (unlocked by default) /// [SerializeField] private bool startUnlocked = false; private SpriteRenderer iconRenderer; // Settings reference private IInteractionSettings interactionSettings; private bool switchActive = true; private bool isUnlocked; /// /// Unity Awake callback. Sets up icon, interactable, and event handlers. /// protected override void OnManagedAwake() { base.OnManagedAwake(); switchActive = true; if (iconRenderer == null) iconRenderer = GetComponent(); // If startUnlocked is true, set isUnlocked and skip save/load logic if (startUnlocked) { isUnlocked = true; } // Initialize settings reference interactionSettings = GameManager.GetSettingsObject(); ApplySwitchData(); } protected override void OnManagedStart() { base.OnManagedStart(); // If startUnlocked is true, always start active if (startUnlocked) { gameObject.SetActive(true); return; } if (PuzzleManager.Instance != null) { PuzzleManager.Instance.OnAllPuzzlesComplete += HandleAllPuzzlesComplete; } if (!IsRestoringFromSave && !isUnlocked) { gameObject.SetActive(false); } } protected override void OnDestroy() { base.OnDestroy(); if (PuzzleManager.Instance != null) { PuzzleManager.Instance.OnAllPuzzlesComplete -= HandleAllPuzzlesComplete; } } private void HandleAllPuzzlesComplete(PuzzleS.PuzzleLevelDataSO _) { // Unlock the minigame isUnlocked = true; gameObject.SetActive(true); // Add to global unlocked minigames list if (switchData != null && !string.IsNullOrEmpty(switchData.targetLevelSceneName)) { Core.SaveLoad.SaveLoadManager.Instance?.UnlockMinigame(switchData.targetLevelSceneName); } // Save will happen automatically on next save cycle via SaveableInteractable } #if UNITY_EDITOR /// /// Unity OnValidate callback. Ensures icon and data are up to date in editor. /// void OnValidate() { if (iconRenderer == null) iconRenderer = GetComponent(); ApplySwitchData(); } #endif /// /// Applies the switch data to the level switch (icon, name, etc). /// public void ApplySwitchData() { if (switchData != null) { if (iconRenderer != null) iconRenderer.sprite = switchData.mapSprite; gameObject.name = switchData.targetLevelSceneName; // Optionally update other fields, e.g. description } } /// /// High-level validation: Only allow interaction if unlocked. /// protected override bool CanBeClicked() { return base.CanBeClicked() && isUnlocked; } /// /// Setup: Prevent re-entry while interaction is in progress. /// protected override void OnInteractionStarted() { switchActive = false; } /// /// Main interaction logic: Spawn menu and switch input mode. /// protected override bool DoInteraction() { if (switchData == null || string.IsNullOrEmpty(switchData.targetLevelSceneName)) { Logging.Warning("MinigameSwitch has no valid switchData!"); return false; } var menuPrefab = interactionSettings?.MinigameSwitchMenuPrefab; if (menuPrefab == null) { Debug.LogError("MinigameSwitchMenu prefab not assigned in InteractionSettings!"); return false; } // Spawn the menu overlay var menuGo = Instantiate(menuPrefab); var menu = menuGo.GetComponent(); if (menu == null) { Debug.LogError("MinigameSwitchMenu component missing on prefab!"); Destroy(menuGo); return false; } // Setup menu with data and callbacks menu.Setup(switchData, OnLevelSelectedWrapper, OnMenuCancel); // Switch input mode to UI only InputManager.Instance.SetInputMode(InputMode.UI); return true; // Menu spawned successfully } private void OnLevelSelectedWrapper() { _ = OnLevelSelected(); } private async Task OnLevelSelected() { var progress = new Progress(p => Logging.Debug($"Loading progress: {p * 100:F0}%")); await SceneManagerService.Instance.SwitchSceneAsync(switchData.targetLevelSceneName, progress); } private void OnMenuCancel() { switchActive = true; // Allow interaction again if cancelled InputManager.Instance.SetInputMode(InputMode.GameAndUI); } #region Save/Load Implementation protected override object GetSerializableState() { // Skip saving if startUnlocked is true if (startUnlocked) return null; return new MinigameSwitchSaveData { isUnlocked = isUnlocked }; } protected override void ApplySerializableState(string serializedData) { // Skip loading if startUnlocked is true if (startUnlocked) return; MinigameSwitchSaveData data = JsonUtility.FromJson(serializedData); if (data == null) { Logging.Warning($"[MinigameSwitch] Failed to deserialize save data for {gameObject.name}"); return; } isUnlocked = data.isUnlocked; // Sync with global unlocked minigames list if (isUnlocked && switchData != null && !string.IsNullOrEmpty(switchData.targetLevelSceneName)) { Core.SaveLoad.SaveLoadManager.Instance?.UnlockMinigame(switchData.targetLevelSceneName); } // Show/hide based on unlock state gameObject.SetActive(isUnlocked); } #endregion } }