private static void Prefix(ObstacleController __instance, ref VectorState?__state, ref float time, ObstacleData ____obstacleData,
                                   ref Vector3 ____startPos, ref Vector3 ____midPos, ref Vector3 ____endPos, ref Quaternion ____worldRotation, ref Quaternion ____inverseWorldRotation)
        {
            if (____obstacleData is CustomObstacleData customData)
            {
                dynamic dynData = customData.customData;

                Quaternion?rotation = GetWorldRotation(dynData, time);
                if (rotation.HasValue)
                {
                    ____worldRotation        = rotation.Value;
                    ____inverseWorldRotation = Quaternion.Inverse(rotation.Value);
                }

                Quaternion?localRotation = GetLocalRotation(dynData, time);

                __instance.transform.localRotation = ____worldRotation * localRotation.GetValueOrDefault(Quaternion.identity);

                // fucky wucky
                List <PositionData> positionData = Trees.at(dynData, "varPosition");
                if (positionData != null)
                {
                    Vector3 startPos = ____startPos;
                    Vector3 midPos   = ____midPos;
                    Vector3 endPos   = ____endPos;

                    float timeCopy = time;
                    IEnumerable <PositionData> truncatedPosition = positionData
                                                                   .Where(n => n.time < timeCopy);

                    float        movementTime       = 0;
                    PositionData activePositionData = null;
                    foreach (PositionData pos in truncatedPosition)
                    {
                        if (pos.time + pos.duration < time)
                        {
                            if (!pos.relative)
                            {
                                movementTime += pos.duration;
                            }
                            ____startPos += (pos.endPosition * _noteLinesDistance);
                            ____midPos   += (pos.endPosition * _noteLinesDistance);
                            ____endPos   += (pos.endPosition * _noteLinesDistance);
                        }
                        else
                        {
                            if (!pos.relative)
                            {
                                movementTime += time - pos.time;
                            }
                            activePositionData = pos;
                        }
                    }

                    __state = new VectorState(startPos, midPos, endPos, timeCopy, activePositionData);
                    time   -= movementTime;
                }
            }
        }
 private static void Postfix(ref Vector3 __result, VectorState?__state, ref Vector3 ____startPos, ref Vector3 ____midPos, ref Vector3 ____endPos)
 {
     // variable position kinda wacky
     if (__state.HasValue)
     {
         VectorState vectorState = __state.Value;
         ____startPos = vectorState.startPos;
         ____midPos   = vectorState.midPos;
         ____endPos   = vectorState.endPos;
         PositionData pos = vectorState.activePositionData;
         if (pos != null)
         {
             __result += Vector3.Lerp(pos.startPosition, pos.endPosition,
                                      Easings.Interpolate((vectorState.time - pos.time) / pos.duration, pos.easing)) * _noteLinesDistance;
         }
     }
 }
 public void SetForce(VectorState input)
 {
     SetForce(input.state);
 }