Exemplo n.º 1
0
        protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
        {
            base.ApplyDefaultsToSelf(controlPointInfo, difficulty);

            TimePreempt = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450);
            TimeFadein  = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1200, 800, 300);

            Scale = (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5) / 2;
        }
Exemplo n.º 2
0
        protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
        {
            base.ApplyDefaultsToSelf(controlPointInfo, difficulty);

            SpinsRequired = (int)(Duration / 1000 * BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 3, 5, 7.5));

            // spinning doesn't match 1:1 with stable, so let's fudge them easier for the time being.
            SpinsRequired = (int)(SpinsRequired * 0.6);
        }
Exemplo n.º 3
0
        protected override void ApplyBeatmap(Beatmap <ManiaHitObject> beatmap)
        {
            base.ApplyBeatmap(beatmap);

            BeatmapDifficulty difficulty = beatmap.BeatmapInfo.BaseDifficulty;

            hpMultiplier     = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_min, hp_multiplier_mid, hp_multiplier_max);
            hpMissMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_miss_min, hp_multiplier_miss_mid, hp_multiplier_miss_max);
        }
Exemplo n.º 4
0
 /// <summary>
 /// Constructs hit windows by fitting a parameter to a 2-part piecewise linear function for each hit window.
 /// </summary>
 /// <param name="difficulty">The parameter.</param>
 public HitWindows(double difficulty)
 {
     Perfect = BeatmapDifficulty.DifficultyRange(difficulty, perfect_max, perfect_mid, perfect_min);
     Great   = BeatmapDifficulty.DifficultyRange(difficulty, great_max, great_mid, great_min);
     Good    = BeatmapDifficulty.DifficultyRange(difficulty, good_max, good_mid, good_min);
     Ok      = BeatmapDifficulty.DifficultyRange(difficulty, ok_max, ok_mid, ok_min);
     Bad     = BeatmapDifficulty.DifficultyRange(difficulty, bad_max, bad_mid, bad_min);
     Miss    = BeatmapDifficulty.DifficultyRange(difficulty, miss_max, miss_mid, miss_min);
 }
Exemplo n.º 5
0
        protected override void SimulateAutoplay(Beatmap <TaikoHitObject> beatmap)
        {
            double hpMultiplierNormal = 1 / (hp_hit_great * beatmap.HitObjects.FindAll(o => o is Hit).Count *BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.5, 0.75, 0.98));

            hpIncreaseTick  = hp_hit_tick;
            hpIncreaseGreat = hpMultiplierNormal * hp_hit_great;
            hpIncreaseGood  = hpMultiplierNormal * hp_hit_good;
            hpIncreaseMiss  = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, hp_miss_min, hp_miss_mid, hp_miss_max);

            foreach (var obj in beatmap.HitObjects)
            {
                switch (obj)
                {
                case Hit _:
                    AddJudgement(new TaikoJudgement {
                        Result = HitResult.Great
                    });
                    if (obj.IsStrong)
                    {
                        AddJudgement(new TaikoStrongHitJudgement());
                    }
                    break;

                case DrumRoll drumRoll:
                    var count = drumRoll.NestedHitObjects.OfType <DrumRollTick>().Count();
                    for (int i = 0; i < count; i++)
                    {
                        AddJudgement(new TaikoDrumRollTickJudgement {
                            Result = HitResult.Great
                        });

                        if (obj.IsStrong)
                        {
                            AddJudgement(new TaikoStrongHitJudgement());
                        }
                    }

                    AddJudgement(new TaikoJudgement {
                        Result = HitResult.Great
                    });

                    if (obj.IsStrong)
                    {
                        AddJudgement(new TaikoStrongHitJudgement());
                    }
                    break;

                case Swell _:
                    AddJudgement(new TaikoJudgement {
                        Result = HitResult.Great
                    });
                    break;
                }
            }
        }
Exemplo n.º 6
0
        public TauCursor(BeatmapDifficulty difficulty)
        {
            angleRange = (float)BeatmapDifficulty.DifficultyRange(difficulty.CircleSize, 75, 25, 10);

            Origin = Anchor.Centre;
            Anchor = Anchor.Centre;

            RelativeSizeAxes   = Axes.Both;
            AddInternal(paddle = new Paddle(angleRange));
            AddInternal(cursor = new AbsoluteCursor());
        }
Exemplo n.º 7
0
        protected override void ApplyBeatmap(Beatmap <TaikoHitObject> beatmap)
        {
            base.ApplyBeatmap(beatmap);

            double hpMultiplierNormal = 1 / (hp_hit_great * beatmap.HitObjects.FindAll(o => o is Hit).Count *BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.5, 0.75, 0.98));

            hpIncreaseTick  = hp_hit_tick;
            hpIncreaseGreat = hpMultiplierNormal * hp_hit_great;
            hpIncreaseGood  = hpMultiplierNormal * hp_hit_good;
            hpIncreaseMiss  = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, hp_miss_min, hp_miss_mid, hp_miss_max);
        }
Exemplo n.º 8
0
        protected override void SimulateAutoplay(Beatmap <TaikoHitObject> beatmap)
        {
            double hpMultiplierNormal = 1 / (hp_hit_great * beatmap.HitObjects.FindAll(o => o is Hit).Count *BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.5, 0.75, 0.98));

            hpIncreaseTick  = hp_hit_tick;
            hpIncreaseGreat = hpMultiplierNormal * hp_hit_great;
            hpIncreaseGood  = hpMultiplierNormal * hp_hit_good;
            hpIncreaseMiss  = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, hp_miss_min, hp_miss_mid, hp_miss_max);

            foreach (var obj in beatmap.HitObjects)
            {
                if (obj is Hit)
                {
                    AddJudgement(new TaikoJudgement {
                        Result = HitResult.Great
                    });
                    if (obj.IsStrong)
                    {
                        AddJudgement(new TaikoStrongHitJudgement());
                    }
                }
                else if (obj is DrumRoll)
                {
                    for (int i = 0; i < ((DrumRoll)obj).TotalTicks; i++)
                    {
                        AddJudgement(new TaikoDrumRollTickJudgement {
                            Result = HitResult.Great
                        });

                        if (obj.IsStrong)
                        {
                            AddJudgement(new TaikoStrongHitJudgement());
                        }
                    }

                    AddJudgement(new TaikoJudgement {
                        Result = HitResult.Great
                    });

                    if (obj.IsStrong)
                    {
                        AddJudgement(new TaikoStrongHitJudgement());
                    }
                }
                else if (obj is Swell)
                {
                    AddJudgement(new TaikoJudgement {
                        Result = HitResult.Great
                    });
                }
            }
        }
Exemplo n.º 9
0
        public SpinnerCounter(Spinner spinner, Score score)
        {
            IsRX    = score.Mods.Any(m => m.ShortenedName == "RX");
            IsDT    = score.Mods.Any(m => m.ShortenedName == "DT");
            IsSO    = score.Mods.Any(m => m.ShortenedName == "SO");
            IsAP    = score.Mods.Any(m => m.ShortenedName == "AP");
            IsHT    = score.Mods.Any(m => m.ShortenedName == "HT");
            Spinner = spinner;

            SpinnerRotationRatio = BeatmapDifficulty.DifficultyRange(score.Beatmap.BaseDifficulty.OverallDifficulty, 3, 5, 7.5);

            rotationRequirement = (int)((float)spinner.Duration / 1000 * SpinnerRotationRatio);

            maxAccel = 0.00008 + Math.Max(0, (5000 - (double)spinner.Duration) / 1000 / 2000);
        }
Exemplo n.º 10
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
                }
            }
            ;

            double aimRating   = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier;
            double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier;

            double baseAimPerformance   = Math.Pow(5 * Math.Max(1, aimRating / 0.0675) - 4, 3) / 100000;
            double baseSpeedPerformance = Math.Pow(5 * Math.Max(1, speedRating / 0.0675) - 4, 3) / 100000;
            double basePerformance      = Math.Pow(Math.Pow(baseAimPerformance, 1.1) + Math.Pow(baseSpeedPerformance, 1.1), 1 / 1.1);
            double starRating           = basePerformance > 0.00001 ? Math.Cbrt(1.12) * 0.027 * (Math.Cbrt(100000 / Math.Pow(2, 1 / 1.1) * basePerformance) + 4) : 0;

            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
            });
        }
Exemplo n.º 11
0
        protected override void SimulateAutoplay(Beatmap <ManiaHitObject> beatmap)
        {
            BeatmapDifficulty difficulty = beatmap.BeatmapInfo.BaseDifficulty;

            hpMultiplier     = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_min, hp_multiplier_mid, hp_multiplier_max);
            hpMissMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_miss_min, hp_multiplier_miss_mid, hp_multiplier_miss_max);

            while (true)
            {
                foreach (var obj in beatmap.HitObjects)
                {
                    var holdNote = obj as HoldNote;

                    if (holdNote != null)
                    {
                        // Head
                        AddJudgement(new ManiaJudgement {
                            Result = HitResult.Perfect
                        });

                        // Ticks
                        int tickCount = holdNote.NestedHitObjects.OfType <HoldNoteTick>().Count();
                        for (int i = 0; i < tickCount; i++)
                        {
                            AddJudgement(new HoldNoteTickJudgement {
                                Result = HitResult.Perfect
                            });
                        }
                    }

                    AddJudgement(new ManiaJudgement {
                        Result = HitResult.Perfect
                    });
                }

                if (!HasFailed)
                {
                    break;
                }

                hpMultiplier     *= 1.01;
                hpMissMultiplier *= 0.98;

                Reset(false);
            }
        }
        protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate, double upTo = double.PositiveInfinity)
        {
            var hitObjects = double.IsPositiveInfinity(upTo)
                ? beatmap.HitObjects
                : beatmap.HitObjects.Where(h => h.StartTime <= upTo).ToList();

            if (hitObjects.Count == 0)
            {
                return new OsuDifficultyAttributes {
                           Mods = mods, Skills = skills
                }
            }
            ;

            double aimRating   = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier;
            double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier;
            double starRating  = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2;

            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 = 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 += hitObjects.OfType <Slider>().Sum(s => s.NestedHitObjects.Count - 1);

            int hitCircles = hitObjects.OfType <HitCircle>().Count();

            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,
                CountHitCircles = hitCircles,
                Skills = skills
            });
        }
Exemplo n.º 13
0
        protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate)
        {
            if (!beatmap.HitObjects.Any())
            {
                return(new CatchDifficultyAttributes(mods, 0));
            }

            var   catcher        = new CatcherArea.Catcher(beatmap.BeatmapInfo.BaseDifficulty);
            float halfCatchWidth = catcher.CatchWidth * 0.5f;

            var difficultyHitObjects = new List <CatchDifficultyHitObject>();

            foreach (var hitObject in beatmap.HitObjects)
            {
                switch (hitObject)
                {
                // We want to only consider fruits that contribute to the combo. Droplets are addressed as accuracy and spinners are not relevant for "skill" calculations.
                case Fruit fruit:
                    difficultyHitObjects.Add(new CatchDifficultyHitObject(fruit, halfCatchWidth));
                    break;

                case JuiceStream _:
                    difficultyHitObjects.AddRange(hitObject.NestedHitObjects.OfType <CatchHitObject>().Where(o => !(o is TinyDroplet)).Select(o => new CatchDifficultyHitObject(o, halfCatchWidth)));
                    break;
                }
            }

            difficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime));

            if (!calculateStrainValues(difficultyHitObjects, timeRate))
            {
                return(new CatchDifficultyAttributes(mods, 0));
            }

            // this is the same as osu!, so there's potential to share the implementation... maybe
            double preempt    = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / timeRate;
            double starRating = Math.Sqrt(calculateDifficulty(difficultyHitObjects, timeRate)) * star_scaling_factor;

            return(new CatchDifficultyAttributes(mods, starRating)
            {
                ApproachRate = preempt > 1200.0 ? -(preempt - 1800.0) / 120.0 : -(preempt - 1200.0) / 150.0 + 5.0,
                MaxCombo = difficultyHitObjects.Count
            });
        }
Exemplo n.º 14
0
        protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
        {
            if (beatmap.HitObjects.Count == 0)
            {
                return new TauDifficultyAttributes {
                           Mods = mods, Skills = skills
                }
            }
            ;

            double aimRating   = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier;
            double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier;
            double starRating  = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2;

            // Uncomment to see aimrating vs speedrating of a map: if aim rating is 3.5 and speed rating is 2.6 then sr will be 352.6
            // starRating = Math.Round(aimRating,1)*100 + Math.Round(speedRating,1);

            HitWindows hitWindows = new TauHitWindows();

            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;

            // IReadOnlyList<double> aimPeaks = skills[0].StrainPeaks;

            // 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);

            return(new TauDifficultyAttributes
            {
                StarRating = starRating,
                Mods = mods,
                AimStrain = aimRating,
                SpeedStrain = speedRating,
                ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5,
                OverallDifficulty = (80 - hitWindowGreat) / 6,
                MaxCombo = maxCombo,
                Skills = skills,
            });
        }
Exemplo n.º 15
0
        public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty)
        {
            base.ApplyDefaults(timing, difficulty);

            PreEmpt = 600 / (timing.SliderVelocityAt(StartTime) * difficulty.SliderMultiplier) * 1000;

            ControlPoint overridePoint;

            Kiai = timing.TimingPointAt(StartTime, out overridePoint).KiaiMode;

            if (overridePoint != null)
            {
                Kiai |= overridePoint.KiaiMode;
            }

            HitWindowGreat = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 50, 35, 20);
            HitWindowGood  = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 120, 80, 50);
            HitWindowMiss  = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 135, 95, 70);
        }
Exemplo n.º 16
0
        protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
        {
            if (beatmap.HitObjects.Count == 0)
            {
                return new CatchDifficultyAttributes {
                           Mods = mods
                }
            }
            ;

            // this is the same as osu!, so there's potential to share the implementation... maybe
            double preempt = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate;

            return(new CatchDifficultyAttributes
            {
                StarRating = Math.Sqrt(skills[0].DifficultyValue()) * star_scaling_factor,
                Mods = mods,
                ApproachRate = preempt > 1200.0 ? -(preempt - 1800.0) / 120.0 : -(preempt - 1200.0) / 150.0 + 5.0,
                MaxCombo = beatmap.HitObjects.Count(h => h is Fruit) + beatmap.HitObjects.OfType <JuiceStream>().SelectMany(j => j.NestedHitObjects).Count(h => !(h is TinyDroplet))
            });
        }
Exemplo n.º 17
0
        protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double clockRate)
        {
            var hitObjects = beatmap.HitObjects as List <OsuHitObject>;

            if (beatmap.HitObjects.Count == 0)
            {
                return new OsuDifficultyAttributes {
                           Mods = mods
                }
            }
            ;

            double mapLength = (beatmap.HitObjects.Last().StartTime - beatmap.HitObjects.First().StartTime) / 1000 / clockRate;

            // Tap
            (var tapDiff, var streamNoteCount, var mashLevels, var tapSkills, var strainHistory) =
                Tap.CalculateTapAttributes(hitObjects, clockRate);

            // Finger Control
            double fingerControlDiff = FingerControl.CalculateFingerControlDiff(hitObjects, clockRate);

            // Aim
            (var aimDiff, var fcTimeTP, var comboTPs, var missTPs, var missCounts,
             var cheeseNoteCount, var cheeseLevels, var cheeseFactors, var graphText) =
                Aim.CalculateAimAttributes(hitObjects, clockRate, strainHistory);

            // graph for aim
            string graphFilePath = Path.Combine("cache", $"graph_{beatmap.BeatmapInfo.OnlineBeatmapID}.txt");

            File.WriteAllText(graphFilePath, graphText);

            double tapSR           = tapMultiplier * Math.Pow(tapDiff, srExponent);
            double aimSR           = aimMultiplier * Math.Pow(aimDiff, srExponent);
            double fingerControlSR = fingerControlMultiplier * Math.Pow(fingerControlDiff, srExponent);
            double sr = Mean.PowerMean(new double[] { tapSR, aimSR, fingerControlSR }, 7) * 1.131;

            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.Great / 2) / 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);

            return(new OsuDifficultyAttributes
            {
                StarRating = sr,
                Mods = mods,
                Length = mapLength,

                TapSR = tapSR,
                TapDiff = tapDiff,
                StreamNoteCount = streamNoteCount,
                MashLevels = mashLevels,
                TapSkills = tapSkills,

                FingerControlSR = fingerControlSR,
                FingerControlDiff = fingerControlDiff,

                AimSR = aimSR,
                AimDiff = aimDiff,
                ComboTPs = comboTPs,
                MissTPs = missTPs,
                MissCounts = missCounts,
                CheeseNoteCount = cheeseNoteCount,
                CheeseLevels = cheeseLevels,
                CheeseFactors = cheeseFactors,

                ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5,
                OverallDifficulty = (80 - hitWindowGreat) / 6,
                MaxCombo = maxCombo
            });
        }
Exemplo n.º 18
0
        protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate)
        {
            if (!beatmap.HitObjects.Any())
            {
                return(new OsuDifficultyAttributes(mods, 0));
            }

            OsuDifficultyBeatmap difficultyBeatmap = new OsuDifficultyBeatmap(beatmap.HitObjects.Cast <OsuHitObject>().ToList(), timeRate);

            Skill[] skills =
            {
                new Aim(),
                new Speed()
            };

            double sectionLength = section_length * timeRate;

            // The first object doesn't generate a strain, so we begin with an incremented section end
            double currentSectionEnd = Math.Ceiling(beatmap.HitObjects.First().StartTime / sectionLength) * sectionLength;

            foreach (OsuDifficultyHitObject h in difficultyBeatmap)
            {
                while (h.BaseObject.StartTime > currentSectionEnd)
                {
                    foreach (Skill s in skills)
                    {
                        s.SaveCurrentPeak();
                        s.StartNewSectionFrom(currentSectionEnd);
                    }

                    currentSectionEnd += sectionLength;
                }

                foreach (Skill s in skills)
                {
                    s.Process(h);
                }
            }

            // The peak strain will not be saved for the last section in the above loop
            foreach (Skill s in skills)
            {
                s.SaveCurrentPeak();
            }

            double aimRating   = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier;
            double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier;
            double starRating  = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2;

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

            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);

            return(new OsuDifficultyAttributes(mods, starRating)
            {
                AimStrain = aimRating,
                SpeedStrain = speedRating,
                ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5,
                OverallDifficulty = (80 - hitWindowGreat) / 6,
                MaxCombo = maxCombo
            });
        }
Exemplo n.º 19
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
            });
        }
        public override void ApplyBeatmap(IBeatmap beatmap)
        {
            base.ApplyBeatmap(beatmap);

            hpMultiplier     = 1 / (object_count_factor * Math.Max(1, beatmap.HitObjects.OfType <Hit>().Count()) * BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.5, 0.75, 0.98));
            hpMissMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.0018, 0.0075, 0.0120);
        }
Exemplo n.º 21
0
        protected override IEnumerable <TaikoHitObject> ConvertHitObject(HitObject obj, IBeatmap beatmap)
        {
            var distanceData = obj as IHasDistance;
            var repeatsData  = obj as IHasRepeats;
            var endTimeData  = obj as IHasEndTime;
            var curveData    = obj as IHasCurve;

            // Old osu! used hit sounding to determine various hit type information
            List <SampleInfo> samples = obj.Samples;

            bool strong = samples.Any(s => s.Name == SampleInfo.HIT_FINISH);

            if (distanceData != null)
            {
                // Number of spans of the object - one for the initial length and for each repeat
                int spans = repeatsData?.SpanCount() ?? 1;

                TimingControlPoint     timingPoint     = beatmap.ControlPointInfo.TimingPointAt(obj.StartTime);
                DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(obj.StartTime);

                double speedAdjustment         = difficultyPoint.SpeedMultiplier;
                double speedAdjustedBeatLength = timingPoint.BeatLength / speedAdjustment;

                // The true distance, accounting for any repeats. This ends up being the drum roll distance later
                double distance = distanceData.Distance * spans * legacy_velocity_multiplier;

                // The velocity of the taiko hit object - calculated as the velocity of a drum roll
                double taikoVelocity = taiko_base_distance * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier / speedAdjustedBeatLength;
                // The duration of the taiko hit object
                double taikoDuration = distance / taikoVelocity;

                // The velocity of the osu! hit object - calculated as the velocity of a slider
                double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier / speedAdjustedBeatLength;
                // The duration of the osu! hit object
                double osuDuration = distance / osuVelocity;

                // osu-stable always uses the speed-adjusted beatlength to determine the velocities, but
                // only uses it for tick rate if beatmap version < 8
                if (beatmap.BeatmapInfo.BeatmapVersion >= 8)
                {
                    speedAdjustedBeatLength *= speedAdjustment;
                }

                // If the drum roll is to be split into hit circles, assume the ticks are 1/8 spaced within the duration of one beat
                double tickSpacing = Math.Min(speedAdjustedBeatLength / beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate, taikoDuration / spans);

                if (!isForCurrentRuleset && tickSpacing > 0 && osuDuration < 2 * speedAdjustedBeatLength)
                {
                    List <List <SampleInfo> > allSamples = curveData != null ? curveData.NodeSamples : new List <List <SampleInfo> >(new[] { samples });

                    int i = 0;
                    for (double j = obj.StartTime; j <= obj.StartTime + taikoDuration + tickSpacing / 8; j += tickSpacing)
                    {
                        List <SampleInfo> currentSamples = allSamples[i];
                        bool isRim = currentSamples.Any(s => s.Name == SampleInfo.HIT_CLAP || s.Name == SampleInfo.HIT_WHISTLE);
                        strong = currentSamples.Any(s => s.Name == SampleInfo.HIT_FINISH);

                        if (isRim)
                        {
                            yield return(new RimHit
                            {
                                StartTime = j,
                                Samples = currentSamples,
                                IsStrong = strong
                            });
                        }
                        else
                        {
                            yield return(new CentreHit
                            {
                                StartTime = j,
                                Samples = currentSamples,
                                IsStrong = strong
                            });
                        }

                        i = (i + 1) % allSamples.Count;
                    }
                }
                else
                {
                    yield return(new DrumRoll
                    {
                        StartTime = obj.StartTime,
                        Samples = obj.Samples,
                        IsStrong = strong,
                        Duration = taikoDuration,
                        TickRate = beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate == 3 ? 3 : 4
                    });
                }
            }
            else if (endTimeData != null)
            {
                double hitMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty, 3, 5, 7.5) * swell_hit_multiplier;

                yield return(new Swell
                {
                    StartTime = obj.StartTime,
                    Samples = obj.Samples,
                    Duration = endTimeData.Duration,
                    RequiredHits = (int)Math.Max(1, endTimeData.Duration / 1000 * hitMultiplier)
                });
            }
            else
            {
                bool isRim = samples.Any(s => s.Name == SampleInfo.HIT_CLAP || s.Name == SampleInfo.HIT_WHISTLE);

                if (isRim)
                {
                    yield return(new RimHit
                    {
                        StartTime = obj.StartTime,
                        Samples = obj.Samples,
                        IsStrong = strong
                    });
                }
                else
                {
                    yield return(new CentreHit
                    {
                        StartTime = obj.StartTime,
                        Samples = obj.Samples,
                        IsStrong = strong
                    });
                }
            }
        }
 public DrawableCatchRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList <Mod> mods = null)
     : base(ruleset, beatmap, mods)
 {
     Direction.Value = ScrollingDirection.Down;
     TimeRange.Value = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450);
 }
Exemplo n.º 23
0
        protected override void ComputeTargets(Beatmap <TaikoHitObject> beatmap)
        {
            double hpMultiplierNormal = 1 / (hp_hit_great * beatmap.HitObjects.FindAll(o => o is Hit).Count *BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.Difficulty.DrainRate, 0.5, 0.75, 0.98));

            hpIncreaseTick  = hp_hit_tick;
            hpIncreaseGreat = hpMultiplierNormal * hp_hit_great;
            hpIncreaseGood  = hpMultiplierNormal * hp_hit_good;
            hpIncreaseMiss  = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.Difficulty.DrainRate, hp_miss_min, hp_miss_mid, hp_miss_max);

            var strongHits = beatmap.HitObjects.FindAll(o => o is Hit && o.IsStrong);

            // This is a linear function that awards:
            // 10 times bonus points for hitting a strong hit object with both keys with 30 strong hit objects in the map
            // 3 times bonus points for hitting a strong hit object with both keys with 120 strong hit objects in the map
            strongHitScale = -7d / 90d * MathHelper.Clamp(strongHits.Count, 30, 120) + 111d / 9d;

            foreach (var obj in beatmap.HitObjects)
            {
                if (obj is Hit)
                {
                    AddJudgement(new TaikoJudgement
                    {
                        Result      = HitResult.Hit,
                        TaikoResult = TaikoHitResult.Great,
                        SecondHit   = obj.IsStrong
                    });
                }
                else if (obj is DrumRoll)
                {
                    for (int i = 0; i < ((DrumRoll)obj).TotalTicks; i++)
                    {
                        AddJudgement(new TaikoDrumRollTickJudgement
                        {
                            Result      = HitResult.Hit,
                            TaikoResult = TaikoHitResult.Great,
                            SecondHit   = obj.IsStrong
                        });
                    }

                    AddJudgement(new TaikoJudgement
                    {
                        Result      = HitResult.Hit,
                        TaikoResult = TaikoHitResult.Great,
                        SecondHit   = obj.IsStrong
                    });
                }
                else if (obj is Swell)
                {
                    AddJudgement(new TaikoJudgement
                    {
                        Result      = HitResult.Hit,
                        TaikoResult = TaikoHitResult.Great
                    });
                }
            }

            maxTotalHits    = totalHits;
            maxComboPortion = comboPortion;
        }
Exemplo n.º 24
0
        private IEnumerable <TaikoHitObject> convertHitObject(HitObject obj, Beatmap beatmap)
        {
            // Check if this HitObject is already a TaikoHitObject, and return it if so
            var originalTaiko = obj as TaikoHitObject;

            if (originalTaiko != null)
            {
                yield return(originalTaiko);
            }

            var distanceData = obj as IHasDistance;
            var repeatsData  = obj as IHasRepeats;
            var endTimeData  = obj as IHasEndTime;

            // Old osu! used hit sounding to determine various hit type information
            List <SampleInfo> samples = obj.Samples;

            bool strong = samples.Any(s => s.Name == SampleInfo.HIT_FINISH);

            if (distanceData != null)
            {
                int repeats = repeatsData?.RepeatCount ?? 1;

                double speedAdjustment         = beatmap.TimingInfo.SpeedMultiplierAt(obj.StartTime);
                double speedAdjustedBeatLength = beatmap.TimingInfo.BeatLengthAt(obj.StartTime) * speedAdjustment;

                // The true distance, accounting for any repeats. This ends up being the drum roll distance later
                double distance = distanceData.Distance * repeats * legacy_velocity_multiplier;

                // The velocity of the taiko hit object - calculated as the velocity of a drum roll
                double taikoVelocity = taiko_base_distance * beatmap.BeatmapInfo.Difficulty.SliderMultiplier * legacy_velocity_multiplier / speedAdjustedBeatLength;
                // The duration of the taiko hit object
                double taikoDuration = distance / taikoVelocity;

                // For some reason, old osu! always uses speedAdjustment to determine the taiko velocity, but
                // only uses it to determine osu! velocity if beatmap version < 8. Let's account for that here.
                if (beatmap.BeatmapInfo.BeatmapVersion >= 8)
                {
                    speedAdjustedBeatLength /= speedAdjustment;
                }

                // The velocity of the osu! hit object - calculated as the velocity of a slider
                double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.Difficulty.SliderMultiplier * legacy_velocity_multiplier / speedAdjustedBeatLength;
                // The duration of the osu! hit object
                double osuDuration = distance / osuVelocity;

                // If the drum roll is to be split into hit circles, assume the ticks are 1/8 spaced within the duration of one beat
                double tickSpacing = Math.Min(speedAdjustedBeatLength / beatmap.BeatmapInfo.Difficulty.SliderTickRate, taikoDuration / repeats) / 8;

                if (tickSpacing > 0 && osuDuration < 2 * speedAdjustedBeatLength)
                {
                    for (double j = obj.StartTime; j <= distanceData.EndTime + tickSpacing; j += tickSpacing)
                    {
                        // Todo: This should generate different type of hits (including strongs)
                        // depending on hitobject sound additions (not implemented fully yet)
                        yield return(new CentreHit
                        {
                            StartTime = j,
                            Samples = obj.Samples,
                            IsStrong = strong,
                        });
                    }
                }
                else
                {
                    yield return(new DrumRoll
                    {
                        StartTime = obj.StartTime,
                        Samples = obj.Samples,
                        IsStrong = strong,
                        Duration = taikoDuration,
                        TickRate = beatmap.BeatmapInfo.Difficulty.SliderTickRate == 3 ? 3 : 4,
                    });
                }
            }
            else if (endTimeData != null)
            {
                double hitMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.Difficulty.OverallDifficulty, 3, 5, 7.5) * swell_hit_multiplier;

                yield return(new Swell
                {
                    StartTime = obj.StartTime,
                    Samples = obj.Samples,
                    IsStrong = strong,
                    Duration = endTimeData.Duration,
                    RequiredHits = (int)Math.Max(1, endTimeData.Duration / 1000 * hitMultiplier),
                });
            }
            else
            {
                bool isRim = samples.Any(s => s.Name == SampleInfo.HIT_CLAP || s.Name == SampleInfo.HIT_WHISTLE);

                if (isRim)
                {
                    yield return(new RimHit
                    {
                        StartTime = obj.StartTime,
                        Samples = obj.Samples,
                        IsStrong = strong,
                    });
                }
                else
                {
                    yield return(new CentreHit
                    {
                        StartTime = obj.StartTime,
                        Samples = obj.Samples,
                        IsStrong = strong,
                    });
                }
            }
        }
Exemplo n.º 25
0
        public override double Calculate(Dictionary <string, double> categoryRatings = null)
        {
            mods          = Score.Mods;
            accuracy      = Score.Accuracy;
            scoreMaxCombo = Score.MaxCombo;
            count300      = Convert.ToInt32(Score.Statistics[HitResult.Great]);
            count100      = Convert.ToInt32(Score.Statistics[HitResult.Good]);
            count50       = Convert.ToInt32(Score.Statistics[HitResult.Meh]);
            countMiss     = Convert.ToInt32(Score.Statistics[HitResult.Miss]);

            // Don't count scores made with supposedly unranked mods
            if (mods.Any(m => !m.Ranked))
            {
                return(0);
            }

            // Todo: In the future we should apply changes to PreEmpt/AR at an OsuHitObject/BaseDifficulty level, but this is done
            // locally for now as doing so would modify animations and other things unexpectedly
            // DO NOT MODIFY THIS
            double ar = Beatmap.BeatmapInfo.BaseDifficulty.ApproachRate;

            if (mods.Any(m => m is OsuModHardRock))
            {
                ar = Math.Min(10, ar * 1.4);
            }
            if (mods.Any(m => m is OsuModEasy))
            {
                ar = Math.Max(0, ar / 2);
            }
            double preEmpt = BeatmapDifficulty.DifficultyRange(ar, 1800, 1200, 450);

            realApproachRate = preEmpt > 1200 ? (1800 - preEmpt) / 120 : (1200 - preEmpt) / 150 + 5;

            // Custom multipliers for NoFail and SpunOut.
            double multiplier = 1.12f; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things

            if (mods.Any(m => m is OsuModNoFail))
            {
                multiplier *= 0.90f;
            }

            if (mods.Any(m => m is OsuModSpunOut))
            {
                multiplier *= 0.95f;
            }

            double aimValue      = computeAimValue();
            double speedValue    = computeSpeedValue();
            double accuracyValue = computeAccuracyValue();
            double totalValue    =
                Math.Pow(
                    Math.Pow(aimValue, 1.1f) +
                    Math.Pow(speedValue, 1.1f) +
                    Math.Pow(accuracyValue, 1.1f), 1.0f / 1.1f
                    ) * multiplier;

            if (categoryRatings != null)
            {
                categoryRatings.Add("Aim", aimValue);
                categoryRatings.Add("Speed", speedValue);
                categoryRatings.Add("Accuracy", accuracyValue);
            }

            return(totalValue);
        }
Exemplo n.º 26
0
        protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double clockRate)
        {
            var hitObjects = beatmap.HitObjects as List <OsuHitObject>;

            double mapLength = 0;

            if (beatmap.HitObjects.Count > 0)
            {
                mapLength = (beatmap.HitObjects.Last().StartTime - beatmap.HitObjects.First().StartTime) / 1000 / clockRate;
            }

            double preemptNoClockRate = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450);
            var    noteDensities      = NoteDensity.CalculateNoteDensities(hitObjects, preemptNoClockRate);

            // Tap
            var tapAttributes = Tap.CalculateTapAttributes(hitObjects, clockRate);

            // Finger Control
            double fingerControlDiff = FingerControl.CalculateFingerControlDiff(hitObjects, clockRate);

            // Aim
            var aimAttributes = Aim.CalculateAimAttributes(hitObjects, clockRate, tapAttributes.StrainHistory, noteDensities);

            double tapSr           = tap_multiplier * Math.Pow(tapAttributes.TapDifficulty, sr_exponent);
            double aimSr           = aim_multiplier * Math.Pow(aimAttributes.FcProbabilityThroughput, sr_exponent);
            double fingerControlSr = finger_control_multiplier * Math.Pow(fingerControlDiff, sr_exponent);
            double sr = Mean.PowerMean(new[] { tapSr, aimSr, fingerControlSr }, 7) * 1.131;

            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);

            return(new OsuDifficultyAttributes
            {
                StarRating = sr,
                Mods = mods,
                Length = mapLength,

                TapSr = tapSr,
                TapDiff = tapAttributes.TapDifficulty,
                StreamNoteCount = tapAttributes.StreamNoteCount,
                MashTapDiff = tapAttributes.MashedTapDifficulty,

                FingerControlSr = fingerControlSr,
                FingerControlDiff = fingerControlDiff,

                AimSr = aimSr,
                AimDiff = aimAttributes.FcProbabilityThroughput,
                AimHiddenFactor = aimAttributes.HiddenFactor,
                ComboTps = aimAttributes.ComboThroughputs,
                MissTps = aimAttributes.MissThroughputs,
                MissCounts = aimAttributes.MissCounts,
                CheeseNoteCount = aimAttributes.CheeseNoteCount,
                CheeseLevels = aimAttributes.CheeseLevels,
                CheeseFactors = aimAttributes.CheeseFactors,

                ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5,
                OverallDifficulty = (80 - hitWindowGreat) / 6,
                MaxCombo = maxCombo
            });
        }
Exemplo n.º 27
0
 public DrawableCatchRuleset(Ruleset ruleset, WorkingBeatmap beatmap)
     : base(ruleset, beatmap)
 {
     Direction.Value = ScrollingDirection.Down;
     TimeRange.Value = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450);
 }
Exemplo n.º 28
0
        protected override IEnumerable <TaikoHitObject> ConvertHitObject(HitObject obj, Beatmap beatmap)
        {
            var distanceData = obj as IHasDistance;
            var repeatsData  = obj as IHasRepeats;
            var endTimeData  = obj as IHasEndTime;
            var curveData    = obj as IHasCurve;

            // Old osu! used hit sounding to determine various hit type information
            SampleInfoList samples = obj.Samples;

            bool strong = samples.Any(s => s.Name == SampleInfo.HIT_FINISH);

            if (distanceData != null)
            {
                int repeats = repeatsData?.RepeatCount ?? 1;

                double speedAdjustment         = beatmap.TimingInfo.SpeedMultiplierAt(obj.StartTime);
                double speedAdjustedBeatLength = beatmap.TimingInfo.BeatLengthAt(obj.StartTime) * speedAdjustment;

                // The true distance, accounting for any repeats. This ends up being the drum roll distance later
                double distance = distanceData.Distance * repeats * legacy_velocity_multiplier;

                // The velocity of the taiko hit object - calculated as the velocity of a drum roll
                double taikoVelocity = taiko_base_distance * beatmap.BeatmapInfo.Difficulty.SliderMultiplier * legacy_velocity_multiplier / speedAdjustedBeatLength;
                // The duration of the taiko hit object
                double taikoDuration = distance / taikoVelocity;

                // For some reason, old osu! always uses speedAdjustment to determine the taiko velocity, but
                // only uses it to determine osu! velocity if beatmap version < 8. Let's account for that here.
                if (beatmap.BeatmapInfo.BeatmapVersion >= 8)
                {
                    speedAdjustedBeatLength /= speedAdjustment;
                }

                // The velocity of the osu! hit object - calculated as the velocity of a slider
                double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.Difficulty.SliderMultiplier * legacy_velocity_multiplier / speedAdjustedBeatLength;
                // The duration of the osu! hit object
                double osuDuration = distance / osuVelocity;

                // If the drum roll is to be split into hit circles, assume the ticks are 1/8 spaced within the duration of one beat
                double tickSpacing = Math.Min(speedAdjustedBeatLength / beatmap.BeatmapInfo.Difficulty.SliderTickRate, taikoDuration / repeats);

                if (tickSpacing > 0 && osuDuration < 2 * speedAdjustedBeatLength)
                {
                    List <SampleInfoList> allSamples = curveData != null ? curveData.RepeatSamples : new List <SampleInfoList>(new[] { samples });

                    int i = 0;
                    for (double j = obj.StartTime; j <= obj.StartTime + taikoDuration + tickSpacing / 8; j += tickSpacing)
                    {
                        SampleInfoList currentSamples = allSamples[i];
                        bool           isRim          = currentSamples.Any(s => s.Name == SampleInfo.HIT_CLAP || s.Name == SampleInfo.HIT_WHISTLE);
                        strong = currentSamples.Any(s => s.Name == SampleInfo.HIT_FINISH);

                        if (isRim)
                        {
                            yield return(new RimHit
                            {
                                StartTime = j,
                                Samples = currentSamples,
                                IsStrong = strong
                            });
                        }
                        else
                        {
                            yield return(new CentreHit
                            {
                                StartTime = j,
                                Samples = currentSamples,
                                IsStrong = strong,
                            });
                        }

                        i = (i + 1) % allSamples.Count;
                    }
                }
                else
                {
                    yield return(new DrumRoll
                    {
                        StartTime = obj.StartTime,
                        Samples = obj.Samples,
                        IsStrong = strong,
                        Duration = taikoDuration,
                        TickRate = beatmap.BeatmapInfo.Difficulty.SliderTickRate == 3 ? 3 : 4,
                    });
                }
            }
            else if (endTimeData != null)
            {
                double hitMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.Difficulty.OverallDifficulty, 3, 5, 7.5) * swell_hit_multiplier;

                yield return(new Swell
                {
                    StartTime = obj.StartTime,
                    Samples = obj.Samples,
                    IsStrong = strong,
                    Duration = endTimeData.Duration,
                    RequiredHits = (int)Math.Max(1, endTimeData.Duration / 1000 * hitMultiplier),
                });
            }
            else
            {
                bool isRim = samples.Any(s => s.Name == SampleInfo.HIT_CLAP || s.Name == SampleInfo.HIT_WHISTLE);

                if (isRim)
                {
                    yield return(new RimHit
                    {
                        StartTime = obj.StartTime,
                        Samples = obj.Samples,
                        IsStrong = strong,
                    });
                }
                else
                {
                    yield return(new CentreHit
                    {
                        StartTime = obj.StartTime,
                        Samples = obj.Samples,
                        IsStrong = strong,
                    });
                }
            }
        }
Exemplo n.º 29
0
        protected override void ComputeTargets(Beatmap <ManiaHitObject> beatmap)
        {
            BeatmapDifficulty difficulty = beatmap.BeatmapInfo.Difficulty;

            hpMultiplier     = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_min, hp_multiplier_mid, hp_multiplier_max);
            hpMissMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_miss_min, hp_multiplier_miss_mid, hp_multiplier_miss_max);

            while (true)
            {
                foreach (var obj in beatmap.HitObjects)
                {
                    var holdNote = obj as HoldNote;

                    if (obj is Note)
                    {
                        AddJudgement(new ManiaJudgement
                        {
                            Result      = HitResult.Hit,
                            ManiaResult = ManiaHitResult.Perfect
                        });
                    }
                    else if (holdNote != null)
                    {
                        // Head
                        AddJudgement(new ManiaJudgement
                        {
                            Result      = HitResult.Hit,
                            ManiaResult = ManiaJudgement.MAX_HIT_RESULT
                        });

                        // Ticks
                        int tickCount = holdNote.Ticks.Count();
                        for (int i = 0; i < tickCount; i++)
                        {
                            AddJudgement(new HoldNoteTickJudgement
                            {
                                Result      = HitResult.Hit,
                                ManiaResult = ManiaJudgement.MAX_HIT_RESULT,
                            });
                        }

                        AddJudgement(new HoldNoteTailJudgement
                        {
                            Result      = HitResult.Hit,
                            ManiaResult = ManiaJudgement.MAX_HIT_RESULT
                        });
                    }
                }

                if (!HasFailed)
                {
                    break;
                }

                hpMultiplier     *= 1.01;
                hpMissMultiplier *= 0.98;

                Reset();
            }

            maxTotalHits    = totalHits;
            maxComboPortion = comboPortion;
        }
Exemplo n.º 30
0
        protected override void ApplyBeatmap(Beatmap <TaikoHitObject> beatmap)
        {
            base.ApplyBeatmap(beatmap);

            hpMultiplier = 1 / (object_count_factor * beatmap.HitObjects.FindAll(o => o is Hit).Count *BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.5, 0.75, 0.98));

            hpMissMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.0018, 0.0075, 0.0120);
        }