public static RingRotationEffectController Create(TrackLaneRingsRotationEffectSpawner baseEffect)
        {
            BeatmapEventType eventTypeForThisEffect = Helper.GetValue <BeatmapEventType>(baseEffect, "_beatmapEventType");
            RotationStepType stepType = Helper.GetValue <RotationStepType>(baseEffect, "_rotationStepType");
            float            stepMax  = Helper.GetValue <float>(baseEffect, "_rotationStep");
            float            rotation = Helper.GetValue <float>(baseEffect, "_rotation");
            int   propagationSpeed    = Helper.GetValue <int>(baseEffect, "_rotationPropagationSpeed");
            float flexySpeed          = Helper.GetValue <float>(baseEffect, "_rotationFlexySpeed");

            TrackLaneRingsRotationEffect rotationEffect = Helper.GetValue <TrackLaneRingsRotationEffect>(baseEffect, "_trackLaneRingsRotationEffect");

            TrackLaneRingsManager manager = Helper.GetValue <TrackLaneRingsManager>(rotationEffect, "_trackLaneRingsManager");

            float startupAngle            = Helper.GetValue <float>(rotationEffect, "_startupRotationAngle");
            float startupStep             = Helper.GetValue <float>(rotationEffect, "_startupRotationStep");
            int   startupPropagationSpeed = Helper.GetValue <int>(rotationEffect, "_startupRotationPropagationSpeed");
            float startupFlexySpeed       = Helper.GetValue <float>(rotationEffect, "_startupRotationFlexySpeed");

            RotationEffect startupRotationEffect = new RotationEffect
            {
                progress         = 0,
                angle            = startupAngle,
                step             = startupStep,
                propagationSpeed = startupPropagationSpeed,
                flexySpeed       = startupFlexySpeed
            };

            RingRotationEffectController controller = new GameObject("TwitchFXRingRotationController").AddComponent <RingRotationEffectController>();

            controller.eventTypeForThisEffect = eventTypeForThisEffect;
            controller.manager  = manager;
            controller.name     = baseEffect.name;
            controller.isBig    = baseEffect.name.Contains("Big");
            controller.stepType = stepType;
            controller.stepMax  = stepMax;
            controller.startupRotationEffect = startupRotationEffect;
            controller.rotation         = rotation;
            controller.propagationSpeed = propagationSpeed;
            controller.flexySpeed       = flexySpeed;

            return(controller);
        }
        private static bool Prefix(
            TrackLaneRingsRotationEffectSpawner __instance,
            BeatmapEventData beatmapEventData,
            BeatmapEventType ____beatmapEventType,
            TrackLaneRingsRotationEffect ____trackLaneRingsRotationEffect,
            float ____rotation,
            float ____rotationStep,
            int ____rotationPropagationSpeed,
            float ____rotationFlexySpeed,
            TrackLaneRingsRotationEffectSpawner.RotationStepType ____rotationStepType)
        {
            if (beatmapEventData.type == ____beatmapEventType)
            {
                ChromaEventData?chromaData = TryGetEventData(beatmapEventData);
                if (chromaData == null)
                {
                    return(true);
                }

                // Added in 1.8
                float rotationStep = 0f;
                switch (____rotationStepType)
                {
                case TrackLaneRingsRotationEffectSpawner.RotationStepType.Range0ToMax:
                    rotationStep = UnityEngine.Random.Range(0f, ____rotationStep);
                    break;

                case TrackLaneRingsRotationEffectSpawner.RotationStepType.Range:
                    rotationStep = UnityEngine.Random.Range(-____rotationStep, ____rotationStep);
                    break;

                case TrackLaneRingsRotationEffectSpawner.RotationStepType.MaxOr0:
                    rotationStep = (UnityEngine.Random.value < 0.5f) ? ____rotationStep : 0f;
                    break;
                }

                string?nameFilter = chromaData.NameFilter;
                if (nameFilter != null)
                {
                    if (!__instance.name.ToLower().Equals(nameFilter.ToLower()))
                    {
                        return(false);
                    }
                }

                int?dir = chromaData.Direction;

                bool rotRight;
                if (!dir.HasValue)
                {
                    rotRight = UnityEngine.Random.value < 0.5f;
                }
                else
                {
                    rotRight = dir == 1;
                }

                bool?counterSpin = chromaData.CounterSpin;
                if (counterSpin.HasValue && counterSpin == true)
                {
                    if (!__instance.name.Contains("Big"))
                    {
                        rotRight = !rotRight;
                    }
                }

                bool?reset = chromaData.Reset;
                if (reset.HasValue && reset == true)
                {
                    TriggerRotation(____trackLaneRingsRotationEffect, rotRight, ____rotation, 0, 50, 50);
                    return(false);
                }

                float step     = chromaData.Step.GetValueOrDefault(rotationStep);
                float prop     = chromaData.Prop.GetValueOrDefault(____rotationPropagationSpeed);
                float speed    = chromaData.Speed.GetValueOrDefault(____rotationFlexySpeed);
                float rotation = chromaData.Rotation.GetValueOrDefault(____rotation);

                float stepMult  = chromaData.StepMult;
                float propMult  = chromaData.PropMult;
                float speedMult = chromaData.SpeedMult;

                TriggerRotation(____trackLaneRingsRotationEffect, rotRight, rotation, step * stepMult, prop * propMult, speed * speedMult);
                return(false);
            }

            return(true);
        }
        private static bool Prefix(
            TrackLaneRingsRotationEffectSpawner __instance,
            BeatmapEventData beatmapEventData,
            BeatmapEventType ____beatmapEventType,
            TrackLaneRingsRotationEffect ____trackLaneRingsRotationEffect,
            float ____rotation,
            float ____rotationStep,
            int ____rotationPropagationSpeed,
            float ____rotationFlexySpeed,
            TrackLaneRingsRotationEffectSpawner.RotationStepType ____rotationStepType)
        {
            if (beatmapEventData.type == ____beatmapEventType)
            {
                if (beatmapEventData is CustomBeatmapEventData customData)
                {
                    // Added in 1.8
                    float rotationStep = 0f;
                    switch (____rotationStepType)
                    {
                    case TrackLaneRingsRotationEffectSpawner.RotationStepType.Range0ToMax:
                        rotationStep = UnityEngine.Random.Range(0f, ____rotationStep);
                        break;

                    case TrackLaneRingsRotationEffectSpawner.RotationStepType.Range:
                        rotationStep = UnityEngine.Random.Range(-____rotationStep, ____rotationStep);
                        break;

                    case TrackLaneRingsRotationEffectSpawner.RotationStepType.MaxOr0:
                        rotationStep = (UnityEngine.Random.value < 0.5f) ? ____rotationStep : 0f;
                        break;
                    }

                    dynamic dynData = customData.customData;

                    string nameFilter = Trees.at(dynData, NAMEFILTER);
                    if (nameFilter != null)
                    {
                        if (!__instance.name.ToLower().Equals(nameFilter.ToLower()))
                        {
                            return(false);
                        }
                    }

                    int?dir = (int?)Trees.at(dynData, DIRECTION);
                    if (!dir.HasValue)
                    {
                        dir = -1;
                    }

                    bool rotRight;
                    if (dir == -1)
                    {
                        rotRight = UnityEngine.Random.value < 0.5f;
                    }
                    else
                    {
                        rotRight = dir == 1 ? true : false;
                    }

                    bool?counterSpin = Trees.at(dynData, COUNTERSPIN);
                    if (counterSpin.HasValue && counterSpin == true)
                    {
                        if (!__instance.name.Contains("Big"))
                        {
                            rotRight = !rotRight;
                        }
                    }

                    bool?reset = Trees.at(dynData, RESET);
                    if (reset.HasValue && reset == true)
                    {
                        TriggerRotation(____trackLaneRingsRotationEffect, rotRight, ____rotation, 0, 50, 50);
                        return(false);
                    }

                    float step     = ((float?)Trees.at(dynData, STEP)).GetValueOrDefault(rotationStep);
                    float prop     = ((float?)Trees.at(dynData, PROP)).GetValueOrDefault(____rotationPropagationSpeed);
                    float speed    = ((float?)Trees.at(dynData, SPEED)).GetValueOrDefault(____rotationFlexySpeed);
                    float rotation = ((float?)Trees.at(dynData, ROTATION)).GetValueOrDefault(____rotation);

                    float stepMult  = ((float?)Trees.at(dynData, STEPMULT)).GetValueOrDefault(1f);
                    float propMult  = ((float?)Trees.at(dynData, PROPMULT)).GetValueOrDefault(1f);
                    float speedMult = ((float?)Trees.at(dynData, SPEEDMULT)).GetValueOrDefault(1f);

                    TriggerRotation(____trackLaneRingsRotationEffect, rotRight, rotation, step * stepMult, prop * propMult, speed * speedMult);
                    return(false);
                }
            }

            return(true);
        }
#pragma warning disable SA1313 // Parameter names should begin with lower-case letter
        private static bool Prefix(
            TrackLaneRingsRotationEffectSpawner __instance,
            BeatmapEventData beatmapEventData,
            BeatmapEventType ____beatmapEventType,
            TrackLaneRingsRotationEffect ____trackLaneRingsRotationEffect,
            float ____rotation,
            float ____rotationStep,
            int ____rotationPropagationSpeed,
            float ____rotationFlexySpeed,
            TrackLaneRingsRotationEffectSpawner.RotationStepType ____rotationStepType)
#pragma warning restore SA1313 // Parameter names should begin with lower-case letter
        {
            if (beatmapEventData.type == ____beatmapEventType)
            {
                if (beatmapEventData is CustomBeatmapEventData customData)
                {
                    // Added in 1.8
                    float rotationStep = 0f;
                    switch (____rotationStepType)
                    {
                    case TrackLaneRingsRotationEffectSpawner.RotationStepType.Range0ToMax:
                        rotationStep = UnityEngine.Random.Range(0f, ____rotationStep);
                        break;

                    case TrackLaneRingsRotationEffectSpawner.RotationStepType.Range:
                        rotationStep = UnityEngine.Random.Range(-____rotationStep, ____rotationStep);
                        break;

                    case TrackLaneRingsRotationEffectSpawner.RotationStepType.MaxOr0:
                        rotationStep = (UnityEngine.Random.value < 0.5f) ? ____rotationStep : 0f;
                        break;
                    }

                    dynamic dynData = customData.customData;

                    string nameFilter = Trees.at(dynData, "_nameFilter");
                    if (nameFilter != null)
                    {
                        if (!__instance.name.ToLower().Equals(nameFilter.ToLower()))
                        {
                            return(false);
                        }
                    }

                    int?dir = (int?)Trees.at(dynData, "_direction");
                    if (!dir.HasValue)
                    {
                        dir = -1;
                    }

                    bool rotRight;
                    if (dir == -1)
                    {
                        rotRight = UnityEngine.Random.value < 0.5f;
                    }
                    else
                    {
                        rotRight = dir == 1 ? true : false;
                    }

                    bool?counterSpin = Trees.at(dynData, "_counterSpin");
                    if (counterSpin.HasValue && counterSpin == true)
                    {
                        if (!__instance.name.Contains("Big"))
                        {
                            rotRight = !rotRight;
                        }
                    }

                    bool?reset = Trees.at(dynData, "_reset");
                    if (reset.HasValue && reset == true)
                    {
                        TriggerRotation(____trackLaneRingsRotationEffect, rotRight, ____rotation, 0, 50, 50);
                        return(false);
                    }

                    float step  = ((float?)Trees.at(dynData, "_step")).GetValueOrDefault(rotationStep);
                    float prop  = ((float?)Trees.at(dynData, "_prop")).GetValueOrDefault(____rotationPropagationSpeed);
                    float speed = ((float?)Trees.at(dynData, "_speed")).GetValueOrDefault(____rotationFlexySpeed);

                    float stepMult  = ((float?)Trees.at(dynData, "_stepMult")).GetValueOrDefault(1f);
                    float propMult  = ((float?)Trees.at(dynData, "_propMult")).GetValueOrDefault(1f);
                    float speedMult = ((float?)Trees.at(dynData, "_speedMult")).GetValueOrDefault(1f);

                    TriggerRotation(____trackLaneRingsRotationEffect, rotRight, ____rotation, step * stepMult, prop * propMult, speed * speedMult);
                    return(false);
                }
            }

            return(true);
        }