SaveGame.cs

Static helper for saving and loading game state to FileSystem.Data. It serializes various systems into a SaveData instance on save, writes JSON, and reads/validates the save then applies it to subsystems on load.

File Access
namespace NoChillquarium;

/// <summary>
/// Persist wallet + tank fish to FileSystem.Data.
/// </summary>
public static class SaveGame
{
	public static bool HasSave
	{
		get
		{
			try
			{
				return FileSystem.Data.FileExists( SaveData.FileName );
			}
			catch
			{
				return false;
			}
		}
	}

	/// <param name="quiet">When true (autosave), skip console chatter.</param>
	public static bool TrySave( bool quiet = false )
	{
		if ( !TankSim.IsActive )
			return false;

		try
		{
			var data = new SaveData
			{
				Version = SaveData.CurrentVersion
			};

			Economy.WriteToSave( data );
			Chaos.WriteToSave( data );
			Karma.WriteToSave( data );
			Shop.WriteInventory( data );
			Achievements.WriteToSave( data );
			FeedSystem.WriteToSave( data );
			TrainingSystem.WriteToSave( data );
			MethRush.WriteToSave( data );
			BattleSystem.WriteToSave( data );
			SharkSystem.WriteToSave( data );
			EndingSystem.WriteToSave( data );
			TankSim.WriteToSave( data );
			FileSystem.Data.WriteJson( SaveData.FileName, data );
			if ( !quiet )
				Log.Info( $"[NO-CHILLquarium] Saved. Ð{Economy.BalanceText} · {TankSim.FishCount} fish." );
			return true;
		}
		catch ( Exception e )
		{
			Log.Warning( $"[NO-CHILLquarium] Save failed: {e.Message}" );
			return false;
		}
	}

	public static bool TryLoad( out SaveData data )
	{
		data = null;

		try
		{
			if ( !FileSystem.Data.FileExists( SaveData.FileName ) )
				return false;

			data = FileSystem.Data.ReadJsonOrDefault<SaveData>( SaveData.FileName, null );
			if ( data is null )
				return false;

			var hasFish =
				(data.FreshFish is { Count: > 0 })
				|| (data.SaltFish is { Count: > 0 })
				|| (data.Fish is { Count: > 0 });

			// Allow empty-but-valid dual-tank unlock saves.
			if ( !hasFish && !data.SaltUnlocked && data.Dogecoin <= 0 )
				return false;

			return true;
		}
		catch ( Exception e )
		{
			Log.Warning( $"[NO-CHILLquarium] Load failed: {e.Message}" );
			data = null;
			return false;
		}
	}

	public static bool ApplyLoadedSave()
	{
		if ( !TryLoad( out var data ) )
			return false;

		Economy.LoadFromSave( data );
		Chaos.LoadFromSave( data );
		Karma.LoadFromSave( data );
		Shop.LoadInventory( data.MethBags, data.M80Bags );
		Achievements.Load( data.Achievements, data.LifetimeExplodes, data.TimesFed, data.TimesMeth, data.DecorPlaced );
		FeedSystem.Load( data.UnlockedFoods, data.SelectedFoodId );
		TrainingSystem.Load( data.Syringes );
		MethRush.Load( data.TankCracks );
		BattleSystem.Load( data.BattleWins, data.BattleLosses, data.BattlesBeaten, data.PrizeHabitatsWon );
		SharkSystem.Load( data.SharkKills, data.SharkEaten, data.SharkEverSpawned );
		EndingSystem.Load( data.HasWmd, data.HasWhale, data.EndingReached );
		TankSim.LoadFromSave( data );
		return TankSim.IsActive;
	}

	/// <summary>Menu-only peek so Achievements list reflects the save without loading the tank.</summary>
	public static void PrefetchAchievementsForMenu()
	{
		if ( !TryLoad( out var data ) )
			return;
		Achievements.Load( data.Achievements, data.LifetimeExplodes, data.TimesFed, data.TimesMeth, data.DecorPlaced );
	}
}