Game gameplay state helper that tracks "Karma" (Grandma's respect meter). Stores a float value with max, exposes read-only formatted texts and label, provides Reset/Set/Add, periodic ticks that modify karma based on tank/battle/progression state, and save/load serialization helpers.
namespace NoChillquarium;
/// <summary>
/// Grandma's respect meter. Opposite pressure to <see cref="Chaos"/>.
/// She loves abominations, championships, and quality-control booms.
/// She cools off if you chicken out of fights, and blames you when fish die.
/// </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}";
/// <summary>Grandma's current vibe — player-facing label.</summary>
public static string Label =>
_value >= 70f ? "PROUD" :
_value >= 50f ? "FOND" :
_value >= 30f ? "COOLING" :
"DISAPPOINTED";
/// <summary>HUD / credits kicker (was "Karma").</summary>
public static string MeterName => "Grandma";
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 fondness while the tank is thriving — she likes a full, ugly empire.
/// </summary>
public static void TickCalmCare( float dt )
{
if ( !TankSim.IsActive || dt <= 0f )
return;
if ( !EndingSystem.ThrivingTank )
return;
if ( Chaos.Value > 40f )
return;
// Prefer abom tanks: monsters get a little more love.
var rate = TankSim.HasAnyMonster ? 0.035f : 0.02f;
Add( dt * rate );
}
/// <summary>
/// Chicken out of the fight ladder too long and Grandma stops respecting you.
/// Only after Fight is actually on the menu (trained + can pay).
/// </summary>
public static void TickWuss( float dt )
{
if ( !TankSim.IsActive || dt <= 0f )
return;
// Don't punish people still learning gym / still broke.
if ( !Progression.FightUnlocked )
return;
// Already fighting = not a wuss.
if ( BattleSystem.Wins + BattleSystem.Losses >= 2 )
return;
if ( BattleSystem.ChampionshipTitles > 0 )
return;
// ~-1 respect per ~60s of hiding once Fight is available.
Add( -dt * 0.017f );
}
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++;
}
}