mirror of
https://github.com/ppy/osu.git
synced 2025-01-14 00:42:55 +08:00
Merge branch 'master' into timeline-audio-scrolling
This commit is contained in:
commit
a20ec0d4cc
@ -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 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;
|
public override int? LegacyID => 2;
|
||||||
|
|
||||||
|
@ -1,18 +1,19 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Difficulty;
|
using osu.Game.Rulesets.Difficulty;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Difficulty
|
namespace osu.Game.Rulesets.Catch.Difficulty
|
||||||
{
|
{
|
||||||
public class CatchDifficultyCalculator : DifficultyCalculator
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
|||||||
{
|
{
|
||||||
public virtual bool CanBePlated => false;
|
public virtual bool CanBePlated => false;
|
||||||
|
|
||||||
|
public virtual bool StaysOnPlate => CanBePlated;
|
||||||
|
|
||||||
protected DrawableCatchHitObject(CatchHitObject hitObject)
|
protected DrawableCatchHitObject(CatchHitObject hitObject)
|
||||||
: base(hitObject)
|
: base(hitObject)
|
||||||
{
|
{
|
||||||
|
@ -13,6 +13,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
|||||||
{
|
{
|
||||||
private Pulp pulp;
|
private Pulp pulp;
|
||||||
|
|
||||||
|
public override bool StaysOnPlate => false;
|
||||||
|
|
||||||
public DrawableDroplet(Droplet h)
|
public DrawableDroplet(Droplet h)
|
||||||
: base(h)
|
: base(h)
|
||||||
{
|
{
|
||||||
|
@ -124,6 +124,9 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
X = X + Curve.PositionAt(reversed ? 0 : 1).X / CatchPlayfield.BASE_WIDTH
|
X = X + Curve.PositionAt(reversed ? 0 : 1).X / CatchPlayfield.BASE_WIDTH
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (NestedHitObjects.LastOrDefault() is IHasComboInformation lastNested)
|
||||||
|
lastNested.LastInCombo = LastInCombo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public double EndTime => StartTime + this.SpanCount() * Curve.Distance / Velocity;
|
public double EndTime => StartTime + this.SpanCount() * Curve.Distance / Velocity;
|
||||||
|
@ -48,6 +48,16 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
|
|
||||||
public void OnJudgement(DrawableCatchHitObject fruit, Judgement judgement)
|
public void OnJudgement(DrawableCatchHitObject fruit, Judgement judgement)
|
||||||
{
|
{
|
||||||
|
void runAfterLoaded(Action action)
|
||||||
|
{
|
||||||
|
// this is required to make this run after the last caught fruit runs UpdateState at least once.
|
||||||
|
// TODO: find a better alternative
|
||||||
|
if (lastPlateableFruit.IsLoaded)
|
||||||
|
action();
|
||||||
|
else
|
||||||
|
lastPlateableFruit.OnLoadComplete = _ => action();
|
||||||
|
}
|
||||||
|
|
||||||
if (judgement.IsHit && fruit.CanBePlated)
|
if (judgement.IsHit && fruit.CanBePlated)
|
||||||
{
|
{
|
||||||
var caughtFruit = (DrawableCatchHitObject)GetVisualRepresentation?.Invoke(fruit.HitObject);
|
var caughtFruit = (DrawableCatchHitObject)GetVisualRepresentation?.Invoke(fruit.HitObject);
|
||||||
@ -63,21 +73,17 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
caughtFruit.LifetimeEnd = double.MaxValue;
|
caughtFruit.LifetimeEnd = double.MaxValue;
|
||||||
|
|
||||||
MovableCatcher.Add(caughtFruit);
|
MovableCatcher.Add(caughtFruit);
|
||||||
|
|
||||||
lastPlateableFruit = caughtFruit;
|
lastPlateableFruit = caughtFruit;
|
||||||
|
|
||||||
|
if (!fruit.StaysOnPlate)
|
||||||
|
runAfterLoaded(() => MovableCatcher.Explode(caughtFruit));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fruit.HitObject.LastInCombo)
|
if (fruit.HitObject.LastInCombo)
|
||||||
{
|
{
|
||||||
if (judgement.IsHit)
|
if (judgement.IsHit)
|
||||||
{
|
runAfterLoaded(() => MovableCatcher.Explode());
|
||||||
// this is required to make this run after the last caught fruit runs UpdateState at least once.
|
|
||||||
// TODO: find a better alternative
|
|
||||||
if (lastPlateableFruit.IsLoaded)
|
|
||||||
MovableCatcher.Explode();
|
|
||||||
else
|
|
||||||
lastPlateableFruit.OnLoadComplete = _ => { MovableCatcher.Explode(); };
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
MovableCatcher.Drop();
|
MovableCatcher.Drop();
|
||||||
}
|
}
|
||||||
@ -378,28 +384,31 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
var fruit = caughtFruit.ToArray();
|
var fruit = caughtFruit.ToArray();
|
||||||
|
|
||||||
foreach (var f in fruit)
|
foreach (var f in fruit)
|
||||||
|
Explode(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Explode(DrawableHitObject fruit)
|
||||||
|
{
|
||||||
|
var originalX = fruit.X * Scale.X;
|
||||||
|
|
||||||
|
if (ExplodingFruitTarget != null)
|
||||||
{
|
{
|
||||||
var originalX = f.X * Scale.X;
|
fruit.Anchor = Anchor.TopLeft;
|
||||||
|
fruit.Position = caughtFruit.ToSpaceOfOtherDrawable(fruit.DrawPosition, ExplodingFruitTarget);
|
||||||
|
|
||||||
if (ExplodingFruitTarget != null)
|
caughtFruit.Remove(fruit);
|
||||||
{
|
|
||||||
f.Anchor = Anchor.TopLeft;
|
|
||||||
f.Position = caughtFruit.ToSpaceOfOtherDrawable(f.DrawPosition, ExplodingFruitTarget);
|
|
||||||
|
|
||||||
caughtFruit.Remove(f);
|
ExplodingFruitTarget.Add(fruit);
|
||||||
|
|
||||||
ExplodingFruitTarget.Add(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
f.MoveToY(f.Y - 50, 250, Easing.OutSine)
|
|
||||||
.Then()
|
|
||||||
.MoveToY(f.Y + 50, 500, Easing.InSine);
|
|
||||||
|
|
||||||
f.MoveToX(f.X + originalX * 6, 1000);
|
|
||||||
f.FadeOut(750);
|
|
||||||
|
|
||||||
f.Expire();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fruit.MoveToY(fruit.Y - 50, 250, Easing.OutSine)
|
||||||
|
.Then()
|
||||||
|
.MoveToY(fruit.Y + 50, 500, Easing.InSine);
|
||||||
|
|
||||||
|
fruit.MoveToX(fruit.X + originalX * 6, 1000);
|
||||||
|
fruit.FadeOut(750);
|
||||||
|
|
||||||
|
fruit.Expire();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class CatcherSprite : Sprite
|
private class CatcherSprite : Sprite
|
||||||
|
@ -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())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -58,6 +58,13 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
|
|
||||||
public override Pattern Generate()
|
public override Pattern Generate()
|
||||||
{
|
{
|
||||||
|
if (TotalColumns == 1)
|
||||||
|
{
|
||||||
|
var pattern = new Pattern();
|
||||||
|
addToPattern(pattern, 0, HitObject.StartTime, endTime);
|
||||||
|
return pattern;
|
||||||
|
}
|
||||||
|
|
||||||
if (spanCount > 1)
|
if (spanCount > 1)
|
||||||
{
|
{
|
||||||
if (segmentDuration <= 90)
|
if (segmentDuration <= 90)
|
||||||
|
@ -77,10 +77,25 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
convertType |= PatternType.LowProbability;
|
convertType |= PatternType.LowProbability;
|
||||||
|
|
||||||
|
if ((convertType & PatternType.KeepSingle) == 0)
|
||||||
|
{
|
||||||
|
if (HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH) && TotalColumns != 8)
|
||||||
|
convertType |= PatternType.Mirror;
|
||||||
|
else
|
||||||
|
convertType |= PatternType.Gathered;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Pattern Generate()
|
public override Pattern Generate()
|
||||||
{
|
{
|
||||||
|
if (TotalColumns == 1)
|
||||||
|
{
|
||||||
|
var pattern = new Pattern();
|
||||||
|
addToPattern(pattern, 0);
|
||||||
|
return pattern;
|
||||||
|
}
|
||||||
|
|
||||||
int lastColumn = PreviousPattern.HitObjects.FirstOrDefault()?.Column ?? 0;
|
int lastColumn = PreviousPattern.HitObjects.FirstOrDefault()?.Column ?? 0;
|
||||||
|
|
||||||
if ((convertType & PatternType.Reverse) > 0 && PreviousPattern.HitObjects.Any())
|
if ((convertType & PatternType.Reverse) > 0 && PreviousPattern.HitObjects.Any())
|
||||||
@ -346,7 +361,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
addToCentre = false;
|
addToCentre = false;
|
||||||
|
|
||||||
if ((convertType & PatternType.ForceNotStack) > 0)
|
if ((convertType & PatternType.ForceNotStack) > 0)
|
||||||
return getRandomNoteCount(p2 / 2, p2, (p2 + p3) / 2, p3);
|
return getRandomNoteCount(1 / 2f + p2 / 2, p2, (p2 + p3) / 2, p3);
|
||||||
|
|
||||||
switch (TotalColumns)
|
switch (TotalColumns)
|
||||||
{
|
{
|
||||||
|
@ -29,47 +29,33 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private const double decay_weight = 0.9;
|
private const double decay_weight = 0.9;
|
||||||
|
|
||||||
/// <summary>
|
public ManiaDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap)
|
||||||
/// HitObjects are stored as a member variable.
|
: base(ruleset, beatmap)
|
||||||
/// </summary>
|
|
||||||
private readonly List<ManiaHitObjectDifficulty> difficultyHitObjects = new List<ManiaHitObjectDifficulty>();
|
|
||||||
|
|
||||||
public ManiaDifficultyCalculator(IBeatmap beatmap)
|
|
||||||
: base(beatmap)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public ManiaDifficultyCalculator(IBeatmap beatmap, Mod[] mods)
|
protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate)
|
||||||
: base(beatmap, mods)
|
|
||||||
{
|
{
|
||||||
}
|
var difficultyHitObjects = new List<ManiaHitObjectDifficulty>();
|
||||||
|
|
||||||
public override double Calculate(Dictionary<string, double> categoryDifficulty = null)
|
int columnCount = ((ManiaBeatmap)beatmap).TotalColumns;
|
||||||
{
|
|
||||||
// Fill our custom DifficultyHitObject class, that carries additional information
|
|
||||||
difficultyHitObjects.Clear();
|
|
||||||
|
|
||||||
int columnCount = (Beatmap as ManiaBeatmap)?.TotalColumns ?? 7;
|
|
||||||
|
|
||||||
// Sort DifficultyHitObjects by StartTime of the HitObjects - just to make sure.
|
// 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
|
// 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())
|
if (!calculateStrainValues(difficultyHitObjects, timeRate))
|
||||||
return 0;
|
return new DifficultyAttributes(mods, 0);
|
||||||
|
|
||||||
double starRating = calculateDifficulty() * star_scaling_factor;
|
double starRating = calculateDifficulty(difficultyHitObjects, timeRate) * star_scaling_factor;
|
||||||
|
|
||||||
if (categoryDifficulty != null)
|
return new DifficultyAttributes(mods, starRating);
|
||||||
categoryDifficulty["Strain"] = starRating;
|
|
||||||
|
|
||||||
return 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.
|
// 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())
|
if (!hitObjectsEnumerator.MoveNext())
|
||||||
return false;
|
return false;
|
||||||
@ -80,7 +66,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
|||||||
while (hitObjectsEnumerator.MoveNext())
|
while (hitObjectsEnumerator.MoveNext())
|
||||||
{
|
{
|
||||||
var next = hitObjectsEnumerator.Current;
|
var next = hitObjectsEnumerator.Current;
|
||||||
next?.CalculateStrains(current, TimeRate);
|
next?.CalculateStrains(current, timeRate);
|
||||||
current = next;
|
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
|
// Find the highest strain value within each strain step
|
||||||
List<double> highestStrains = new List<double>();
|
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
|
double maximumStrain = 0; // We need to keep track of the maximum strain in the current interval
|
||||||
|
|
||||||
ManiaHitObjectDifficulty previousHitObject = null;
|
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 we are beyond the current interval push the currently available maximum to our strain list
|
||||||
while (hitObject.BaseHitObject.StartTime > intervalEndTime)
|
while (hitObject.BaseHitObject.StartTime > intervalEndTime)
|
||||||
@ -159,5 +145,6 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
|||||||
new ManiaModKey8(),
|
new ManiaModKey8(),
|
||||||
new ManiaModKey9(),
|
new ManiaModKey9(),
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
|||||||
private int countMeh;
|
private int countMeh;
|
||||||
private int countMiss;
|
private int countMiss;
|
||||||
|
|
||||||
public ManiaPerformanceCalculator(Ruleset ruleset, IBeatmap beatmap, Score score)
|
public ManiaPerformanceCalculator(Ruleset ruleset, WorkingBeatmap beatmap, Score score)
|
||||||
: base(ruleset, beatmap, score)
|
: base(ruleset, beatmap, score)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -82,7 +82,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
|||||||
private double computeStrainValue()
|
private double computeStrainValue()
|
||||||
{
|
{
|
||||||
// Obtain strain difficulty
|
// 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
|
// Longer maps are worth more
|
||||||
strainValue *= 1.0 + 0.1 * Math.Min(1.0, totalHits / 1500.0);
|
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 RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap) => new ManiaRulesetContainer(this, beatmap);
|
||||||
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(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)
|
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 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;
|
public override int? LegacyID => 3;
|
||||||
|
|
||||||
|
@ -15,6 +15,8 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
{
|
{
|
||||||
internal class HitExplosion : CompositeDrawable
|
internal class HitExplosion : CompositeDrawable
|
||||||
{
|
{
|
||||||
|
public override bool RemoveWhenNotAlive => true;
|
||||||
|
|
||||||
private readonly CircularContainer circle;
|
private readonly CircularContainer circle;
|
||||||
|
|
||||||
public HitExplosion(DrawableHitObject judgedObject)
|
public HitExplosion(DrawableHitObject judgedObject)
|
||||||
|
@ -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
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Linq;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Difficulty;
|
using osu.Game.Rulesets.Difficulty;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
@ -18,31 +18,26 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
private const int section_length = 400;
|
private const int section_length = 400;
|
||||||
private const double difficulty_multiplier = 0.0675;
|
private const double difficulty_multiplier = 0.0675;
|
||||||
|
|
||||||
public OsuDifficultyCalculator(IBeatmap beatmap)
|
public OsuDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap)
|
||||||
: base(beatmap)
|
: base(ruleset, beatmap)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public OsuDifficultyCalculator(IBeatmap beatmap, Mod[] mods)
|
protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate)
|
||||||
: base(beatmap, mods)
|
|
||||||
{
|
{
|
||||||
}
|
OsuDifficultyBeatmap difficultyBeatmap = new OsuDifficultyBeatmap(beatmap.HitObjects.Cast<OsuHitObject>().ToList(), timeRate);
|
||||||
|
|
||||||
public override double Calculate(Dictionary<string, double> categoryDifficulty = null)
|
|
||||||
{
|
|
||||||
OsuDifficultyBeatmap beatmap = new OsuDifficultyBeatmap((List<OsuHitObject>)Beatmap.HitObjects, TimeRate);
|
|
||||||
Skill[] skills =
|
Skill[] skills =
|
||||||
{
|
{
|
||||||
new Aim(),
|
new Aim(),
|
||||||
new Speed()
|
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
|
// The first object doesn't generate a strain, so we begin with an incremented section end
|
||||||
double currentSectionEnd = 2 * sectionLength;
|
double currentSectionEnd = 2 * sectionLength;
|
||||||
|
|
||||||
foreach (OsuDifficultyHitObject h in beatmap)
|
foreach (OsuDifficultyHitObject h in difficultyBeatmap)
|
||||||
{
|
{
|
||||||
while (h.BaseObject.StartTime > currentSectionEnd)
|
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 aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier;
|
||||||
double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier;
|
double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier;
|
||||||
|
|
||||||
double starRating = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2;
|
double starRating = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2;
|
||||||
|
|
||||||
if (categoryDifficulty != null)
|
return new OsuDifficultyAttributes(mods, starRating)
|
||||||
{
|
{
|
||||||
categoryDifficulty.Add("Aim", aimRating);
|
AimStrain = aimRating,
|
||||||
categoryDifficulty.Add("Speed", speedRating);
|
SpeedStrain = speedRating
|
||||||
}
|
};
|
||||||
|
|
||||||
return starRating;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Mod[] DifficultyAdjustmentMods => new Mod[]
|
protected override Mod[] DifficultyAdjustmentMods => new Mod[]
|
||||||
|
@ -15,6 +15,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
{
|
{
|
||||||
public class OsuPerformanceCalculator : PerformanceCalculator
|
public class OsuPerformanceCalculator : PerformanceCalculator
|
||||||
{
|
{
|
||||||
|
public new OsuDifficultyAttributes Attributes => (OsuDifficultyAttributes)base.Attributes;
|
||||||
|
|
||||||
private readonly int countHitCircles;
|
private readonly int countHitCircles;
|
||||||
private readonly int beatmapMaxCombo;
|
private readonly int beatmapMaxCombo;
|
||||||
|
|
||||||
@ -37,7 +39,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
private int countMeh;
|
private int countMeh;
|
||||||
private int countMiss;
|
private int countMiss;
|
||||||
|
|
||||||
public OsuPerformanceCalculator(Ruleset ruleset, IBeatmap beatmap, Score score)
|
public OsuPerformanceCalculator(Ruleset ruleset, WorkingBeatmap beatmap, Score score)
|
||||||
: base(ruleset, beatmap, score)
|
: base(ruleset, beatmap, score)
|
||||||
{
|
{
|
||||||
countHitCircles = Beatmap.HitObjects.Count(h => h is HitCircle);
|
countHitCircles = Beatmap.HitObjects.Count(h => h is HitCircle);
|
||||||
@ -102,7 +104,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
|
|
||||||
private double computeAimValue()
|
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
|
// Longer maps are worth more
|
||||||
double lengthBonus = 0.95f + 0.4f * Math.Min(1.0f, totalHits / 2000.0f) +
|
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()
|
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
|
// Longer maps are worth more
|
||||||
speedValue *= 0.95f + 0.4f * Math.Min(1.0f, totalHits / 2000.0f) +
|
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 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);
|
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>
|
/// </summary>
|
||||||
private const double decay_weight = 0.9;
|
private const double decay_weight = 0.9;
|
||||||
|
|
||||||
/// <summary>
|
public TaikoDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap)
|
||||||
/// HitObjects are stored as a member variable.
|
: base(ruleset, beatmap)
|
||||||
/// </summary>
|
|
||||||
private readonly List<TaikoHitObjectDifficulty> difficultyHitObjects = new List<TaikoHitObjectDifficulty>();
|
|
||||||
|
|
||||||
public TaikoDifficultyCalculator(IBeatmap beatmap)
|
|
||||||
: base(beatmap)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public TaikoDifficultyCalculator(IBeatmap beatmap, Mod[] mods)
|
protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate)
|
||||||
: base(beatmap, mods)
|
|
||||||
{
|
{
|
||||||
}
|
var difficultyHitObjects = new List<TaikoHitObjectDifficulty>();
|
||||||
|
|
||||||
public override double Calculate(Dictionary<string, double> categoryDifficulty = null)
|
foreach (var hitObject in beatmap.HitObjects)
|
||||||
{
|
|
||||||
// Fill our custom DifficultyHitObject class, that carries additional information
|
|
||||||
difficultyHitObjects.Clear();
|
|
||||||
|
|
||||||
foreach (var hitObject in Beatmap.HitObjects)
|
|
||||||
difficultyHitObjects.Add(new TaikoHitObjectDifficulty((TaikoHitObject)hitObject));
|
difficultyHitObjects.Add(new TaikoHitObjectDifficulty((TaikoHitObject)hitObject));
|
||||||
|
|
||||||
// Sort DifficultyHitObjects by StartTime of the HitObjects - just to make sure.
|
// Sort DifficultyHitObjects by StartTime of the HitObjects - just to make sure.
|
||||||
difficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime));
|
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)
|
return new DifficultyAttributes(mods, starRating);
|
||||||
categoryDifficulty["Strain"] = starRating;
|
|
||||||
|
|
||||||
return starRating;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Mod[] DifficultyAdjustmentMods => new Mod[]
|
private bool calculateStrainValues(List<TaikoHitObjectDifficulty> objects, double timeRate)
|
||||||
{
|
|
||||||
new TaikoModDoubleTime(),
|
|
||||||
new TaikoModHalfTime(),
|
|
||||||
new TaikoModEasy(),
|
|
||||||
new TaikoModHardRock(),
|
|
||||||
};
|
|
||||||
|
|
||||||
private bool calculateStrainValues()
|
|
||||||
{
|
{
|
||||||
// Traverse hitObjects in pairs to calculate the strain value of NextHitObject from the strain value of CurrentHitObject and environment.
|
// 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;
|
if (!hitObjectsEnumerator.MoveNext()) return false;
|
||||||
|
|
||||||
@ -84,7 +63,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
while (hitObjectsEnumerator.MoveNext())
|
while (hitObjectsEnumerator.MoveNext())
|
||||||
{
|
{
|
||||||
var next = hitObjectsEnumerator.Current;
|
var next = hitObjectsEnumerator.Current;
|
||||||
next?.CalculateStrains(current, TimeRate);
|
next?.CalculateStrains(current, timeRate);
|
||||||
current = next;
|
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
|
// Find the highest strain value within each strain step
|
||||||
List<double> highestStrains = new List<double>();
|
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
|
double maximumStrain = 0; // We need to keep track of the maximum strain in the current interval
|
||||||
|
|
||||||
TaikoHitObjectDifficulty previousHitObject = null;
|
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 we are beyond the current interval push the currently available maximum to our strain list
|
||||||
while (hitObject.BaseHitObject.StartTime > intervalEndTime)
|
while (hitObject.BaseHitObject.StartTime > intervalEndTime)
|
||||||
@ -144,5 +123,13 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
|
|
||||||
return 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 countMeh;
|
||||||
private int countMiss;
|
private int countMiss;
|
||||||
|
|
||||||
public TaikoPerformanceCalculator(Ruleset ruleset, IBeatmap beatmap, Score score)
|
public TaikoPerformanceCalculator(Ruleset ruleset, WorkingBeatmap beatmap, Score score)
|
||||||
: base(ruleset, beatmap, 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)
|
public override double Calculate(Dictionary<string, double> categoryDifficulty = null)
|
||||||
@ -68,7 +68,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
|
|
||||||
private double computeStrainValue()
|
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
|
// Longer maps are worth more
|
||||||
double lengthBonus = 1 + 0.1f * Math.Min(1.0, totalHits / 1500.0);
|
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 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;
|
public override int? LegacyID => 1;
|
||||||
|
|
||||||
|
@ -18,6 +18,8 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal class HitExplosion : CircularContainer
|
internal class HitExplosion : CircularContainer
|
||||||
{
|
{
|
||||||
|
public override bool RemoveWhenNotAlive => true;
|
||||||
|
|
||||||
public readonly DrawableHitObject JudgedObject;
|
public readonly DrawableHitObject JudgedObject;
|
||||||
|
|
||||||
private readonly Box innerFill;
|
private readonly Box innerFill;
|
||||||
@ -66,7 +68,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
this.ScaleTo(3f, 1000, Easing.OutQuint);
|
this.ScaleTo(3f, 1000, Easing.OutQuint);
|
||||||
this.FadeOut(500);
|
this.FadeOut(500);
|
||||||
|
|
||||||
Expire();
|
Expire(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -14,6 +14,8 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
{
|
{
|
||||||
public class KiaiHitExplosion : CircularContainer
|
public class KiaiHitExplosion : CircularContainer
|
||||||
{
|
{
|
||||||
|
public override bool RemoveWhenNotAlive => true;
|
||||||
|
|
||||||
public readonly DrawableHitObject JudgedObject;
|
public readonly DrawableHitObject JudgedObject;
|
||||||
|
|
||||||
private readonly bool isRim;
|
private readonly bool isRim;
|
||||||
@ -62,7 +64,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
this.ScaleTo(new Vector2(1, 3f), 500, Easing.OutQuint);
|
this.ScaleTo(new Vector2(1, 3f), 500, Easing.OutQuint);
|
||||||
this.FadeOut(250);
|
this.FadeOut(250);
|
||||||
|
|
||||||
Expire();
|
Expire(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Difficulty;
|
using osu.Game.Rulesets.Difficulty;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
@ -139,14 +139,14 @@ namespace osu.Game.Tests.NonVisual
|
|||||||
private class TestDifficultyCalculator : DifficultyCalculator
|
private class TestDifficultyCalculator : DifficultyCalculator
|
||||||
{
|
{
|
||||||
public TestDifficultyCalculator(params Mod[] mods)
|
public TestDifficultyCalculator(params Mod[] mods)
|
||||||
: base(null)
|
: base(null, null)
|
||||||
{
|
{
|
||||||
DifficultyAdjustmentMods = mods;
|
DifficultyAdjustmentMods = mods;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override double Calculate(Dictionary<string, double> categoryDifficulty = null) => throw new NotImplementedException();
|
|
||||||
|
|
||||||
protected override Mod[] DifficultyAdjustmentMods { get; }
|
protected override Mod[] DifficultyAdjustmentMods { get; }
|
||||||
|
|
||||||
|
protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate) => throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Overlays.Volume;
|
using osu.Game.Overlays.Volume;
|
||||||
|
using OpenTK;
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual
|
namespace osu.Game.Tests.Visual
|
||||||
@ -17,13 +18,21 @@ namespace osu.Game.Tests.Visual
|
|||||||
{
|
{
|
||||||
VolumeMeter meter;
|
VolumeMeter meter;
|
||||||
MuteButton mute;
|
MuteButton mute;
|
||||||
Add(meter = new VolumeMeter("MASTER", 125, Color4.Blue));
|
Add(meter = new VolumeMeter("MASTER", 125, Color4.Blue) { Position = new Vector2(10) });
|
||||||
|
AddSliderStep("master volume", 0, 10, 0, i => meter.Bindable.Value = i * 0.1);
|
||||||
|
|
||||||
|
Add(new VolumeMeter("BIG", 250, Color4.Red)
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Position = new Vector2(10),
|
||||||
|
});
|
||||||
|
|
||||||
Add(mute = new MuteButton
|
Add(mute = new MuteButton
|
||||||
{
|
{
|
||||||
Margin = new MarginPadding { Top = 200 }
|
Margin = new MarginPadding { Top = 200 }
|
||||||
});
|
});
|
||||||
|
|
||||||
AddSliderStep("master volume", 0, 10, 0, i => meter.Bindable.Value = i * 0.1);
|
|
||||||
AddToggleStep("mute", b => mute.Current.Value = b);
|
AddToggleStep("mute", b => mute.Current.Value = b);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -366,8 +366,7 @@ namespace osu.Game.Beatmaps
|
|||||||
if (ruleset != null)
|
if (ruleset != null)
|
||||||
{
|
{
|
||||||
// TODO: this should be done in a better place once we actually need to dynamically update it.
|
// 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(new DummyConversionBeatmap(beatmap)).Calculate().StarRating;
|
||||||
beatmap.BeatmapInfo.StarDifficulty = ruleset.CreateInstance().CreateDifficultyCalculator(converted).Calculate();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
beatmap.BeatmapInfo.StarDifficulty = 0;
|
beatmap.BeatmapInfo.StarDifficulty = 0;
|
||||||
|
@ -62,7 +62,7 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new DummyBeatmapConverter { Beatmap = beatmap };
|
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";
|
public override string Description => "dummy";
|
||||||
|
|
||||||
|
@ -181,24 +181,6 @@ namespace osu.Game.Database
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Migrate()
|
public void Migrate() => Database.Migrate();
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Database.Migrate();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
throw new MigrationFailedException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class MigrationFailedException : Exception
|
|
||||||
{
|
|
||||||
public MigrationFailedException(Exception exception)
|
|
||||||
: base("sqlite-net migration failed", exception)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ namespace osu.Game.Graphics.Containers
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether mouse input should be blocked screen-wide while this overlay is visible.
|
/// Whether mouse input should be blocked screen-wide while this overlay is visible.
|
||||||
/// Performing mouse actions outside of the valid extents will hide the overlay but pass the events through.
|
/// Performing mouse actions outside of the valid extents will hide the overlay.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual bool BlockScreenWideMouse => BlockPassThroughMouse;
|
public virtual bool BlockScreenWideMouse => BlockPassThroughMouse;
|
||||||
|
|
||||||
|
@ -149,7 +149,7 @@ namespace osu.Game.Overlays.Direct
|
|||||||
{
|
{
|
||||||
new OsuSpriteText
|
new OsuSpriteText
|
||||||
{
|
{
|
||||||
Text = $"{SetInfo.Metadata.Source}",
|
Text = SetInfo.Metadata.Source,
|
||||||
TextSize = 14,
|
TextSize = 14,
|
||||||
Shadow = false,
|
Shadow = false,
|
||||||
Colour = colours.Gray5,
|
Colour = colours.Gray5,
|
||||||
|
@ -160,7 +160,7 @@ namespace osu.Game.Overlays.Direct
|
|||||||
},
|
},
|
||||||
new OsuSpriteText
|
new OsuSpriteText
|
||||||
{
|
{
|
||||||
Text = $"from {SetInfo.Metadata.Source}",
|
Text = SetInfo.Metadata.Source,
|
||||||
Anchor = Anchor.TopRight,
|
Anchor = Anchor.TopRight,
|
||||||
Origin = Anchor.TopRight,
|
Origin = Anchor.TopRight,
|
||||||
TextSize = 14,
|
TextSize = 14,
|
||||||
|
@ -66,34 +66,6 @@ namespace osu.Game.Overlays
|
|||||||
AlwaysPresent = true;
|
AlwaysPresent = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Vector2 dragStart;
|
|
||||||
|
|
||||||
protected override bool OnDragStart(InputState state)
|
|
||||||
{
|
|
||||||
base.OnDragStart(state);
|
|
||||||
dragStart = state.Mouse.Position;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override bool OnDrag(InputState state)
|
|
||||||
{
|
|
||||||
if (base.OnDrag(state)) return true;
|
|
||||||
|
|
||||||
Vector2 change = state.Mouse.Position - dragStart;
|
|
||||||
|
|
||||||
// Diminish the drag distance as we go further to simulate "rubber band" feeling.
|
|
||||||
change *= change.Length <= 0 ? 0 : (float)Math.Pow(change.Length, 0.7f) / change.Length;
|
|
||||||
|
|
||||||
dragContainer.MoveTo(change);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override bool OnDragEnd(InputState state)
|
|
||||||
{
|
|
||||||
dragContainer.MoveTo(Vector2.Zero, 800, Easing.OutElastic);
|
|
||||||
return base.OnDragEnd(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(BindableBeatmap beatmap, BeatmapManager beatmaps, OsuColour colours, LocalisationEngine localisation)
|
private void load(BindableBeatmap beatmap, BeatmapManager beatmaps, OsuColour colours, LocalisationEngine localisation)
|
||||||
{
|
{
|
||||||
@ -103,7 +75,7 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
dragContainer = new Container
|
dragContainer = new DragContainer
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
@ -470,5 +442,36 @@ namespace osu.Game.Overlays
|
|||||||
sprite.Texture = beatmap?.Background ?? textures.Get(@"Backgrounds/bg4");
|
sprite.Texture = beatmap?.Background ?? textures.Get(@"Backgrounds/bg4");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class DragContainer : Container
|
||||||
|
{
|
||||||
|
private Vector2 dragStart;
|
||||||
|
|
||||||
|
protected override bool OnDragStart(InputState state)
|
||||||
|
{
|
||||||
|
base.OnDragStart(state);
|
||||||
|
dragStart = state.Mouse.Position;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnDrag(InputState state)
|
||||||
|
{
|
||||||
|
if (base.OnDrag(state)) return true;
|
||||||
|
|
||||||
|
Vector2 change = state.Mouse.Position - dragStart;
|
||||||
|
|
||||||
|
// Diminish the drag distance as we go further to simulate "rubber band" feeling.
|
||||||
|
change *= change.Length <= 0 ? 0 : (float)Math.Pow(change.Length, 0.7f) / change.Length;
|
||||||
|
|
||||||
|
this.MoveTo(change);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnDragEnd(InputState state)
|
||||||
|
{
|
||||||
|
this.MoveTo(Vector2.Zero, 800, Easing.OutElastic);
|
||||||
|
return base.OnDragEnd(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,8 @@ namespace osu.Game.Overlays.Volume
|
|||||||
public class VolumeMeter : Container, IKeyBindingHandler<GlobalAction>
|
public class VolumeMeter : Container, IKeyBindingHandler<GlobalAction>
|
||||||
{
|
{
|
||||||
private CircularProgress volumeCircle;
|
private CircularProgress volumeCircle;
|
||||||
|
private CircularProgress volumeCircleGlow;
|
||||||
|
|
||||||
public BindableDouble Bindable { get; } = new BindableDouble { MinValue = 0, MaxValue = 1 };
|
public BindableDouble Bindable { get; } = new BindableDouble { MinValue = 0, MaxValue = 1 };
|
||||||
private readonly float circleSize;
|
private readonly float circleSize;
|
||||||
private readonly Color4 meterColour;
|
private readonly Color4 meterColour;
|
||||||
@ -44,90 +46,143 @@ namespace osu.Game.Overlays.Volume
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours)
|
private void load(OsuColour colours)
|
||||||
{
|
{
|
||||||
Add(new Container
|
Color4 backgroundColour = colours.Gray1;
|
||||||
{
|
|
||||||
Size = new Vector2(120, 20),
|
|
||||||
CornerRadius = 10,
|
|
||||||
Masking = true,
|
|
||||||
Margin = new MarginPadding { Left = circleSize + 10 },
|
|
||||||
Origin = Anchor.CentreLeft,
|
|
||||||
Anchor = Anchor.CentreLeft,
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Colour = colours.Gray1,
|
|
||||||
Alpha = 0.9f,
|
|
||||||
},
|
|
||||||
new OsuSpriteText
|
|
||||||
{
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
Font = "Exo2.0-Bold",
|
|
||||||
Text = name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
CircularProgress bgProgress;
|
CircularProgress bgProgress;
|
||||||
|
|
||||||
Add(new CircularContainer
|
const float progress_start_radius = 0.75f;
|
||||||
|
const float progress_size = 0.03f;
|
||||||
|
const float progress_end_radius = progress_start_radius + progress_size;
|
||||||
|
|
||||||
|
const float blur_amount = 5;
|
||||||
|
|
||||||
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
Masking = true,
|
new Container
|
||||||
Size = new Vector2(circleSize),
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
{
|
||||||
new Box
|
Size = new Vector2(circleSize),
|
||||||
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
new BufferedContainer
|
||||||
Colour = colours.Gray1,
|
|
||||||
Alpha = 0.9f,
|
|
||||||
},
|
|
||||||
bgProgress = new CircularProgress
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
InnerRadius = 0.05f,
|
|
||||||
Rotation = 180,
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
Colour = colours.Gray2,
|
|
||||||
Size = new Vector2(0.8f)
|
|
||||||
},
|
|
||||||
new Container
|
|
||||||
{
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Size = new Vector2(0.8f),
|
|
||||||
Padding = new MarginPadding(-Blur.KernelSize(5)),
|
|
||||||
Rotation = 180,
|
|
||||||
Child = (volumeCircle = new CircularProgress
|
|
||||||
{
|
{
|
||||||
|
Alpha = 0.9f,
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
InnerRadius = 0.05f,
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Circle
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = backgroundColour,
|
||||||
|
},
|
||||||
|
new CircularContainer
|
||||||
|
{
|
||||||
|
Masking = true,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Size = new Vector2(progress_end_radius),
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
bgProgress = new CircularProgress
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Rotation = 180,
|
||||||
|
Colour = backgroundColour,
|
||||||
|
},
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Name = "Progress under covers for smoothing",
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Rotation = 180,
|
||||||
|
Child = volumeCircle = new CircularProgress
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new Circle
|
||||||
|
{
|
||||||
|
Name = "Inner Cover",
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = backgroundColour,
|
||||||
|
Size = new Vector2(progress_start_radius),
|
||||||
|
},
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
Name = "Progress overlay for glow",
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Size = new Vector2(progress_start_radius + progress_size / 1.5f),
|
||||||
|
Rotation = 180,
|
||||||
|
Padding = new MarginPadding(-Blur.KernelSize(blur_amount)),
|
||||||
|
Child = (volumeCircleGlow = new CircularProgress
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
InnerRadius = progress_size * 0.8f,
|
||||||
|
}).WithEffect(new GlowEffect
|
||||||
|
{
|
||||||
|
Colour = meterColour,
|
||||||
|
BlurSigma = new Vector2(blur_amount),
|
||||||
|
Strength = 5,
|
||||||
|
PadExtent = true
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
maxGlow = (text = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Font = "Venera",
|
||||||
|
TextSize = 0.16f * circleSize
|
||||||
}).WithEffect(new GlowEffect
|
}).WithEffect(new GlowEffect
|
||||||
{
|
{
|
||||||
Colour = meterColour,
|
Colour = Color4.Transparent,
|
||||||
Strength = 2,
|
PadExtent = true,
|
||||||
PadExtent = true
|
})
|
||||||
}),
|
}
|
||||||
},
|
},
|
||||||
maxGlow = (text = new OsuSpriteText
|
new Container
|
||||||
|
{
|
||||||
|
Size = new Vector2(120, 20),
|
||||||
|
CornerRadius = 10,
|
||||||
|
Masking = true,
|
||||||
|
Margin = new MarginPadding { Left = circleSize + 10 },
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
new Box
|
||||||
Origin = Anchor.Centre,
|
{
|
||||||
Font = "Venera",
|
Alpha = 0.9f,
|
||||||
TextSize = 0.16f * circleSize
|
RelativeSizeAxes = Axes.Both,
|
||||||
}).WithEffect(new GlowEffect
|
Colour = backgroundColour,
|
||||||
{
|
},
|
||||||
Colour = Color4.Transparent,
|
new OsuSpriteText
|
||||||
PadExtent = true,
|
{
|
||||||
})
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Font = "Exo2.0-Bold",
|
||||||
|
Text = name
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
Bindable.ValueChanged += newVolume =>
|
||||||
Bindable.ValueChanged += newVolume => { this.TransformTo("DisplayVolume", newVolume, 400, Easing.OutQuint); };
|
{
|
||||||
|
this.TransformTo("DisplayVolume",
|
||||||
|
newVolume,
|
||||||
|
400,
|
||||||
|
Easing.OutQuint);
|
||||||
|
};
|
||||||
bgProgress.Current.Value = 0.75f;
|
bgProgress.Current.Value = 0.75f;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,6 +213,7 @@ namespace osu.Game.Overlays.Volume
|
|||||||
}
|
}
|
||||||
|
|
||||||
volumeCircle.Current.Value = displayVolume * 0.75f;
|
volumeCircle.Current.Value = displayVolume * 0.75f;
|
||||||
|
volumeCircleGlow.Current.Value = displayVolume * 0.75f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
public abstract class DifficultyCalculator
|
||||||
{
|
{
|
||||||
protected readonly IBeatmap Beatmap;
|
private readonly Ruleset ruleset;
|
||||||
protected readonly Mod[] Mods;
|
private readonly WorkingBeatmap beatmap;
|
||||||
|
|
||||||
protected double TimeRate { get; private set; } = 1;
|
protected DifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap)
|
||||||
|
|
||||||
protected DifficultyCalculator(IBeatmap beatmap, Mod[] mods = null)
|
|
||||||
{
|
{
|
||||||
Beatmap = beatmap;
|
this.ruleset = ruleset;
|
||||||
Mods = mods ?? new Mod[0];
|
this.beatmap = beatmap;
|
||||||
|
|
||||||
ApplyMods(Mods);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
var clock = new StopwatchClock();
|
||||||
mods.OfType<IApplicableToClock>().ForEach(m => m.ApplyToClock(clock));
|
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>
|
/// <summary>
|
||||||
@ -75,6 +91,13 @@ namespace osu.Game.Rulesets.Difficulty
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual Mod[] DifficultyAdjustmentMods => Array.Empty<Mod>();
|
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
|
public abstract class PerformanceCalculator
|
||||||
{
|
{
|
||||||
private readonly Dictionary<string, double> attributes = new Dictionary<string, double>();
|
protected readonly DifficultyAttributes Attributes;
|
||||||
protected IDictionary<string, double> Attributes => attributes;
|
|
||||||
|
|
||||||
protected readonly Ruleset Ruleset;
|
protected readonly Ruleset Ruleset;
|
||||||
protected readonly IBeatmap Beatmap;
|
protected readonly IBeatmap Beatmap;
|
||||||
@ -22,14 +21,15 @@ namespace osu.Game.Rulesets.Difficulty
|
|||||||
|
|
||||||
protected double TimeRate { get; private set; } = 1;
|
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;
|
Ruleset = ruleset;
|
||||||
Beatmap = beatmap;
|
|
||||||
Score = score;
|
Score = score;
|
||||||
|
|
||||||
var diffCalc = ruleset.CreateDifficultyCalculator(beatmap, score.Mods);
|
beatmap.Mods.Value = score.Mods;
|
||||||
diffCalc.Calculate(attributes);
|
Beatmap = beatmap.GetPlayableBeatmap(ruleset.RulesetInfo);
|
||||||
|
|
||||||
|
Attributes = ruleset.CreateDifficultyCalculator(beatmap).Calculate(score.Mods);
|
||||||
|
|
||||||
ApplyMods(score.Mods);
|
ApplyMods(score.Mods);
|
||||||
}
|
}
|
||||||
|
@ -61,9 +61,9 @@ namespace osu.Game.Rulesets
|
|||||||
|
|
||||||
public virtual IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => null;
|
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;
|
public virtual HitObjectComposer CreateHitObjectComposer() => null;
|
||||||
|
|
||||||
|
@ -16,7 +16,8 @@ using osu.Game.Rulesets.Objects;
|
|||||||
namespace osu.Game.Tests.Beatmaps
|
namespace osu.Game.Tests.Beatmaps
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public abstract class BeatmapConversionTest<TConvertValue>
|
public abstract class BeatmapConversionTest<TConvertMapping, TConvertValue>
|
||||||
|
where TConvertMapping : ConvertMapping<TConvertValue>, IEquatable<TConvertMapping>, new()
|
||||||
where TConvertValue : IEquatable<TConvertValue>
|
where TConvertValue : IEquatable<TConvertValue>
|
||||||
{
|
{
|
||||||
private const string resource_namespace = "Testing.Beatmaps";
|
private const string resource_namespace = "Testing.Beatmaps";
|
||||||
@ -59,9 +60,13 @@ namespace osu.Game.Tests.Beatmaps
|
|||||||
else if (objectCounter >= expectedMapping.Objects.Count)
|
else if (objectCounter >= expectedMapping.Objects.Count)
|
||||||
Assert.Fail($"The conversion generated a hitobject, but should not have, for hitobject at time: {ourMapping.StartTime}:\n"
|
Assert.Fail($"The conversion generated a hitobject, but should not have, for hitobject at time: {ourMapping.StartTime}:\n"
|
||||||
+ $"Received: {JsonConvert.SerializeObject(ourMapping.Objects[objectCounter])}\n");
|
+ $"Received: {JsonConvert.SerializeObject(ourMapping.Objects[objectCounter])}\n");
|
||||||
else if (!EqualityComparer<TConvertValue>.Default.Equals(expectedMapping.Objects[objectCounter], ourMapping.Objects[objectCounter]))
|
else if (!expectedMapping.Equals(ourMapping))
|
||||||
|
Assert.Fail($"The conversion mapping differed for object at time {expectedMapping.StartTime}:\n"
|
||||||
|
+ $"Expected {JsonConvert.SerializeObject(expectedMapping)}\n"
|
||||||
|
+ $"Received: {JsonConvert.SerializeObject(ourMapping)}\n");
|
||||||
|
else if (!expectedMapping.Objects[objectCounter].Equals(ourMapping.Objects[objectCounter]))
|
||||||
{
|
{
|
||||||
Assert.Fail($"The conversion generated differing hitobjects for object at time: {expectedMapping.StartTime}\n"
|
Assert.Fail($"The conversion generated differing hitobjects for object at time: {expectedMapping.StartTime}:\n"
|
||||||
+ $"Expected: {JsonConvert.SerializeObject(expectedMapping.Objects[objectCounter])}\n"
|
+ $"Expected: {JsonConvert.SerializeObject(expectedMapping.Objects[objectCounter])}\n"
|
||||||
+ $"Received: {JsonConvert.SerializeObject(ourMapping.Objects[objectCounter])}\n");
|
+ $"Received: {JsonConvert.SerializeObject(ourMapping.Objects[objectCounter])}\n");
|
||||||
}
|
}
|
||||||
@ -84,19 +89,22 @@ namespace osu.Game.Tests.Beatmaps
|
|||||||
beatmap.BeatmapInfo.Ruleset = beatmap.BeatmapInfo.RulesetID == rulesetInstance.RulesetInfo.ID ? rulesetInstance.RulesetInfo : new RulesetInfo();
|
beatmap.BeatmapInfo.Ruleset = beatmap.BeatmapInfo.RulesetID == rulesetInstance.RulesetInfo.ID ? rulesetInstance.RulesetInfo : new RulesetInfo();
|
||||||
|
|
||||||
var result = new ConvertResult();
|
var result = new ConvertResult();
|
||||||
|
|
||||||
var converter = rulesetInstance.CreateBeatmapConverter(beatmap);
|
var converter = rulesetInstance.CreateBeatmapConverter(beatmap);
|
||||||
|
|
||||||
converter.ObjectConverted += (orig, converted) =>
|
converter.ObjectConverted += (orig, converted) =>
|
||||||
{
|
{
|
||||||
converted.ForEach(h => h.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty));
|
converted.ForEach(h => h.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty));
|
||||||
|
|
||||||
var mapping = new ConvertMapping { StartTime = orig.StartTime };
|
var mapping = CreateConvertMapping();
|
||||||
|
mapping.StartTime = orig.StartTime;
|
||||||
|
|
||||||
foreach (var obj in converted)
|
foreach (var obj in converted)
|
||||||
mapping.Objects.AddRange(CreateConvertValue(obj));
|
mapping.Objects.AddRange(CreateConvertValue(obj));
|
||||||
result.Mappings.Add(mapping);
|
result.Mappings.Add(mapping);
|
||||||
};
|
};
|
||||||
|
|
||||||
converter.Convert();
|
IBeatmap convertedBeatmap = converter.Convert();
|
||||||
|
rulesetInstance.CreateBeatmapProcessor(convertedBeatmap)?.PostProcess();
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -128,21 +136,54 @@ namespace osu.Game.Tests.Beatmaps
|
|||||||
return Assembly.LoadFrom(Path.Combine(localPath, $"{ResourceAssembly}.dll")).GetManifestResourceStream($@"{ResourceAssembly}.Resources.{name}");
|
return Assembly.LoadFrom(Path.Combine(localPath, $"{ResourceAssembly}.dll")).GetManifestResourceStream($@"{ResourceAssembly}.Resources.{name}");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract IEnumerable<TConvertValue> CreateConvertValue(HitObject hitObject);
|
/// <summary>
|
||||||
protected abstract Ruleset CreateRuleset();
|
/// Creates the conversion mapping for a <see cref="HitObject"/>. A conversion mapping stores important information about the conversion process.
|
||||||
|
/// This is generated _after_ the <see cref="HitObject"/> has been converted.
|
||||||
|
/// <para>
|
||||||
|
/// This should be used to validate the integrity of the conversion process after a conversion has occurred.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
|
protected virtual TConvertMapping CreateConvertMapping() => new TConvertMapping();
|
||||||
|
|
||||||
private class ConvertMapping
|
/// <summary>
|
||||||
{
|
/// Creates the conversion value for a <see cref="HitObject"/>. A conversion value stores information about the converted <see cref="HitObject"/>.
|
||||||
[JsonProperty]
|
/// <para>
|
||||||
public double StartTime;
|
/// This should be used to validate the integrity of the converted <see cref="HitObject"/>.
|
||||||
[JsonProperty]
|
/// </para>
|
||||||
public List<TConvertValue> Objects = new List<TConvertValue>();
|
/// </summary>
|
||||||
}
|
/// <param name="hitObject">The converted <see cref="HitObject"/>.</param>
|
||||||
|
protected abstract IEnumerable<TConvertValue> CreateConvertValue(HitObject hitObject);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates the <see cref="Ruleset"/> applicable to this <see cref="BeatmapConversionTest{TConvertMapping,TConvertValue}"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
protected abstract Ruleset CreateRuleset();
|
||||||
|
|
||||||
private class ConvertResult
|
private class ConvertResult
|
||||||
{
|
{
|
||||||
[JsonProperty]
|
[JsonProperty]
|
||||||
public List<ConvertMapping> Mappings = new List<ConvertMapping>();
|
public List<TConvertMapping> Mappings = new List<TConvertMapping>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public abstract class BeatmapConversionTest<TConvertValue> : BeatmapConversionTest<ConvertMapping<TConvertValue>, TConvertValue>
|
||||||
|
where TConvertValue : IEquatable<TConvertValue>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ConvertMapping<TConvertValue> : IEquatable<ConvertMapping<TConvertValue>>
|
||||||
|
where TConvertValue : IEquatable<TConvertValue>
|
||||||
|
{
|
||||||
|
[JsonProperty]
|
||||||
|
public double StartTime;
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public List<TConvertValue> Objects = new List<TConvertValue>();
|
||||||
|
|
||||||
|
[JsonProperty("Objects")]
|
||||||
|
private List<TConvertValue> setObjects { set => Objects = value; }
|
||||||
|
|
||||||
|
public virtual bool Equals(ConvertMapping<TConvertValue> other) => StartTime.Equals(other?.StartTime);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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