Your problem is that the character movement component sets the state to falling in several places.
SimulateMovement:
if (!CurrentFloor.IsWalkableFloor())
{
if (!bSimGravityDisabled)
{
// No floor, must fall.
Velocity = NewFallVelocity(Velocity, FVector(0.f, 0.f, GetGravityZ()), DeltaSeconds);
}
SetMovementMode(MOVE_Falling);
}
Also in ApplyRootMotionToVelocity (and 1 or 2 more physics calc functions):
if( AppliedVelocityDelta.Z > LiftoffBound )
{
SetMovementMode(MOVE_Falling);
}
And more importantly, any time you perform the action “jump”.
DoJump:
// Don't jump if we can't move up/down.
if (!bConstrainToPlane || FMath::Abs(PlaneConstraintNormal.Z) != 1.f)
{
Velocity.Z = JumpZVelocity;
SetMovementMode(MOVE_Falling);
return true;
}
The right way to do this would be to write your own movement logic, is it worth it? Porbably not…
You can have a boolean that forcibly sets the state to Grappling within tick if true, this way you only cheat the engine for as long as you need it.