예제 #1
0
        /// <summary>
        /// Calculates the strain for one difficulty type and stores it in <paramref name="obj"/>. This assumes that
        /// <see cref="HitObject.Normpos"/> is already computed. This also sets <see cref="HitObject.IsSingle"/> if
        /// <paramref name="type"/> is <seealso cref="StrainType.Speed"/>.
        /// </summary>
        private static void DiffStrain(StrainType type, HitObject obj, HitObject prev, double speedMul)
        {
            double value       = 0.0;
            double timeElapsed = (obj.Time - prev.Time) / speedMul;
            double decay       =
                Math.Pow(DecayBase[type], timeElapsed / 1000.0);

            obj.DeltaTime = timeElapsed;

            /* this implementation doesn't account for sliders */
            if ((obj.Type & (HitObjectType.Slider | HitObjectType.Circle)) != 0)
            {
                double distance = (obj.Normpos - prev.Normpos).Length;
                obj.DeltaDistance = distance;

                if (type == StrainType.Speed)
                {
                    obj.IsSingle = distance > SingleSpacing;
                }

                value  = DiffSpacingWeight(type, distance, timeElapsed, prev.DeltaDistance, prev.DeltaTime, obj.Angle);
                value *= WeightScaling[type];
            }

            obj.Strains[type] = prev.Strains[type] * decay + value;
        }
예제 #2
0
        private static double DiffSpacingWeight(StrainType type, double distance)
        {
            switch (type)
            {
            case StrainType.Aim:
                return(Math.Pow(distance, 0.99));

            case StrainType.Speed:
                if (distance > SingleSpacing)
                {
                    return(2.5);
                }
                else if (distance > StreamSpacing)
                {
                    return(1.6 + 0.9 * (distance - StreamSpacing) / (SingleSpacing - StreamSpacing));
                }
                else if (distance > AlmostDiameter)
                {
                    return(1.2 + 0.4 * (distance - AlmostDiameter) / (StreamSpacing - AlmostDiameter));
                }
                else if (distance > AlmostDiameter / 2.0)
                {
                    return(0.95 + 0.25 * (distance - AlmostDiameter / 2.0) / (AlmostDiameter / 2.0));
                }

                return(0.95);

            default:
                throw new InvalidOperationException("this difficulty type does not exist");
            }
        }
예제 #3
0
        private double CalcIndividual(StrainType type)
        {
            strains.Clear();

            double strainStep  = StrainStep * speedMul;
            double intervalEnd = strainStep;
            double maxStrain   = 0.0;

            //calculate all strains
            for (int i = 0; i < Beatmap.Objects.Count; ++i)
            {
                HitObject obj  = Beatmap.Objects[i];
                HitObject prev = i > 0
                    ? Beatmap.Objects[i - 1]
                    : null;

                if (prev != null)
                {
                    DiffStrain(type, obj, prev, speedMul);
                }

                while (obj.Time > intervalEnd)
                {
                    //add max strain for this interval
                    strains.Add(maxStrain);

                    if (prev != null)
                    {
                        //decay last object's strains until the next interval and use that as the initial max strain
                        double decay = Math.Pow(DecayBase[type], (intervalEnd - prev.Time) / 1000.0);
                        maxStrain = prev.Strains[type] * decay;
                    }
                    else
                    {
                        maxStrain = 0.0;
                    }

                    intervalEnd += strainStep;
                }

                maxStrain = Math.Max(maxStrain, obj.Strains[type]);
            }

            //weigh the top strains sorted from highest to lowest
            double weight     = 1.0;
            double difficulty = 0.0;

            strains.Sort();
            strains.Reverse();

            foreach (double strain in strains)
            {
                difficulty += strain * weight;
                weight     *= DecayWeight;
            }

            return(difficulty);
        }
예제 #4
0
        private static double DiffSpacingWeight(StrainType type, double distance,
                                                double deltaTime,
                                                double prevDistance, double prevDeltaTime,
                                                double angle)
        {
            double angleBonus;
            double strainTime = Math.Max(deltaTime, 50.0f);

            switch (type)
            {
            case StrainType.Aim:
            {
                double result         = 0;
                double prevStrainTime = Math.Max(prevDeltaTime, 50.0f);
                if (!(Double.IsNaN(angle) || Double.IsInfinity(angle)) && angle > AimAngleBonusBegin)
                {
                    angleBonus = Math.Sqrt(
                        Math.Max(prevDistance - AngleBonusScale, 0)
                        * Math.Pow(Math.Sin(angle - AimAngleBonusBegin), 2)
                        * Math.Max(distance - AngleBonusScale, 0)
                        );
                    result = 1.5f * Math.Pow(Math.Max(0, angleBonus), 0.99)
                             / Math.Max(AimTimingThreshold, prevStrainTime);
                }

                var weightedDistance = Math.Pow(distance, 0.99);
                return(Math.Max(
                           result + weightedDistance /
                           Math.Max(AimTimingThreshold, strainTime),
                           weightedDistance / strainTime
                           ));
            }

            case StrainType.Speed:
            {
                distance  = Math.Min(distance, SingleSpacing);
                deltaTime = Math.Max(deltaTime, MaxSpeedBonus);
                double speedBonus = 1.0f;
                if (deltaTime < MinSpeedBonus)
                {
                    speedBonus += Math.Pow((MinSpeedBonus - deltaTime) / 40.0f, 2);
                }

                angleBonus = 1.0f;
                if (!(Double.IsNaN(angle) || Double.IsInfinity(angle)) && angle < SpeedAngleBonusBegin)
                {
                    double s = Math.Sin(1.5 * (SpeedAngleBonusBegin - angle));
                    angleBonus += Math.Pow(s, 2) / 3.57f;
                    if (angle < Math.PI / 2)
                    {
                        angleBonus = 1.28f;
                        if (distance < AngleBonusScale && angle < Math.PI / 4)
                        {
                            angleBonus += (1 - angleBonus)
                                          * Math.Min((AngleBonusScale - distance) / 10, 1);
                        }
                        else if (distance < AngleBonusScale)
                        {
                            angleBonus += (1 - angleBonus)
                                          * Math.Min((AngleBonusScale - distance) / 10, 1)
                                          * Math.Sin((Math.PI / 2 - angle) * 4 / Math.PI);
                        }
                    }
                }

                return((
                           (1 + (speedBonus - 1) * 0.75f) *
                           angleBonus *
                           (0.95f + speedBonus * Math.Pow(distance / SingleSpacing, 3.5))
                           ) / strainTime);
            }

            default:
                throw new InvalidOperationException("this difficulty type does not exist");
            }
        }