diff --git a/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs
index f4ee3f5a42..5580358f89 100644
--- a/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs
+++ b/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs
@@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Catch.Tests
public void Test(double expected, string name)
=> base.Test(expected, name);
- [TestCase(5.0565038923984691d, "diffcalc-test")]
+ [TestCase(5.169743871843191d, "diffcalc-test")]
public void TestClockRateAdjusted(double expected, string name)
=> Test(expected, name, new CatchModDoubleTime());
diff --git a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj
index 728af5124e..c2d9a923d9 100644
--- a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj
+++ b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj
@@ -2,7 +2,7 @@
-
+
diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaDifficultyCalculatorTest.cs
index 09ca04be8a..6e6500a339 100644
--- a/osu.Game.Rulesets.Mania.Tests/ManiaDifficultyCalculatorTest.cs
+++ b/osu.Game.Rulesets.Mania.Tests/ManiaDifficultyCalculatorTest.cs
@@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Mania.Tests
public void Test(double expected, string name)
=> base.Test(expected, name);
- [TestCase(2.7646128945056723d, "diffcalc-test")]
+ [TestCase(2.7879104989252959d, "diffcalc-test")]
public void TestClockRateAdjusted(double expected, string name)
=> Test(expected, name, new ManiaModDoubleTime());
diff --git a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj
index af16f39563..64e934efd2 100644
--- a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj
+++ b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj
@@ -2,7 +2,7 @@
-
+
diff --git a/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs b/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs
index d6ea58ee78..830b6004a6 100644
--- a/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs
+++ b/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs
@@ -73,8 +73,8 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Skills
}
protected override double GetPeakStrain(double offset)
- => applyDecay(individualStrain, offset - Previous[0].BaseObject.StartTime, individual_decay_base)
- + applyDecay(overallStrain, offset - Previous[0].BaseObject.StartTime, overall_decay_base);
+ => applyDecay(individualStrain, offset - Previous[0].StartTime, individual_decay_base)
+ + applyDecay(overallStrain, offset - Previous[0].StartTime, overall_decay_base);
private double applyDecay(double value, double deltaTime, double decayBase)
=> value * Math.Pow(decayBase, deltaTime / 1000);
diff --git a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs
index c2119585ab..afd94f4570 100644
--- a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs
+++ b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs
@@ -20,8 +20,8 @@ namespace osu.Game.Rulesets.Osu.Tests
public void Test(double expected, string name)
=> base.Test(expected, name);
- [TestCase(8.6228371119271454d, "diffcalc-test")]
- [TestCase(1.2864585280364178d, "zero-length-sliders")]
+ [TestCase(8.7212283220412345d, "diffcalc-test")]
+ [TestCase(1.3212137158641493d, "zero-length-sliders")]
public void TestClockRateAdjusted(double expected, string name)
=> Test(expected, name, new OsuModDoubleTime());
diff --git a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj
index 3d2d1f3fec..f743d65db3 100644
--- a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj
+++ b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj
@@ -2,7 +2,7 @@
-
+
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs b/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs
index 5470d0fcb4..882f848190 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs
@@ -44,6 +44,9 @@ namespace osu.Game.Rulesets.Osu.Mods
[SettingSource("Use fixed slider follow circle hit area", "Makes the slider follow circle track its final size at all times.")]
public Bindable FixedFollowCircleHitArea { get; } = new BindableBool(true);
+ [SettingSource("Always play a slider's tail sample", "Always plays a slider's tail sample regardless of whether it was hit or not.")]
+ public Bindable AlwaysPlayTailSample { get; } = new BindableBool(true);
+
public void ApplyToHitObject(HitObject hitObject)
{
switch (hitObject)
@@ -79,6 +82,10 @@ namespace osu.Game.Rulesets.Osu.Mods
case DrawableSliderHead head:
head.TrackFollowCircle = !NoSliderHeadMovement.Value;
break;
+
+ case DrawableSliderTail tail:
+ tail.SamplePlaysOnlyOnHit = !AlwaysPlayTailSample.Value;
+ break;
}
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
index 9122f347d0..04708a5ece 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
@@ -280,7 +280,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
// rather than doing it this way, we should probably attach the sample to the tail circle.
// this can only be done after we stop using LegacyLastTick.
- if (TailCircle.IsHit)
+ if (!TailCircle.SamplePlaysOnlyOnHit || TailCircle.IsHit)
base.PlaySamples();
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs
index 6a8e02e886..87f098dd29 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs
@@ -26,6 +26,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
///
public override bool DisplayResult => false;
+ ///
+ /// Whether the hit samples only play on successful hits.
+ /// If false, the hit samples will also play on misses.
+ ///
+ public bool SamplePlaysOnlyOnHit { get; set; } = true;
+
public bool Tracking { get; set; }
private SkinnableDrawable circlePiece;
diff --git a/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs
index eb21c02d5f..dd3c6b317a 100644
--- a/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs
+++ b/osu.Game.Rulesets.Taiko.Tests/TaikoDifficultyCalculatorTest.cs
@@ -19,8 +19,8 @@ namespace osu.Game.Rulesets.Taiko.Tests
public void Test(double expected, string name)
=> base.Test(expected, name);
- [TestCase(3.1473940254109078d, "diffcalc-test")]
- [TestCase(3.1473940254109078d, "diffcalc-test-strong")]
+ [TestCase(3.1704781712282624d, "diffcalc-test")]
+ [TestCase(3.1704781712282624d, "diffcalc-test-strong")]
public void TestClockRateAdjusted(double expected, string name)
=> Test(expected, name, new TaikoModDoubleTime());
diff --git a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj
index fa00922706..eab144592f 100644
--- a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj
+++ b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj
@@ -2,7 +2,7 @@
-
+
diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj
index e36b3cdc74..0e1f6f6b0c 100644
--- a/osu.Game.Tests/osu.Game.Tests.csproj
+++ b/osu.Game.Tests/osu.Game.Tests.csproj
@@ -3,7 +3,7 @@
-
+
diff --git a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj
index b20583dd7e..a4e52f8cd4 100644
--- a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj
+++ b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj
@@ -5,7 +5,7 @@
-
+
diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs
index a898e10e4f..bf7906bd5c 100644
--- a/osu.Game/Beatmaps/BeatmapInfo.cs
+++ b/osu.Game/Beatmaps/BeatmapInfo.cs
@@ -147,7 +147,7 @@ namespace osu.Game.Beatmaps
{
string version = string.IsNullOrEmpty(Version) ? string.Empty : $"[{Version}]";
- return $"{Metadata} {version}".Trim();
+ return $"{Metadata ?? BeatmapSet?.Metadata} {version}".Trim();
}
public bool Equals(BeatmapInfo other)
diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs
index 5608002513..da1bbd18c7 100644
--- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs
+++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs
@@ -61,6 +61,9 @@ namespace osu.Game.Online.Leaderboards
[Resolved(CanBeNull = true)]
private SongSelect songSelect { get; set; }
+ [Resolved]
+ private ScoreManager scoreManager { get; set; }
+
public LeaderboardScore(ScoreInfo score, int? rank, bool allowHighlight = true)
{
this.score = score;
@@ -388,6 +391,9 @@ namespace osu.Game.Online.Leaderboards
if (score.Mods.Length > 0 && modsContainer.Any(s => s.IsHovered) && songSelect != null)
items.Add(new OsuMenuItem("Use these mods", MenuItemType.Highlighted, () => songSelect.Mods.Value = score.Mods));
+ if (score.Files.Count > 0)
+ items.Add(new OsuMenuItem("Export", MenuItemType.Standard, () => scoreManager.Export(score)));
+
if (score.ID != 0)
items.Add(new OsuMenuItem("Delete", MenuItemType.Destructive, () => dialogOverlay?.Push(new LocalScoreDeleteDialog(score))));
diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
index a25dc3e6db..9d06f960b7 100644
--- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
+++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs
@@ -83,7 +83,7 @@ namespace osu.Game.Rulesets.Difficulty
foreach (Skill s in skills)
{
s.SaveCurrentPeak();
- s.StartNewSectionFrom(currentSectionEnd);
+ s.StartNewSectionFrom(currentSectionEnd / clockRate);
}
currentSectionEnd += sectionLength;
diff --git a/osu.Game/Rulesets/Difficulty/Preprocessing/DifficultyHitObject.cs b/osu.Game/Rulesets/Difficulty/Preprocessing/DifficultyHitObject.cs
index ebbffb5143..576fbb2af0 100644
--- a/osu.Game/Rulesets/Difficulty/Preprocessing/DifficultyHitObject.cs
+++ b/osu.Game/Rulesets/Difficulty/Preprocessing/DifficultyHitObject.cs
@@ -25,6 +25,16 @@ namespace osu.Game.Rulesets.Difficulty.Preprocessing
///
public readonly double DeltaTime;
+ ///
+ /// Clockrate adjusted start time of .
+ ///
+ public readonly double StartTime;
+
+ ///
+ /// Clockrate adjusted end time of .
+ ///
+ public readonly double EndTime;
+
///
/// Creates a new .
///
@@ -36,6 +46,8 @@ namespace osu.Game.Rulesets.Difficulty.Preprocessing
BaseObject = hitObject;
LastObject = lastObject;
DeltaTime = (hitObject.StartTime - lastObject.StartTime) / clockRate;
+ StartTime = hitObject.StartTime / clockRate;
+ EndTime = hitObject.GetEndTime() / clockRate;
}
}
}
diff --git a/osu.Game/Rulesets/Difficulty/Skills/Skill.cs b/osu.Game/Rulesets/Difficulty/Skills/Skill.cs
index 95117be073..126e30ed73 100644
--- a/osu.Game/Rulesets/Difficulty/Skills/Skill.cs
+++ b/osu.Game/Rulesets/Difficulty/Skills/Skill.cs
@@ -88,7 +88,7 @@ namespace osu.Game.Rulesets.Difficulty.Skills
///
/// Sets the initial strain level for a new section.
///
- /// The beginning of the new section in milliseconds.
+ /// The beginning of the new section in milliseconds, adjusted by clockrate.
public void StartNewSectionFrom(double time)
{
// The maximum strain of the new section is not zero by default, strain decays as usual regardless of section boundaries.
@@ -100,9 +100,9 @@ namespace osu.Game.Rulesets.Difficulty.Skills
///
/// Retrieves the peak strain at a point in time.
///
- /// The time to retrieve the peak strain at.
+ /// The time to retrieve the peak strain at, adjusted by clockrate.
/// The peak strain.
- protected virtual double GetPeakStrain(double time) => CurrentStrain * strainDecay(time - Previous[0].BaseObject.StartTime);
+ protected virtual double GetPeakStrain(double time) => CurrentStrain * strainDecay(time - Previous[0].StartTime);
///
/// Returns the calculated difficulty value representing all processed s.
diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs
index deabea57ef..4261ee3d47 100644
--- a/osu.Game/Rulesets/RulesetStore.cs
+++ b/osu.Game/Rulesets/RulesetStore.cs
@@ -173,7 +173,7 @@ namespace osu.Game.Rulesets
{
var filename = Path.GetFileNameWithoutExtension(file);
- if (loadedAssemblies.Values.Any(t => t.Namespace == filename))
+ if (loadedAssemblies.Values.Any(t => Path.GetFileNameWithoutExtension(t.Assembly.Location) == filename))
return;
try