Example #1
0
        protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
        {
            if (beatmap.HitObjects.Count == 0)
            {
                return new OsuDifficultyAttributes {
                           Mods = mods, Skills = skills
                }
            }
            ;

            List <double> combinedBonuses = calculateCombinedBonuses(skills);
            int           totalHits       = beatmap.HitObjects.Count(h => h is HitCircle || h is Slider);

            Aim   aimSkill   = skills[0] as Aim;
            Speed speedSkill = skills[1] as Speed;

            //minor discrepancy here because the combined bonus is not taken into account for the length value
            double aimTotal   = aimSkill.LengthValue(totalHits) * 2;
            double speedTotal = speedSkill.LengthValue(totalHits) * 2;

            aimSkill.AddCombinedCorrection(combinedBonuses);
            speedSkill.AddCombinedCorrection(combinedBonuses, skills[4]);

            double aimDifficulty   = aimSkill.CombinedDifficultyValue();
            double speedDifficulty = speedSkill.CombinedDifficultyValue();

            double aimRating   = Math.Sqrt(aimDifficulty);
            double speedRating = Math.Sqrt(speedDifficulty);

            //consistency
            double totalAimBonus = Math.Pow(aimSkill.ConsistencyValue(aimDifficulty), 0.7) * 0.045
                                   + calculateMixedAimBonus(skills[2] as SnapAim, skills[3] as FlowAim);
            double totalSpeedBonus = Math.Pow(speedSkill.ConsistencyValue(speedDifficulty), 0.7) * 0.045;

            //calculate the SR first to avoid unnecessary inflation, this should pose no problems as it is just a display value
            double displayAim   = aimRating * (1.0 + totalAimBonus) * difficulty_multiplier;
            double displaySpeed = speedRating * (1.0 + totalSpeedBonus) * difficulty_multiplier;

            double starRating = displayAim + displaySpeed + Math.Abs(displayAim - displaySpeed) / 2;

            //length bonus
            double aimDiffMultiplier = 1.0 + Math.Pow(aimRating, 1.5) / 15000;

            aimTotal = Math.Pow(aimTotal, aimDiffMultiplier);

            double aimLengthBonus = 1.0 + 0.36 * Math.Min(1.0, aimTotal / 2000.0) +
                                    (aimTotal > 2000 ? Math.Log10(aimTotal / 2000.0) * 0.48 : 0.0);
            double speedLengthBonus = 1.0 + 0.1 * Math.Min(1.0, speedTotal / 2000.0) +
                                      (speedTotal > 2000 ? Math.Log10(speedTotal / 2000.0) * 0.2 : 0.0);

            totalAimBonus   += Math.Pow(aimLengthBonus, 0.33);
            totalSpeedBonus += Math.Pow(speedLengthBonus, 0.33);

            aimRating   *= totalAimBonus * difficulty_multiplier;
            speedRating *= totalSpeedBonus * difficulty_multiplier;

            HitWindows hitWindows = new OsuHitWindows();

            hitWindows.SetDifficulty(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty);

            // Todo: These int casts are temporary to achieve 1:1 results with osu!stable, and should be removed in the future
            double hitWindowGreat = (int)(hitWindows.WindowFor(HitResult.Great)) / clockRate;
            double preempt        = (int)BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate;

            int maxCombo = beatmap.HitObjects.Count;

            // Add the ticks + tail of the slider. 1 is subtracted because the head circle would be counted twice (once for the slider itself in the line above)
            maxCombo += beatmap.HitObjects.OfType <Slider>().Sum(s => s.NestedHitObjects.Count - 1);

            int hitCirclesCount = beatmap.HitObjects.Count(h => h is HitCircle);
            int spinnerCount    = beatmap.HitObjects.Count(h => h is Spinner);

            return(new OsuDifficultyAttributes
            {
                StarRating = starRating,
                Mods = mods,
                AimStrain = aimRating,
                SpeedStrain = speedRating,
                ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5,
                OverallDifficulty = (80 - hitWindowGreat) / 6,
                MaxCombo = maxCombo,
                HitCircleCount = hitCirclesCount,
                SpinnerCount = spinnerCount,
                Skills = skills
            });
        }