SatelliteLaser.cs
using Sandbox.UI;
using SpriteTools;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Reflection.Emit;
using System.Text;
using System.Threading.Tasks;
using static Manager;

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

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

	public float Damage { get; set; }

	//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()
	{
		TimeSinceSpawn = 0f;
		_lifetime = 1.5f;

		//LocalRotation = new Angles( 0f, 0f, 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 ( Game.Random.Float(0f, 1f) < 0.5f )
			Sprite.SpriteFlags = SpriteFlags.HorizontalFlip;
	}

	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 );

		WorldPosition = new Vector3( WorldPosition.x, WorldPosition.y, Globals.GetZPos( WorldPosition.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.Map( progress, 0f, 1f, 150f, 0f, 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.Map( progress, 0f, 1f, 0f, 1.2f );
		ShadowSprite.LocalScale = new Vector3( shadowSize * Globals.SPRITE_SCALE, shadowSize * Globals.SPRITE_SCALE, 1f );
		ShadowSprite.Tint = Color.Black.WithAlpha( Utils.Map( progress, 0f, 1f, 0.2f, 1f ) );

		if( TimeSinceSpawn > _lifetime )
		{
			Explode();

			//Manager.Instance.PlaySfxNearby( "lava_puddle_01", EndPos, pitch: Game.Random.Float( 0.9f, 1.1f ), volume: 1.4f, maxDist: 6f );
			Manager.Instance.RemoveSatelliteLaser( this );
			GameObject.Destroy();
		}
	}

	void Explode()
	{
		List<Thing> nearbyThings = new List<Thing>();

		var gridPos = Manager.Instance.GetGridSquareForPos( Position2D );

		for ( int dx = -2; dx <= 2; dx++ )
			for ( int dy = -2; dy <= 2; dy++ )
				Manager.Instance.AddThingsInGridSquare( new Manager.GridSquare( gridPos.x + dx, gridPos.y + dy ), nearbyThings );

		var player = Manager.Instance.Player;
		float radius = 0.425f * player.Stats[PlayerStat.RadiusMultiplier];
		float damage = Damage * player.GetDamageMultiplier();

		//Manager.Instance.PlaySfxNearby( "shoot", Player.Position2D, pitch: Utils.Map( xp, 1, 16, 0.85f, 1.4f ), volume: Utils.Map( xp, 1, 16, 0.6f, 1f ), maxDist: 3f );

		foreach ( Thing thing in nearbyThings )
		{
			if ( thing is Enemy enemy && !enemy.IsDying && (!enemy.IsSpawning || enemy.TimeSinceSpawn > 0.75f) && !enemy.IgnoreCollision && !enemy.IsCharmed )
			{
				var dist_sqr = (thing.Position2D - Position2D).LengthSquared;
				if ( dist_sqr < MathF.Pow( radius, 2f ) )
				{
					var addVel = (thing.Position2D - Position2D).Normal * 0.5f;
					var addTempWeight = 0f;
					enemy.Damage( damage, player, addVel, addTempWeight, false, DamageType.Ranged );
				}
			}
			else if ( thing == player )
			{
				if ( !player.IsDead && !player.IsInvulnerable && !player.IsTimePausedForChoosing )
				{
					var dist_sqr = (thing.Position2D - Position2D).LengthSquared;
					if ( dist_sqr < MathF.Pow( radius, 2f ) * 0.94f )
					{
						var dmg = player.CheckDamageAmount( damage, DamageType.Ranged );
						player.Damage( Math.Max(dmg * 0.1f, 1f), PlayerDamageType.Self );
						player.AddVelocity( (thing.Position2D - Position2D).Normal * 0.5f );
					}
				}
			}
		}

		var explosion = Manager.Instance.SpawnExplosionEffect( Position2D, new Color( 1f, 1f, 0f ), new Color( 1f, 1f, 0.5f ), opacity: 0.05f, scaleModifier: 0.75f * radius );
		explosion.Sprite.PlaybackSpeed = 5f;
		explosion.Lifetime = 0.2f;

		Manager.Instance.PlaySfxNearby( "laser_hit_2", Position2D, pitch: Game.Random.Float( 0.8f, 0.85f ), volume: 0.6f, maxDist: 7.5f );
	}
}