/// <summary>
        /// Plays a transient haptic, signature with more fine control
        /// </summary>
        /// <param name="vibrateiOS"></param>
        /// <param name="iOSIntensity"></param>
        /// <param name="iOSSharpness"></param>
        /// <param name="vibrateAndroid"></param>
        /// <param name="androidIntensity"></param>
        /// <param name="androidSharpness"></param>
        /// <param name="vibrateAndroidIfNoSupport"></param>
        /// <param name="rumble"></param>
        /// <param name="rumbleLowFrequency"></param>
        /// <param name="rumbleHighFrequency"></param>
        /// <param name="controllerID"></param>
        /// <param name="coroutineSupport"></param>
        public static void TransientHaptic(bool vibrateiOS, float iOSIntensity, float iOSSharpness,
                                           bool vibrateAndroid, float androidIntensity = 1f, float androidSharpness = 1f,
                                           bool vibrateAndroidIfNoSupport = false,
                                           bool rumble = true, float rumbleLowFrequency         = 1f, float rumbleHighFrequency = 1f, int controllerID = -1,
                                           MonoBehaviour coroutineSupport = null, bool threaded = true)
        {
            if (!_vibrationsActive)
            {
                return;
            }

            DebugLog("[MMVibrationManager] Transient Haptic");

            if (Android() && vibrateAndroid)
            {
                if (!MMNVAndroid.AndroidHasAmplitudeControl() && !vibrateAndroidIfNoSupport)
                {
                    return;
                }
                androidIntensity = Remap(androidIntensity, 0f, 1f, 0, 255);
                MMNVAndroid.AndroidVibrate(100, (int)(androidIntensity));
            }
            else if (iOS() && vibrateiOS)
            {
                if ((iOSVersion >= 13) && HapticsSupported())
                {
                    MMNViOSCoreHaptics.PlayTransientHapticPattern(iOSIntensity, iOSSharpness, threaded);
                    _hapticsPlayedOnce = true;
                }
                else
                {
                    if (iOSIntensity < 0.3f)
                    {
                        MMNViOS.iOSTriggerHaptics(HapticTypes.LightImpact);
                    }
                    else if ((iOSIntensity >= 0.3f) && (iOSIntensity < 0.6f))
                    {
                        MMNViOS.iOSTriggerHaptics(HapticTypes.MediumImpact);
                    }
                    else
                    {
                        MMNViOS.iOSTriggerHaptics(HapticTypes.HeavyImpact);
                    }
                }
            }
            if (rumble && (coroutineSupport != null))
            {
                #if MOREMOUNTAINS_NICEVIBRATIONS_RUMBLE
                MMNVRumble.Rumble(rumbleLowFrequency, rumbleHighFrequency, 0.08f, coroutineSupport, controllerID);
                #endif
            }
        }
        /// <summary>
        /// Plays a advanced haptic pattern,
        /// </summary>
        /// <param name="ios"></param>
        /// <param name="iOSJSONString"></param>
        /// <param name="android"></param>
        /// <param name="androidPattern"></param>
        /// <param name="androidAmplitudes"></param>
        /// <param name="androidRepeat"></param>
        /// <param name="vibrateAndroidIfNoSupport"></param>
        /// <param name="rumble"></param>
        /// <param name="rumblePattern"></param>
        /// <param name="rumbleLowFreqAmplitudes"></param>
        /// <param name="rumbleHighFreqAmplitudes"></param>
        /// <param name="rumbleRepeat"></param>
        /// <param name="fallbackOldiOS"></param>
        /// <param name="coroutineSupport"></param>
        /// <param name="controllerID"></param>
        public static void AdvancedHapticPattern(bool ios, string iOSJSONString,
                                                 bool android, long[] androidPattern, int[] androidAmplitudes, int androidRepeat,
                                                 bool vibrateAndroidIfNoSupport,
                                                 bool rumble,
                                                 long[] rumblePattern, int[] rumbleLowFreqAmplitudes, int[] rumbleHighFreqAmplitudes, int rumbleRepeat,
                                                 HapticTypes fallbackOldiOS     = HapticTypes.None,
                                                 MonoBehaviour coroutineSupport = null, int controllerID = -1, bool threaded = false)
        {
            if (!_vibrationsActive)
            {
                return;
            }

            DebugLog("[MMVibrationManager] Advanced Haptic Pattern");

            if (Android())
            {
                if (!MMNVAndroid.AndroidHasAmplitudeControl() && !vibrateAndroidIfNoSupport)
                {
                    return;
                }
                MMNVAndroid.AndroidVibrate(androidPattern, androidAmplitudes, androidRepeat, threaded);
            }
            else if (iOS())
            {
                if ((iOSVersion >= 13) && HapticsSupported())
                {
                    MMNViOSCoreHaptics.PlayCoreHapticsFromJSON(iOSJSONString, threaded);
                    _hapticsPlayedOnce = true;
                }
                else
                {
                    MMNViOS.iOSTriggerHaptics(fallbackOldiOS);
                }
            }
#if MOREMOUNTAINS_NICEVIBRATIONS_RUMBLE
            if (coroutineSupport != null)
            {
                MMNVRumble.Rumble(rumblePattern, rumbleLowFreqAmplitudes, rumbleHighFreqAmplitudes, rumbleRepeat, coroutineSupport, controllerID);
            }
#endif
        }
        /// <summary>
        /// Triggers a haptic feedback of the specified type
        /// </summary>
        /// <param name="type">Type.</param>
        public static void Haptic(HapticTypes type, bool defaultToRegularVibrate = false, bool alsoRumble = false, MonoBehaviour coroutineSupport = null, int controllerID = -1)
        {
            if (!_vibrationsActive)
            {
                return;
            }

            DebugLog("[MMVibrationManager] Regular Haptic");

            if (Android())
            {
                switch (type)
                {
                case HapticTypes.None:
                    // do nothing
                    break;

                case HapticTypes.Selection:
                    MMNVAndroid.AndroidVibrate(LightDuration, LightAmplitude);
                    break;

                case HapticTypes.Success:
                    MMNVAndroid.AndroidVibrate(_successPattern, _successPatternAmplitude, -1);
                    break;

                case HapticTypes.Warning:
                    MMNVAndroid.AndroidVibrate(_warningPattern, _warningPatternAmplitude, -1);
                    break;

                case HapticTypes.Failure:
                    MMNVAndroid.AndroidVibrate(_failurePattern, _failurePatternAmplitude, -1);
                    break;

                case HapticTypes.LightImpact:
                    MMNVAndroid.AndroidVibrate(_lightImpactPattern, _lightImpactPatternAmplitude, -1);
                    break;

                case HapticTypes.MediumImpact:
                    MMNVAndroid.AndroidVibrate(_mediumImpactPattern, _mediumImpactPatternAmplitude, -1);
                    break;

                case HapticTypes.HeavyImpact:
                    MMNVAndroid.AndroidVibrate(_HeavyImpactPattern, _HeavyImpactPatternAmplitude, -1);
                    break;

                case HapticTypes.RigidImpact:
                    MMNVAndroid.AndroidVibrate(_rigidImpactPattern, _rigidImpactPatternAmplitude, -1);
                    break;

                case HapticTypes.SoftImpact:
                    MMNVAndroid.AndroidVibrate(_softImpactPattern, _softImpactPatternAmplitude, -1);
                    break;
                }
            }
            else if (iOS())
            {
                MMNViOS.iOSTriggerHaptics(type, defaultToRegularVibrate);
            }

            if (alsoRumble && (coroutineSupport != null))
            {
                #if MOREMOUNTAINS_NICEVIBRATIONS_RUMBLE
                switch (type)
                {
                case HapticTypes.None:
                    // do nothing
                    break;

                case HapticTypes.Selection:
                    MMNVRumble.Rumble(_rumbleLight.x, _rumbleMedium.y, _rumbleLight.z, coroutineSupport, controllerID);
                    break;

                case HapticTypes.Success:
                    MMNVRumble.Rumble(_successPattern, _successPatternAmplitude, -1, coroutineSupport, controllerID);
                    break;

                case HapticTypes.Warning:
                    MMNVRumble.Rumble(_warningPattern, _warningPatternAmplitude, -1, coroutineSupport, controllerID);
                    break;

                case HapticTypes.Failure:
                    MMNVRumble.Rumble(_failurePattern, _failurePatternAmplitude, -1, coroutineSupport, controllerID);
                    break;

                case HapticTypes.LightImpact:
                    MMNVRumble.Rumble(_rumbleLight.x, _rumbleLight.y, _rumbleLight.z, coroutineSupport, controllerID);
                    break;

                case HapticTypes.MediumImpact:
                    MMNVRumble.Rumble(_rumbleMedium.x, _rumbleMedium.y, _rumbleMedium.z, coroutineSupport, controllerID);
                    break;

                case HapticTypes.HeavyImpact:
                    MMNVRumble.Rumble(_rumbleHeavy.x, _rumbleHeavy.y, _rumbleHeavy.z, coroutineSupport, controllerID);
                    break;

                case HapticTypes.RigidImpact:
                    MMNVRumble.Rumble(_rumbleRigid.x, _rumbleRigid.y, _rumbleRigid.z, coroutineSupport, controllerID);
                    break;

                case HapticTypes.SoftImpact:
                    MMNVRumble.Rumble(_rumbleSoft.x, _rumbleSoft.y, _rumbleSoft.z, coroutineSupport, controllerID);
                    break;
                }
                #endif
            }
        }