Pickup.cs
using Sandbox;
using System;
using System.Linq;

namespace Facepunch.BombRoyale;

public class PickupChanceAttribute : Attribute
{
	public float Chance { get; set; }
	
	public PickupChanceAttribute( float chance )
	{
		Chance = chance;
	}
}

public abstract class Pickup : Component, IRestartable, Component.ITriggerListener
{
	public static Pickup CreateRandom( Vector3 position )
	{
		var possibleTypes = TypeLibrary.GetTypes<Pickup>()
			.Where( t => !t.IsAbstract )
			.Where( t => t.HasAttribute<PickupChanceAttribute>() )
			.Shuffle()
			.ToList();

		if ( !possibleTypes.Any() )
			return default;

		var u = possibleTypes.Sum( p => p.GetAttribute<PickupChanceAttribute>().Chance );
		var r = Game.Random.Float() * u;
		var s = 0f;

		foreach ( var type in possibleTypes )
		{
			var chance = type.GetAttribute<PickupChanceAttribute>().Chance;

			s += chance;

			if ( r < s )
			{
				return CreatePickup( type, position );
			}
		}

		return default;
	}

	private static Pickup CreatePickup( TypeDescription type, Vector3 position )
	{
		var go = BombRoyale.Instance.PickupPrefab.Clone();
		var pickup = go.Components.Create( type ) as Pickup;
		go.WorldPosition = position;
		go.Name = type.ClassName;
		go.NetworkSpawn();
		return pickup;
	}

	public virtual string PickupSound => null;
	public virtual string SpawnSound => null;
	public virtual Color Color => Color.Orange;
	public virtual string Icon => null;

	private PointLight Light { get; set; }
	private PickupSprite Sprite { get; set; }
	private GameObject IdleEffect { get; set; }

	void IRestartable.OnRestart()
	{
		GameObject.Destroy();
	}

	protected override void OnStart()
	{
		Light = Components.Get<PointLight>();
		Light.LightColor = Color;

		Sprite = Components.GetInDescendantsOrSelf<PickupSprite>();
		Sprite.Pickup = this;

		if ( !string.IsNullOrEmpty( SpawnSound ) )
			Sound.Play( SpawnSound, WorldPosition );

		IdleEffect = PickupIdleEffect.Create( GameObject, Color );


		base.OnStart();
	}

	void ITriggerListener.OnTriggerEnter( Collider collider )
	{
		if ( !Networking.IsHost ) return;

		var player = collider.Components.GetInAncestorsOrSelf<Player>();
		if ( !player.IsValid() ) return;

		if ( !OnPickup( player ) )
			return;

		DoPickupEffects();
		GameObject.Destroy();
	}

	protected virtual bool OnPickup( Player player )
	{
		return false;
	}

	protected override void OnUpdate()
	{
		base.OnUpdate();
	}

	protected override void OnDestroy()
	{
		base.OnDestroy();
	}

	[Rpc.Broadcast( NetFlags.HostOnly )]
	private void DoPickupEffects()
	{
		CollectPickupEffect.Create( Scene, WorldPosition, Color );

		if ( !string.IsNullOrEmpty( PickupSound ) )
		{
			Sound.Play( PickupSound, WorldPosition );
		}
	}

	[Rpc.Broadcast]
	private void PlaySound( string soundName, Vector3 position )
	{
		Sound.Play( soundName, position );
	}
}