LavaBlob.cs
using Sandbox.UI;
using SpriteTools;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using static Manager;

public class LavaBlob : Component
{
	[Property] public SpriteRendererLayer Sprite { get; set; }

	public float ShadowOpacity { get; set; }
	public float ShadowScale { get; set; }
	public SpriteRendererLayer ShadowSprite { get; set; }

	private Vector2 _startPos;
	public Vector2 EndPos { get; private set; }
	private float _peakHeight;
	private float _lifetime;

	//private Vector2 _lastSpritePos;

	public TimeSince TimeSinceSpawn { get; set; }

	public Vector2 Position2D
	{
		get { return (Vector2)WorldPosition; }
		set { WorldPosition = new Vector3( value.x, value.y, WorldPosition.z ); }
	}

	public void Init(Vector2 startPos, Vector2 endPos)
	{
		_startPos = startPos;
		EndPos = endPos;
		_peakHeight = Game.Random.Float( 2f, 2.5f );
		_lifetime = Game.Random.Float( 1.5f, 2.5f );
		TimeSinceSpawn = 0f;

		var shadowObj = Manager.Instance.ShadowPrefab.Clone( WorldPosition );
		shadowObj.SetParent( GameObject );
		shadowObj.LocalPosition = new Vector3( 0f, 0f, Globals.SHADOW_DEPTH_OFFSET );
		shadowObj.LocalRotation = new Angles( 0f, -90f, 0f );
		shadowObj.NetworkMode = NetworkMode.Never;

		float size = 5f;
		ShadowSprite = shadowObj.Components.Get<SpriteRendererLayer>();
		ShadowSprite.LocalScale = new Vector3( size * Globals.SPRITE_SCALE, size * Globals.SPRITE_SCALE, 1f );
		float shadowOpacity = 0.2f;
		ShadowSprite.Tint = Color.Black.WithAlpha( shadowOpacity );

		Sprite.LocalScale = new Vector3( 0.5f, 0.5f, 1f ) * Globals.SPRITE_SCALE;

		if ( EndPos.x < _startPos.x )
			Sprite.SpriteFlags = SpriteFlags.HorizontalFlip;

		var warning = Manager.Instance.SpawnWarning( endPos, Color.Red, Color.Yellow );
		warning.Lifetime = _lifetime + 0.2f;
	}

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

		//Gizmo.Draw.Color = Color.Black.WithAlpha(0.2f);
		//Gizmo.Draw.Text( $"{TimeSinceSpawn}", new global::Transform( (Vector3)Position2D + new Vector3( 0f, -0.2f, 0f ) ) );

		//Gizmo.Draw.Color = Color.White.WithAlpha( 0.01f );
		//Gizmo.Draw.LineSphere( (Vector3)Position2D, 0.5f );

		//Gizmo.Draw.Color = Color.Blue.WithAlpha( 0.2f );
		//Gizmo.Draw.Line( _startPos, _endPos );

		float progress = Utils.Map( TimeSinceSpawn, 0f, _lifetime, 0f, 1f );
		var currPos = Vector2.Lerp( _startPos, EndPos, progress );

		WorldPosition = new Vector3( currPos.x, currPos.y, Globals.GetZPos( currPos.y ) );

		float stretch = Utils.MapReturn( progress, 0f, 1f, 0.2f, 0f, EasingType.QuadOut );
		Sprite.LocalScale = new Vector3( 0.5f + stretch, 0.5f - stretch, 1f ) * Globals.SPRITE_SCALE;

		//_lastSpritePos = Sprite.WorldPosition;
		Sprite.LocalPosition = new Vector3(0f, Utils.MapReturn( progress, 0f, 1f, 0.1f, _peakHeight, EasingType.SineOut ), 0f );

		//Sprite.LocalRotation = new Angles( 0f, Utils.VectorToDegrees( ((Vector2)Sprite.WorldPosition - _lastSpritePos).Normal ), 0f );

		Sprite.Tint = Color.Lerp( new Color( 1f, 0.4f, 0f ), new Color( 1f, 0f, 0f ), 0.5f + Utils.FastSin( TimeSinceSpawn * 16f ) * 0.5f );

		float shadowSize = Utils.MapReturn( progress, 0f, 1f, 1.2f, 0.6f );
		ShadowSprite.LocalScale = new Vector3( shadowSize * Globals.SPRITE_SCALE, shadowSize * Globals.SPRITE_SCALE, 1f );
		ShadowSprite.Tint = Color.Black.WithAlpha( Utils.MapReturn( progress, 0f, 1f, 1f, 0.3f ) );

		if( TimeSinceSpawn > _lifetime )
		{
			Manager.Instance.PlaySfxNearby( "lava_puddle_02", EndPos, pitch: Game.Random.Float( 0.9f, 1.1f ), volume: 1.4f, maxDist: 6f );

			if(Scene.GetAll<LavaPuddle>().Count() == 0)
				Manager.Instance.PlaySfxNearby( "lava_puddle_01", EndPos, pitch: Game.Random.Float( 0.9f, 1.1f ), volume: 1.4f, maxDist: 6f );

			Manager.Instance.SpawnLavaPuddle( EndPos, lifetime: Game.Random.Float( 8f, 11f ), new Color( 1f, 0.2f, 0f ), new Color( 1f, 0.1f, 0f ), 
				damage: 2f + Math.Min(Manager.Instance.Difficulty, 5),
				radius: Game.Random.Float( 1.75f, 2.5f ) );
			Manager.Instance.RemoveLavaBlob( this );
			GameObject.Destroy();
		}
	}
}