diff --git a/Assets/_Classic Game Starter Kit/GMTK+ Platformer Toolkit/CharacterJump.cs b/Assets/_Classic Game Starter Kit/GMTK+ Platformer Toolkit/CharacterJump.cs
index cf441521b9deb4225c251b7b4ea6f1bdbf30d056..9b977b33fbf4900abc8ead98d894f01747e7e202 100644
--- a/Assets/_Classic Game Starter Kit/GMTK+ Platformer Toolkit/CharacterJump.cs	
+++ b/Assets/_Classic Game Starter Kit/GMTK+ Platformer Toolkit/CharacterJump.cs	
@@ -1,3 +1,4 @@
+using NaughtyAttributes;
 using UnityEngine;
 using UnityEngine.InputSystem;
 
@@ -10,7 +11,8 @@
 [RequireComponent(typeof(CharacterGround))]
 public class CharacterJump : MonoBehaviour
 {
-    [Header("Components")]
+    // [Header("Components")]
+    [InfoBox("If you want to see a prediction of the jump when this GameObject is selected, check showJumpLine in the Character_Settings_SO that you're using.", EInfoBoxType.Normal)]
     [XnTools.Hidden] public Rigidbody2D rigid;
     [XnTools.Hidden] public Vector2 velocity;
 
@@ -33,7 +35,7 @@ public class CharacterJump : MonoBehaviour
 //    [SerializeField, Range(0, 1)][Tooltip("How many times can you jump in the air?")] public int maxAirJumps = 0;
 
 //    [Header("Options")]
-//    [Tooltip("Should the character drop when you let go of jump?")] public bool variablejumpHeight;
+//    [Tooltip("Should the character drop when you let go of jump?")] public bool variableJumpHeight;
 //    [SerializeField, Range(1f, 10f)][Tooltip("Gravity multiplier when you let go of jump")] public float jumpCutOff;
 //    [SerializeField][Tooltip("The fastest speed the character can fall")] public float speedLimit;
 //    [SerializeField, Range(0f, 0.3f)][Tooltip("How long should coyote time last?")] public float coyoteTime = 0.15f;
@@ -82,7 +84,7 @@ public void OnJump(InputAction.CallbackContext context)
 
     private void SetPhysics() {
         //Determine the character's gravity scale, using the stats provided. Multiply it by a gravMultiplier, used later
-        Vector2 newGravity = new Vector2( 0, ( -2 * characterSettingsSO.jumpHeight ) / ( characterSettingsSO.timeToJumpApex * characterSettingsSO.timeToJumpApex ) );
+        Vector2 newGravity = new Vector2( 0, ( -2 * characterSettingsSO.jumpHeight ) / ( characterSettingsSO.jumpDuration.up * characterSettingsSO.jumpDuration.up ) );
         rigid.gravityScale = ( newGravity.y / Physics2D.gravity.y ) * gravMultiplier;
     }
 
@@ -165,22 +167,20 @@ private void CalculateGravity()
             else
             {
                 //If we're using variable jump height...)
-                if ( characterSettingsSO.variablejumpHeight )
+                if ( characterSettingsSO.jumpSettingsVariableHeight.useVariableJumpHeight )
                 {
                     //Apply upward multiplier if player is rising and holding jump
-                    if (pressingJump && currentlyJumping)
-                    {
-                        gravMultiplier = characterSettingsSO.upwardMovementMultiplier;
+                    if (pressingJump && currentlyJumping) {
+                        gravMultiplier = characterSettingsSO.jumpGrav.up;// characterSettingsSO.upwardMovementMultiplier;
                     }
                     //But apply a special downward multiplier if the player lets go of jump
-                    else
-                    {
-                        gravMultiplier = characterSettingsSO.jumpCutOff;
+                    else {
+                        gravMultiplier = characterSettingsSO.jumpGrav.up * characterSettingsSO.jumpSettingsVariableHeight.gravUpMultiplierOnRelease;// characterSettingsSO.jumpCutOff;
                     }
                 }
                 else
                 {
-                    gravMultiplier = characterSettingsSO.upwardMovementMultiplier;
+                    gravMultiplier = characterSettingsSO.jumpGrav.up;// characterSettingsSO.upwardMovementMultiplier;
                 }
             }
         }
@@ -197,7 +197,7 @@ private void CalculateGravity()
             else
             {
                 //Otherwise, apply the downward gravity multiplier as Kit comes back to Earth
-                gravMultiplier = characterSettingsSO.downwardMovementMultiplier;
+                gravMultiplier = characterSettingsSO.jumpGrav.down; //characterSettingsSO.downwardMovementMultiplier;
             }
 
         }
@@ -233,8 +233,9 @@ private void DoAJump()
             SetPhysics();
 
             //If we have double jump on, allow us to jump again (but only once)
-            canJumpAgain = ( characterSettingsSO.maxAirJumps == 1 && canJumpAgain == false);
-
+            // canJumpAgain = ( characterSettingsSO.maxAirJumps == 1 && canJumpAgain == false);
+            canJumpAgain = ( characterSettingsSO.jumpsBetweenGrounding > 1 && canJumpAgain == false);
+            
             //Determine the power of the jump, based on our gravity and stats
             jumpSpeed = Mathf.Sqrt(-2f * Physics2D.gravity.y * rigid.gravityScale * characterSettingsSO.jumpHeight );
             if (jumpSpeed > 100) {
@@ -308,22 +309,39 @@ private void OnSceneGUI() {
             if ( !csso.showJumpLine ) return;
             if ( csso.jumpSettingsType == Character_Settings_SO.eJumpSettingsType.GMTK_GameMakersToolKit ) return;
             if (csso.jumpLinePoints == null) cMove.characterSettingsSO.CalculateJumpLine();
+
+            GUIStyle labelStyle = new GUIStyle( EditorStyles.foldoutHeader );
+            labelStyle.imagePosition = ImagePosition.TextOnly; // NOTE: This didn't seem to do anything.
+            labelStyle.richText = true;
             
-            Handles.matrix = Matrix4x4.Translate(cJump.transform.position); // Not needed because jump will be shown at origin.
+            Handles.matrix = Matrix4x4.Translate(cJump.transform.position);
             Handles.color = Color.green;
             Handles.DrawAAPolyLine(4, csso.jumpLinePoints);
+            Vector3 tVec;
             Vector3[] jSME = csso.jumpStartMidEndPoints;
             if ( jSME != null && jSME.Length == 3 ) {
                 Vector3 offset = Vector3.up * 0.2f;
                 Handles.DrawDottedLine( jSME[0] + offset, jSME[2] + offset, dashSize );
-                Vector3 tVec = ( jSME[0] + jSME[2] ) / 2f + offset * 4 + Vector3.left * 0.4f;
-                Handles.Label( tVec, $"Dist: {csso.maxJumpDistHeight.x:0.##}" );
+                tVec = ( jSME[0] + jSME[2] ) / 2f + offset * 4 + Vector3.left * 0.4f;
+                Handles.Label( tVec, $"<b>Dist: {csso.maxJumpDistHeight.x:0.##}</b>", labelStyle );
                 tVec = jSME[1];
                 tVec.y = 0;
                 Handles.DrawDottedLine( tVec, jSME[1], dashSize );
                 tVec = ( tVec + jSME[1] ) / 2f + Vector3.left * 0.4f;
-                Handles.Label( tVec, $"Height: {csso.maxJumpDistHeight.y:0.##}" );
+                Handles.Label( tVec, $"<b>Height: {csso.maxJumpDistHeight.y:0.##}</b>", labelStyle );
             }
+
+            if ( csso.jumpSettingsVariableHeight.useVariableJumpHeight ) {
+                Handles.color = Color.magenta;
+                Handles.DrawAAPolyLine( 8, csso.minJumpLinePoints.ToArray() );
+                if ( csso.minJumpStartMidEndPoints        != null &&
+                     csso.minJumpStartMidEndPoints.Length == 3 ) {
+                    tVec = csso.minJumpStartMidEndPoints[0] + Vector3.down * 0.25f;
+                    Handles.Label( tVec, $"<b>Min: Ht: {csso.minJumpDistHeight.y:0.##}   Dst: {csso.minJumpDistHeight.x:0.##}" +
+                                         $"   tApex: {csso.minTimeApexFull.x:0.##}   tFull: {csso.minTimeApexFull.y:0.##}</b>", labelStyle );
+                }
+            }
+
         }
     }    
     
diff --git a/Assets/_Classic Game Starter Kit/GMTK+ Platformer Toolkit/Character_Settings_SO.cs b/Assets/_Classic Game Starter Kit/GMTK+ Platformer Toolkit/Character_Settings_SO.cs
index 5b69a794eb9cf7c84c0b9ade2894fc483eb340c7..fc1906a46fb27c30b8bd16b3bbe2680a23f56b41 100644
--- a/Assets/_Classic Game Starter Kit/GMTK+ Platformer Toolkit/Character_Settings_SO.cs	
+++ b/Assets/_Classic Game Starter Kit/GMTK+ Platformer Toolkit/Character_Settings_SO.cs	
@@ -3,6 +3,7 @@
 using System.Collections.Generic;
 using UnityEngine;
 using NaughtyAttributes;
+using UnityEngine.Timeline;
 #if UNITY_EDITOR
 using UnityEditor;
 #endif
@@ -11,130 +12,180 @@
 /// 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 )]
+[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;
+    [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;
+    [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;
+    [Range( 1f, 10f )]
+    public float jumpHeight = 4f;
 
-    [ShowIf("jumpSettingsType", eJumpSettingsType.CGSK_Time)]
+    [ShowIf( "jumpSettingsType", eJumpSettingsType.CGSK_Time )]
     public CGSK_JumpSettings_Time jumpSettingsTime;
-    
-    [ShowIf("jumpSettingsType", eJumpSettingsType.CGSK_Distance)]
+
+    [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( "CGSK Derived Jump Properties" )]
+    [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 Settings - GameMakers ToolKit")]
-    [ShowIf("jumpSettingsType", eJumpSettingsType.GMTK_GameMakersToolKit)]
-    [SerializeField, Range( 1f, 10f )] [Tooltip( "How long it takes to reach that height before coming back down" )]
-    public float jumpDurationFromVideo = 5;
-    //If you're using your stats from Platformer Toolkit with this character controller,
-    // please note that the number on the Jump Duration handle does not match this stat
-    // It is re-scaled, from 0.2f - 1.25f, to 1 - 10.
-    [XnTools.ReadOnly]
-    [SerializeField, Range( 0.1f, 1.25f )][ShowIf("jumpSettingsType", eJumpSettingsType.GMTK_GameMakersToolKit)]
-    [Tooltip( "For some reason, the GMTK version of the value in the video is different from the one here, so I calculate this from the other value." )]
-    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;
-    
     [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;
+    [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;
+    [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;
+    [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:
-            CalculateDerivedCGSKJumpValues_Time();
+            CalculateDerivedJumpValues_Time();
             break;
-        
+
         case eJumpSettingsType.CGSK_Distance:
-            CalculateDerivedCGSKJumpValues_Distance();
+            CalculateDerivedJumpValues_Distance();
             break;
-        
+
         case eJumpSettingsType.GMTK_GameMakersToolKit:
-            timeToJumpApex = scale( 1, 10, 0.2f, 1.25f, jumpDurationFromVideo );
+            CalculateDerivedJumpValues_GMTK();
             break;
         }
-        
+
         CalculateJumpLine();
     }
 
-    
+
     static private int       jumpLineResolution = 64; // NOTE: This must be a positive even number
     internal       Vector3[] jumpLinePoints;
-    [HideIf( "jumpSettingsType", eJumpSettingsType.GMTK_GameMakersToolKit )]
-    [BoxGroup( "CGSK Derived Jump Properties" )][SerializeField][XnTools.ReadOnly]
-    internal       Vector2   maxJumpDistHeight;
-    internal       Vector3[] jumpStartMidEndPoints;
+    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;
@@ -142,6 +193,7 @@ internal void CalculateJumpLine() {
         }
         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];
@@ -150,10 +202,10 @@ internal void CalculateJumpLine() {
         Vector3 v = new Vector3( maxSpeed, jumpVel.up, 0 );
         int numSteps = jumpLineResolution / 2;
         // Jumping Up
-        float timeStep = jumpDuration.up  / (float) numSteps;
+        float timeStepUp = jumpDuration.up / (float) numSteps;
         int i = 1;
         for ( ; i <= jumpLineResolution / 2; i++ ) {
-            SimplifiedVelocityVerletIntegration(ref p, ref v, acc, timeStep);
+            SimplifiedVelocityVerletIntegration( ref p, ref v, acc, timeStepUp );
             // p.x += v.x         * timeStep;
             // v.y += jumpGrav.up * timeStep;
             // p.y += v.y         * timeStep;
@@ -163,9 +215,9 @@ internal void CalculateJumpLine() {
         maxJumpDistHeight.y = p.y;
         // Jumping Down
         acc.y = jumpGrav.down;
-        timeStep = jumpDuration.down / (float) (numSteps-1);
+        float timeStepDown = jumpDuration.down / (float) ( numSteps - 1 );
         for ( ; i < jumpLineResolution; i++ ) {
-            SimplifiedVelocityVerletIntegration(ref p, ref v, acc, timeStep);
+            SimplifiedVelocityVerletIntegration( ref p, ref v, acc, timeStepDown );
             // p.x += v.x           * timeStep;
             // v.y += jumpGrav.down * timeStep;
             // p.y += v.y           * timeStep;
@@ -173,15 +225,93 @@ internal void CalculateJumpLine() {
         }
         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
+    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 );
@@ -193,40 +323,46 @@ public float scale( float OldMin, float OldMax, float NewMin, float NewMax, floa
 
     [System.Serializable]
     public class CGSK_JumpSettings_Time {
-        [Header("Classic Game Starter Kit - Time Jump Settings")]
+        [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;
+        [Tooltip( "The fraction of the jump that is going up" )]
+        [Range( 0.05f, 0.95f )]
+        public float jumpApexFraction = 0.6f;
     }
-    
-    private void CalculateDerivedCGSKJumpValues_Time() {
+
+    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;
+        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")]
+        [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;
+        [Tooltip( "The fraction of the jump that is going up" )]
+        [Range( 0.05f, 0.95f )]
+        public float jumpApexFraction = 0.6f;
     }
-    
-    private void CalculateDerivedCGSKJumpValues_Distance() {
+
+    private void CalculateDerivedJumpValues_Distance() {
         jumpDist.up = jumpSettingsDistance.fullJumpDistanceMax *
-                       jumpSettingsDistance.jumpApexFraction;
+                      jumpSettingsDistance.jumpApexFraction;
         jumpDist.down = jumpSettingsDistance.fullJumpDistanceMax - jumpDist.up;
         // Th = Xh / Vh
         jumpDuration.up = jumpDist.up     / maxSpeed;
@@ -235,8 +371,80 @@ private void CalculateDerivedCGSKJumpValues_Distance() {
         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 );
+        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;
     }
 
 }
diff --git a/Assets/_Classic Game Starter Kit/__Scripts/XnTools/InfoComponent.cs b/Assets/_Classic Game Starter Kit/__Scripts/XnTools/InfoComponent.cs
index 8d5928a61d86ea1db406e91766acebeee826dc2a..f116eafaf5b9d6b946bf243df826e5938fba428b 100644
--- a/Assets/_Classic Game Starter Kit/__Scripts/XnTools/InfoComponent.cs	
+++ b/Assets/_Classic Game Starter Kit/__Scripts/XnTools/InfoComponent.cs	
@@ -25,9 +25,9 @@ public class InfoComponent : MonoBehaviour {
 //[CanEditMultipleObjects]
 public class InfoComponentEditor : Editor {
 	static private bool TURN_ON_DEFAULT_INSPECTORS = false;
+	static private bool DEBUG_GUI_ERRORS           = false;
 
-	private const int delayGUIStart = 2;
-	private int delayGUI = 0;
+	private double failedGUITimeStamp = -1f;
 
 	//static float kSpace = 16f;
 	InfoComponent info;
@@ -51,7 +51,6 @@ public class InfoComponentEditor : Editor {
 
 
 	void OnEnable() {
-		delayGUI = delayGUIStart;
 		info = (InfoComponent)target;
 		Init();
 	}
@@ -67,13 +66,15 @@ static void EnableEdit(MenuCommand command) {
 	}
 
 	public override void OnInspectorGUI() {
+		if ( failedGUITimeStamp == EditorApplication.timeSinceStartup ) return;
 		if (info == null) {
 			info = (InfoComponent)target;
 			Init();
 		}
-		if ( !m_Initialized ) return;
-		if ( delayGUI > 0 ) {
-			delayGUI--;
+		if ( !m_Initialized ) {
+			failedGUITimeStamp = EditorApplication.timeSinceStartup;
+			if ( DEBUG_GUI_ERRORS )
+				Debug.LogWarning( $"InfoComponent Init() failed but it didn't cause an error message!)" ); //at {failedGUITimeStamp:#,0.###}.");
 			return;
 		}
 
diff --git a/Assets/_Classic Game Starter Kit/___Demo Level/CGSK_Settings_SuperMarioBros.asset b/Assets/_Classic Game Starter Kit/___Demo Level/CGSK_Settings_SuperMarioBros.asset
index 200452f5c4d0304ca6a6f838b3fa6af919d7ea4c..6dd3fc6ebf45d24b24847808828a51b1cbc68a77 100644
--- a/Assets/_Classic Game Starter Kit/___Demo Level/CGSK_Settings_SuperMarioBros.asset	
+++ b/Assets/_Classic Game Starter Kit/___Demo Level/CGSK_Settings_SuperMarioBros.asset	
@@ -32,6 +32,16 @@ MonoBehaviour:
     fullJumpDistanceMin: 0.5
     fullJumpDistanceMax: 5.5
     jumpApexFraction: 0.6
+  jumpSettingsVariableHeightCGSK:
+    useVariableJumpHeight: 1
+    upwardVelocityZeroing: 0
+    minJumpButtonHeldTime: 0.05
+    gravUpMultiplierOnRelease: 2.54
+  jumpSettingsVariableHeightGMTK:
+    useVariableJumpHeight: 1
+    upwardVelocityZeroing: 0
+    minJumpButtonHeldTime: 0.05
+    gravUpMultiplierOnRelease: 1
   jumpDist:
     up: 3.3000002
     down: 2.1999998
@@ -44,16 +54,18 @@ MonoBehaviour:
   jumpGrav:
     up: -22.22222
     down: -50.000008
-  jumpDurationFromVideo: 5
-  timeToJumpApex: 0.6666666
-  upwardMovementMultiplier: 1
-  downwardMovementMultiplier: 1.4
-  maxAirJumps: 0
-  variablejumpHeight: 1
-  jumpCutOff: 2.5
+  maxJumpDistHeight: {x: 5.499999, y: 4}
+  minJumpDistHeight: {x: 3.0800004, y: 1.9994661}
+  jumpSettingsGMTK:
+    jumpDuration: 5
+    downGravity: 6.17
+    doubleJump: 0
+    variableJumpHeight: 0
+    jumpCutOff: 0
   speedLimit: 26.45
   coyoteTime: 0.15
   jumpBuffer: 0.15
+  jumpsBetweenGrounding: 1
   squashAndStretch: 0
   jumpSquashSettings: {x: 0, y: 0, z: 0}
   landSquashSettings: {x: 0, y: 0, z: 0}
@@ -63,4 +75,3 @@ MonoBehaviour:
   leanForward: 0
   maxTilt: 0
   tiltSpeed: 0
-  maxJumpDistHeight: {x: 5.499999, y: 4}