Fome further save load work
This commit is contained in:
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Interactions;
|
||||
using Bootstrap;
|
||||
using Core.SaveLoad;
|
||||
|
||||
namespace Core
|
||||
{
|
||||
@@ -59,6 +60,7 @@ namespace Core
|
||||
{
|
||||
// Subscribe to scene load completed so we can clear registrations when scenes change
|
||||
SceneManagerService.Instance.SceneLoadStarted += OnSceneLoadStarted;
|
||||
|
||||
Logging.Debug("[ItemManager] Subscribed to SceneManagerService events");
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using AppleHills.Core.Settings;
|
||||
using Bootstrap;
|
||||
using UnityEngine;
|
||||
|
||||
@@ -55,12 +56,18 @@ namespace Core.SaveLoad
|
||||
#if UNITY_EDITOR
|
||||
OnSceneLoadCompleted("RestoreInEditor");
|
||||
#endif
|
||||
Load();
|
||||
if (DeveloperSettingsProvider.Instance.GetSettings<DebugSettings>().useSaveLoadSystem)
|
||||
{
|
||||
Load();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnApplicationQuit()
|
||||
{
|
||||
Save();
|
||||
if (DeveloperSettingsProvider.Instance.GetSettings<DebugSettings>().useSaveLoadSystem)
|
||||
{
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializePostBoot()
|
||||
|
||||
@@ -28,6 +28,10 @@ namespace AppleHills.Core.Settings
|
||||
[Tooltip("Should Time.timeScale be set to 0 when the game is paused")]
|
||||
[SerializeField] public bool pauseTimeOnPauseGame = true;
|
||||
|
||||
[Header("Save Load Options")]
|
||||
[Tooltip("Should use save laod system?")]
|
||||
[SerializeField] public bool useSaveLoadSystem = true;
|
||||
|
||||
[Header("Logging Options")]
|
||||
[Tooltip("Logging level for bootstrap services")]
|
||||
[SerializeField] public LogVerbosity bootstrapLogVerbosity = LogVerbosity.Warning;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
using Input;
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Linq; // added for Action<T>
|
||||
using System.Linq;
|
||||
using Bootstrap; // added for Action<T>
|
||||
using Core; // register with ItemManager
|
||||
|
||||
namespace Interactions
|
||||
@@ -13,6 +14,7 @@ namespace Interactions
|
||||
public class PickupSaveData
|
||||
{
|
||||
public bool isPickedUp;
|
||||
public bool wasHeldByFollower; // Track if held by follower for bilateral restoration
|
||||
public Vector3 worldPosition;
|
||||
public Quaternion worldRotation;
|
||||
public bool isActive;
|
||||
@@ -52,11 +54,13 @@ namespace Interactions
|
||||
{
|
||||
base.Start(); // Register with save system
|
||||
|
||||
// Don't register with ItemManager if already picked up (restored from save)
|
||||
if (!IsPickedUp)
|
||||
// Always register with ItemManager, even if picked up
|
||||
// This allows the save/load system to find held items when restoring state
|
||||
BootCompletionService.RegisterInitAction(() =>
|
||||
{
|
||||
ItemManager.Instance?.RegisterPickup(this);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -162,9 +166,13 @@ namespace Interactions
|
||||
|
||||
protected override object GetSerializableState()
|
||||
{
|
||||
// Check if this pickup is currently held by the follower
|
||||
bool isHeldByFollower = IsPickedUp && !gameObject.activeSelf && transform.parent != null;
|
||||
|
||||
return new PickupSaveData
|
||||
{
|
||||
isPickedUp = this.IsPickedUp,
|
||||
wasHeldByFollower = isHeldByFollower,
|
||||
worldPosition = transform.position,
|
||||
worldRotation = transform.rotation,
|
||||
isActive = gameObject.activeSelf
|
||||
@@ -187,6 +195,18 @@ namespace Interactions
|
||||
{
|
||||
// Hide the pickup if it was already picked up
|
||||
gameObject.SetActive(false);
|
||||
|
||||
// If this was held by the follower, try bilateral restoration
|
||||
if (data.wasHeldByFollower)
|
||||
{
|
||||
// Try to give this pickup to the follower
|
||||
// This might succeed or fail depending on timing
|
||||
var follower = FollowerController.FindInstance();
|
||||
if (follower != null)
|
||||
{
|
||||
follower.TryClaimHeldItem(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -14,6 +14,15 @@ namespace Interactions
|
||||
[SerializeField]
|
||||
[Tooltip("Optional custom save ID. If empty, will auto-generate from hierarchy path.")]
|
||||
private string customSaveId = "";
|
||||
|
||||
/// <summary>
|
||||
/// Sets a custom save ID for this interactable.
|
||||
/// Used when spawning dynamic objects that need stable save IDs.
|
||||
/// </summary>
|
||||
public void SetCustomSaveId(string saveId)
|
||||
{
|
||||
customSaveId = saveId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flag to indicate we're currently restoring from save data.
|
||||
@@ -258,4 +267,3 @@ namespace Interactions
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
@@ -100,6 +100,8 @@ public class FollowerController : MonoBehaviour, ISaveParticipant
|
||||
|
||||
// Save system tracking
|
||||
private bool hasBeenRestored;
|
||||
private bool _hasRestoredHeldItem; // Track if held item restoration completed
|
||||
private string _expectedHeldItemSaveId; // Expected saveId during restoration
|
||||
|
||||
void Awake()
|
||||
{
|
||||
@@ -785,10 +787,11 @@ public class FollowerController : MonoBehaviour, ISaveParticipant
|
||||
transform.position = saveData.worldPosition;
|
||||
transform.rotation = saveData.worldRotation;
|
||||
|
||||
// Restore held item if any
|
||||
// Try bilateral restoration of held item
|
||||
if (!string.IsNullOrEmpty(saveData.heldItemSaveId))
|
||||
{
|
||||
RestoreHeldItem(saveData.heldItemSaveId, saveData.heldItemDataAssetPath);
|
||||
_expectedHeldItemSaveId = saveData.heldItemSaveId;
|
||||
TryRestoreHeldItem(saveData.heldItemSaveId, saveData.heldItemDataAssetPath);
|
||||
}
|
||||
|
||||
hasBeenRestored = true;
|
||||
@@ -801,48 +804,110 @@ public class FollowerController : MonoBehaviour, ISaveParticipant
|
||||
}
|
||||
}
|
||||
|
||||
private void RestoreHeldItem(string heldItemSaveId, string heldItemDataAssetPath)
|
||||
/// <summary>
|
||||
/// Bilateral restoration: Follower tries to find and claim the held item.
|
||||
/// If pickup doesn't exist yet, it will try to claim us when it restores.
|
||||
/// </summary>
|
||||
private void TryRestoreHeldItem(string heldItemSaveId, string heldItemDataAssetPath)
|
||||
{
|
||||
// Try to find the item by its save ID using ItemManager
|
||||
GameObject heldObject = ItemManager.Instance?.FindPickupBySaveId(heldItemSaveId);
|
||||
|
||||
if (heldObject == null)
|
||||
if (_hasRestoredHeldItem)
|
||||
{
|
||||
Logging.Warning($"[FollowerController] Could not find held item with save ID: {heldItemSaveId}");
|
||||
Logging.Debug("[FollowerController] Held item already restored");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the item data
|
||||
PickupItemData heldData = null;
|
||||
#if UNITY_EDITOR
|
||||
if (!string.IsNullOrEmpty(heldItemDataAssetPath))
|
||||
// Try to find the pickup immediately
|
||||
GameObject heldObject = ItemManager.Instance?.FindPickupBySaveId(heldItemSaveId);
|
||||
|
||||
if (heldObject == null)
|
||||
{
|
||||
heldData = UnityEditor.AssetDatabase.LoadAssetAtPath<PickupItemData>(heldItemDataAssetPath);
|
||||
Logging.Debug($"[FollowerController] Held item not found yet: {heldItemSaveId}, waiting for pickup to restore");
|
||||
return; // Pickup will find us when it restores
|
||||
}
|
||||
|
||||
var pickup = heldObject.GetComponent<Pickup>();
|
||||
if (pickup == null)
|
||||
{
|
||||
Logging.Warning($"[FollowerController] Found object but no Pickup component: {heldItemSaveId}");
|
||||
return;
|
||||
}
|
||||
|
||||
// Claim the pickup
|
||||
TakeOwnership(pickup, heldItemDataAssetPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bilateral restoration entry point: Pickup calls this to offer itself to the Follower.
|
||||
/// Returns true if claim was successful, false if Follower already has an item or wrong pickup.
|
||||
/// </summary>
|
||||
public bool TryClaimHeldItem(Pickup pickup)
|
||||
{
|
||||
if (pickup == null)
|
||||
return false;
|
||||
|
||||
if (_hasRestoredHeldItem)
|
||||
{
|
||||
Logging.Debug("[FollowerController] Already restored held item, rejecting claim");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify this is the expected pickup
|
||||
if (pickup is SaveableInteractable saveable)
|
||||
{
|
||||
if (saveable.GetSaveId() != _expectedHeldItemSaveId)
|
||||
{
|
||||
Logging.Warning($"[FollowerController] Pickup tried to claim but saveId mismatch: {saveable.GetSaveId()} != {_expectedHeldItemSaveId}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Claim the pickup
|
||||
TakeOwnership(pickup, null);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Takes ownership of a pickup during restoration. Called by both restoration paths.
|
||||
/// </summary>
|
||||
private void TakeOwnership(Pickup pickup, string itemDataAssetPath)
|
||||
{
|
||||
if (_hasRestoredHeldItem)
|
||||
return; // Already claimed
|
||||
|
||||
// Get the item data
|
||||
PickupItemData heldData = pickup.itemData;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
// Try loading from asset path if available and pickup doesn't have data
|
||||
if (heldData == null && !string.IsNullOrEmpty(itemDataAssetPath))
|
||||
{
|
||||
heldData = UnityEditor.AssetDatabase.LoadAssetAtPath<PickupItemData>(itemDataAssetPath);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (heldData == null)
|
||||
{
|
||||
var pickup = heldObject.GetComponent<Pickup>();
|
||||
if (pickup != null)
|
||||
{
|
||||
heldData = pickup.itemData;
|
||||
}
|
||||
Logging.Warning($"[FollowerController] Could not get item data for pickup: {pickup.gameObject.name}");
|
||||
return;
|
||||
}
|
||||
|
||||
// Restore the held item state
|
||||
if (heldData != null)
|
||||
{
|
||||
var pickup = heldObject.GetComponent<Pickup>();
|
||||
if (pickup != null)
|
||||
{
|
||||
_cachedPickupObject = heldObject;
|
||||
_cachedPickupObject.SetActive(false); // Held items should be hidden
|
||||
SetHeldItem(heldData, pickup.iconRenderer);
|
||||
_animator.SetBool("IsCarrying", true);
|
||||
Logging.Debug($"[FollowerController] Restored held item: {heldData.itemName}");
|
||||
}
|
||||
}
|
||||
// Setup the held item
|
||||
_cachedPickupObject = pickup.gameObject;
|
||||
_cachedPickupObject.SetActive(false); // Held items should be hidden
|
||||
SetHeldItem(heldData, pickup.iconRenderer);
|
||||
_animator.SetBool("IsCarrying", true);
|
||||
_hasRestoredHeldItem = true;
|
||||
|
||||
Logging.Debug($"[FollowerController] Successfully restored held item: {heldData.itemName}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Static method to find the FollowerController instance in the scene.
|
||||
/// Used by Pickup during bilateral restoration.
|
||||
/// </summary>
|
||||
public static FollowerController FindInstance()
|
||||
{
|
||||
return FindObjectOfType<FollowerController>();
|
||||
}
|
||||
|
||||
#endregion ISaveParticipant Implementation
|
||||
|
||||
@@ -7,16 +7,28 @@ using UnityEngine.SceneManagement;
|
||||
using AppleHills.Core.Settings;
|
||||
using Bootstrap;
|
||||
using Core;
|
||||
using Core.SaveLoad;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
using Utils;
|
||||
|
||||
namespace PuzzleS
|
||||
{
|
||||
/// <summary>
|
||||
/// Save data structure for puzzle progress
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class PuzzleSaveData
|
||||
{
|
||||
public string levelId;
|
||||
public List<string> completedStepIds;
|
||||
public List<string> unlockedStepIds;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Manages puzzle step registration, dependency management, and step completion for the puzzle system.
|
||||
/// </summary>
|
||||
public class PuzzleManager : MonoBehaviour
|
||||
public class PuzzleManager : MonoBehaviour, ISaveParticipant
|
||||
{
|
||||
private static PuzzleManager _instance;
|
||||
|
||||
@@ -48,10 +60,23 @@ namespace PuzzleS
|
||||
public event Action<PuzzleLevelDataSO> OnLevelDataLoaded;
|
||||
public event Action<PuzzleLevelDataSO> OnAllPuzzlesComplete;
|
||||
|
||||
private HashSet<PuzzleStepSO> _completedSteps = new HashSet<PuzzleStepSO>();
|
||||
private HashSet<PuzzleStepSO> _unlockedSteps = new HashSet<PuzzleStepSO>();
|
||||
// Save/Load state tracking - string-based for timing independence
|
||||
private HashSet<string> _completedSteps = new HashSet<string>();
|
||||
private HashSet<string> _unlockedSteps = new HashSet<string>();
|
||||
|
||||
// Save/Load restoration tracking
|
||||
private bool _isDataRestored = false;
|
||||
private bool _hasBeenRestored = false;
|
||||
private List<ObjectiveStepBehaviour> _pendingRegistrations = new List<ObjectiveStepBehaviour>();
|
||||
|
||||
// Registration for ObjectiveStepBehaviour
|
||||
private Dictionary<PuzzleStepSO, ObjectiveStepBehaviour> _stepBehaviours = new Dictionary<PuzzleStepSO, ObjectiveStepBehaviour>();
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this participant has already had its state restored.
|
||||
/// Used by SaveLoadManager to prevent double-restoration.
|
||||
/// </summary>
|
||||
public bool HasBeenRestored => _hasBeenRestored;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
@@ -70,6 +95,13 @@ namespace PuzzleS
|
||||
SceneManagerService.Instance.SceneLoadCompleted += OnSceneLoadCompleted;
|
||||
SceneManagerService.Instance.SceneLoadStarted += OnSceneLoadStarted;
|
||||
|
||||
// Register with save/load system
|
||||
BootCompletionService.RegisterInitAction(() =>
|
||||
{
|
||||
SaveLoadManager.Instance.RegisterParticipant(this);
|
||||
Logging.Debug("[PuzzleManager] Registered with SaveLoadManager");
|
||||
});
|
||||
|
||||
// Find player transform
|
||||
_playerTransform = GameObject.FindGameObjectWithTag("Player")?.transform;
|
||||
|
||||
@@ -96,6 +128,11 @@ namespace PuzzleS
|
||||
SceneManagerService.Instance.SceneLoadStarted -= OnSceneLoadStarted;
|
||||
}
|
||||
|
||||
// Unregister from save/load system
|
||||
SaveLoadManager.Instance.UnregisterParticipant(GetSaveId());
|
||||
Logging.Debug("[PuzzleManager] Unregistered from SaveLoadManager");
|
||||
|
||||
|
||||
// Release addressable handle if needed
|
||||
if (_levelDataLoadOperation.IsValid())
|
||||
{
|
||||
@@ -296,12 +333,20 @@ namespace PuzzleS
|
||||
_stepBehaviours.Add(behaviour.stepData, behaviour);
|
||||
Logging.Debug($"[Puzzles] Registered step: {behaviour.stepData.stepId} on {behaviour.gameObject.name}");
|
||||
|
||||
// Only update state if data is already loaded
|
||||
if (_isDataLoaded && _currentLevelData != null)
|
||||
// Use pending registration pattern for save/load timing independence
|
||||
if (_isDataRestored)
|
||||
{
|
||||
// Data already restored - update immediately
|
||||
UpdateStepState(behaviour);
|
||||
}
|
||||
// Otherwise, the state will be updated when data loads in UpdateAllRegisteredBehaviors
|
||||
else
|
||||
{
|
||||
// Data not restored yet - add to pending queue
|
||||
if (!_pendingRegistrations.Contains(behaviour))
|
||||
{
|
||||
_pendingRegistrations.Add(behaviour);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -313,11 +358,11 @@ namespace PuzzleS
|
||||
if (behaviour?.stepData == null) return;
|
||||
|
||||
// If step is already completed, ignore
|
||||
if (_completedSteps.Contains(behaviour.stepData))
|
||||
if (_completedSteps.Contains(behaviour.stepData.stepId))
|
||||
return;
|
||||
|
||||
// If step is already unlocked, update the behaviour
|
||||
if (_unlockedSteps.Contains(behaviour.stepData))
|
||||
if (_unlockedSteps.Contains(behaviour.stepData.stepId))
|
||||
{
|
||||
behaviour.UnlockStep();
|
||||
}
|
||||
@@ -349,6 +394,13 @@ namespace PuzzleS
|
||||
{
|
||||
if (_currentLevelData == null) return;
|
||||
|
||||
// Don't unlock initial steps if we've restored from save
|
||||
if (_isDataRestored)
|
||||
{
|
||||
Logging.Debug("[Puzzles] Skipping UnlockInitialSteps - data was restored from save");
|
||||
return;
|
||||
}
|
||||
|
||||
// Unlock initial steps
|
||||
foreach (var step in _currentLevelData.initialSteps)
|
||||
{
|
||||
@@ -364,10 +416,10 @@ namespace PuzzleS
|
||||
/// <param name="step">The completed step.</param>
|
||||
public void MarkPuzzleStepCompleted(PuzzleStepSO step)
|
||||
{
|
||||
if (_completedSteps.Contains(step)) return;
|
||||
if (_completedSteps.Contains(step.stepId)) return;
|
||||
if (_currentLevelData == null) return;
|
||||
|
||||
_completedSteps.Add(step);
|
||||
_completedSteps.Add(step.stepId);
|
||||
Logging.Debug($"[Puzzles] Step completed: {step.stepId}");
|
||||
|
||||
// Broadcast completion
|
||||
@@ -408,18 +460,11 @@ namespace PuzzleS
|
||||
{
|
||||
foreach (var depId in dependencies)
|
||||
{
|
||||
// Find the dependency step
|
||||
bool dependencyMet = false;
|
||||
foreach (var completedStep in _completedSteps)
|
||||
// Check if dependency is in completed steps
|
||||
if (!_completedSteps.Contains(depId))
|
||||
{
|
||||
if (completedStep.stepId == depId)
|
||||
{
|
||||
dependencyMet = true;
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!dependencyMet) return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -432,8 +477,8 @@ namespace PuzzleS
|
||||
/// <param name="step">The step to unlock.</param>
|
||||
private void UnlockStep(PuzzleStepSO step)
|
||||
{
|
||||
if (_unlockedSteps.Contains(step)) return;
|
||||
_unlockedSteps.Add(step);
|
||||
if (_unlockedSteps.Contains(step.stepId)) return;
|
||||
_unlockedSteps.Add(step.stepId);
|
||||
|
||||
if (_stepBehaviours.TryGetValue(step, out var behaviour))
|
||||
{
|
||||
@@ -452,7 +497,18 @@ namespace PuzzleS
|
||||
{
|
||||
if (_currentLevelData == null) return;
|
||||
|
||||
if (_currentLevelData.IsLevelComplete(_completedSteps))
|
||||
// Check if all steps are completed
|
||||
bool allComplete = true;
|
||||
foreach (var step in _currentLevelData.allSteps)
|
||||
{
|
||||
if (step != null && !_completedSteps.Contains(step.stepId))
|
||||
{
|
||||
allComplete = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (allComplete)
|
||||
{
|
||||
Logging.Debug("[Puzzles] All puzzles complete! Level finished.");
|
||||
|
||||
@@ -466,7 +522,7 @@ namespace PuzzleS
|
||||
/// </summary>
|
||||
public bool IsStepUnlocked(PuzzleStepSO step)
|
||||
{
|
||||
return _unlockedSteps.Contains(step);
|
||||
return step != null && _unlockedSteps.Contains(step.stepId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -476,7 +532,7 @@ namespace PuzzleS
|
||||
/// <returns>True if the step has been completed, false otherwise</returns>
|
||||
public bool IsPuzzleStepCompleted(string stepId)
|
||||
{
|
||||
return _completedSteps.Any(step => step.stepId == stepId);
|
||||
return _completedSteps.Contains(stepId); // O(1) lookup!
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -495,5 +551,89 @@ namespace PuzzleS
|
||||
{
|
||||
return _isDataLoaded;
|
||||
}
|
||||
|
||||
#region ISaveParticipant Implementation
|
||||
|
||||
/// <summary>
|
||||
/// Get unique save ID for this puzzle manager instance
|
||||
/// </summary>
|
||||
public string GetSaveId()
|
||||
{
|
||||
string sceneName = SceneManager.GetActiveScene().name;
|
||||
return $"{sceneName}/PuzzleManager";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serialize current puzzle state to JSON
|
||||
/// </summary>
|
||||
public string SerializeState()
|
||||
{
|
||||
if (_currentLevelData == null)
|
||||
{
|
||||
Logging.Warning("[PuzzleManager] Cannot serialize state - no level data loaded");
|
||||
return "{}";
|
||||
}
|
||||
|
||||
var saveData = new PuzzleSaveData
|
||||
{
|
||||
levelId = _currentLevelData.levelId,
|
||||
completedStepIds = _completedSteps.ToList(),
|
||||
unlockedStepIds = _unlockedSteps.ToList()
|
||||
};
|
||||
|
||||
string json = JsonUtility.ToJson(saveData);
|
||||
Logging.Debug($"[PuzzleManager] Serialized puzzle state: {_completedSteps.Count} completed, {_unlockedSteps.Count} unlocked");
|
||||
return json;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restore puzzle state from serialized JSON data
|
||||
/// </summary>
|
||||
public void RestoreState(string data)
|
||||
{
|
||||
if (string.IsNullOrEmpty(data) || data == "{}")
|
||||
{
|
||||
Logging.Debug("[PuzzleManager] No puzzle save data to restore");
|
||||
_isDataRestored = true;
|
||||
_hasBeenRestored = true;
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var saveData = JsonUtility.FromJson<PuzzleSaveData>(data);
|
||||
if (saveData == null)
|
||||
{
|
||||
Logging.Warning("[PuzzleManager] Failed to deserialize puzzle save data");
|
||||
_isDataRestored = true;
|
||||
_hasBeenRestored = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Restore step IDs directly - no timing dependency on level data!
|
||||
_completedSteps = new HashSet<string>(saveData.completedStepIds ?? new List<string>());
|
||||
_unlockedSteps = new HashSet<string>(saveData.unlockedStepIds ?? new List<string>());
|
||||
|
||||
_isDataRestored = true;
|
||||
_hasBeenRestored = true;
|
||||
|
||||
Logging.Debug($"[PuzzleManager] Restored puzzle state: {_completedSteps.Count} completed, {_unlockedSteps.Count} unlocked steps");
|
||||
|
||||
// Update any behaviors that registered before RestoreState was called
|
||||
foreach (var behaviour in _pendingRegistrations)
|
||||
{
|
||||
UpdateStepState(behaviour);
|
||||
}
|
||||
_pendingRegistrations.Clear();
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Debug.LogError($"[PuzzleManager] Error restoring puzzle state: {e.Message}");
|
||||
_isDataRestored = true;
|
||||
_hasBeenRestored = true;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user