Static game state component that tracks a global "chaos" meter influencing mood. It stores value, version, exposes read-only properties, allows resetting, setting, adding with clamps, decays over time in TickCalm, and reads/writes to SaveData while notifying GameAudio on mood changes.
namespace NoChillquarium;
/// <summary>
/// Chaos meter — one input into No-Chill mood.
/// High chaos (≥40) forces metal beds; calm chaos alone keeps chill
/// unless other absurd systems (sharks, meth, M80, death…) take over via <see cref="GameAudio"/>.
/// </summary>
public static class Chaos
{
public const float NoChillThreshold = 40f;
public const float Max = 100f;
static float _value;
static int _version;
public static float Value => _value;
public static int Version => _version;
public static bool IsNoChill => _value >= NoChillThreshold;
public static string MoodLabel => IsNoChill ? "NO-CHILL" : "CHILL";
public static string ValueText => $"{_value:0}";
public static void Reset()
{
_value = 0f;
_version++;
}
public static void Set( float value )
{
_value = Math.Clamp( value, 0f, Max );
_version++;
GameAudio.OnMoodMaybeChanged();
}
public static void Add( float amount )
{
if ( amount == 0f )
return;
var before = IsNoChill;
_value = Math.Clamp( _value + amount, 0f, Max );
_version++;
if ( before != IsNoChill )
GameAudio.OnMoodMaybeChanged( force: true );
}
/// <summary>Good-boy decay while not exploding (slow).</summary>
public static void TickCalm( float dt )
{
if ( _value <= 0f || dt <= 0f )
return;
// Decay without spamming version/music every frame.
var before = IsNoChill;
var next = Math.Clamp( _value - 1.5f * dt, 0f, Max );
if ( MathF.Abs( next - _value ) < 0.0001f )
return;
_value = next;
// HUD refresh ~4 Hz
if ( (int)(_value * 4f) != (int)((_value + 1.5f * dt) * 4f) )
_version++;
if ( before != IsNoChill )
{
_version++;
GameAudio.OnMoodMaybeChanged( force: true );
}
}
public static void WriteToSave( SaveData data )
{
if ( data is not null )
data.Chaos = _value;
}
public static void LoadFromSave( SaveData data )
{
if ( data is null )
{
Reset();
return;
}
Set( data.Chaos );
}
}