ShopSystem.cs
using Sandbox;
using static Sandbox.Component;
public interface Upgrade
{
	public string Name { get; }
	public int Cost { get;}
	// makes the text vibrate in the shop
	public bool IsSpecial { get; }
	public void Action();
}

public class MaxHealthUpgrade : Upgrade
{
	private int increase = 20;

	public string Name => $"Increase max health by {increase}";
	public int Cost { get; set; } = 10;
	public bool IsSpecial => false;

	int TimesBought()
	{
		var healthComponent = Game.ActiveScene.Components.Get<Health>( FindMode.EnabledInSelfAndDescendants );

		return (healthComponent.MaxHealth - 100) / 20;
	}

	public MaxHealthUpgrade()
	{
		Cost *= TimesBought() + 1;
	}

	public void Action()
	{
		var healthComponents = Game.ActiveScene.Components.GetAll<Health>( FindMode.EnabledInSelfAndDescendants );
		foreach ( var health in healthComponents )
		{
			health.MaxHealth += 20;
			var damageable = health.GetComponent<IDamageable>();
			damageable.OnDamage( new DamageInfo()
			{
				Damage = -20
			} );
		}
	}
}

public class WeaponDamageUpgrade : Upgrade
{
	public string Name => "Increase weapon damage";
	public int Cost { get; set; } = 15;
	public bool IsSpecial => false;

	int TimesBought()
	{
		var mp5Component = Game.ActiveScene.GetComponentInChildren<MP5>();

		return (mp5Component.Damage - 6) / 3;
	}

	public WeaponDamageUpgrade()
	{
		Cost *= TimesBought() + 1;
	}

	public void Action()
	{
		var mp5 = Game.ActiveScene.GetComponentInChildren<MP5>().Damage += 3;
	}
}

public class ReplicateUpgrade : Upgrade
{
	public string Name => "Replicate";
	public int Cost { get; set; } = 20;
	public bool IsSpecial => true;

	public void Action()
	{
		Game.ActiveScene.GetComponentInChildren<PlayerReplicator>().ReplicatePlayers();
	}
}

public class MinimapUpgrade : Upgrade
{
	public string Name => "Minimap";
	public int Cost => 10;
	public bool IsSpecial => false;

	public void Action()
	{
		Game.ActiveScene.GetComponentInChildren<Minimap>(true).GameObject.Enabled = true;
	}
}

public class UglifierUpgrade : Upgrade
{
	public string Name => "A simpler world...";
	public int Cost => 5;
	public bool IsSpecial => false;

	public void Action()
	{
		Game.ActiveScene.Children.Find( go => go.Name == "Plane" ).Enabled = true;
		Game.ActiveScene.Children.Find( go => go.Name == "Trees" ).Enabled = false;
		Game.ActiveScene.Children.Find( go => go.Name == "Terrain" ).Enabled = false;
		Game.ActiveScene.Children.Find( go => go.Name == "Random debris" ).Enabled = false;
	}
}

public sealed class ShopSystem : Component, IGoodieEvents, IEnemySpawnerEvents
{
	[Property]
	public int Currency { get; set; }

	[Property]
	public GameObject Interface { get; set; }

	protected override void OnEnabled()
	{
		base.OnEnabled();

		var shopInterface = Interface.GetComponent<ShopInterface>(true);
		shopInterface.Upgrades = [
			new MinimapUpgrade(),
			new MaxHealthUpgrade(),
			new WeaponDamageUpgrade(),
			new UglifierUpgrade(),
		];
	}

	void IGoodieEvents.OnGoodiePickup( int value )
	{
		Currency += value;
	}

	public void OpenShop()
	{
		Interface.Enabled = true;
	}

	void IEnemySpawnerEvents.FinishedWave( int count )
	{
		var shopInterface = Interface.GetComponent<ShopInterface>( true );

		if (count == 2)
		{
			shopInterface.Upgrades.Add( new ReplicateUpgrade() );
		}
		if ( count == 8 )
		{
			var u = new ReplicateUpgrade();
			u.Cost = 50;
			shopInterface.Upgrades.Add(u);
		}
		if (!shopInterface.Upgrades.Any(u => u is WeaponDamageUpgrade) && Scene.Get<MP5>().Damage < 25)
		{
			shopInterface.Upgrades.Add( new WeaponDamageUpgrade() );
		}
		if ( !shopInterface.Upgrades.Any( u => u is MaxHealthUpgrade ) )
		{
			shopInterface.Upgrades.Add( new MaxHealthUpgrade() );
		}
	}
}