Skip to content

Save/Load System

Persistence layer for game progress, profiles, and settings.

Key Classes

SaveLoadManager

Location: Assets/_Game/Scripts/Managers/SaveLoadManager.cs

Service: [Service(LoadScene = "BootstrapScene")] - Persistent across scenes

Core operations:

MethodPurpose
SaveGame()Persist current state to disk
HasSaveData()Check if save exists
GetSaveData()Retrieve current save data
DeleteSaveData()Clear save data

ProfileManager

Location: Assets/_Game/Scripts/Managers/ProfileManager.cs

Service: [Service(LoadScene = "BootstrapScene")] - Persistent

Multi-profile system:

public class ProfileManager : MonoBehaviour
{
public int CurrentActiveProfileIndex;
public RunDifficulty GameDifficulty;
public void CreateProfile(string name, RunDifficulty difficulty);
public void SelectProfile(int index);
public void DeleteProfile(int index);
}

GameSaveData

Location: Assets/_Game/Scripts/Data/GameSaveData.cs

Master save container:

[Serializable]
public class GameSaveData
{
// Progress
public WorkDayProgress workDayProgress;
public GameRunState currentGameState;
public ShopPrepareSubstate currentSubstate;
// Player data
public CharacterCustomizationData characterCustomization;
public List<string> helperCatIds;
public GamePlaythroughData playthroughData;
// Session data
public GameSessionMetadata lastSessionMetadata;
public GameLevelProgression levelProgression;
// Upgrades and shop
public List<CakeUpgradeSaveEntry> cakeUpgrades;
public List<string> offeredHelperCatIds;
public List<string> offeredCakeUpgradeIds;
// Flags and history
public PlayerGameFlags playerGameFlags;
public List<StarCompletionRecord> starCompletionHistory;
// Meta
public float totalPlaytimeSeconds;
public string saveVersion;
}

GameProfileData

Profile with auto-backup saves:

[Serializable]
public class GameProfileData
{
public string profileName;
public GameSaveData currentSave;
public List<GameSaveData> autoBackups;
public DateTime lastPlayedTime;
}

Save Triggers

Saves happen automatically at:

TriggerLocation
Entering OverworldSceneOverworldSceneInitializer
Entering Shop stateGameRunOrchestrator
After level completionGameRunManager
Application pause/quitOverworldSceneInitializer / GameRunSceneInitializer

Never during active gameplay - prevents save scumming.

Serialization

Uses Unity’s JsonUtility:

string json = JsonUtility.ToJson(saveData, prettyPrint: true);
File.WriteAllText(savePath, json);

Save location: Application.persistentDataPath

Restoration Flow

Mid-Run Save (shop/gameplay/lost)

1. ProfileManager loads save, determines currentGameState
2. Creates GameRunStartConfig (mode=RestoreSave, saveData=...)
3. SceneTransitionCoordinator.TransitionToGameRun(config)
4. GameRunSceneInitializer.RestoreSave():
- Restore helper cats (via PersistentHelperCatManager)
- Restore star completion history
- Restore deck configuration
- Restore game flags
5. GameRunOrchestrator.RestoreSavedRunAfterSceneLoad()
6. Transition to saved state

Overworld Save

1. ProfileManager loads save
2. SceneTransitionCoordinator.TransitionToOverworld()
3. OverworldSceneInitializer.RestorePersistentData():
- Restore helper cats
- Restore star completion history
- Restore game flags
4. InitializeOverworld() activates hub world

Validation

private bool ValidateSaveData(GameSaveData saveData)
{
if (saveData == null) return false;
if (saveData.workDayProgress == null) return false;
// Check restaurant still exists
var restaurant = ProgressionDataManager.Instance
.GetRestaurant(saveData.workDayProgress.CurrentRestaurantId);
return restaurant != null;
}

Playtime Tracking

Both GameRunSceneInitializer and OverworldSceneInitializer track session duration:

public void UpdatePlaytime()
{
float sessionDuration = Time.time - sessionStartTime;
saveData.totalPlaytimeSeconds += sessionDuration;
}

Called on:

  • Application pause
  • Application quit
  • Before saves

Error Handling

  • Invalid saves log warnings but don’t crash
  • Missing data uses defaults
  • Corrupted saves can be deleted via profile manager