mirror of
https://github.com/ppy/osu.git
synced 2024-11-11 11:37:28 +08:00
Merge pull request #2802 from smoogipoo/diffcalc-rework
Rework difficulty calculators to better support mods and attributes
This commit is contained in:
commit
cdc88a6c48
@ -1,16 +0,0 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
|
||||
{
|
||||
public TestCasePerformancePoints()
|
||||
: base(new CatchRuleset())
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -112,7 +112,7 @@ namespace osu.Game.Rulesets.Catch
|
||||
|
||||
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_fruits_o };
|
||||
|
||||
public override DifficultyCalculator CreateDifficultyCalculator(IBeatmap beatmap, Mod[] mods = null) => new CatchDifficultyCalculator(beatmap);
|
||||
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new CatchDifficultyCalculator(this, beatmap);
|
||||
|
||||
public override int? LegacyID => 2;
|
||||
|
||||
|
@ -1,18 +1,19 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Difficulty;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Difficulty
|
||||
{
|
||||
public class CatchDifficultyCalculator : DifficultyCalculator
|
||||
{
|
||||
public CatchDifficultyCalculator(IBeatmap beatmap) : base(beatmap)
|
||||
public CatchDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap)
|
||||
: base(ruleset, beatmap)
|
||||
{
|
||||
}
|
||||
|
||||
public override double Calculate(Dictionary<string, double> categoryDifficulty = null) => 0;
|
||||
protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate) => new DifficultyAttributes(mods, 0);
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +0,0 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
|
||||
{
|
||||
public TestCasePerformancePoints()
|
||||
: base(new ManiaRuleset())
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -29,47 +29,33 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
||||
/// </summary>
|
||||
private const double decay_weight = 0.9;
|
||||
|
||||
/// <summary>
|
||||
/// HitObjects are stored as a member variable.
|
||||
/// </summary>
|
||||
private readonly List<ManiaHitObjectDifficulty> difficultyHitObjects = new List<ManiaHitObjectDifficulty>();
|
||||
|
||||
public ManiaDifficultyCalculator(IBeatmap beatmap)
|
||||
: base(beatmap)
|
||||
public ManiaDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap)
|
||||
: base(ruleset, beatmap)
|
||||
{
|
||||
}
|
||||
|
||||
public ManiaDifficultyCalculator(IBeatmap beatmap, Mod[] mods)
|
||||
: base(beatmap, mods)
|
||||
protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate)
|
||||
{
|
||||
}
|
||||
var difficultyHitObjects = new List<ManiaHitObjectDifficulty>();
|
||||
|
||||
public override double Calculate(Dictionary<string, double> categoryDifficulty = null)
|
||||
{
|
||||
// Fill our custom DifficultyHitObject class, that carries additional information
|
||||
difficultyHitObjects.Clear();
|
||||
|
||||
int columnCount = (Beatmap as ManiaBeatmap)?.TotalColumns ?? 7;
|
||||
int columnCount = ((ManiaBeatmap)beatmap).TotalColumns;
|
||||
|
||||
// Sort DifficultyHitObjects by StartTime of the HitObjects - just to make sure.
|
||||
// Note: Stable sort is done so that the ordering of hitobjects with equal start times doesn't change
|
||||
difficultyHitObjects.AddRange(Beatmap.HitObjects.Select(h => new ManiaHitObjectDifficulty((ManiaHitObject)h, columnCount)).OrderBy(h => h.BaseHitObject.StartTime));
|
||||
difficultyHitObjects.AddRange(beatmap.HitObjects.Select(h => new ManiaHitObjectDifficulty((ManiaHitObject)h, columnCount)).OrderBy(h => h.BaseHitObject.StartTime));
|
||||
|
||||
if (!calculateStrainValues())
|
||||
return 0;
|
||||
if (!calculateStrainValues(difficultyHitObjects, timeRate))
|
||||
return new DifficultyAttributes(mods, 0);
|
||||
|
||||
double starRating = calculateDifficulty() * star_scaling_factor;
|
||||
double starRating = calculateDifficulty(difficultyHitObjects, timeRate) * star_scaling_factor;
|
||||
|
||||
if (categoryDifficulty != null)
|
||||
categoryDifficulty["Strain"] = starRating;
|
||||
|
||||
return starRating;
|
||||
return new DifficultyAttributes(mods, starRating);
|
||||
}
|
||||
|
||||
private bool calculateStrainValues()
|
||||
private bool calculateStrainValues(List<ManiaHitObjectDifficulty> objects, double timeRate)
|
||||
{
|
||||
// Traverse hitObjects in pairs to calculate the strain value of NextHitObject from the strain value of CurrentHitObject and environment.
|
||||
using (List<ManiaHitObjectDifficulty>.Enumerator hitObjectsEnumerator = difficultyHitObjects.GetEnumerator())
|
||||
using (var hitObjectsEnumerator = objects.GetEnumerator())
|
||||
{
|
||||
if (!hitObjectsEnumerator.MoveNext())
|
||||
return false;
|
||||
@ -80,7 +66,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
||||
while (hitObjectsEnumerator.MoveNext())
|
||||
{
|
||||
var next = hitObjectsEnumerator.Current;
|
||||
next?.CalculateStrains(current, TimeRate);
|
||||
next?.CalculateStrains(current, timeRate);
|
||||
current = next;
|
||||
}
|
||||
|
||||
@ -88,9 +74,9 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
||||
}
|
||||
}
|
||||
|
||||
private double calculateDifficulty()
|
||||
private double calculateDifficulty(List<ManiaHitObjectDifficulty> objects, double timeRate)
|
||||
{
|
||||
double actualStrainStep = strain_step * TimeRate;
|
||||
double actualStrainStep = strain_step * timeRate;
|
||||
|
||||
// Find the highest strain value within each strain step
|
||||
List<double> highestStrains = new List<double>();
|
||||
@ -98,7 +84,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
||||
double maximumStrain = 0; // We need to keep track of the maximum strain in the current interval
|
||||
|
||||
ManiaHitObjectDifficulty previousHitObject = null;
|
||||
foreach (var hitObject in difficultyHitObjects)
|
||||
foreach (var hitObject in objects)
|
||||
{
|
||||
// While we are beyond the current interval push the currently available maximum to our strain list
|
||||
while (hitObject.BaseHitObject.StartTime > intervalEndTime)
|
||||
@ -159,5 +145,6 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
||||
new ManiaModKey8(),
|
||||
new ManiaModKey9(),
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
||||
private int countMeh;
|
||||
private int countMiss;
|
||||
|
||||
public ManiaPerformanceCalculator(Ruleset ruleset, IBeatmap beatmap, Score score)
|
||||
public ManiaPerformanceCalculator(Ruleset ruleset, WorkingBeatmap beatmap, Score score)
|
||||
: base(ruleset, beatmap, score)
|
||||
{
|
||||
}
|
||||
@ -82,7 +82,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
||||
private double computeStrainValue()
|
||||
{
|
||||
// Obtain strain difficulty
|
||||
double strainValue = Math.Pow(5 * Math.Max(1, Attributes["Strain"] / 0.2) - 4.0, 2.2) / 135.0;
|
||||
double strainValue = Math.Pow(5 * Math.Max(1, Attributes.StarRating / 0.2) - 4.0, 2.2) / 135.0;
|
||||
|
||||
// Longer maps are worth more
|
||||
strainValue *= 1.0 + 0.1 * Math.Min(1.0, totalHits / 1500.0);
|
||||
|
@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Mania
|
||||
{
|
||||
public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap) => new ManiaRulesetContainer(this, beatmap);
|
||||
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap);
|
||||
public override PerformanceCalculator CreatePerformanceCalculator(IBeatmap beatmap, Score score) => new ManiaPerformanceCalculator(this, beatmap, score);
|
||||
public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, Score score) => new ManiaPerformanceCalculator(this, beatmap, score);
|
||||
|
||||
public override IEnumerable<Mod> ConvertLegacyMods(LegacyMods mods)
|
||||
{
|
||||
@ -147,7 +147,7 @@ namespace osu.Game.Rulesets.Mania
|
||||
|
||||
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_mania_o };
|
||||
|
||||
public override DifficultyCalculator CreateDifficultyCalculator(IBeatmap beatmap, Mod[] mods = null) => new ManiaDifficultyCalculator(beatmap, mods);
|
||||
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new ManiaDifficultyCalculator(this, beatmap);
|
||||
|
||||
public override int? LegacyID => 3;
|
||||
|
||||
|
@ -1,16 +0,0 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
|
||||
{
|
||||
public TestCasePerformancePoints()
|
||||
: base(new OsuRuleset())
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
19
osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs
Normal file
19
osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Rulesets.Difficulty;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
{
|
||||
public class OsuDifficultyAttributes : DifficultyAttributes
|
||||
{
|
||||
public double AimStrain;
|
||||
public double SpeedStrain;
|
||||
|
||||
public OsuDifficultyAttributes(Mod[] mods, double starRating)
|
||||
: base(mods, starRating)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Difficulty;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
@ -18,31 +18,26 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
private const int section_length = 400;
|
||||
private const double difficulty_multiplier = 0.0675;
|
||||
|
||||
public OsuDifficultyCalculator(IBeatmap beatmap)
|
||||
: base(beatmap)
|
||||
public OsuDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap)
|
||||
: base(ruleset, beatmap)
|
||||
{
|
||||
}
|
||||
|
||||
public OsuDifficultyCalculator(IBeatmap beatmap, Mod[] mods)
|
||||
: base(beatmap, mods)
|
||||
protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate)
|
||||
{
|
||||
}
|
||||
|
||||
public override double Calculate(Dictionary<string, double> categoryDifficulty = null)
|
||||
{
|
||||
OsuDifficultyBeatmap beatmap = new OsuDifficultyBeatmap((List<OsuHitObject>)Beatmap.HitObjects, TimeRate);
|
||||
OsuDifficultyBeatmap difficultyBeatmap = new OsuDifficultyBeatmap(beatmap.HitObjects.Cast<OsuHitObject>().ToList(), timeRate);
|
||||
Skill[] skills =
|
||||
{
|
||||
new Aim(),
|
||||
new Speed()
|
||||
};
|
||||
|
||||
double sectionLength = section_length * TimeRate;
|
||||
double sectionLength = section_length * timeRate;
|
||||
|
||||
// The first object doesn't generate a strain, so we begin with an incremented section end
|
||||
double currentSectionEnd = 2 * sectionLength;
|
||||
|
||||
foreach (OsuDifficultyHitObject h in beatmap)
|
||||
foreach (OsuDifficultyHitObject h in difficultyBeatmap)
|
||||
{
|
||||
while (h.BaseObject.StartTime > currentSectionEnd)
|
||||
{
|
||||
@ -61,16 +56,13 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
|
||||
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;
|
||||
|
||||
if (categoryDifficulty != null)
|
||||
return new OsuDifficultyAttributes(mods, starRating)
|
||||
{
|
||||
categoryDifficulty.Add("Aim", aimRating);
|
||||
categoryDifficulty.Add("Speed", speedRating);
|
||||
}
|
||||
|
||||
return starRating;
|
||||
AimStrain = aimRating,
|
||||
SpeedStrain = speedRating
|
||||
};
|
||||
}
|
||||
|
||||
protected override Mod[] DifficultyAdjustmentMods => new Mod[]
|
||||
|
@ -15,6 +15,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
{
|
||||
public class OsuPerformanceCalculator : PerformanceCalculator
|
||||
{
|
||||
public new OsuDifficultyAttributes Attributes => (OsuDifficultyAttributes)base.Attributes;
|
||||
|
||||
private readonly int countHitCircles;
|
||||
private readonly int beatmapMaxCombo;
|
||||
|
||||
@ -37,7 +39,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
private int countMeh;
|
||||
private int countMiss;
|
||||
|
||||
public OsuPerformanceCalculator(Ruleset ruleset, IBeatmap beatmap, Score score)
|
||||
public OsuPerformanceCalculator(Ruleset ruleset, WorkingBeatmap beatmap, Score score)
|
||||
: base(ruleset, beatmap, score)
|
||||
{
|
||||
countHitCircles = Beatmap.HitObjects.Count(h => h is HitCircle);
|
||||
@ -102,7 +104,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
|
||||
private double computeAimValue()
|
||||
{
|
||||
double aimValue = Math.Pow(5.0f * Math.Max(1.0f, Attributes["Aim"] / 0.0675f) - 4.0f, 3.0f) / 100000.0f;
|
||||
double aimValue = Math.Pow(5.0f * Math.Max(1.0f, Attributes.AimStrain / 0.0675f) - 4.0f, 3.0f) / 100000.0f;
|
||||
|
||||
// Longer maps are worth more
|
||||
double lengthBonus = 0.95f + 0.4f * Math.Min(1.0f, totalHits / 2000.0f) +
|
||||
@ -151,7 +153,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
|
||||
private double computeSpeedValue()
|
||||
{
|
||||
double speedValue = Math.Pow(5.0f * Math.Max(1.0f, Attributes["Speed"] / 0.0675f) - 4.0f, 3.0f) / 100000.0f;
|
||||
double speedValue = Math.Pow(5.0f * Math.Max(1.0f, Attributes.SpeedStrain / 0.0675f) - 4.0f, 3.0f) / 100000.0f;
|
||||
|
||||
// Longer maps are worth more
|
||||
speedValue *= 0.95f + 0.4f * Math.Min(1.0f, totalHits / 2000.0f) +
|
||||
|
@ -120,9 +120,9 @@ namespace osu.Game.Rulesets.Osu
|
||||
|
||||
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_osu_o };
|
||||
|
||||
public override DifficultyCalculator CreateDifficultyCalculator(IBeatmap beatmap, Mod[] mods = null) => new OsuDifficultyCalculator(beatmap, mods);
|
||||
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new OsuDifficultyCalculator(this, beatmap);
|
||||
|
||||
public override PerformanceCalculator CreatePerformanceCalculator(IBeatmap beatmap, Score score) => new OsuPerformanceCalculator(this, beatmap, score);
|
||||
public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, Score score) => new OsuPerformanceCalculator(this, beatmap, score);
|
||||
|
||||
public override HitObjectComposer CreateHitObjectComposer() => new OsuHitObjectComposer(this);
|
||||
|
||||
|
@ -1,16 +0,0 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
|
||||
{
|
||||
public TestCasePerformancePoints()
|
||||
: base(new TaikoRuleset())
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -27,54 +27,33 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
||||
/// </summary>
|
||||
private const double decay_weight = 0.9;
|
||||
|
||||
/// <summary>
|
||||
/// HitObjects are stored as a member variable.
|
||||
/// </summary>
|
||||
private readonly List<TaikoHitObjectDifficulty> difficultyHitObjects = new List<TaikoHitObjectDifficulty>();
|
||||
|
||||
public TaikoDifficultyCalculator(IBeatmap beatmap)
|
||||
: base(beatmap)
|
||||
public TaikoDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap)
|
||||
: base(ruleset, beatmap)
|
||||
{
|
||||
}
|
||||
|
||||
public TaikoDifficultyCalculator(IBeatmap beatmap, Mod[] mods)
|
||||
: base(beatmap, mods)
|
||||
protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate)
|
||||
{
|
||||
}
|
||||
var difficultyHitObjects = new List<TaikoHitObjectDifficulty>();
|
||||
|
||||
public override double Calculate(Dictionary<string, double> categoryDifficulty = null)
|
||||
{
|
||||
// Fill our custom DifficultyHitObject class, that carries additional information
|
||||
difficultyHitObjects.Clear();
|
||||
|
||||
foreach (var hitObject in Beatmap.HitObjects)
|
||||
foreach (var hitObject in beatmap.HitObjects)
|
||||
difficultyHitObjects.Add(new TaikoHitObjectDifficulty((TaikoHitObject)hitObject));
|
||||
|
||||
// Sort DifficultyHitObjects by StartTime of the HitObjects - just to make sure.
|
||||
difficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime));
|
||||
|
||||
if (!calculateStrainValues()) return 0;
|
||||
if (!calculateStrainValues(difficultyHitObjects, timeRate))
|
||||
return new DifficultyAttributes(mods, 0);
|
||||
|
||||
double starRating = calculateDifficulty() * star_scaling_factor;
|
||||
double starRating = calculateDifficulty(difficultyHitObjects, timeRate) * star_scaling_factor;
|
||||
|
||||
if (categoryDifficulty != null)
|
||||
categoryDifficulty["Strain"] = starRating;
|
||||
|
||||
return starRating;
|
||||
return new DifficultyAttributes(mods, starRating);
|
||||
}
|
||||
|
||||
protected override Mod[] DifficultyAdjustmentMods => new Mod[]
|
||||
{
|
||||
new TaikoModDoubleTime(),
|
||||
new TaikoModHalfTime(),
|
||||
new TaikoModEasy(),
|
||||
new TaikoModHardRock(),
|
||||
};
|
||||
|
||||
private bool calculateStrainValues()
|
||||
private bool calculateStrainValues(List<TaikoHitObjectDifficulty> objects, double timeRate)
|
||||
{
|
||||
// Traverse hitObjects in pairs to calculate the strain value of NextHitObject from the strain value of CurrentHitObject and environment.
|
||||
using (List<TaikoHitObjectDifficulty>.Enumerator hitObjectsEnumerator = difficultyHitObjects.GetEnumerator())
|
||||
using (var hitObjectsEnumerator = objects.GetEnumerator())
|
||||
{
|
||||
if (!hitObjectsEnumerator.MoveNext()) return false;
|
||||
|
||||
@ -84,7 +63,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
||||
while (hitObjectsEnumerator.MoveNext())
|
||||
{
|
||||
var next = hitObjectsEnumerator.Current;
|
||||
next?.CalculateStrains(current, TimeRate);
|
||||
next?.CalculateStrains(current, timeRate);
|
||||
current = next;
|
||||
}
|
||||
|
||||
@ -92,9 +71,9 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
||||
}
|
||||
}
|
||||
|
||||
private double calculateDifficulty()
|
||||
private double calculateDifficulty(List<TaikoHitObjectDifficulty> objects, double timeRate)
|
||||
{
|
||||
double actualStrainStep = strain_step * TimeRate;
|
||||
double actualStrainStep = strain_step * timeRate;
|
||||
|
||||
// Find the highest strain value within each strain step
|
||||
List<double> highestStrains = new List<double>();
|
||||
@ -102,7 +81,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
||||
double maximumStrain = 0; // We need to keep track of the maximum strain in the current interval
|
||||
|
||||
TaikoHitObjectDifficulty previousHitObject = null;
|
||||
foreach (var hitObject in difficultyHitObjects)
|
||||
foreach (var hitObject in objects)
|
||||
{
|
||||
// While we are beyond the current interval push the currently available maximum to our strain list
|
||||
while (hitObject.BaseHitObject.StartTime > intervalEndTime)
|
||||
@ -144,5 +123,13 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
||||
|
||||
return difficulty;
|
||||
}
|
||||
|
||||
protected override Mod[] DifficultyAdjustmentMods => new Mod[]
|
||||
{
|
||||
new TaikoModDoubleTime(),
|
||||
new TaikoModHalfTime(),
|
||||
new TaikoModEasy(),
|
||||
new TaikoModHardRock(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -22,10 +22,10 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
||||
private int countMeh;
|
||||
private int countMiss;
|
||||
|
||||
public TaikoPerformanceCalculator(Ruleset ruleset, IBeatmap beatmap, Score score)
|
||||
public TaikoPerformanceCalculator(Ruleset ruleset, WorkingBeatmap beatmap, Score score)
|
||||
: base(ruleset, beatmap, score)
|
||||
{
|
||||
beatmapMaxCombo = beatmap.HitObjects.Count(h => h is Hit);
|
||||
beatmapMaxCombo = Beatmap.HitObjects.Count(h => h is Hit);
|
||||
}
|
||||
|
||||
public override double Calculate(Dictionary<string, double> categoryDifficulty = null)
|
||||
@ -68,7 +68,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
||||
|
||||
private double computeStrainValue()
|
||||
{
|
||||
double strainValue = Math.Pow(5.0 * Math.Max(1.0, Attributes["Strain"] / 0.0075) - 4.0, 2.0) / 100000.0;
|
||||
double strainValue = Math.Pow(5.0 * Math.Max(1.0, Attributes.StarRating / 0.0075) - 4.0, 2.0) / 100000.0;
|
||||
|
||||
// Longer maps are worth more
|
||||
double lengthBonus = 1 + 0.1f * Math.Min(1.0, totalHits / 1500.0);
|
||||
|
@ -114,9 +114,9 @@ namespace osu.Game.Rulesets.Taiko
|
||||
|
||||
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_taiko_o };
|
||||
|
||||
public override DifficultyCalculator CreateDifficultyCalculator(IBeatmap beatmap, Mod[] mods = null) => new TaikoDifficultyCalculator(beatmap, mods);
|
||||
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new TaikoDifficultyCalculator(this, beatmap);
|
||||
|
||||
public override PerformanceCalculator CreatePerformanceCalculator(IBeatmap beatmap, Score score) => new TaikoPerformanceCalculator(this, beatmap, score);
|
||||
public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, Score score) => new TaikoPerformanceCalculator(this, beatmap, score);
|
||||
|
||||
public override int? LegacyID => 1;
|
||||
|
||||
|
@ -2,8 +2,8 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Difficulty;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
|
||||
@ -139,14 +139,14 @@ namespace osu.Game.Tests.NonVisual
|
||||
private class TestDifficultyCalculator : DifficultyCalculator
|
||||
{
|
||||
public TestDifficultyCalculator(params Mod[] mods)
|
||||
: base(null)
|
||||
: base(null, null)
|
||||
{
|
||||
DifficultyAdjustmentMods = mods;
|
||||
}
|
||||
|
||||
public override double Calculate(Dictionary<string, double> categoryDifficulty = null) => throw new NotImplementedException();
|
||||
|
||||
protected override Mod[] DifficultyAdjustmentMods { get; }
|
||||
|
||||
protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate) => throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -366,8 +366,7 @@ namespace osu.Game.Beatmaps
|
||||
if (ruleset != null)
|
||||
{
|
||||
// TODO: this should be done in a better place once we actually need to dynamically update it.
|
||||
var converted = new DummyConversionBeatmap(beatmap).GetPlayableBeatmap(ruleset);
|
||||
beatmap.BeatmapInfo.StarDifficulty = ruleset.CreateInstance().CreateDifficultyCalculator(converted).Calculate();
|
||||
beatmap.BeatmapInfo.StarDifficulty = ruleset.CreateInstance().CreateDifficultyCalculator(new DummyConversionBeatmap(beatmap)).Calculate().StarRating;
|
||||
}
|
||||
else
|
||||
beatmap.BeatmapInfo.StarDifficulty = 0;
|
||||
|
@ -62,7 +62,7 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new DummyBeatmapConverter { Beatmap = beatmap };
|
||||
|
||||
public override DifficultyCalculator CreateDifficultyCalculator(IBeatmap beatmap, Mod[] mods = null) => null;
|
||||
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => null;
|
||||
|
||||
public override string Description => "dummy";
|
||||
|
||||
|
19
osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs
Normal file
19
osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Rulesets.Mods;
|
||||
|
||||
namespace osu.Game.Rulesets.Difficulty
|
||||
{
|
||||
public class DifficultyAttributes
|
||||
{
|
||||
public readonly Mod[] Mods;
|
||||
public readonly double StarRating;
|
||||
|
||||
public DifficultyAttributes(Mod[] mods, double starRating)
|
||||
{
|
||||
Mods = mods;
|
||||
StarRating = starRating;
|
||||
}
|
||||
}
|
||||
}
|
@ -13,28 +13,44 @@ namespace osu.Game.Rulesets.Difficulty
|
||||
{
|
||||
public abstract class DifficultyCalculator
|
||||
{
|
||||
protected readonly IBeatmap Beatmap;
|
||||
protected readonly Mod[] Mods;
|
||||
private readonly Ruleset ruleset;
|
||||
private readonly WorkingBeatmap beatmap;
|
||||
|
||||
protected double TimeRate { get; private set; } = 1;
|
||||
|
||||
protected DifficultyCalculator(IBeatmap beatmap, Mod[] mods = null)
|
||||
protected DifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap)
|
||||
{
|
||||
Beatmap = beatmap;
|
||||
Mods = mods ?? new Mod[0];
|
||||
|
||||
ApplyMods(Mods);
|
||||
this.ruleset = ruleset;
|
||||
this.beatmap = beatmap;
|
||||
}
|
||||
|
||||
protected virtual void ApplyMods(Mod[] mods)
|
||||
/// <summary>
|
||||
/// Calculates the difficulty of the beatmap using a specific mod combination.
|
||||
/// </summary>
|
||||
/// <param name="mods">The mods that should be applied to the beatmap.</param>
|
||||
/// <returns>A structure describing the difficulty of the beatmap.</returns>
|
||||
public DifficultyAttributes Calculate(params Mod[] mods)
|
||||
{
|
||||
beatmap.Mods.Value = mods;
|
||||
IBeatmap playableBeatmap = beatmap.GetPlayableBeatmap(ruleset.RulesetInfo);
|
||||
|
||||
var clock = new StopwatchClock();
|
||||
mods.OfType<IApplicableToClock>().ForEach(m => m.ApplyToClock(clock));
|
||||
TimeRate = clock.Rate;
|
||||
|
||||
return Calculate(playableBeatmap, mods, clock.Rate);
|
||||
}
|
||||
|
||||
protected virtual void PreprocessHitObjects()
|
||||
/// <summary>
|
||||
/// Calculates the difficulty of the beatmap using all mod combinations applicable to the beatmap.
|
||||
/// </summary>
|
||||
/// <returns>A collection of structures describing the difficulty of the beatmap for each mod combination.</returns>
|
||||
public IEnumerable<DifficultyAttributes> CalculateAll()
|
||||
{
|
||||
foreach (var combination in CreateDifficultyAdjustmentModCombinations())
|
||||
{
|
||||
if (combination is MultiMod multi)
|
||||
yield return Calculate(multi.Mods);
|
||||
else
|
||||
yield return Calculate(combination);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -75,6 +91,13 @@ namespace osu.Game.Rulesets.Difficulty
|
||||
/// </summary>
|
||||
protected virtual Mod[] DifficultyAdjustmentMods => Array.Empty<Mod>();
|
||||
|
||||
public abstract double Calculate(Dictionary<string, double> categoryDifficulty = null);
|
||||
/// <summary>
|
||||
/// Calculates the difficulty of a <see cref="Beatmap"/> using a specific <see cref="Mod"/> combination.
|
||||
/// </summary>
|
||||
/// <param name="beatmap">The <see cref="IBeatmap"/> to compute the difficulty for.</param>
|
||||
/// <param name="mods">The <see cref="Mod"/>s that should be applied.</param>
|
||||
/// <param name="timeRate">The rate of time in <paramref name="beatmap"/>.</param>
|
||||
/// <returns>A structure containing the difficulty attributes.</returns>
|
||||
protected abstract DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate);
|
||||
}
|
||||
}
|
||||
|
@ -13,8 +13,7 @@ namespace osu.Game.Rulesets.Difficulty
|
||||
{
|
||||
public abstract class PerformanceCalculator
|
||||
{
|
||||
private readonly Dictionary<string, double> attributes = new Dictionary<string, double>();
|
||||
protected IDictionary<string, double> Attributes => attributes;
|
||||
protected readonly DifficultyAttributes Attributes;
|
||||
|
||||
protected readonly Ruleset Ruleset;
|
||||
protected readonly IBeatmap Beatmap;
|
||||
@ -22,14 +21,15 @@ namespace osu.Game.Rulesets.Difficulty
|
||||
|
||||
protected double TimeRate { get; private set; } = 1;
|
||||
|
||||
protected PerformanceCalculator(Ruleset ruleset, IBeatmap beatmap, Score score)
|
||||
protected PerformanceCalculator(Ruleset ruleset, WorkingBeatmap beatmap, Score score)
|
||||
{
|
||||
Ruleset = ruleset;
|
||||
Beatmap = beatmap;
|
||||
Score = score;
|
||||
|
||||
var diffCalc = ruleset.CreateDifficultyCalculator(beatmap, score.Mods);
|
||||
diffCalc.Calculate(attributes);
|
||||
beatmap.Mods.Value = score.Mods;
|
||||
Beatmap = beatmap.GetPlayableBeatmap(ruleset.RulesetInfo);
|
||||
|
||||
Attributes = ruleset.CreateDifficultyCalculator(beatmap).Calculate(score.Mods);
|
||||
|
||||
ApplyMods(score.Mods);
|
||||
}
|
||||
|
@ -61,9 +61,9 @@ namespace osu.Game.Rulesets
|
||||
|
||||
public virtual IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => null;
|
||||
|
||||
public abstract DifficultyCalculator CreateDifficultyCalculator(IBeatmap beatmap, Mod[] mods = null);
|
||||
public abstract DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap);
|
||||
|
||||
public virtual PerformanceCalculator CreatePerformanceCalculator(IBeatmap beatmap, Score score) => null;
|
||||
public virtual PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, Score score) => null;
|
||||
|
||||
public virtual HitObjectComposer CreateHitObjectComposer() => null;
|
||||
|
||||
|
@ -1,403 +0,0 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Caching;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
public abstract class TestCasePerformancePoints : OsuTestCase
|
||||
{
|
||||
protected TestCasePerformancePoints(Ruleset ruleset)
|
||||
{
|
||||
Child = new GridContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Content = new[]
|
||||
{
|
||||
new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black,
|
||||
Alpha = 0.5f,
|
||||
},
|
||||
new ScrollContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = new BeatmapList(ruleset, Beatmap)
|
||||
}
|
||||
}
|
||||
},
|
||||
null,
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black,
|
||||
Alpha = 0.5f,
|
||||
},
|
||||
new ScrollContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = new StarRatingGrid()
|
||||
}
|
||||
}
|
||||
},
|
||||
null,
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black,
|
||||
Alpha = 0.5f,
|
||||
},
|
||||
new ScrollContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = new PerformanceList()
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
ColumnDimensions = new[]
|
||||
{
|
||||
new Dimension(),
|
||||
new Dimension(GridSizeMode.Absolute, 20),
|
||||
new Dimension(),
|
||||
new Dimension(GridSizeMode.Absolute, 20)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private class BeatmapList : CompositeDrawable
|
||||
{
|
||||
private readonly Container<BeatmapDisplay> beatmapDisplays;
|
||||
private readonly Ruleset ruleset;
|
||||
private readonly BindableBeatmap beatmapBindable;
|
||||
|
||||
public BeatmapList(Ruleset ruleset, BindableBeatmap beatmapBindable)
|
||||
{
|
||||
this.ruleset = ruleset;
|
||||
this.beatmapBindable = beatmapBindable;
|
||||
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
InternalChild = beatmapDisplays = new FillFlowContainer<BeatmapDisplay>
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(0, 4)
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(BeatmapManager beatmaps)
|
||||
{
|
||||
var sets = beatmaps.GetAllUsableBeatmapSets();
|
||||
var allBeatmaps = sets.SelectMany(s => s.Beatmaps).Where(b => ruleset.LegacyID == null || b.RulesetID == ruleset.LegacyID);
|
||||
|
||||
allBeatmaps.ForEach(b => beatmapDisplays.Add(new BeatmapDisplay(b, beatmapBindable)));
|
||||
}
|
||||
|
||||
private class BeatmapDisplay : CompositeDrawable, IHasTooltip
|
||||
{
|
||||
private readonly OsuSpriteText text;
|
||||
private readonly BeatmapInfo beatmap;
|
||||
|
||||
private readonly BindableBeatmap beatmapBindable;
|
||||
|
||||
private BeatmapManager beatmaps;
|
||||
|
||||
private bool isSelected;
|
||||
|
||||
public string TooltipText => text.Text;
|
||||
|
||||
public BeatmapDisplay(BeatmapInfo beatmap, BindableBeatmap beatmapBindable)
|
||||
{
|
||||
this.beatmap = beatmap;
|
||||
this.beatmapBindable = beatmapBindable;
|
||||
|
||||
AutoSizeAxes = Axes.Both;
|
||||
InternalChild = text = new OsuSpriteText();
|
||||
|
||||
this.beatmapBindable.ValueChanged += beatmapChanged;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(BeatmapManager beatmaps)
|
||||
{
|
||||
this.beatmaps = beatmaps;
|
||||
|
||||
var working = beatmaps.GetWorkingBeatmap(beatmap);
|
||||
text.Text = $"{working.Metadata.Artist} - {working.Metadata.Title} ({working.Metadata.AuthorString}) [{working.BeatmapInfo.Version}]";
|
||||
}
|
||||
|
||||
private void beatmapChanged(WorkingBeatmap newBeatmap)
|
||||
{
|
||||
if (isSelected)
|
||||
this.FadeColour(Color4.White, 100);
|
||||
isSelected = false;
|
||||
}
|
||||
|
||||
protected override bool OnClick(InputState state)
|
||||
{
|
||||
if (beatmapBindable.Value.BeatmapInfo.ID == beatmap.ID)
|
||||
return false;
|
||||
|
||||
beatmapBindable.Value = beatmaps.GetWorkingBeatmap(beatmap);
|
||||
isSelected = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool OnHover(InputState state)
|
||||
{
|
||||
if (isSelected)
|
||||
return false;
|
||||
this.FadeColour(Color4.Yellow, 100);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(InputState state)
|
||||
{
|
||||
if (isSelected)
|
||||
return;
|
||||
this.FadeColour(Color4.White, 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class PerformanceList : CompositeDrawable
|
||||
{
|
||||
private readonly FillFlowContainer<PerformanceDisplay> scores;
|
||||
private APIAccess api;
|
||||
|
||||
private readonly IBindable<WorkingBeatmap> currentBeatmap = new Bindable<WorkingBeatmap>();
|
||||
|
||||
public PerformanceList()
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
InternalChild = scores = new FillFlowContainer<PerformanceDisplay>
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(0, 4)
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(IBindableBeatmap beatmap, APIAccess api)
|
||||
{
|
||||
this.api = api;
|
||||
|
||||
if (!api.IsLoggedIn)
|
||||
{
|
||||
InternalChild = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Text = "Please sign in to see online scores",
|
||||
};
|
||||
}
|
||||
|
||||
currentBeatmap.ValueChanged += beatmapChanged;
|
||||
currentBeatmap.BindTo(beatmap);
|
||||
}
|
||||
|
||||
private GetScoresRequest lastRequest;
|
||||
private void beatmapChanged(WorkingBeatmap newBeatmap)
|
||||
{
|
||||
if (!IsAlive) return;
|
||||
|
||||
lastRequest?.Cancel();
|
||||
scores.Clear();
|
||||
|
||||
if (!api.IsLoggedIn)
|
||||
return;
|
||||
|
||||
lastRequest = new GetScoresRequest(newBeatmap.BeatmapInfo, newBeatmap.BeatmapInfo.Ruleset);
|
||||
lastRequest.Success += res => res.Scores.ForEach(s => scores.Add(new PerformanceDisplay(s, newBeatmap.Beatmap)));
|
||||
api.Queue(lastRequest);
|
||||
}
|
||||
|
||||
private class PerformanceDisplay : CompositeDrawable
|
||||
{
|
||||
private readonly OsuSpriteText text;
|
||||
|
||||
private readonly Score score;
|
||||
private readonly IBeatmap beatmap;
|
||||
|
||||
public PerformanceDisplay(Score score, IBeatmap beatmap)
|
||||
{
|
||||
this.score = score;
|
||||
this.beatmap = beatmap;
|
||||
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
InternalChild = text = new OsuSpriteText();
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
var ruleset = beatmap.BeatmapInfo.Ruleset.CreateInstance();
|
||||
var calculator = ruleset.CreatePerformanceCalculator(beatmap, score);
|
||||
if (calculator == null)
|
||||
return;
|
||||
|
||||
var attributes = new Dictionary<string, double>();
|
||||
double performance = calculator.Calculate(attributes);
|
||||
|
||||
text.Text = $"{score.User.Username} -> online: {score.PP:n2}pp | local: {performance:n2}pp";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class StarRatingGrid : CompositeDrawable
|
||||
{
|
||||
private readonly FillFlowContainer<OsuCheckbox> modFlow;
|
||||
private readonly OsuSpriteText totalText;
|
||||
private readonly FillFlowContainer categoryTexts;
|
||||
|
||||
public StarRatingGrid()
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
InternalChild = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
modFlow = new FillFlowContainer<OsuCheckbox>
|
||||
{
|
||||
Name = "Checkbox flow",
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Spacing = new Vector2(4, 4)
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
Name = "Information display",
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Spacing = new Vector2(0, 4),
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
totalText = new OsuSpriteText { TextSize = 24 },
|
||||
categoryTexts = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Vertical
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(IBindableBeatmap beatmap)
|
||||
{
|
||||
beatmap.ValueChanged += beatmapChanged;
|
||||
}
|
||||
|
||||
private Cached informationCache = new Cached();
|
||||
|
||||
private Ruleset ruleset;
|
||||
private WorkingBeatmap beatmap;
|
||||
|
||||
private void beatmapChanged(WorkingBeatmap newBeatmap)
|
||||
{
|
||||
beatmap = newBeatmap;
|
||||
|
||||
modFlow.Clear();
|
||||
|
||||
ruleset = newBeatmap.BeatmapInfo.Ruleset.CreateInstance();
|
||||
foreach (var mod in ruleset.GetAllMods())
|
||||
{
|
||||
var checkBox = new OsuCheckbox
|
||||
{
|
||||
RelativeSizeAxes = Axes.None,
|
||||
Width = 50,
|
||||
LabelText = mod.ShortenedName
|
||||
};
|
||||
|
||||
checkBox.Current.ValueChanged += v => informationCache.Invalidate();
|
||||
modFlow.Add(checkBox);
|
||||
}
|
||||
|
||||
informationCache.Invalidate();
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (ruleset == null)
|
||||
return;
|
||||
|
||||
if (!informationCache.IsValid)
|
||||
{
|
||||
totalText.Text = string.Empty;
|
||||
categoryTexts.Clear();
|
||||
|
||||
var allMods = ruleset.GetAllMods().ToList();
|
||||
Mod[] activeMods = modFlow.Where(c => c.Current.Value).Select(c => allMods.First(m => m.ShortenedName == c.LabelText)).ToArray();
|
||||
|
||||
var diffCalc = ruleset.CreateDifficultyCalculator(beatmap.Beatmap, activeMods);
|
||||
if (diffCalc != null)
|
||||
{
|
||||
var categories = new Dictionary<string, double>();
|
||||
double totalSr = diffCalc.Calculate(categories);
|
||||
|
||||
totalText.Text = $"Star rating: {totalSr:n2}";
|
||||
foreach (var kvp in categories)
|
||||
categoryTexts.Add(new OsuSpriteText { Text = $"{kvp.Key}: {kvp.Value:n2}" });
|
||||
}
|
||||
|
||||
informationCache.Validate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user