UI/Num.cs
namespace Monolith;
/// <summary>
/// Short number formatting for an incremental game. Values here run from 1 to well past a
/// quintillion, so everything user-facing goes through this.
/// </summary>
public static class Num
{
private static readonly string[] Suffixes =
{
"", "K", "M", "B", "T", "Qa", "Qi", "Sx", "Sp", "Oc", "No", "Dc",
};
public static string Short( double value )
{
if ( double.IsNaN( value ) || double.IsInfinity( value ) )
return "0";
bool negative = value < 0;
value = Math.Abs( value );
if ( value < 1000 )
return (negative ? "-" : "") + ((long)value).ToString( "N0" );
int tier = 0;
while ( value >= 1000 && tier < Suffixes.Length - 1 )
{
value /= 1000;
tier++;
}
var text = value < 10 ? value.ToString( "0.00" )
: value < 100 ? value.ToString( "0.0" )
: value.ToString( "0" );
return (negative ? "-" : "") + text + Suffixes[tier];
}
public static string Short( long value ) => Short( (double)value );
public static string Percent( float fraction, int decimals = 1 )
=> (fraction * 100f).ToString( "F" + decimals ) + "%";
/// <summary>Seconds as a speedrun time: 1:23.4, or 1h02:33 once it runs long.</summary>
public static string Duration( double seconds )
{
if ( seconds <= 0 || double.IsNaN( seconds ) )
return "-";
int total = (int)seconds;
int hours = total / 3600;
int minutes = (total % 3600) / 60;
int secs = total % 60;
if ( hours > 0 )
return $"{hours}h{minutes:00}:{secs:00}";
double frac = seconds - total;
return $"{minutes}:{secs:00}.{(int)(frac * 10)}";
}
}