Code/Vehicle/VehicleController.Powertrain.cs
using System;
using System.Collections.Generic;
using System.Linq;
using Meteor.VehicleTool.Vehicle.Powertrain;
using Meteor.VehicleTool.Vehicle.Wheel;
using Sandbox;
namespace Meteor.VehicleTool.Vehicle;

public partial class VehicleController
{
	[Property, FeatureEnabled( "Powertrain", Icon = "power" )]
	public bool UsePowertrain { get; set; } = true;

	[Property, Feature( "Powertrain" ), Group( "Properties" )] public float MaxBrakeTorque { get; set; } = 3000;
	[Property, Feature( "Powertrain" ), Group( "Properties" )] public float HandBrakeTorque { get; set; } = 50000;

	[Property, Feature( "Powertrain" ), Group( "Components" )] public Engine Engine { get; set; }
	[Property, Feature( "Powertrain" ), Group( "Components" )] public Clutch Clutch { get; set; }
	[Property, Feature( "Powertrain" ), Group( "Components" )] public Transmission Transmission { get; set; }
	[Property, Feature( "Powertrain" ), Group( "Components" )] public Differential Differential { get; set; }


	private GameObject powertrainGameObject;

	[Property, Feature( "Powertrain" )] public List<WheelCollider> MotorWheels { get; set; }
	[Property, Feature( "Powertrain" )] public List<WheelCollider> HandBrakeWheels { get; set; }
	public List<WheelPowertrain> PowertrainWheels { get; private set; } = [];

	[Button, Feature( "Powertrain" )]
	internal void FindWheels()
	{
		PowertrainWheels = Differential.Components.GetAll<WheelPowertrain>( FindMode.InDescendants ).ToList();
	}


	[Button, Feature( "Powertrain" )]
	internal void CreatePowertrain()
	{
		using var undoScope = Scene.Editor?.UndoScope( "Create Powertrain" ).WithComponentCreations().WithGameObjectCreations().Push();
		if ( !powertrainGameObject.IsValid() )
		{
			powertrainGameObject = null;
		}
		powertrainGameObject ??= new GameObject( true, "Powertrain" );

		if ( !Engine.IsValid() )
			Engine = new GameObject( powertrainGameObject, true, "Engine" ).GetOrAddComponent<Engine>();

		Engine.Controller = this;
		Engine.Inertia = 0.25f;
		Engine.Ignition = false;

		if ( !Clutch.IsValid() )
			Clutch = new GameObject( Engine.GameObject, true, "Clutch" ).GetOrAddComponent<Clutch>();

		Clutch.Controller = this;
		Clutch.Inertia = 0.02f;

		Engine.Output = Clutch;

		if ( !Transmission.IsValid() )
			Transmission = new GameObject( Clutch.GameObject, true, "Transmission" ).GetOrAddComponent<Transmission>();

		Transmission.Controller = this;
		Transmission.Inertia = 0.01f;

		Clutch.Output = Transmission;

		Differential = new TreeBuilder( Transmission, MotorWheels ).Root.Diff;

		Transmission.Output = Differential;

		PowertrainWheels = Differential.Components.GetAll<WheelPowertrain>( FindMode.InDescendants ).ToList();

	}


	private void UpdateBrakes()
	{
		foreach ( var wheel in Wheels )
			wheel.BrakeTorque = SwappedBrakes * MaxBrakeTorque;

		foreach ( var wheel in HandBrakeWheels )
			wheel.BrakeTorque += Handbrake * MaxBrakeTorque;
	}

}



internal class TreeNode
{
	internal TreeNode Left { get; set; }
	internal TreeNode Right { get; set; }
	public WheelPowertrain Item { get; set; }
	public Differential Diff { get; set; }
	public bool IsLeaf => Left == null && Right == null;
}

internal class TreeBuilder
{
	internal TreeNode Root { get; private set; }

	internal TreeBuilder( PowertrainComponent parent, List<WheelCollider> items )
	{
		if ( items == null || items.Count == 0 )
			throw new ArgumentException( "Items list cannot be null or empty." );

		Root = BuildTree( parent, items, 0, items.Count - 1 );
	}

	private static TreeNode BuildTree( PowertrainComponent parent, List<WheelCollider> items, int start, int end )
	{
		if ( start > end )
			return null;

		if ( start == end )
		{

			var leaf = new TreeNode() { Item = new GameObject( parent.GameObject, true, $"Wheel {items[start].GameObject.Name}" ).GetOrAddComponent<WheelPowertrain>() };
			leaf.Item.Controller = parent.Controller;
			leaf.Item.Wheel = items[start];
			leaf.Item.Inertia = 0.01f;
			var parentd = parent as Differential;
			if ( (start + 1) % 2 == 0 )
			{
				GameTask.RunInThreadAsync( () =>
				{
					parentd.OutputB = leaf.Item;
				} );
			}
			else
			{
				GameTask.RunInThreadAsync( () =>
				{
					parent.Output = leaf.Item;
				} );
			}

			return leaf;
		}

		int mid = (start + end) / 2;
		var diff = new GameObject( parent.GameObject, true, "Differential" ).GetOrAddComponent<Differential>();
		diff.Controller = parent.Controller;
		diff.Inertia = 0.1f;
		var node = new TreeNode
		{
			Left = BuildTree( diff, items, start, mid ),
			Right = BuildTree( diff, items, mid + 1, end ),
			Diff = diff
		};
		diff.Output = node.Left.Diff;
		diff.OutputB = node.Right.Diff;
		return node;
	}
}