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++;
}
}