Files
sbox-public/engine/Sandbox.System/Math/RangedFloat.cs
s&box team 71f266059a Open source release
This commit imports the C# engine code and game files, excluding C++ source code.

[Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
2025-11-24 09:05:18 +00:00

198 lines
4.9 KiB
C#

using System.ComponentModel;
using Sandbox;
using System.Runtime.InteropServices;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
/// <summary>
/// A float between two values, which can be randomized or fixed.
/// </summary>
[JsonConverter( typeof( Sandbox.Internal.JsonConvert.RangedFloatConverter ) )]
[StructLayout( LayoutKind.Sequential )]
public partial struct RangedFloat
{
[Obsolete( $"Use {nameof( RangedFloat )}.{nameof( Min )}" )]
[EditorBrowsable( EditorBrowsableState.Never )]
public float x;
[Obsolete( $"Use {nameof( RangedFloat )}.{nameof( Max )}" )]
[EditorBrowsable( EditorBrowsableState.Never )]
public float y;
/// <summary>
/// The minimum value of the float range.
/// </summary>
public float Min
{
#pragma warning disable CS0618 // Type or member is obsolete
get => x;
set => x = value;
#pragma warning restore CS0618 // Type or member is obsolete
}
/// <summary>
/// The maximum value of the float range. For <see cref="RangeType.Fixed"/>,
/// this will be the same as <see cref="Min"/>.
/// </summary>
public float Max
{
#pragma warning disable CS0618 // Type or member is obsolete
get => Range == RangeType.Between ? y : x;
set
{
y = value;
Range = RangeType.Between;
}
#pragma warning restore CS0618 // Type or member is obsolete
}
/// <summary>
/// The fixed value. Setting this will convert us to a fixed value
/// </summary>
public float FixedValue
{
#pragma warning disable CS0618 // Type or member is obsolete
get => Min;
set
{
x = y = value;
Range = RangeType.Fixed;
}
#pragma warning restore CS0618 // Type or member is obsolete
}
/// <summary>
/// The range value. Setting this will convert us to a range value
/// </summary>
public Vector2 RangeValue
{
get => new Vector2( Min, Max );
set
{
Min = value.x;
Max = value.y;
}
}
/// <summary>
/// Range type of <see cref="RangedFloat"/>.
/// </summary>
public enum RangeType
{
/// <summary>
/// Single value, both minimum and maximum value.
/// </summary>
[Icon( "fiber_manual_record" )]
Fixed,
/// <summary>
/// Random value between given minimum and maximum.
/// </summary>
[Icon( "join_full" )]
Between
}
/// <summary>
/// Range type of this float.
/// </summary>
public RangeType Range { get; set; }
/// <summary>
/// Initialize the float as a fixed value.
/// </summary>
public RangedFloat( float fixedValue )
{
FixedValue = fixedValue;
Range = RangeType.Fixed;
}
/// <summary>
/// Initialize the float as a random value between given min and max.
/// </summary>
/// <param name="min">The minimum possible value for this float.</param>
/// <param name="max">The maximum possible value for this float.</param>
public RangedFloat( float min, float max )
{
Min = min;
Max = max;
Range = RangeType.Between;
}
/// <summary>
/// Returns the final value of this ranged float, randomizing between min and max values.
/// </summary>
public float GetValue()
{
return Range == RangeType.Between ? SandboxSystem.Random.Float( Min, Max ) : Min;
}
[GeneratedRegex( """^[\[\]\s"]*(?<min>-?\d+(?:\.\d+)?)(?:[\s,;]+(?<max>-?\d+(?:\.\d+)?))?(?:[\s,;]+(?<format>\d+))?[\[\]\s"]*$""" )]
private static partial Regex Pattern();
private static float? ParseOptionalFloat( Group group )
{
if ( !group.Success ) return default;
return float.TryParse( group.Value, out var value ) ? value : 0f;
}
private static int? ParseOptionalInt( Group group )
{
if ( !group.Success ) return null;
return int.TryParse( group.Value, out var value ) ? value : 0;
}
/// <summary>
/// Parse a ranged float from a string. Format is <c>"min[ max]"</c>.
/// </summary>
public static RangedFloat Parse( string str )
{
var match = Pattern().Match( str );
if ( !match.Success )
{
return default;
}
var min = ParseOptionalFloat( match.Groups["min"] );
var max = ParseOptionalFloat( match.Groups["max"] );
// Support legacy format
if ( ParseOptionalInt( match.Groups["format"] ) is { } format )
{
return (RangeType)format switch
{
RangeType.Fixed => new RangedFloat( min ?? 0 ),
RangeType.Between => new RangedFloat( min ?? 0, max ?? min ?? 0 ),
_ => default
};
}
return max is not null ? new RangedFloat( min ?? 0f, max.Value ) : new RangedFloat( min ?? 0f );
}
/// <summary>
/// Returns a string representation of this range, that can be passed to <see cref="Parse"/> to re-create this range.
/// Format is <c>"min[ max]"</c>.
/// </summary>
public override string ToString()
{
return Range switch
{
RangeType.Fixed => Min.ToString( "R" ),
RangeType.Between => $"{Min:R} {Max:R}",
_ => "0"
};
}
public static implicit operator RangedFloat( float input ) => new( input );
public static implicit operator RangedFloat( (float Min, float Max) range ) => new( range.Min, range.Max );
public void Deconstruct( out float min, out float max )
{
min = Min;
max = Max;
}
}