Karma.cs

Global static Karma tracker for the game. Stores a float karma value with max 100, exposes read-only properties for value, version, textual label and value text, supports reset/set/add, a calm-care drift tick, and save/load serialization handling old saves.

namespace NoChillquarium;

/// <summary>
/// Good-boy meter. Opposite pressure to <see cref="Chaos"/>.
/// Endings and UI use both axes.
/// </summary>
public static class Karma
{
	public const float Max = 100f;

	static float _value = 40f; // slightly neutral-good start
	static int _version;

	public static float Value => _value;
	public static int Version => _version;
	public static string ValueText => $"{_value:0}";
	public static string Label =>
		_value >= 70f ? "SAINT-ISH" :
		_value >= 50f ? "DECENT" :
		_value >= 30f ? "SKETCHY" :
		"MENACE";

	public static void Reset()
	{
		_value = 40f;
		_version++;
	}

	public static void Set( float value )
	{
		_value = Math.Clamp( value, 0f, Max );
		_version++;
	}

	public static void Add( float amount )
	{
		if ( MathF.Abs( amount ) < 0.0001f )
			return;
		_value = Math.Clamp( _value + amount, 0f, Max );
		_version++;
	}

	/// <summary>
	/// Slow good-boy drift while the tank is genuinely calm and thriving.
	/// </summary>
	public static void TickCalmCare( float dt )
	{
		if ( !TankSim.IsActive || dt <= 0f )
			return;
		if ( !EndingSystem.ThrivingTank )
			return;
		if ( Chaos.Value > 25f || GameAudio.WantsNoChillMusic )
			return;

		// ~+1 karma per 40s of peaceful care
		Add( dt * 0.025f );
	}

	public static void WriteToSave( SaveData data )
	{
		if ( data is not null )
			data.Karma = _value;
	}

	public static void LoadFromSave( SaveData data )
	{
		if ( data is null )
		{
			Reset();
			return;
		}

		// Old saves without karma field deserialize as 0 — treat as fresh mid.
		if ( data.Karma <= 0.01f && data.PlaytimeSeconds < 5f )
			_value = 40f;
		else if ( data.Version < 5 && data.Karma <= 0.01f )
			_value = 40f;
		else
			_value = Math.Clamp( data.Karma, 0f, Max );
		_version++;
	}
}