using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using NaughtyAttributes;
using UnityEngine.Timeline;
#if UNITY_EDITOR
using UnityEditor;
#endif

/// <summary>
/// Created by Jeremy Bond for MI 231 at Michigan State University
/// Built to work with a modified version of the GMTK Platformer Toolkit
/// </summary>
[CreateAssetMenu( fileName = "GMTK_Settings_[GameName]",
    menuName = "ScriptableObjects/GMTK_Settings", order = 1 )]
public class Character_Settings_SO : ScriptableObject {
    static bool DEBUG_JUMP_LINE_CALCULATION = false;

    [Header( "Movement Stats" )]
    [SerializeField, Range( 0f, 20f )]
    [Tooltip( "Maximum movement speed" )]
    public float maxSpeed = 10f;
    [SerializeField, Range( 0f, 100f )]
    [Tooltip( "How fast to reach max speed" )]
    public float maxAcceleration = 52f;
    [SerializeField, Range( 0f, 100f )]
    [Tooltip( "How fast to stop after letting go" )]
    public float maxDeceleration = 52f;
    [SerializeField, Range( 0f, 100f )]
    [Tooltip( "How fast to stop when changing direction" )]
    public float maxTurnSpeed = 80f;
    [SerializeField, Range( 0f, 100f )]
    [Tooltip( "How fast to reach max speed when in mid-air" )]
    public float maxAirAcceleration = 0;
    [SerializeField, Range( 0f, 100f )]
    [Tooltip( "How fast to stop in mid-air when no direction is used" )]
    public float maxAirDeceleration = 0;
    [SerializeField, Range( 0f, 100f )]
    [Tooltip( "How fast to stop when changing direction when in mid-air" )]
    public float maxAirTurnSpeed = 80f;
    [SerializeField]
    [Tooltip( "Friction to apply against movement on stick" )]
    public float friction = 0;

    [Header( "Movement Options" )]
    [Tooltip(
        "When false, the charcter will skip acceleration and deceleration and instantly move and stop" )]
    public bool useAcceleration = true;



    // NOTE: CGSK jummp math comes from Math for Game Programmers: Building a Better Jump
    // https://www.youtube.com/watch?v=hG9SzQxaCm8&t=9m35s & https://www.youtube.com/watch?v=hG9SzQxaCm8&t=784s
    // Th = Xh/Vx     V0 = 2H / Th     G = -2H / (Th * Th)     V0 = 2HVx / Xh     G = -2H(Vx*Vx) / (Xh*Xh) 

    [Header( "Jump Settings" )]
    public eJumpSettingsType jumpSettingsType = eJumpSettingsType.CGSK_Time;
    public enum eJumpSettingsType { CGSK_Distance, CGSK_Time, GMTK_GameMakersToolKit };

    public bool showJumpLine = true;

    [Tooltip( "Maximum jump height" )]
    [Range( 1f, 10f )]
    public float jumpHeight = 4f;

    [ShowIf( "jumpSettingsType", eJumpSettingsType.CGSK_Time )]
    public CGSK_JumpSettings_Time jumpSettingsTime;

    [ShowIf( "jumpSettingsType", eJumpSettingsType.CGSK_Distance )]
    public CGSK_JumpSettings_Distance jumpSettingsDistance;

    [HideIf( "jumpSettingsType", eJumpSettingsType.GMTK_GameMakersToolKit )]
    public CGSK_JumpSettings_VariableHeight jumpSettingsVariableHeightCGSK;
    [ShowIf("jumpSettingsType", eJumpSettingsType.GMTK_GameMakersToolKit )]
    public CGSK_JumpSettings_VariableHeight jumpSettingsVariableHeightGMTK;
    [HideInInspector] internal CGSK_JumpSettings_VariableHeight jumpSettingsVariableHeight;

    // [HideIf( "jumpSettingsType", eJumpSettingsType.GMTK_GameMakersToolKit )]
    // [XnTools.ReadOnly][BoxGroup("CGSK Derived Jump Properties")]
    // public float jumpDistUp, jumpDurationUp, jumpVelUp, jumpGravUp, jumpDistDown, jumpDurationDown, jumpVelDown, jumpGravDown;


    [HideIf( "jumpSettingsType", eJumpSettingsType.GMTK_GameMakersToolKit )]
    [BoxGroup( "Derived Jump Properties" )]
    public CSSO_FloatUpDown jumpDist, jumpDuration, jumpVel, jumpGrav;
    [BoxGroup( "Derived Jump Properties" )] [SerializeField] [XnTools.ReadOnly]
    internal Vector2 maxJumpDistHeight, minJumpDistHeight;



    [ShowIf( "jumpSettingsType", eJumpSettingsType.GMTK_GameMakersToolKit )]
    public GMTK_JumpSettings jumpSettingsGMTK;


    [Header( "Jump Options" )]
    [SerializeField]
    [Tooltip( "The fastest speed the character can fall" )]
    public float speedLimit = 26.45f;
    [SerializeField, Range( 0f, 0.3f )]
    [Tooltip( "How long should coyote time last?" )]
    public float coyoteTime = 0.15f;
    [SerializeField, Range( 0f, 0.3f )]
    [Tooltip( "How far from ground should we cache your jump?" )]
    public float jumpBuffer = 0.15f;
    [Tooltip( "Max jumps between grounding. (2 for Double Jump, 3 for Triple Jump, etc.) " )]
    [Range( 1, 10 )]
    public int jumpsBetweenGrounding = 1;





    [Header( "Juice Settings - Squash and Stretch" )]
    [SerializeField]
    public bool squashAndStretch;
    [SerializeField, Tooltip( "Width Squeeze, Height Squeeze, Duration" )]
    public Vector3 jumpSquashSettings;
    [SerializeField, Tooltip( "Width Squeeze, Height Squeeze, Duration" )]
    public Vector3 landSquashSettings;
    [SerializeField, Tooltip( "How powerful should the effect be?" )]
    public float landSqueezeMultiplier;
    [SerializeField, Tooltip( "How powerful should the effect be?" )]
    public float jumpSqueezeMultiplier;
    [SerializeField]
    public float landDrop = 1;

    [Header( "Juice Settings - Tilting" )]

    [SerializeField]
    public bool leanForward;
    [SerializeField, Tooltip( "How far should the character tilt?" )]
    public float maxTilt;
    [SerializeField, Tooltip( "How fast should the character tilt?" )]
    public float tiltSpeed;



    // public float timeToJumpApex;
    // [ShowIf( "jumpSettingsType", eJumpSettingsType.GMTK_GameMakersToolKit )]
    // [SerializeField, Range( 0f, 5f )]
    // [Tooltip( "Gravity multiplier to apply when going up" )]
    // public float upwardMovementMultiplier = 1f;
    // [ShowIf( "jumpSettingsType", eJumpSettingsType.GMTK_GameMakersToolKit )]
    // [SerializeField, Range( 1f, 10f )]
    // [Tooltip( "Gravity multiplier to apply when coming down" )]
    // public float downwardMovementMultiplier = 6.17f;
    // [ShowIf( "jumpSettingsType", eJumpSettingsType.GMTK_GameMakersToolKit )]
    // [SerializeField, Range( 0, 1 )]
    // [Tooltip( "How many times can you jump in the air?" )]
    // public int maxAirJumps = 0;
    // [ShowIf( "jumpSettingsType", eJumpSettingsType.GMTK_GameMakersToolKit )]
    // [Tooltip( "Should the character drop when you let go of jump?" )]
    // public bool variableJumpHeight;
    // [ShowIf( "jumpSettingsType", eJumpSettingsType.GMTK_GameMakersToolKit )]
    // [SerializeField, Range( 1f, 10f )]
    // [Tooltip( "Gravity multiplier when you let go of jump" )]
    // public float jumpCutOff;




    private void OnValidate() {
        switch ( jumpSettingsType ) {
        case eJumpSettingsType.CGSK_Time:
            CalculateDerivedJumpValues_Time();
            break;

        case eJumpSettingsType.CGSK_Distance:
            CalculateDerivedJumpValues_Distance();
            break;

        case eJumpSettingsType.GMTK_GameMakersToolKit:
            CalculateDerivedJumpValues_GMTK();
            break;
        }

        CalculateJumpLine();
    }


    static private int       jumpLineResolution = 64; // NOTE: This must be a positive even number
    internal       Vector3[] jumpLinePoints;
    internal List<Vector3> minJumpLinePoints;
    // [HideIf( "jumpSettingsType", eJumpSettingsType.GMTK_GameMakersToolKit )]
    internal Vector3[] jumpStartMidEndPoints, minJumpStartMidEndPoints;
    internal Vector2   minTimeApexFull;

    internal void CalculateJumpLine() {
        if ( jumpSettingsType == eJumpSettingsType.GMTK_GameMakersToolKit ) {
            jumpLinePoints = null;
            return;
        }
        maxJumpDistHeight = Vector2.zero;
        Vector3 acc = new Vector3( 0, jumpGrav.up, 0 );
        Vector3 newAcc = acc;
        Vector3 p = Vector3.zero;
        jumpLinePoints = new Vector3[jumpLineResolution];
        jumpStartMidEndPoints = new Vector3[3];
        jumpLinePoints[0] = p;
        jumpStartMidEndPoints[0] = p;
        Vector3 v = new Vector3( maxSpeed, jumpVel.up, 0 );
        int numSteps = jumpLineResolution / 2;
        // Jumping Up
        float timeStepUp = jumpDuration.up / (float) numSteps;
        int i = 1;
        for ( ; i <= jumpLineResolution / 2; i++ ) {
            SimplifiedVelocityVerletIntegration( ref p, ref v, acc, timeStepUp );
            // p.x += v.x         * timeStep;
            // v.y += jumpGrav.up * timeStep;
            // p.y += v.y         * timeStep;
            jumpLinePoints[i] = p;
        }
        jumpStartMidEndPoints[1] = p;
        maxJumpDistHeight.y = p.y;
        // Jumping Down
        acc.y = jumpGrav.down;
        float timeStepDown = jumpDuration.down / (float) ( numSteps - 1 );
        for ( ; i < jumpLineResolution; i++ ) {
            SimplifiedVelocityVerletIntegration( ref p, ref v, acc, timeStepDown );
            // p.x += v.x           * timeStep;
            // v.y += jumpGrav.down * timeStep;
            // p.y += v.y           * timeStep;
            jumpLinePoints[i] = p;
        }
        jumpStartMidEndPoints[2] = p;
        maxJumpDistHeight.x = p.x;

        // Calculate jump line if jump button is released immediately
        if ( !jumpSettingsVariableHeight.useVariableJumpHeight ) {
            minJumpLinePoints = null;
            return;
        }
        minJumpDistHeight = Vector2.zero;
        minTimeApexFull = Vector2.zero;
        v = new Vector3( maxSpeed, jumpVel.up, 0 );
        newAcc = acc = new Vector3( 0, jumpGrav.up, 0 );
        p = Vector3.zero;
        minJumpLinePoints = new List<Vector3>();
        minJumpStartMidEndPoints = new Vector3[3];
        minJumpLinePoints.Add( p );
        minJumpStartMidEndPoints[0] = p;
        // We'll use timeStepUp, the timeStep from jumpDuration.up
        i = 0;
        float time = 0;
        int minJumpPhase = 0; // 0=up and button held, 1=button released, 2=down 
        Vector3 debugAcc = new Vector3(acc.y, -1, -1);
        float minTimeStep = 0.01f;
        for ( ; time < 10; i++ ) { // time<10 or i<jumpLineResolution to keep it from getting out of hand
            time += minTimeStep;
            if ( minJumpPhase == 0 ) { // Up and button held
                if ( v.y <= 0 || time >= jumpSettingsVariableHeight.minJumpButtonHeldTime ) {
                    if ( v.y <= 0 || jumpSettingsVariableHeight.upwardVelocityZeroing ) {
                        v.y = 0;
                        newAcc.y = jumpGrav.down;
                        debugAcc.z = newAcc.y;
                        minJumpStartMidEndPoints[1] = p;
                        minJumpPhase = 2;
                        minTimeApexFull.x = time;
                    } else {
                        newAcc.y = jumpGrav.up * jumpSettingsVariableHeight.gravUpMultiplierOnRelease;
                        debugAcc.y = newAcc.y;
                        minJumpPhase = 1;
                    }
                }
            } else if ( minJumpPhase == 1 ) { // Still up, but button has been released
                if ( v.y <= 0 ) { // We're starting down
                    newAcc.y = jumpGrav.down;
                    debugAcc.z = newAcc.y;
                    minJumpStartMidEndPoints[1] = p;
                    minJumpDistHeight.y = p.y;
                    minJumpPhase = 2;
                    minTimeApexFull.x = time;
                }
            } else { // minJumpPhase == 2 // Moving down
                if ( p.y < 0 ) break; // This shouldn't ever happen because it should be caught after last SVVI call in minJumpPhase 2
            }
            
            VelocityVerletIntegration( ref p, ref v, ref acc, newAcc, minTimeStep );
            if ( p.y < 0 ) {
                p.y = 0; // This is a fudging of the numbers, but it should be ok. - JGB 2023-03-12
                minJumpStartMidEndPoints[2] = p;
                minJumpLinePoints.Add( p );
                minJumpDistHeight.x = p.x;
                minTimeApexFull.y = time;
                break;
            }
            minJumpLinePoints.Add( p );
        }
        if (DEBUG_JUMP_LINE_CALCULATION) Debug.LogWarning(
            $"gUMOR: {jumpSettingsVariableHeight.gravUpMultiplierOnRelease:0.##}"
            + $"\tp0acc: {debugAcc.x:#,0.##}"
            + $"\tp1acc: {debugAcc.y:#,0.##}"
            + $"\tp2acc: {debugAcc.z:#,0.##}");

    }

    // NOTE: Simplified Velocity Verlet Integration from Math for Game Programmers: Building a Better Jump
    // https://www.youtube.com/watch?v=hG9SzQxaCm8&t=23m2s
    void SimplifiedVelocityVerletIntegration( ref Vector3 pos, ref Vector3 vel, Vector3 acc,
                                              float deltaTime ) {
        pos += vel * deltaTime +
               acc * ( 0.5f * deltaTime * deltaTime ); // pos += vel*dT + 1/2*acc*dT*dT
        vel += acc * deltaTime;
    }

    void VelocityVerletIntegration( ref Vector3 pos, ref Vector3 vel, ref Vector3 acc,
                                    Vector3 newAcc, float deltaTime ) {
        pos += vel * deltaTime +
               acc * ( 0.5f * deltaTime * deltaTime ); // pos += vel*dT + 1/2*acc*dT*dT
        vel += (acc + newAcc) * (0.5f * deltaTime);
        acc = newAcc;
    }

    public float scale( float OldMin, float OldMax, float NewMin, float NewMax, float OldValue ) {
        float OldRange = ( OldMax - OldMin );
        float NewRange = ( NewMax - NewMin );
        float NewValue = ( ( ( OldValue - OldMin ) * NewRange ) / OldRange ) + NewMin;

        return ( NewValue );
    }


    [System.Serializable]
    public class CGSK_JumpSettings_Time {
        [Header( "Classic Game Starter Kit - Time Jump Settings" )]
        [Tooltip( "The full duration of the shortest jump possible (by tapping the button)" )]
        public float fullJumpDurationMin = 0.5f;
        [Tooltip( "The full duration of the longest jump possible (by holding the button)" )]
        public float fullJumpDurationMax = 1f;
        [Tooltip( "The fraction of the jump that is going up" )]
        [Range( 0.05f, 0.95f )]
        public float jumpApexFraction = 0.6f;
    }

    private void CalculateDerivedJumpValues_Time() {
        jumpDuration.up = jumpSettingsTime.fullJumpDurationMax * jumpSettingsTime.jumpApexFraction;
        jumpDuration.down = jumpSettingsTime.fullJumpDurationMax - jumpDuration.up;
        jumpVel.up = jumpHeight * 2 / jumpDuration.up;
        jumpVel.down =
            jumpHeight * 2 /
            jumpDuration.down; // This is the velocity when the character lands. - GB 2023-03-10
        jumpGrav.up = -2   * jumpHeight   / ( jumpDuration.up   * jumpDuration.up );
        jumpGrav.down = -2 * jumpHeight   / ( jumpDuration.down * jumpDuration.down );
        jumpDist.up = jumpDuration.up     * maxSpeed;
        jumpDist.down = jumpDuration.down * maxSpeed;
        
        jumpSettingsVariableHeight = jumpSettingsVariableHeightCGSK;
    }

    [System.Serializable]
    public class CGSK_JumpSettings_Distance {
        [Header( "Classic Game Starter Kit - Distance Jump Settings" )]
        [Tooltip( "The horizontal distance at full run speed of the shortest jump possible (by tapping the button)" )]
        public float fullJumpDistanceMin = 0.5f;
        [Tooltip( "The horizontal distance at full run speed of the longest jump possible (by holding the button)" )]
        public float fullJumpDistanceMax = 1f;
        [Tooltip( "The fraction of the jump that is going up" )]
        [Range( 0.05f, 0.95f )]
        public float jumpApexFraction = 0.6f;
    }

    private void CalculateDerivedJumpValues_Distance() {
        jumpDist.up = jumpSettingsDistance.fullJumpDistanceMax *
                      jumpSettingsDistance.jumpApexFraction;
        jumpDist.down = jumpSettingsDistance.fullJumpDistanceMax - jumpDist.up;
        // Th = Xh / Vh
        jumpDuration.up = jumpDist.up     / maxSpeed;
        jumpDuration.down = jumpDist.down / maxSpeed;
        // Vy = 2hVh / Xh
        jumpVel.up = 2   * jumpHeight * maxSpeed / jumpDist.up;
        jumpVel.down = 2 * jumpHeight * maxSpeed / jumpDist.down;
        // G = -2h(Vx*Vx) / (Xh*Xh)
        jumpGrav.up = -2 * jumpHeight * ( maxSpeed * maxSpeed ) / ( jumpDist.up * jumpDist.up );
        jumpGrav.down = -2 * jumpHeight * ( maxSpeed * maxSpeed ) /
                        ( jumpDist.down * jumpDist.down );
        
        jumpSettingsVariableHeight = jumpSettingsVariableHeightCGSK;
    }

    [System.Serializable]
    public class CGSK_JumpSettings_VariableHeight {
        [Header( "Classic Game Starter Kit - Variable Jump Height" )]

        [Tooltip("Should the character jump differently based on how long the jump button is held?")]
        public bool useVariableJumpHeight = true;
        [Tooltip( "Should upward velocity be set to 0 when the jump button is released? (Like in Metroid for NES)" )]
        public bool upwardVelocityZeroing = false;
        [Tooltip( "The minimum amount of time that the jump button will be forced to be held" +
            " Set this to 0.1f if you want to ensure that the player can't release the button before 0.1 seconds have passed." +
            " 0.05f is the default value because 100ms is a typical shortest time for a button to be held." )]
        [Range( 0.05f, 2f )]
        public float minJumpButtonHeldTime = 0.05f; // 100ms is a typical shortest time for a button to be held.;
        [Tooltip( "The multiplier applied to jumpGrav.up to slow upward velocity faster after the jump button has been released." +
                  "\nIf this is set to 1 and upwardVelocityZeroing=false, then it is the same as useVariableJumpHeight=false." +
                  "\nIf this were extremely high, it would similar to upwardVelocityZeroing=true." )]
        [Range(1,20)]
        public float gravUpMultiplierOnRelease = 1;
    }

    // NOTE: CGSK jummp math comes from Math for Game Programmers: Building a Better Jump
    // https://www.youtube.com/watch?v=hG9SzQxaCm8&t=9m35s & https://www.youtube.com/watch?v=hG9SzQxaCm8&t=784s
    // Th = Xh/Vx     V0 = 2H / Th     G = -2H / (Th * Th)     V0 = 2HVx / Xh     G = -2H(Vx*Vx) / (Xh*Xh) 
    
    [System.Serializable]
    public class GMTK_JumpSettings {
        [Header( "Jump Settings - GameMakers ToolKit" )]
        [SerializeField, Range( 1f, 10f )]
        [Tooltip( "This number is converted from the rather meaningless [1..10] to a time to jump apex of [0.2sec..1.25sec]" )]
        public float jumpDuration = 5;
        [SerializeField, Range( 1f, 10f )]
        [Tooltip( "Gravity multiplier to apply when coming down" )]
        public float downGravity = 6.17f;
        public bool doubleJump = false;
        [Tooltip( "Should the character drop when you let go of jump?" )]
        public bool variableJumpHeight;
        [SerializeField, Range( 1f, 10f )]
        [ShowIf("useVariableJumpHeight")]
        [Tooltip( "Gravity multiplier when you let go of jump and character is still moving up" )]
        public float jumpCutOff;
    }

    void CalculateDerivedJumpValues_GMTK() {
        // Jump Duration up is set by the [1..10] value from the GMTK app 
        jumpDuration.up = scale( 1, 10, 0.2f, 1.25f, jumpSettingsGMTK.jumpDuration );
        // These are the only derived values where the initial gravity is based on Physics2D.gravity - JGB 2023-03-12
        jumpGrav.up = Physics2D.gravity.y;
        // downGravity is a multiplier on the up gravity
        jumpGrav.down = jumpGrav.up * jumpSettingsGMTK.downGravity;
        // Calculate jumpDuration.down from G = -2H / (Th * Th), which solves for Th to Th = √(-2H / G)
        jumpDuration.down = Mathf.Sqrt( -2 * jumpHeight / jumpGrav.down );
        // Calculate jumpVel from V = 2H / Th
        jumpVel.up = 2   * jumpHeight / jumpDuration.up;
        jumpVel.down = 2 * jumpHeight / jumpDuration.down;
        // Calculate jumpDist from Th = Xh/Vx which is Vx = Xh/Th
        jumpDist.up = maxSpeed   / jumpDuration.up;
        jumpDist.down = maxSpeed / jumpDuration.down;

        // Set double jump
        jumpsBetweenGrounding = jumpSettingsGMTK.doubleJump ? 2 : 1;

        jumpSettingsVariableHeightGMTK.useVariableJumpHeight = jumpSettingsGMTK.variableJumpHeight;
        jumpSettingsVariableHeightGMTK.gravUpMultiplierOnRelease = jumpSettingsGMTK.jumpCutOff;
        jumpSettingsVariableHeightGMTK.upwardVelocityZeroing = false;
        jumpSettingsVariableHeightGMTK.minJumpButtonHeldTime = 0.05f; // 100ms is a typical shortest time for a button to be held.
        
        jumpSettingsVariableHeight = jumpSettingsVariableHeightGMTK;
    }

}

[System.Serializable]
public class CSSO_FloatUpDown {
    public float up, down;
}

#if UNITY_EDITOR
[CustomPropertyDrawer( typeof( CSSO_FloatUpDown ) )]
public class CSSO_FloatUpDown_Drawer : PropertyDrawer {
    static public GUIStyle styleLabelGray = null, styleLabelGrayBold = null; 
    // SerializedProperty m_stat;

    // Draw the property inside the given rect
    public override void OnGUI( Rect position, SerializedProperty property, GUIContent label ) {
        // Init the SerializedProperty fields
        //if ( m_show == null ) m_show = property.FindPropertyRelative( "show" );
        //if ( m_recNum == null ) m_recNum = property.FindPropertyRelative( "recNum" );
        //if ( m_playerName == null ) m_playerName = property.FindPropertyRelative( "playerName" );
        //if ( m_dateTime == null ) m_dateTime = property.FindPropertyRelative( "dateTime" );

        CSSO_FloatUpDown fud = fieldInfo.GetValue( property.serializedObject.targetObject ) as CSSO_FloatUpDown;

        // Using BeginProperty / EndProperty on the parent property means that
        // prefab override logic works on the entire property.
        EditorGUI.BeginProperty( position, label, property );

        // Draw label
        //position = EditorGUI.PrefixLabel( position, GUIUtility.GetControlID( FocusType.Passive ), GUIContent.none );// label );
        if ( styleLabelGray == null) {
            styleLabelGray = new GUIStyle( EditorStyles.label );
            styleLabelGray.richText = true;
        }

        string colorString = "#606060ff";

        // Don't make child fields be indented
        var indent = EditorGUI.indentLevel;
        EditorGUI.indentLevel = 1;

        EditorGUI.LabelField(position, $"<b><color={colorString}>{property.displayName}</color></b>", styleLabelGray );
        EditorGUI.indentLevel = 8;
        EditorGUI.LabelField( position, $"<color={colorString}>up: {fud.up:0.0###}</color>", styleLabelGray );
        EditorGUI.indentLevel = 14;
        EditorGUI.LabelField( position, $"<color={colorString}>down: {fud.down:0.0###}</color>", styleLabelGray );

        // Set indent back to what it was
        EditorGUI.indentLevel = indent;

        EditorGUI.EndProperty();
    }
}

// This was a bad idea because the CSSO doesn't know who the character is. Moved to CharacterJump
// [CustomEditor( typeof(Character_Settings_SO) )]
// public class CSSO_Editor : Editor {
//     private Character_Settings_SO csso;
//
//     private void OnEnable() {
//         csso = (Character_Settings_SO) target;
//     }
//
//
//     private void OnSceneGUI() {
//         if ( csso == null || csso.jumpLinePoints == null ) return;
//         
//         // Handles.matrix = Matrix4x4.Translate(); // Not needed because jump will be shown at origin.
//         Handles.color = Color.green;
//         Handles.DrawAAPolyLine(4, csso.jumpLinePoints);
//     }
// }


#endif