StatusManager.cs
using Sandbox;
using System.ComponentModel;

public struct StatusData
{
	public int maxLevel;
	public int reqLevel;
	public float weight;
	public List<string> reqStatuses;

	public StatusData( int _maxLevel, int _reqLevel, float _weight, List<string> _reqStatuses = null )
	{
		maxLevel = _maxLevel;
		reqLevel = _reqLevel;
		weight = _weight;
		reqStatuses = _reqStatuses;
	}
}

public class StatusAttribute : Attribute
{
	public int MaxLevel { get; }
	public int ReqLevel { get; }
	public float Weight { get; }
	public int MaxDifficulty { get; }
	public Type[] ReqStatuses { get; }
	public bool IsCurse { get; }

	public StatusAttribute( int maxLevel, int reqLevel, float weight, int maxDifficulty, bool isCurse, params Type[] reqStatuses )
	{
		MaxLevel = maxLevel;
		ReqLevel = reqLevel;
		Weight = weight;
		MaxDifficulty = maxDifficulty;
		ReqStatuses = reqStatuses;
		IsCurse = isCurse;
	}
}

public class StatusManager
{
	public static List<TypeDescription> GetRandomStatuses( Player player, int numStatuses, bool curses = false )
	{
		List<(TypeDescription Type, float Weight)> valid = new List<(TypeDescription, float)>();

		var specialistAmount = player.Stats[PlayerStat.SpecialistStatusAmount];
		var difficulty = Manager.Instance.Difficulty;

		foreach ( var type in TypeLibrary.GetTypes<Status>() )
		{
			//Log.Info("--------------- " + type.Name);

			var attrib = type.GetAttribute<StatusAttribute>();
			if ( attrib == null )
			{
				//Log.Info("not valid - no status attribute!");
				continue;
			}

			if ( player.Level < attrib.ReqLevel )
			{
				//Log.Info("not valid - ReqLevel is " + attrib.ReqLevel + " and player level is " + player.Level);
				continue;
			}

			var existingLevel = player.GetStatusLevel( type );
			if ( existingLevel >= attrib.MaxLevel )
			{
				//Log.Info("not valid - MaxLevel is " + attrib.MaxLevel + " and player status level is " + player.GetStatusLevel(type));
				continue;
			}

			if ( attrib.ReqStatuses.Length > 0 && attrib.ReqStatuses.All( x => !player.HasStatus( TypeLibrary.GetType( x ) ) ) )
			{
				//Log.Info("not valid - none of the required statuses owned");
				continue;
			}

			if ( attrib.MaxDifficulty > 0 && difficulty > attrib.MaxDifficulty )
				continue;

			if ( Manager.Instance.HasSpawnedBoss && type == TypeLibrary.GetType( typeof( EarlyBossStatus ) ) )
				continue;

			if ( (!curses && attrib.IsCurse) || (curses && !attrib.IsCurse) )
				continue;

			if ( curses && type == TypeLibrary.GetType( typeof( CurseNumDashesStatus ) ) && (int)player.Stats[PlayerStat.NumDashes] < 2 )
				continue;

			if ( player.Stats[PlayerStat.NoDashInvuln] > 0f && (type == TypeLibrary.GetType( typeof( NoDashInvulnDamageStatus ) ) || type == TypeLibrary.GetType( typeof( CurseNoDashInvulnDamageStatus ) )) )
				continue;

			//int currLevel = player.GetStatusLevel(type);

			var weight = attrib.Weight;

			if ( existingLevel > 0 )
				weight += specialistAmount;

			//Log.Info( $"valid: adding {type} with weight of " + weight );
			valid.Add( (type, weight) );
		}

		List<TypeDescription> output = new List<TypeDescription>();

		// todo: handle if valid has < elements than numStatuses
		if ( valid.Count < numStatuses && !curses )
			return output;

		while ( output.Count < numStatuses )
		{
			float totalWeight = valid.Sum( x => x.Weight );
			var rand = Game.Random.Float( 0f, totalWeight );
			//Log.Info("--- output.Count: " + output.Count + " totalWeight: " + totalWeight +" rand: " + rand);

			for ( int i = valid.Count - 1; i >= 0; i-- )
			{
				var (type, weight) = valid[i];
				rand -= weight;

				//Log.Info("i: " + i + " type: " + type.Name + " weight: " + weight + " rand is now " + rand);

				if ( rand < 0f )
				{
					output.Add( type );

					//if( !curses )
					valid.Remove( (type, weight) );

					break;
				}
			}
		}

		return output;
	}

	public static Status CreateStatus( TypeDescription type )
	{
		var status = type.Create<Status>();

		var attrib = type.GetAttribute<StatusAttribute>();
		if ( attrib != null )
			status.MaxLevel = attrib.MaxLevel;
		else
			status.MaxLevel = 1;

		return status;
	}

	//public static Type GetStatusType(string statusName)
	//{
	//    return TypeLibrary.GetDescription(statusName).TargetType;
	//}

	public static int TypeToIdentity( TypeDescription type )
	{
		return type.Identity;
	}

	public static TypeDescription IdentityToType( int typeIdentity )
	{
		return TypeLibrary.GetTypeByIdent( typeIdentity );
	}
}