Code/SmokeEmitter.cs
using System;
using System.Collections.Generic;
using Meteor.VehicleTool.Vehicle.Wheel;
using Sandbox;

namespace Meteor.VehicleTool;

public sealed class SmokeEmitter : Component
{
	[Property, RequireComponent] public ParticleSphereEmitter Emitter { get; set; }
	[Property, RequireComponent] public ParticleSpriteRenderer Renderer { get; set; }
	[Property, RequireComponent] public ParticleEffect Effect { get; set; }

	[Property] static Texture SmokeTexture { get; set; } = Texture.Load( "textures/smoketexturesheet.vtex_c" );

	[Button]
	public void SetupSmoke()
	{
		Effect ??= Components.GetOrCreate<ParticleEffect>( FindMode.InSelf );
		Effect.MaxParticles = 1000;
		Effect.Lifetime = new()
		{
			Type = ParticleFloat.ValueType.Range,
			Evaluation = ParticleFloat.EvaluationType.Particle,
			ConstantA = 2,
			ConstantB = 4.2f,
		};

		Effect.StartVelocity = new()
		{
			Type = ParticleFloat.ValueType.Range,
			Evaluation = ParticleFloat.EvaluationType.Particle,
			ConstantA = 10,
			ConstantB = 70,
		};

		Effect.Damping = 0.9f;
		Effect.ApplyRotation = true;
		Effect.Roll = new()
		{
			Type = ParticleFloat.ValueType.Range,
			Evaluation = ParticleFloat.EvaluationType.Particle,
			ConstantA = 0,
			ConstantB = 360,
		};
		Effect.ApplyShape = true;
		Effect.Scale = new()
		{
			Type = ParticleFloat.ValueType.Curve,
			Evaluation = ParticleFloat.EvaluationType.Life,
			CurveA = new( new List<Curve.Frame>() { new( 0, 10f ), new( 0.05f, 50f ), new( 1f, 200f ) } ),
		};
		Effect.ApplyColor = true;
		Effect.Alpha = new()
		{
			Type = ParticleFloat.ValueType.Curve,
			Evaluation = ParticleFloat.EvaluationType.Particle,
			CurveA = new( new List<Curve.Frame>() { new( 0, 0 ), new( 0.2f, 0.5f ), new( 1, 0 ) } ),
		};
		Effect.ApplyAlpha = true;
		Effect.Gradient = new()
		{
			Type = ParticleGradient.ValueType.Range,
			Evaluation = ParticleGradient.EvaluationType.Life,
			ConstantA = Color.White,
			ConstantB = Color.Transparent,
		};
		Effect.Force = true;
		Effect.ForceDirection = Vector3.Up * 100f;
		Effect.SheetSequence = true;
		Effect.SequenceSpeed = 0.5f;
		Effect.SequenceTime = 1f;

		Renderer ??= Components.GetOrCreate<ParticleSpriteRenderer>( FindMode.InSelf );
		Renderer.Texture = SmokeTexture;

		Emitter ??= Components.GetOrCreate<ParticleSphereEmitter>( FindMode.InSelf );
		Emitter.Velocity = 0;
		Emitter.Duration = 5;
		Emitter.Burst = 5;
		Emitter.Rate = 50;

	}


	[Property] private WheelCollider Wheel { get; set; }

	protected override void OnStart()
	{
		Wheel ??= Components.Get<WheelCollider>( FindMode.EverythingInAncestors );
	}

	private float smokeRate;
	void UpdateSmokeRate( float target )
	{
		if ( target > smokeRate )
		{
			smokeRate = MathX.Lerp( smokeRate, target, Time.Delta * 0.05f );
		}
		else if ( target < smokeRate )
		{
			smokeRate = MathX.Lerp( smokeRate, target, Time.Delta * 0.8f );
		}
	}
	protected override void OnUpdate()
	{

		float latEmission = Wheel.IsSkiddingLaterally ? Wheel.NormalizedLateralSlip : 0;
		float lngEmission = Wheel.IsSkiddingLongitudinally ? Wheel.NormalizedLongitudinalSlip : 0;

		if ( Wheel.FrictionPreset.PeakValue > 0.9f )
			UpdateSmokeRate( Math.Clamp( latEmission + lngEmission, 0, 1 ) );
		else
			UpdateSmokeRate( 0 );

		Emitter.Rate = smokeRate * 100f;
		Emitter.Enabled = smokeRate > 0.1f;
		Emitter.RateOverDistance = smokeRate * 100f;

	}
}