My Contributions in Fast And Foolish

int middleRayIndex = (forwardSensorCount == 1) ? 0 : Mathf.CeilToInt(forwardSensorCount / 2.0f);

     Vector3 origin = transform.position;
 
     //Check for left side
 
     hitSomething = Physics.SphereCastNonAlloc(origin - transform.right * sideSensingWidth/2.0f,
                                       sensorRadius,
                                       -transform.right,
                                       obstacleHits,
                                       sideSensingDistance,
                                       obstacleMask.value & ~roadMask.value) > 0;
 
 
     avoidSteerPercent = (hitSomething) ? 1.0f - obstacleHits[0].distance / sideSensingDistance : 0.0f;
 
     //Debug.Log("Avoid Steer Percent from left: " + avoidSteerPercent);
     leftAvoidSteerPercent = avoidSteerPercent;
 
     requiredInterest = -transform.right;
     dangers[0] = hitSomething;
 
     if (dangers[0])
     {
         requiredInterest = Vector3.zero;
     }
 
     interests[0] = requiredInterest;
 
     //Check for right side
 
     hitSomething = Physics.SphereCastNonAlloc(origin + transform.right * sideSensingWidth / 2.0f,
                                       sensorRadius,
                                       transform.right,
                                       obstacleHits,
                                       sideSensingDistance,
                                       obstacleMask.value & ~roadMask.value) > 0;
 
     avoidSteerPercent = (hitSomething) ? 1.0f - obstacleHits[0].distance / sideSensingDistance : 0.0f;
 
     //Debug.Log("Avoid Steer Percent from right: " + avoidSteerPercent);
     rightAvoidSteerPercent = avoidSteerPercent;
 
     steerPercentDifference = rightAvoidSteerPercent - leftAvoidSteerPercent;
     //Debug.Log("Difference in avoid steer percent: " + steerPercentDifference);
 
     if(allowSidewaysRepelForce && carManager.ForwardNormalizedSpeed > 0.3f && Mathf.Abs(steerPercentDifference) >= 0.85f)
     {
         carManager.Repel(-transform.right * sidewaysRepelForce * steerPercentDifference);
     }
 
     requiredInterest = transform.right;
     dangers[length - 1] = hitSomething;
 
     if (dangers[length - 1])
     {
         requiredInterest = Vector3.zero;
     }
 
     interests[length - 1] = requiredInterest;
 
     //Now, check between left and right sides.
 
     origin = ForwardCheckPos;
 
     for (int i = 1; i < length - 1; i++)
     {
         if(i == middleRayIndex && (forwardSensorCount + 2) % 2 == 0)
         {
             interests[i] = Vector3.zero;
             continue;
         }
 
         angle = Mathf.Lerp(-forwardSensingHalfAngle, forwardSensingHalfAngle, (float)i / (float)forwardSensorCount);
 
         checkDirection = Quaternion.AngleAxis(angle, transform.up) * transform.forward;
 
         hitSomething = Physics.SphereCastNonAlloc(origin,
                                       sensorRadius,
                                       checkDirection,
                                       obstacleHits,
                                       forwardSensingDistance,
                                       obstacleMask.value & ~roadMask.value) > 0;
 
         requiredInterest = checkDirection;
         dangers[i] = hitSomething;
 
         if (dangers[i])
         {
             requiredInterest = Vector3.zero;
         }
 
         interests[i] = requiredInterest;
     }
 
     requiredAvoidSteerInput = 0.0f;
     Vector3 total = Vector3.zero;
     for(int i = 0; i < length; i++)
     {
         total += interests[i];
     }
 
     total.Normalize();
public Vector3 GetRoutePosition(float dist)
{
    int point = 0;

    dist = Mathf.Repeat(dist, trackLength);

    while (distances[point] < dist)
    {
        ++point;
    }


    // get nearest two points, ensuring points wrap-around start & end of circuit
    p1n = ((point - 1) + numPoints) % numPoints;
    p2n = point;

    // found point numbers, now find interpolation value between the two middle points

    interpolation = Mathf.InverseLerp(distances[p1n], distances[p2n], dist);

    if (smoothPath)
    {
        // smooth catmull-rom calculation between the two relevant points

        // get indices for the surrounding 2 points, because
        // four points are required by the catmull-rom function
        p0n = ((point - 2) + numPoints) % numPoints;
        p3n = (point + 1) % numPoints;

        // 2nd point may have been the 'last' point - a dupe of the first,
        // (to give a value of max track distance instead of zero)
        // but now it must be wrapped back to zero if that was the case.
        p2n = p2n % numPoints;

        P0 = points[p0n];
        P1 = points[p1n];
        P2 = points[p2n];
        P3 = points[p3n];

        return CatmullRom(P0, P1, P2, P3, interpolation);
    }
    // simple linear lerp between the two points:

    p1n = ((point - 1) + numPoints) % numPoints;
    p2n = point;

    return Vector3.Lerp(points[p1n], points[p2n], interpolation);
}

private Vector3 CatmullRom(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float i)
{
    // You can google it to see what is this.
    return 0.5f *
           ((2 * p1) + (-p0 + p2) * i + (2 * p0 - 5 * p1 + 4 * p2 - p3) * i * i +
            (-p0 + 3 * p1 - 3 * p2 + p3) * i * i * i);
}

public bool IsTooCurvy(float checkAngle, int lookaheadDistance)
{
    if(progressStyle == ProgressStyle.SmoothAlongRoute && 
       lookAhead.Length != smoothLookAheadDistance)
    {
        lookAhead = new TrackManager.RoutePoint[smoothLookAheadDistance];
    }


    float halfCheckAngle = checkAngle / 2.0f;
    float angle = 0.0f;
    Vector3 previous = Vector3.zero;
    Vector3 current = Vector3.zero;
    Vector3 next = Vector3.zero;

    Vector3 direction1 = Vector3.zero;
    Vector3 direction2 = Vector3.zero;


    if(progressStyle == ProgressStyle.SmoothAlongRoute)
    {
        for (int i = 0; i < smoothLookAheadDistance && i < lookaheadDistance; i++)
        {
            lookAhead[i] = trackManager.GetRoutePoint(progressDistance +
                                                      i * lookAheadForTargetOffset +
                                                      i * lookAheadForTargetFactor * speed);
        }


        for (int i = 1; i < smoothLookAheadDistance && i < lookaheadDistance - 1; i++)
        {
            previous = lookAhead[i - 1].position;
            current = lookAhead[i].position;
            next = lookAhead[i + 1].position;

            direction1 = (current - previous).normalized;
            direction2 = (next - current).normalized;

            angle = Vector3.SignedAngle(direction1, direction2, Vector3.up);
            
            if (angle >= -halfCheckAngle && angle <= halfCheckAngle)
            {
                continue;
            }
            else
            {
                //Debug.Log("Angle: " + angle);
                return true;
            }
        }
    }
    else if(progressStyle == ProgressStyle.PointToPoint)
    {
        int length = trackManager.WaypointsList.Waypoints.Length;

        for(int i = 1; i < pointLookAheadAmount && i < lookaheadDistance - 1; i++)
        {
            previous = trackManager.WaypointsList.
                                   Waypoints[(progressWaypoint + i - 1) % length].Position;

            current = trackManager.WaypointsList.
                                   Waypoints[(progressWaypoint + i) % length].Position;

            next = trackManager.WaypointsList.
                                Waypoints[(progressWaypoint + i + 1) % length].Position;

            direction1 = (current - previous).normalized;
            direction2 = (next - current).normalized;

            angle = Vector3.SignedAngle(direction1, direction2, Vector3.up);

            if (!(angle >= -halfCheckAngle && angle <= halfCheckAngle))
            {
                return true;
            }
        }
    }
    

    return false;
}

public float CalculateSteeringForce()
{
    if (target == null)
    {
        Debug.LogError("Target is null!");
        return 0.0f;
    }
    // Calculate the direction from the agent position to the next waypoint
    Vector3 desiredDirection = target.position - transform.position;
    desiredDirection.y = 0.0f;
    desiredDirection = desiredDirection.normalized;

    float angle = Vector3.SignedAngle(transform.forward, desiredDirection, Vector3.up);
    
    previousSteeringForce = currentSteeringForce;

    //Calculate steering force based on angle between AI's forward and desired direction and turn curve.

    currentSteeringForce = angle/carManager.MaxSteeringAngle;

    if(currentAccelerationForce < 0.0f)
    {
        currentSteeringForce *= -1.0f;
    }

    //If angle lies between under target, then zero it.
    if (angle >= -underTargetAngle/2f && angle <= underTargetAngle/2f && currentAccelerationForce >= 0.0f)
    {
        currentSteeringForce = 0.0f;
    }

    if (allowObstacleSteering && currentAccelerationForce >= 0.0f)
    {
        currentSteeringForce += CalculateObstacleAvoidanceSteering();   
    }
    
    //Debug.Log("Current steer: " + currentSteeringForce);

    currentSteeringForce *= turnCurve.Evaluate(carManager.NormalizedSpeed);
    currentSteeringForce = Mathf.Clamp(currentSteeringForce, -1.0f, 1.0f);
    return currentSteeringForce;
    
}

public float CalculateHandbrakeForce()
{
    if (target == null)
    {
        Debug.LogError("Target is null!");
        return 0.0f;
    }

    currentHandbrakeForce = 0.0f;

    
    if ((forwardDangersCount >= minDangerCheckCount && carManager.NormalizedSpeed >= 0.1f) || (carManager.NormalizedSpeed >= cornerNormalizedSpeed && wayPtProgress.IsTooCurvy(cornerCheckAngle, brakeLookAheadDistance)))
    {
        currentHandbrakeForce = brakePercent;
    }

    //If some braking is applied, then change drift parameters.
    if (currentHandbrakeForce > 0.0f)
    {
        carManager.MaxCarGrip = driftGrip;
        carManager.GripSpeedFactor = driftGripFactor;
    }
    else
    {
        carManager.MaxCarGrip = maxGrip;
        carManager.GripSpeedFactor = gripFactor;
    }

    return currentHandbrakeForce;
}
#if UNITY_EDITOR
[CustomPropertyDrawer(typeof(TrackManager.WaypointList))]
public class WaypointListPropertyDrawer : PropertyDrawer
{
    private TrackManager.WaypointList instance;
    private SerializedProperty waypointsProp;

    private StyleSheet editorStyleSheet = null;
    private Foldout waypointsFoldout = null;

    public override VisualElement CreatePropertyGUI(SerializedProperty property)
    {
        var root = new VisualElement();

        instance = (TrackManager.WaypointList)property.boxedValue;
        var waypointsProp = property.FindPropertyRelative("waypoints");
        this.waypointsProp = waypointsProp;

        var waypointsFoldout = new Foldout();
        waypointsFoldout.name = "Waypoints Foldout";

        this.waypointsFoldout = waypointsFoldout;

        if(editorStyleSheet == null)
        {
            editorStyleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>("Assets/UI/EditorStuff/CommonEditorStylesheet.uss");
        }

        waypointsFoldout.RegisterCallback<ChangeEvent<bool>>(FoldoutChanged);
        waypointsFoldout.Bind(waypointsProp.serializedObject);

        root.Add(waypointsFoldout);
        root.Bind(property.serializedObject);

        return root;
    }

    private void FoldoutChanged(ChangeEvent<bool> evt)
    {
        this.waypointsFoldout.Clear();
        if (!this.waypointsFoldout.value || this.waypointsProp == null || this.waypointsProp.arraySize == 0)
        {
            this.waypointsFoldout.styleSheets.Remove(editorStyleSheet);
            
            return;
        }

        this.waypointsFoldout.styleSheets.Add(editorStyleSheet);

        int length = this.waypointsProp.arraySize;


        for(int i = 0; i < length; i++)
        {
            TrackManager.Waypoint waypoint = (TrackManager.Waypoint)waypointsProp.GetArrayElementAtIndex(i).boxedValue;
            Vector3 position = waypoint.Position;
            int waypointIndex = i;

            Button wayPtBtn = new Button(()=> FocusOnWaypoint(position, waypointIndex));
            
            wayPtBtn.text = waypoint.Name;
            this.waypointsFoldout.Add(wayPtBtn);
        }
    }


    private void FocusOnWaypoint(Vector3 position, int index)
    {
        SceneView sceneView = SceneView.lastActiveSceneView;
        sceneView.pivot = position;
        sceneView.size = 5.0f;
    }
}
#endif
#if UNITY_EDITOR
[CustomEditor(typeof(TrackManager))]
public class TrackManagerEditor : Editor
{
    private TrackManager trackManager;

    private SerializedProperty trackProp;
    private SerializedProperty roadSamplesProp;
    private SerializedProperty smoothPathProp;
    private SerializedProperty showPositionHandlesProp;
    private SerializedProperty showWaypointPositionsProp;
    private SerializedProperty waypointListProp;

    private SerializedProperty waypointsProp;
    private SceneView sceneView;
    
    private float dashLength = 2.0f;

    private void OnEnable()
    {
        trackManager = (TrackManager)target;

        trackProp = serializedObject.FindProperty("track");
        roadSamplesProp = serializedObject.FindProperty("roadSamples");
        smoothPathProp = serializedObject.FindProperty("smoothPath");
        showPositionHandlesProp = serializedObject.FindProperty("showPositionHandles");
        showWaypointPositionsProp = serializedObject.FindProperty("showWaypointPositions");
        waypointListProp = serializedObject.FindProperty("waypointList");

        waypointsProp = waypointListProp.FindPropertyRelative("waypoints");
        sceneView = SceneView.lastActiveSceneView;
    }
    public override VisualElement CreateInspectorGUI()
    {
        VisualElement root = new VisualElement();

        
        if(waypointListProp == null)
        {
            Debug.LogError("Waypoint List Property is null");
        }

        PropertyField trackField = new PropertyField(trackProp, "Track");
        PropertyField roadSamplesField = new PropertyField(roadSamplesProp, "Road Samples");
        PropertyField smoothPathField = new PropertyField(smoothPathProp, "Smooth Path");
        PropertyField showPositionHandlesField = new PropertyField(showPositionHandlesProp, "Show Gizmo");
        PropertyField showWaypointPositionsField = new PropertyField(showWaypointPositionsProp, "Show Waypoint Positions");
        PropertyField waypointListField = new PropertyField(waypointListProp, "Waypoint List");

        waypointListField.label = "Waypoint List";

        Label waypointListLabel = new Label("Waypoint List");
        float marginLeftValue = waypointListLabel.style.marginLeft.value.value;
        waypointListLabel.style.marginLeft = marginLeftValue + 4;

        Button generateWaypointsButton = new Button(trackManager.GenerateWaypoints);
        generateWaypointsButton.text = "Generate Waypoints";

        root.Add(trackField);
        root.Add(roadSamplesField);
        root.Add(smoothPathField);
        root.Add(showPositionHandlesField);
        root.Add(showWaypointPositionsField);
        root.Add(waypointListField);
        root.Add(generateWaypointsButton);

        return root;
    }

    private void OnSceneGUI()
    {
        var waypoints = trackManager.WaypointsList.Waypoints;
        if(waypoints == null || waypoints.Length == 0)
        {
            Debug.LogError("No waypoints. Spline container reference is probably null or waypoints length is 0.");
            return;
        }
        var trackLength = trackManager.TrackLength;
        for (int i = 0; i < waypoints.Length; i++)
        {
            var currentWayPt = waypoints[i];
            Vector3 position = currentWayPt.Position;

            // Get distance from scene camera to the object
            float sqrDistance = Vector3.SqrMagnitude(sceneView.camera.transform.position - position);

            float minSqrDistance = CustomEditorConstant.MinSceneGUIRenderDistance * CustomEditorConstant.MinSceneGUIRenderDistance;
            if (sqrDistance > minSqrDistance)
            {
                continue;
            }

            if (trackManager.ShowPositionHandles)
            {
                Handles.color = Color.yellow;
                
                Vector3 up = currentWayPt.UpDir;
                Vector3 forward = currentWayPt.ForwardDir;

                // Draw position handle (movable)
                EditorGUI.BeginChangeCheck();

                Quaternion rotation = Quaternion.LookRotation(forward, up);
                position = Handles.PositionHandle(position, rotation);

                //Handles.TransformHandle(ref position, ref rotation);

                /*
                Handles.color = Color.green;
                Handles.ArrowHandleCap(0, position, Quaternion.LookRotation(up, forward), 1f, EventType.Repaint);

                Handles.color = Color.blue;
                Handles.ArrowHandleCap(0, position, Quaternion.LookRotation(forward, up), 1f, EventType.Repaint);
                */

                if (EditorGUI.EndChangeCheck())
                {
                    Undo.RecordObject(target, "Move Waypoint");
                    waypoints[i].Position = position;
                    //waypoints[i].UpDir = rotation * up;
                    //waypoints[i].ForwardDir = rotation * forward;
                    EditorUtility.SetDirty(target);
                }
            }
            
            if(trackManager.ShowWaypointPositions)
            {
                Handles.color = Color.blue;
                Handles.DrawSolidDisc(currentWayPt.Position, currentWayPt.UpDir, 1.0f);
            }

            

            

            Handles.BeginGUI();
            Handles.Label(currentWayPt.Position, currentWayPt.Name, EditorStyles.toolbar);
            Handles.EndGUI();
        }

        Handles.color = Color.white;
        Vector3 prev = waypoints[0].Position;

        for (int n = 0; n < waypoints.Length; ++n)
        {
            Vector3 next = waypoints[(n + 1) % waypoints.Length].Position;
            Handles.DrawDottedLine(prev, next, dashLength);
            prev = next;
        }
    }
}
#endif