mirror of
https://github.com/ppy/osu.git
synced 2026-05-26 13:50:33 +08:00
Compare commits
52 Commits
@@ -290,7 +290,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
public IEnumerable<CaughtObject> CaughtObjects => this.ChildrenOfType<CaughtObject>();
|
||||
|
||||
public TestCatcher(DroppedObjectContainer droppedObjectTarget, BeatmapDifficulty difficulty)
|
||||
public TestCatcher(DroppedObjectContainer droppedObjectTarget, IBeatmapDifficultyInfo difficulty)
|
||||
: base(droppedObjectTarget, difficulty)
|
||||
{
|
||||
}
|
||||
@@ -298,7 +298,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
|
||||
public class TestKiaiFruit : Fruit
|
||||
{
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty)
|
||||
{
|
||||
controlPointInfo.Add(0, new EffectControlPoint { KiaiMode = true });
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
|
||||
private ScheduledDelegate addManyFruit;
|
||||
|
||||
private BeatmapDifficulty beatmapDifficulty;
|
||||
private IBeatmapDifficultyInfo beatmapDifficulty;
|
||||
|
||||
public TestSceneCatcherArea()
|
||||
{
|
||||
@@ -120,7 +120,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
|
||||
private class TestCatcherArea : CatcherArea
|
||||
{
|
||||
public TestCatcherArea(BeatmapDifficulty beatmapDifficulty)
|
||||
public TestCatcherArea(IBeatmapDifficultyInfo beatmapDifficulty)
|
||||
{
|
||||
var droppedObjectContainer = new DroppedObjectContainer();
|
||||
Add(droppedObjectContainer);
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
||||
return new CatchDifficultyAttributes { Mods = mods, Skills = skills };
|
||||
|
||||
// this is the same as osu!, so there's potential to share the implementation... maybe
|
||||
double preempt = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate;
|
||||
double preempt = IBeatmapDifficultyInfo.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate;
|
||||
|
||||
return new CatchDifficultyAttributes
|
||||
{
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Catch.Edit
|
||||
public class CatchEditorPlayfield : CatchPlayfield
|
||||
{
|
||||
// TODO fixme: the size of the catcher is not changed when circle size is changed in setup screen.
|
||||
public CatchEditorPlayfield(BeatmapDifficulty difficulty)
|
||||
public CatchEditorPlayfield(IBeatmapDifficultyInfo difficulty)
|
||||
: base(difficulty)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -128,11 +128,11 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
/// </summary>
|
||||
public int RandomSeed => (int)StartTime;
|
||||
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty)
|
||||
{
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
|
||||
TimePreempt = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450);
|
||||
TimePreempt = (float)IBeatmapDifficultyInfo.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450);
|
||||
|
||||
Scale = (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5) / 2;
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
/// </summary>
|
||||
public double SpanDuration => Duration / this.SpanCount();
|
||||
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty)
|
||||
{
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
|
||||
|
||||
@@ -34,9 +34,9 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
|
||||
internal CatcherArea CatcherArea { get; private set; }
|
||||
|
||||
private readonly BeatmapDifficulty difficulty;
|
||||
private readonly IBeatmapDifficultyInfo difficulty;
|
||||
|
||||
public CatchPlayfield(BeatmapDifficulty difficulty)
|
||||
public CatchPlayfield(IBeatmapDifficultyInfo difficulty)
|
||||
{
|
||||
this.difficulty = difficulty;
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
private readonly DrawablePool<CaughtBanana> caughtBananaPool;
|
||||
private readonly DrawablePool<CaughtDroplet> caughtDropletPool;
|
||||
|
||||
public Catcher([NotNull] DroppedObjectContainer droppedObjectTarget, BeatmapDifficulty difficulty = null)
|
||||
public Catcher([NotNull] DroppedObjectContainer droppedObjectTarget, IBeatmapDifficultyInfo difficulty = null)
|
||||
{
|
||||
this.droppedObjectTarget = droppedObjectTarget;
|
||||
|
||||
@@ -172,7 +172,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
/// <summary>
|
||||
/// Calculates the scale of the catcher based off the provided beatmap difficulty.
|
||||
/// </summary>
|
||||
private static Vector2 calculateScale(BeatmapDifficulty difficulty) => new Vector2(1.0f - 0.7f * (difficulty.CircleSize - 5) / 5);
|
||||
private static Vector2 calculateScale(IBeatmapDifficultyInfo difficulty) => new Vector2(1.0f - 0.7f * (difficulty.CircleSize - 5) / 5);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the width of the area used for attempting catches in gameplay.
|
||||
@@ -184,7 +184,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
/// Calculates the width of the area used for attempting catches in gameplay.
|
||||
/// </summary>
|
||||
/// <param name="difficulty">The beatmap difficulty.</param>
|
||||
public static float CalculateCatchWidth(BeatmapDifficulty difficulty) => CalculateCatchWidth(calculateScale(difficulty));
|
||||
public static float CalculateCatchWidth(IBeatmapDifficultyInfo difficulty) => CalculateCatchWidth(calculateScale(difficulty));
|
||||
|
||||
/// <summary>
|
||||
/// Determine if this catcher can catch a <see cref="CatchHitObject"/> in the current position.
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
: base(ruleset, beatmap, mods)
|
||||
{
|
||||
Direction.Value = ScrollingDirection.Down;
|
||||
TimeRange.Value = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450);
|
||||
TimeRange.Value = IBeatmapDifficultyInfo.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450);
|
||||
}
|
||||
|
||||
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay);
|
||||
|
||||
@@ -81,7 +81,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||
|
||||
protected override Beatmap<ManiaHitObject> ConvertBeatmap(IBeatmap original, CancellationToken cancellationToken)
|
||||
{
|
||||
BeatmapDifficulty difficulty = original.BeatmapInfo.BaseDifficulty;
|
||||
IBeatmapDifficultyInfo difficulty = original.BeatmapInfo.BaseDifficulty;
|
||||
|
||||
int seed = (int)MathF.Round(difficulty.DrainRate + difficulty.CircleSize) * 20 + (int)(difficulty.OverallDifficulty * 41.2) + (int)MathF.Round(difficulty.ApproachRate);
|
||||
Random = new FastRandom(seed);
|
||||
|
||||
@@ -111,7 +111,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
if (drainTime == 0)
|
||||
drainTime = 10000;
|
||||
|
||||
BeatmapDifficulty difficulty = OriginalBeatmap.BeatmapInfo.BaseDifficulty;
|
||||
IBeatmapDifficultyInfo difficulty = OriginalBeatmap.BeatmapInfo.BaseDifficulty;
|
||||
conversionDifficulty = ((difficulty.DrainRate + Math.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + (double)OriginalBeatmap.HitObjects.Count / drainTime * 9f) / 38f * 5f / 1.15;
|
||||
conversionDifficulty = Math.Min(conversionDifficulty.Value, 12);
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ namespace osu.Game.Rulesets.Mania.Objects
|
||||
/// </summary>
|
||||
private double tickSpacing = 50;
|
||||
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty)
|
||||
{
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks
|
||||
public class CheckTooShortSpinnersTest
|
||||
{
|
||||
private CheckTooShortSpinners check;
|
||||
private BeatmapDifficulty difficulty;
|
||||
private IBeatmapDifficultyInfo difficulty;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
@@ -81,12 +81,12 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks
|
||||
assertTooShort(new List<HitObject> { spinnerHighOd }, difficultyHighOd);
|
||||
}
|
||||
|
||||
private void assertOk(List<HitObject> hitObjects, BeatmapDifficulty beatmapDifficulty)
|
||||
private void assertOk(List<HitObject> hitObjects, IBeatmapDifficultyInfo beatmapDifficulty)
|
||||
{
|
||||
Assert.That(check.Run(getContext(hitObjects, beatmapDifficulty)), Is.Empty);
|
||||
}
|
||||
|
||||
private void assertVeryShort(List<HitObject> hitObjects, BeatmapDifficulty beatmapDifficulty)
|
||||
private void assertVeryShort(List<HitObject> hitObjects, IBeatmapDifficultyInfo beatmapDifficulty)
|
||||
{
|
||||
var issues = check.Run(getContext(hitObjects, beatmapDifficulty)).ToList();
|
||||
|
||||
@@ -94,7 +94,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks
|
||||
Assert.That(issues.First().Template is CheckTooShortSpinners.IssueTemplateVeryShort);
|
||||
}
|
||||
|
||||
private void assertTooShort(List<HitObject> hitObjects, BeatmapDifficulty beatmapDifficulty)
|
||||
private void assertTooShort(List<HitObject> hitObjects, IBeatmapDifficultyInfo beatmapDifficulty)
|
||||
{
|
||||
var issues = check.Run(getContext(hitObjects, beatmapDifficulty)).ToList();
|
||||
|
||||
@@ -102,12 +102,12 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks
|
||||
Assert.That(issues.First().Template is CheckTooShortSpinners.IssueTemplateTooShort);
|
||||
}
|
||||
|
||||
private BeatmapVerifierContext getContext(List<HitObject> hitObjects, BeatmapDifficulty beatmapDifficulty)
|
||||
private BeatmapVerifierContext getContext(List<HitObject> hitObjects, IBeatmapDifficultyInfo beatmapDifficulty)
|
||||
{
|
||||
var beatmap = new Beatmap<HitObject>
|
||||
{
|
||||
HitObjects = hitObjects,
|
||||
BeatmapInfo = new BeatmapInfo { BaseDifficulty = beatmapDifficulty }
|
||||
BeatmapInfo = new BeatmapInfo { BaseDifficulty = new BeatmapDifficulty(beatmapDifficulty) }
|
||||
};
|
||||
|
||||
return new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap));
|
||||
|
||||
@@ -452,7 +452,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
|
||||
private class TestSpinner : Spinner
|
||||
{
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty)
|
||||
{
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
SpinsRequired = 1;
|
||||
|
||||
@@ -412,7 +412,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
|
||||
private class TestSpinner : Spinner
|
||||
{
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty)
|
||||
{
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
SpinsRequired = 1;
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
|
||||
double starRating = basePerformance > 0.00001 ? Math.Cbrt(1.12) * 0.027 * (Math.Cbrt(100000 / Math.Pow(2, 1 / 1.1) * basePerformance) + 4) : 0;
|
||||
|
||||
double preempt = (int)BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate;
|
||||
double preempt = (int)IBeatmapDifficultyInfo.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate;
|
||||
|
||||
int maxCombo = beatmap.HitObjects.Count;
|
||||
// Add the ticks + tail of the slider. 1 is subtracted because the head circle would be counted twice (once for the slider itself in the line above)
|
||||
|
||||
@@ -113,7 +113,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
|
||||
#region Reduce AR (IApplicableToDifficulty)
|
||||
|
||||
public void ReadFromDifficulty(BeatmapDifficulty difficulty)
|
||||
public void ReadFromDifficulty(IBeatmapDifficultyInfo difficulty)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -122,11 +122,11 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
});
|
||||
}
|
||||
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty)
|
||||
{
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
|
||||
TimePreempt = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1800, 1200, PREEMPT_MIN);
|
||||
TimePreempt = (float)IBeatmapDifficultyInfo.DifficultyRange(difficulty.ApproachRate, 1800, 1200, PREEMPT_MIN);
|
||||
|
||||
// Preempt time can go below 450ms. Normally, this is achieved via the DT mod which uniformly speeds up all animations game wide regardless of AR.
|
||||
// This uniform speedup is hard to match 1:1, however we can at least make AR>10 (via mods) feel good by extending the upper linear function above.
|
||||
|
||||
@@ -135,7 +135,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
Path.Version.ValueChanged += _ => updateNestedPositions();
|
||||
}
|
||||
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty)
|
||||
{
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
|
||||
public double SpanDuration => slider.SpanDuration;
|
||||
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty)
|
||||
{
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
public int SpanIndex { get; set; }
|
||||
public double SpanStartTime { get; set; }
|
||||
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty)
|
||||
{
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
/// </summary>
|
||||
public int MaximumBonusSpins { get; protected set; } = 1;
|
||||
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty)
|
||||
{
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
|
||||
double secondsDuration = Duration / 1000;
|
||||
|
||||
double minimumRotationsPerSecond = stable_matching_fudge * BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 3, 5, 7.5);
|
||||
double minimumRotationsPerSecond = stable_matching_fudge * IBeatmapDifficultyInfo.DifficultyRange(difficulty.OverallDifficulty, 3, 5, 7.5);
|
||||
|
||||
SpinsRequired = (int)(secondsDuration * minimumRotationsPerSecond);
|
||||
MaximumBonusSpins = (int)((maximum_rotations_per_second - minimumRotationsPerSecond) * secondsDuration);
|
||||
|
||||
@@ -117,7 +117,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
||||
|
||||
case IHasDuration endTimeData:
|
||||
{
|
||||
double hitMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty, 3, 5, 7.5) * swell_hit_multiplier;
|
||||
double hitMultiplier = IBeatmapDifficultyInfo.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty, 3, 5, 7.5) * swell_hit_multiplier;
|
||||
|
||||
yield return new Swell
|
||||
{
|
||||
@@ -193,9 +193,10 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
||||
|
||||
private class TaikoMutliplierAppliedDifficulty : BeatmapDifficulty
|
||||
{
|
||||
public TaikoMutliplierAppliedDifficulty(BeatmapDifficulty difficulty)
|
||||
public TaikoMutliplierAppliedDifficulty(IBeatmapDifficultyInfo difficulty)
|
||||
{
|
||||
difficulty.CopyTo(this);
|
||||
CopyFrom(difficulty);
|
||||
|
||||
SliderMultiplier *= LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.Taiko.Objects
|
||||
|
||||
private float overallDifficulty = BeatmapDifficulty.DEFAULT_DIFFICULTY;
|
||||
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty)
|
||||
{
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
|
||||
|
||||
@@ -40,8 +40,8 @@ namespace osu.Game.Rulesets.Taiko.Scoring
|
||||
{
|
||||
base.ApplyBeatmap(beatmap);
|
||||
|
||||
hpMultiplier = 1 / (object_count_factor * Math.Max(1, beatmap.HitObjects.OfType<Hit>().Count()) * BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.5, 0.75, 0.98));
|
||||
hpMissMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.0018, 0.0075, 0.0120);
|
||||
hpMultiplier = 1 / (object_count_factor * Math.Max(1, beatmap.HitObjects.OfType<Hit>().Count()) * IBeatmapDifficultyInfo.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.5, 0.75, 0.98));
|
||||
hpMissMultiplier = IBeatmapDifficultyInfo.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.0018, 0.0075, 0.0120);
|
||||
}
|
||||
|
||||
protected override double GetHealthIncreaseFor(JudgementResult result)
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace osu.Game.Tests.Localisation
|
||||
Title = "Romanised title",
|
||||
TitleUnicode = "Unicode Title"
|
||||
};
|
||||
var romanisableString = metadata.ToRomanisableString();
|
||||
var romanisableString = metadata.GetDisplayTitleRomanisable();
|
||||
|
||||
Assert.AreEqual(metadata.ToString(), romanisableString.Romanised);
|
||||
Assert.AreEqual($"{metadata.ArtistUnicode} - {metadata.TitleUnicode}", romanisableString.Original);
|
||||
@@ -33,7 +33,7 @@ namespace osu.Game.Tests.Localisation
|
||||
Artist = "Romanised Artist",
|
||||
Title = "Romanised title"
|
||||
};
|
||||
var romanisableString = metadata.ToRomanisableString();
|
||||
var romanisableString = metadata.GetDisplayTitleRomanisable();
|
||||
|
||||
Assert.AreEqual(romanisableString.Romanised, romanisableString.Original);
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace osu.Game.Tests.Online
|
||||
|
||||
AddAssert("response event fired", () => response != null);
|
||||
|
||||
AddAssert("request has response", () => request.Result == response);
|
||||
AddAssert("request has response", () => request.Response == response);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Screens.Edit;
|
||||
using osu.Game.Screens.Menu;
|
||||
using osu.Game.Screens.Select;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Editing
|
||||
{
|
||||
public class TestSceneEditorSaving : OsuGameTestScene
|
||||
{
|
||||
private Editor editor => Game.ChildrenOfType<Editor>().FirstOrDefault();
|
||||
|
||||
private EditorBeatmap editorBeatmap => (EditorBeatmap)editor.Dependencies.Get(typeof(EditorBeatmap));
|
||||
|
||||
/// <summary>
|
||||
/// Tests the general expected flow of creating a new beatmap, saving it, then loading it back from song select.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestNewBeatmapSaveThenLoad()
|
||||
{
|
||||
AddStep("set default beatmap", () => Game.Beatmap.SetDefault());
|
||||
|
||||
PushAndConfirm(() => new EditorLoader());
|
||||
|
||||
AddUntilStep("wait for editor load", () => editor != null);
|
||||
|
||||
AddStep("Add timing point", () => editorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint()));
|
||||
|
||||
AddStep("Enter compose mode", () => InputManager.Key(Key.F1));
|
||||
AddUntilStep("Wait for compose mode load", () => editor.ChildrenOfType<HitObjectComposer>().FirstOrDefault()?.IsLoaded == true);
|
||||
|
||||
AddStep("Change to placement mode", () => InputManager.Key(Key.Number2));
|
||||
AddStep("Move to playfield", () => InputManager.MoveMouseTo(Game.ScreenSpaceDrawQuad.Centre));
|
||||
AddStep("Place single hitcircle", () => InputManager.Click(MouseButton.Left));
|
||||
|
||||
AddStep("Save and exit", () =>
|
||||
{
|
||||
InputManager.Keys(PlatformAction.Save);
|
||||
InputManager.Key(Key.Escape);
|
||||
});
|
||||
|
||||
AddUntilStep("Wait for main menu", () => Game.ScreenStack.CurrentScreen is MainMenu);
|
||||
|
||||
PushAndConfirm(() => new PlaySongSelect());
|
||||
|
||||
AddUntilStep("Wait for beatmap selected", () => !Game.Beatmap.IsDefault);
|
||||
AddStep("Open options", () => InputManager.Key(Key.F3));
|
||||
AddStep("Enter editor", () => InputManager.Key(Key.Number5));
|
||||
|
||||
AddUntilStep("Wait for editor load", () => editor != null);
|
||||
AddAssert("Beatmap contains single hitcircle", () => editorBeatmap.HitObjects.Count == 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
@@ -68,6 +69,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
LoadScreen(dependenciesScreen = new DependenciesScreen(client));
|
||||
});
|
||||
|
||||
AddUntilStep("wait for dependencies screen", () => Stack.CurrentScreen is DependenciesScreen);
|
||||
AddUntilStep("wait for dependencies to start load", () => dependenciesScreen.LoadState > LoadState.NotLoaded);
|
||||
AddUntilStep("wait for dependencies to load", () => dependenciesScreen.IsLoaded);
|
||||
|
||||
AddStep("load multiplayer", () => LoadScreen(multiplayerScreen));
|
||||
|
||||
@@ -221,6 +221,8 @@ namespace osu.Game.Tests.Visual.Ranking
|
||||
list.SelectedScore.Value = middleScore;
|
||||
});
|
||||
|
||||
AddUntilStep("wait for all scores to be visible", () => list.ChildrenOfType<ScorePanelTrackingContainer>().All(t => t.IsPresent));
|
||||
|
||||
assertScoreState(highestScore, false);
|
||||
assertScoreState(middleScore, true);
|
||||
assertScoreState(lowestScore, false);
|
||||
|
||||
@@ -714,10 +714,11 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
});
|
||||
|
||||
FilterableDifficultyIcon difficultyIcon = null;
|
||||
AddStep("Find an icon for different ruleset", () =>
|
||||
AddUntilStep("Find an icon for different ruleset", () =>
|
||||
{
|
||||
difficultyIcon = set.ChildrenOfType<FilterableDifficultyIcon>()
|
||||
.First(icon => icon.Item.BeatmapInfo.Ruleset.ID == 3);
|
||||
.FirstOrDefault(icon => icon.Item.BeatmapInfo.Ruleset.ID == 3);
|
||||
return difficultyIcon != null;
|
||||
});
|
||||
|
||||
AddAssert("Check ruleset is osu!", () => Ruleset.Value.ID == 0);
|
||||
|
||||
@@ -162,6 +162,8 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
|
||||
AddUntilStep("wait for fetch", () => leaderboard.Scores != null);
|
||||
|
||||
AddUntilStep("score removed from leaderboard", () => leaderboard.Scores.All(s => s.OnlineScoreID != scoreBeingDeleted.OnlineScoreID));
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
var req = new GetBeatmapSetRequest(1);
|
||||
api.Queue(req);
|
||||
|
||||
AddUntilStep("wait for api response", () => req.Result != null);
|
||||
AddUntilStep("wait for api response", () => req.Response != null);
|
||||
|
||||
TestUpdateableBeatmapBackgroundSprite background = null;
|
||||
|
||||
@@ -81,7 +81,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
Child = background = new TestUpdateableBeatmapBackgroundSprite
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Beatmap = { Value = new BeatmapInfo { BeatmapSet = req.Result?.ToBeatmapSet(rulesets) } }
|
||||
Beatmap = { Value = new BeatmapInfo { BeatmapSet = req.Response?.ToBeatmapSet(rulesets) } }
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ namespace osu.Game.Tournament.Components
|
||||
if ((mods & LegacyMods.DoubleTime) > 0)
|
||||
{
|
||||
// temporary local calculation (taken from OsuDifficultyCalculator)
|
||||
double preempt = (int)BeatmapDifficulty.DifficultyRange(ar, 1800, 1200, 450) / 1.5;
|
||||
double preempt = (int)IBeatmapDifficultyInfo.DifficultyRange(ar, 1800, 1200, 450) / 1.5;
|
||||
ar = (float)(preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5);
|
||||
|
||||
bpm *= 1.5f;
|
||||
|
||||
@@ -182,7 +182,7 @@ namespace osu.Game.Tournament
|
||||
{
|
||||
var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = b.ID });
|
||||
API.Perform(req);
|
||||
b.BeatmapInfo = req.Result?.ToBeatmapInfo(RulesetStore);
|
||||
b.BeatmapInfo = req.Response?.ToBeatmapInfo(RulesetStore);
|
||||
|
||||
addedInfo = true;
|
||||
}
|
||||
@@ -203,7 +203,7 @@ namespace osu.Game.Tournament
|
||||
{
|
||||
var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = b.ID });
|
||||
req.Perform(API);
|
||||
b.BeatmapInfo = req.Result?.ToBeatmapInfo(RulesetStore);
|
||||
b.BeatmapInfo = req.Response?.ToBeatmapInfo(RulesetStore);
|
||||
|
||||
addedInfo = true;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ using osu.Game.Database;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
{
|
||||
public class BeatmapDifficulty : IHasPrimaryKey
|
||||
public class BeatmapDifficulty : IHasPrimaryKey, IBeatmapDifficultyInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// The default value used for all difficulty settings except <see cref="SliderMultiplier"/> and <see cref="SliderTickRate"/>.
|
||||
@@ -20,6 +20,15 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
private float? approachRate;
|
||||
|
||||
public BeatmapDifficulty()
|
||||
{
|
||||
}
|
||||
|
||||
public BeatmapDifficulty(IBeatmapDifficultyInfo source)
|
||||
{
|
||||
CopyFrom(source);
|
||||
}
|
||||
|
||||
public float ApproachRate
|
||||
{
|
||||
get => approachRate ?? OverallDifficulty;
|
||||
@@ -39,6 +48,17 @@ namespace osu.Game.Beatmaps
|
||||
return diff;
|
||||
}
|
||||
|
||||
public void CopyFrom(IBeatmapDifficultyInfo difficulty)
|
||||
{
|
||||
ApproachRate = difficulty.ApproachRate;
|
||||
DrainRate = difficulty.DrainRate;
|
||||
CircleSize = difficulty.CircleSize;
|
||||
OverallDifficulty = difficulty.OverallDifficulty;
|
||||
|
||||
SliderMultiplier = difficulty.SliderMultiplier;
|
||||
SliderTickRate = difficulty.SliderTickRate;
|
||||
}
|
||||
|
||||
public void CopyTo(BeatmapDifficulty difficulty)
|
||||
{
|
||||
difficulty.ApproachRate = ApproachRate;
|
||||
@@ -49,47 +69,5 @@ namespace osu.Game.Beatmaps
|
||||
difficulty.SliderMultiplier = SliderMultiplier;
|
||||
difficulty.SliderTickRate = SliderTickRate;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps a difficulty value [0, 10] to a two-piece linear range of values.
|
||||
/// </summary>
|
||||
/// <param name="difficulty">The difficulty value to be mapped.</param>
|
||||
/// <param name="min">Minimum of the resulting range which will be achieved by a difficulty value of 0.</param>
|
||||
/// <param name="mid">Midpoint of the resulting range which will be achieved by a difficulty value of 5.</param>
|
||||
/// <param name="max">Maximum of the resulting range which will be achieved by a difficulty value of 10.</param>
|
||||
/// <returns>Value to which the difficulty value maps in the specified range.</returns>
|
||||
public static double DifficultyRange(double difficulty, double min, double mid, double max)
|
||||
{
|
||||
if (difficulty > 5)
|
||||
return mid + (max - mid) * (difficulty - 5) / 5;
|
||||
if (difficulty < 5)
|
||||
return mid - (mid - min) * (5 - difficulty) / 5;
|
||||
|
||||
return mid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps a difficulty value [0, 10] to a two-piece linear range of values.
|
||||
/// </summary>
|
||||
/// <param name="difficulty">The difficulty value to be mapped.</param>
|
||||
/// <param name="range">The values that define the two linear ranges.
|
||||
/// <list type="table">
|
||||
/// <item>
|
||||
/// <term>od0</term>
|
||||
/// <description>Minimum of the resulting range which will be achieved by a difficulty value of 0.</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>od5</term>
|
||||
/// <description>Midpoint of the resulting range which will be achieved by a difficulty value of 5.</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>od10</term>
|
||||
/// <description>Maximum of the resulting range which will be achieved by a difficulty value of 10.</description>
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </param>
|
||||
/// <returns>Value to which the difficulty value maps in the specified range.</returns>
|
||||
public static double DifficultyRange(double difficulty, (double od0, double od5, double od10) range)
|
||||
=> DifficultyRange(difficulty, range.od0, range.od5, range.od10);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Rulesets;
|
||||
@@ -17,7 +16,7 @@ namespace osu.Game.Beatmaps
|
||||
{
|
||||
[ExcludeFromDynamicCompile]
|
||||
[Serializable]
|
||||
public class BeatmapInfo : IEquatable<BeatmapInfo>, IHasPrimaryKey
|
||||
public class BeatmapInfo : IEquatable<BeatmapInfo>, IHasPrimaryKey, IBeatmapInfo
|
||||
{
|
||||
public int ID { get; set; }
|
||||
|
||||
@@ -152,18 +151,7 @@ namespace osu.Game.Beatmaps
|
||||
[JsonIgnore]
|
||||
public DifficultyRating DifficultyRating => BeatmapDifficultyCache.GetDifficultyRating(StarDifficulty);
|
||||
|
||||
public string[] SearchableTerms => new[]
|
||||
{
|
||||
Version
|
||||
}.Concat(Metadata?.SearchableTerms ?? Enumerable.Empty<string>()).Where(s => !string.IsNullOrEmpty(s)).ToArray();
|
||||
|
||||
public override string ToString() => $"{Metadata ?? BeatmapSet?.Metadata} {versionString}".Trim();
|
||||
|
||||
public RomanisableString ToRomanisableString()
|
||||
{
|
||||
var metadata = (Metadata ?? BeatmapSet?.Metadata)?.ToRomanisableString() ?? new RomanisableString(null, null);
|
||||
return new RomanisableString($"{metadata.GetPreferred(true)} {versionString}".Trim(), $"{metadata.GetPreferred(false)} {versionString}".Trim());
|
||||
}
|
||||
public override string ToString() => this.GetDisplayTitle();
|
||||
|
||||
public bool Equals(BeatmapInfo other)
|
||||
{
|
||||
@@ -187,5 +175,22 @@ namespace osu.Game.Beatmaps
|
||||
/// Returns a shallow-clone of this <see cref="BeatmapInfo"/>.
|
||||
/// </summary>
|
||||
public BeatmapInfo Clone() => (BeatmapInfo)MemberwiseClone();
|
||||
|
||||
#region Implementation of IHasOnlineID
|
||||
|
||||
public int? OnlineID => OnlineBeatmapID;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation of IBeatmapInfo
|
||||
|
||||
string IBeatmapInfo.DifficultyName => Version;
|
||||
IBeatmapMetadataInfo IBeatmapInfo.Metadata => Metadata;
|
||||
IBeatmapDifficultyInfo IBeatmapInfo.Difficulty => BaseDifficulty;
|
||||
IBeatmapSetInfo IBeatmapInfo.BeatmapSet => BeatmapSet;
|
||||
IRulesetInfo IBeatmapInfo.Ruleset => Ruleset;
|
||||
double IBeatmapInfo.StarRating => StarDifficulty;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Linq;
|
||||
using osu.Framework.Localisation;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
{
|
||||
public static class BeatmapInfoExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// A user-presentable display title representing this beatmap.
|
||||
/// </summary>
|
||||
public static string GetDisplayTitle(this IBeatmapInfo beatmapInfo) => $"{getClosestMetadata(beatmapInfo)} {getVersionString(beatmapInfo)}".Trim();
|
||||
|
||||
/// <summary>
|
||||
/// A user-presentable display title representing this beatmap, with localisation handling for potentially romanisable fields.
|
||||
/// </summary>
|
||||
public static RomanisableString GetDisplayTitleRomanisable(this IBeatmapInfo beatmapInfo)
|
||||
{
|
||||
var metadata = getClosestMetadata(beatmapInfo).GetDisplayTitleRomanisable();
|
||||
var versionString = getVersionString(beatmapInfo);
|
||||
|
||||
return new RomanisableString($"{metadata.GetPreferred(true)} {versionString}".Trim(), $"{metadata.GetPreferred(false)} {versionString}".Trim());
|
||||
}
|
||||
|
||||
public static string[] GetSearchableTerms(this IBeatmapInfo beatmapInfo) => new[]
|
||||
{
|
||||
beatmapInfo.DifficultyName
|
||||
}.Concat(getClosestMetadata(beatmapInfo).GetSearchableTerms()).Where(s => !string.IsNullOrEmpty(s)).ToArray();
|
||||
|
||||
private static string getVersionString(IBeatmapInfo beatmapInfo) => string.IsNullOrEmpty(beatmapInfo.DifficultyName) ? string.Empty : $"[{beatmapInfo.DifficultyName}]";
|
||||
|
||||
// temporary helper methods until we figure which metadata should be where.
|
||||
private static IBeatmapMetadataInfo getClosestMetadata(IBeatmapInfo beatmapInfo) =>
|
||||
beatmapInfo.Metadata ?? beatmapInfo.BeatmapSet?.Metadata ?? new BeatmapMetadata();
|
||||
}
|
||||
}
|
||||
@@ -45,6 +45,7 @@ namespace osu.Game.Beatmaps
|
||||
workingBeatmapCache = CreateWorkingBeatmapCache(audioManager, resources, new FileStore(contextFactory, storage).Store, defaultBeatmap, host);
|
||||
|
||||
workingBeatmapCache.BeatmapManager = beatmapModelManager;
|
||||
beatmapModelManager.WorkingBeatmapCache = workingBeatmapCache;
|
||||
|
||||
if (performOnlineLookups)
|
||||
{
|
||||
@@ -305,6 +306,9 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
public WorkingBeatmap GetWorkingBeatmap(BeatmapInfo importedBeatmap) => workingBeatmapCache.GetWorkingBeatmap(importedBeatmap);
|
||||
|
||||
void IWorkingBeatmapCache.Invalidate(BeatmapSetInfo beatmapSetInfo) => workingBeatmapCache.Invalidate(beatmapSetInfo);
|
||||
void IWorkingBeatmapCache.Invalidate(BeatmapInfo beatmapInfo) => workingBeatmapCache.Invalidate(beatmapInfo);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation of IModelFileManager<in BeatmapSetInfo,in BeatmapSetFileInfo>
|
||||
|
||||
@@ -4,9 +4,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Users;
|
||||
@@ -15,7 +13,7 @@ namespace osu.Game.Beatmaps
|
||||
{
|
||||
[ExcludeFromDynamicCompile]
|
||||
[Serializable]
|
||||
public class BeatmapMetadata : IEquatable<BeatmapMetadata>, IHasPrimaryKey
|
||||
public class BeatmapMetadata : IEquatable<BeatmapMetadata>, IHasPrimaryKey, IBeatmapMetadataInfo
|
||||
{
|
||||
public int ID { get; set; }
|
||||
|
||||
@@ -83,50 +81,13 @@ namespace osu.Game.Beatmaps
|
||||
public int PreviewTime { get; set; }
|
||||
|
||||
public string AudioFile { get; set; }
|
||||
|
||||
public string BackgroundFile { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string author = Author == null ? string.Empty : $"({Author})";
|
||||
return $"{Artist} - {Title} {author}".Trim();
|
||||
}
|
||||
public bool Equals(BeatmapMetadata other) => ((IBeatmapMetadataInfo)this).Equals(other);
|
||||
|
||||
public RomanisableString ToRomanisableString()
|
||||
{
|
||||
string author = Author == null ? string.Empty : $"({Author})";
|
||||
var artistUnicode = string.IsNullOrEmpty(ArtistUnicode) ? Artist : ArtistUnicode;
|
||||
var titleUnicode = string.IsNullOrEmpty(TitleUnicode) ? Title : TitleUnicode;
|
||||
public override string ToString() => this.GetDisplayTitle();
|
||||
|
||||
return new RomanisableString($"{artistUnicode} - {titleUnicode} {author}".Trim(), $"{Artist} - {Title} {author}".Trim());
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public string[] SearchableTerms => new[]
|
||||
{
|
||||
Author?.Username,
|
||||
Artist,
|
||||
ArtistUnicode,
|
||||
Title,
|
||||
TitleUnicode,
|
||||
Source,
|
||||
Tags
|
||||
}.Where(s => !string.IsNullOrEmpty(s)).ToArray();
|
||||
|
||||
public bool Equals(BeatmapMetadata other)
|
||||
{
|
||||
if (other == null)
|
||||
return false;
|
||||
|
||||
return Title == other.Title
|
||||
&& TitleUnicode == other.TitleUnicode
|
||||
&& Artist == other.Artist
|
||||
&& ArtistUnicode == other.ArtistUnicode
|
||||
&& AuthorString == other.AuthorString
|
||||
&& Source == other.Source
|
||||
&& Tags == other.Tags
|
||||
&& PreviewTime == other.PreviewTime
|
||||
&& AudioFile == other.AudioFile
|
||||
&& BackgroundFile == other.BackgroundFile;
|
||||
}
|
||||
string IBeatmapMetadataInfo.Author => AuthorString;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Linq;
|
||||
using osu.Framework.Localisation;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
{
|
||||
public static class BeatmapMetadataInfoExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// An array of all searchable terms provided in contained metadata.
|
||||
/// </summary>
|
||||
public static string[] GetSearchableTerms(this IBeatmapMetadataInfo metadataInfo) => new[]
|
||||
{
|
||||
metadataInfo.Author,
|
||||
metadataInfo.Artist,
|
||||
metadataInfo.ArtistUnicode,
|
||||
metadataInfo.Title,
|
||||
metadataInfo.TitleUnicode,
|
||||
metadataInfo.Source,
|
||||
metadataInfo.Tags
|
||||
}.Where(s => !string.IsNullOrEmpty(s)).ToArray();
|
||||
|
||||
/// <summary>
|
||||
/// A user-presentable display title representing this metadata.
|
||||
/// </summary>
|
||||
public static string GetDisplayTitle(this IBeatmapMetadataInfo metadataInfo)
|
||||
{
|
||||
string author = string.IsNullOrEmpty(metadataInfo.Author) ? string.Empty : $"({metadataInfo.Author})";
|
||||
return $"{metadataInfo.Artist} - {metadataInfo.Title} {author}".Trim();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A user-presentable display title representing this beatmap, with localisation handling for potentially romanisable fields.
|
||||
/// </summary>
|
||||
public static RomanisableString GetDisplayTitleRomanisable(this IBeatmapMetadataInfo metadataInfo)
|
||||
{
|
||||
string author = string.IsNullOrEmpty(metadataInfo.Author) ? string.Empty : $"({metadataInfo.Author})";
|
||||
var artistUnicode = string.IsNullOrEmpty(metadataInfo.ArtistUnicode) ? metadataInfo.Artist : metadataInfo.ArtistUnicode;
|
||||
var titleUnicode = string.IsNullOrEmpty(metadataInfo.TitleUnicode) ? metadataInfo.Title : metadataInfo.TitleUnicode;
|
||||
|
||||
return new RomanisableString($"{artistUnicode} - {titleUnicode} {author}".Trim(), $"{metadataInfo.Artist} - {metadataInfo.Title} {author}".Trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ namespace osu.Game.Beatmaps
|
||||
/// Handles ef-core storage of beatmaps.
|
||||
/// </summary>
|
||||
[ExcludeFromDynamicCompile]
|
||||
public class BeatmapModelManager : ArchiveModelManager<BeatmapSetInfo, BeatmapSetFileInfo>
|
||||
public class BeatmapModelManager : ArchiveModelManager<BeatmapSetInfo, BeatmapSetFileInfo>, IBeatmapModelManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Fired when a single difficulty has been hidden.
|
||||
@@ -54,7 +54,7 @@ namespace osu.Game.Beatmaps
|
||||
/// <summary>
|
||||
/// The game working beatmap cache, used to invalidate entries on changes.
|
||||
/// </summary>
|
||||
public WorkingBeatmapCache WorkingBeatmapCache { private get; set; }
|
||||
public IWorkingBeatmapCache WorkingBeatmapCache { private get; set; }
|
||||
|
||||
private readonly Bindable<WeakReference<BeatmapInfo>> beatmapRestored = new Bindable<WeakReference<BeatmapInfo>>();
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ namespace osu.Game.Beatmaps
|
||||
// intentionally blocking to limit web request concurrency
|
||||
api.Perform(req);
|
||||
|
||||
var res = req.Result;
|
||||
var res = req.Response;
|
||||
|
||||
if (res != null)
|
||||
{
|
||||
|
||||
@@ -7,7 +7,7 @@ using osu.Game.IO;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
{
|
||||
public class BeatmapSetFileInfo : INamedFileInfo, IHasPrimaryKey
|
||||
public class BeatmapSetFileInfo : INamedFileInfo, IHasPrimaryKey, INamedFileUsage
|
||||
{
|
||||
public int ID { get; set; }
|
||||
|
||||
@@ -19,5 +19,7 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
[Required]
|
||||
public string Filename { get; set; }
|
||||
|
||||
public IFileInfo File => FileInfo;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ using osu.Game.Database;
|
||||
namespace osu.Game.Beatmaps
|
||||
{
|
||||
[ExcludeFromDynamicCompile]
|
||||
public class BeatmapSetInfo : IHasPrimaryKey, IHasFiles<BeatmapSetFileInfo>, ISoftDelete, IEquatable<BeatmapSetInfo>
|
||||
public class BeatmapSetInfo : IHasPrimaryKey, IHasFiles<BeatmapSetFileInfo>, ISoftDelete, IEquatable<BeatmapSetInfo>, IBeatmapSetInfo
|
||||
{
|
||||
public int ID { get; set; }
|
||||
|
||||
@@ -61,8 +61,6 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
public string Hash { get; set; }
|
||||
|
||||
public string StoryboardFile => Files.Find(f => f.Filename.EndsWith(".osb", StringComparison.OrdinalIgnoreCase))?.Filename;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the storage path for the file in this beatmapset with the given filename, if any exists, otherwise null.
|
||||
/// The path returned is relative to the user file storage.
|
||||
@@ -90,5 +88,19 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
return ReferenceEquals(this, other);
|
||||
}
|
||||
|
||||
#region Implementation of IHasOnlineID
|
||||
|
||||
public int? OnlineID => OnlineBeatmapSetID;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation of IBeatmapSetInfo
|
||||
|
||||
IBeatmapMetadataInfo IBeatmapSetInfo.Metadata => Metadata;
|
||||
IEnumerable<IBeatmapInfo> IBeatmapSetInfo.Beatmaps => Beatmaps;
|
||||
IEnumerable<INamedFileUsage> IBeatmapSetInfo.Files => Files;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
{
|
||||
/// <summary>
|
||||
/// A representation of all top-level difficulty settings for a beatmap.
|
||||
/// </summary>
|
||||
public interface IBeatmapDifficultyInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// The default value used for all difficulty settings except <see cref="SliderMultiplier"/> and <see cref="SliderTickRate"/>.
|
||||
/// </summary>
|
||||
const float DEFAULT_DIFFICULTY = 5;
|
||||
|
||||
/// <summary>
|
||||
/// The drain rate of the associated beatmap.
|
||||
/// </summary>
|
||||
float DrainRate { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The circle size of the associated beatmap.
|
||||
/// </summary>
|
||||
float CircleSize { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The overall difficulty of the associated beatmap.
|
||||
/// </summary>
|
||||
float OverallDifficulty { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The approach rate of the associated beatmap.
|
||||
/// </summary>
|
||||
float ApproachRate { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The slider multiplier of the associated beatmap.
|
||||
/// </summary>
|
||||
double SliderMultiplier { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The slider tick rate of the associated beatmap.
|
||||
/// </summary>
|
||||
double SliderTickRate { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Maps a difficulty value [0, 10] to a two-piece linear range of values.
|
||||
/// </summary>
|
||||
/// <param name="difficulty">The difficulty value to be mapped.</param>
|
||||
/// <param name="min">Minimum of the resulting range which will be achieved by a difficulty value of 0.</param>
|
||||
/// <param name="mid">Midpoint of the resulting range which will be achieved by a difficulty value of 5.</param>
|
||||
/// <param name="max">Maximum of the resulting range which will be achieved by a difficulty value of 10.</param>
|
||||
/// <returns>Value to which the difficulty value maps in the specified range.</returns>
|
||||
static double DifficultyRange(double difficulty, double min, double mid, double max)
|
||||
{
|
||||
if (difficulty > 5)
|
||||
return mid + (max - mid) * (difficulty - 5) / 5;
|
||||
if (difficulty < 5)
|
||||
return mid - (mid - min) * (5 - difficulty) / 5;
|
||||
|
||||
return mid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps a difficulty value [0, 10] to a two-piece linear range of values.
|
||||
/// </summary>
|
||||
/// <param name="difficulty">The difficulty value to be mapped.</param>
|
||||
/// <param name="range">The values that define the two linear ranges.
|
||||
/// <list type="table">
|
||||
/// <item>
|
||||
/// <term>od0</term>
|
||||
/// <description>Minimum of the resulting range which will be achieved by a difficulty value of 0.</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>od5</term>
|
||||
/// <description>Midpoint of the resulting range which will be achieved by a difficulty value of 5.</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>od10</term>
|
||||
/// <description>Maximum of the resulting range which will be achieved by a difficulty value of 10.</description>
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </param>
|
||||
/// <returns>Value to which the difficulty value maps in the specified range.</returns>
|
||||
static double DifficultyRange(double difficulty, (double od0, double od5, double od10) range)
|
||||
=> DifficultyRange(difficulty, range.od0, range.od5, range.od10);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Rulesets;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
{
|
||||
/// <summary>
|
||||
/// A single beatmap difficulty.
|
||||
/// </summary>
|
||||
public interface IBeatmapInfo : IHasOnlineID
|
||||
{
|
||||
/// <summary>
|
||||
/// The user-specified name given to this beatmap.
|
||||
/// </summary>
|
||||
string DifficultyName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The metadata representing this beatmap. May be shared between multiple beatmaps.
|
||||
/// </summary>
|
||||
IBeatmapMetadataInfo? Metadata { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The difficulty settings for this beatmap.
|
||||
/// </summary>
|
||||
IBeatmapDifficultyInfo Difficulty { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The beatmap set this beatmap is part of.
|
||||
/// </summary>
|
||||
IBeatmapSetInfo? BeatmapSet { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The playable length in milliseconds of this beatmap.
|
||||
/// </summary>
|
||||
double Length { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The most common BPM of this beatmap.
|
||||
/// </summary>
|
||||
double BPM { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The SHA-256 hash representing this beatmap's contents.
|
||||
/// </summary>
|
||||
string Hash { get; }
|
||||
|
||||
/// <summary>
|
||||
/// MD5 is kept for legacy support (matching against replays etc.).
|
||||
/// </summary>
|
||||
string MD5Hash { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The ruleset this beatmap was made for.
|
||||
/// </summary>
|
||||
IRulesetInfo Ruleset { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The basic star rating for this beatmap (with no mods applied).
|
||||
/// </summary>
|
||||
double StarRating { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
{
|
||||
/// <summary>
|
||||
/// Metadata representing a beatmap. May be shared between multiple beatmap difficulties.
|
||||
/// </summary>
|
||||
public interface IBeatmapMetadataInfo : IEquatable<IBeatmapMetadataInfo>
|
||||
{
|
||||
/// <summary>
|
||||
/// The romanised title of this beatmap.
|
||||
/// </summary>
|
||||
string Title { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The unicode title of this beatmap.
|
||||
/// </summary>
|
||||
string TitleUnicode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The romanised artist of this beatmap.
|
||||
/// </summary>
|
||||
string Artist { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The unicode artist of this beatmap.
|
||||
/// </summary>
|
||||
string ArtistUnicode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The author of this beatmap.
|
||||
/// </summary>
|
||||
string Author { get; } // eventually should be linked to a persisted User.
|
||||
|
||||
/// <summary>
|
||||
/// The source of this beatmap.
|
||||
/// </summary>
|
||||
string Source { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The tags of this beatmap.
|
||||
/// </summary>
|
||||
string Tags { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The time in milliseconds to begin playing the track for preview purposes.
|
||||
/// If -1, the track should begin playing at 40% of its length.
|
||||
/// </summary>
|
||||
int PreviewTime { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The filename of the audio file consumed by this beatmap.
|
||||
/// </summary>
|
||||
string AudioFile { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The filename of the background image file consumed by this beatmap.
|
||||
/// </summary>
|
||||
string BackgroundFile { get; }
|
||||
|
||||
bool IEquatable<IBeatmapMetadataInfo>.Equals(IBeatmapMetadataInfo? other)
|
||||
{
|
||||
if (other == null)
|
||||
return false;
|
||||
|
||||
return Title == other.Title
|
||||
&& TitleUnicode == other.TitleUnicode
|
||||
&& Artist == other.Artist
|
||||
&& ArtistUnicode == other.ArtistUnicode
|
||||
&& Author == other.Author
|
||||
&& Source == other.Source
|
||||
&& Tags == other.Tags
|
||||
&& PreviewTime == other.PreviewTime
|
||||
&& AudioFile == other.AudioFile
|
||||
&& BackgroundFile == other.BackgroundFile;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Game.Database;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
{
|
||||
public interface IBeatmapModelManager : IModelManager<BeatmapSetInfo>
|
||||
{
|
||||
/// <summary>
|
||||
/// Provide an online lookup queue component to handle populating online beatmap metadata.
|
||||
/// </summary>
|
||||
BeatmapOnlineLookupQueue OnlineLookupQueue { set; }
|
||||
|
||||
/// <summary>
|
||||
/// Provide a working beatmap cache, used to invalidate entries on changes.
|
||||
/// </summary>
|
||||
IWorkingBeatmapCache WorkingBeatmapCache { set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Database;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
{
|
||||
/// <summary>
|
||||
/// A representation of a collection of beatmap difficulties, generally packaged as an ".osz" archive.
|
||||
/// </summary>
|
||||
public interface IBeatmapSetInfo : IHasOnlineID
|
||||
{
|
||||
/// <summary>
|
||||
/// The date when this beatmap was imported.
|
||||
/// </summary>
|
||||
DateTimeOffset DateAdded { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The best-effort metadata representing this set. In the case metadata differs between contained beatmaps, one is arbitrarily chosen.
|
||||
/// </summary>
|
||||
IBeatmapMetadataInfo? Metadata { get; }
|
||||
|
||||
/// <summary>
|
||||
/// All beatmaps contained in this set.
|
||||
/// </summary>
|
||||
IEnumerable<IBeatmapInfo> Beatmaps { get; }
|
||||
|
||||
/// <summary>
|
||||
/// All files used by this set.
|
||||
/// </summary>
|
||||
IEnumerable<INamedFileUsage> Files { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The maximum star difficulty of all beatmaps in this set.
|
||||
/// </summary>
|
||||
double MaxStarDifficulty { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The maximum playable length in milliseconds of all beatmaps in this set.
|
||||
/// </summary>
|
||||
double MaxLength { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The maximum BPM of all beatmaps in this set.
|
||||
/// </summary>
|
||||
double MaxBPM { get; }
|
||||
}
|
||||
}
|
||||
@@ -11,5 +11,17 @@ namespace osu.Game.Beatmaps
|
||||
/// <param name="beatmapInfo">The beatmap to lookup.</param>
|
||||
/// <returns>A <see cref="WorkingBeatmap"/> instance correlating to the provided <see cref="BeatmapInfo"/>.</returns>
|
||||
WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo);
|
||||
|
||||
/// <summary>
|
||||
/// Invalidate a cache entry if it exists.
|
||||
/// </summary>
|
||||
/// <param name="beatmapSetInfo">The beatmap set info to invalidate any cached entries for.</param>
|
||||
void Invalidate(BeatmapSetInfo beatmapSetInfo);
|
||||
|
||||
/// <summary>
|
||||
/// Invalidate a cache entry if it exists.
|
||||
/// </summary>
|
||||
/// <param name="beatmapInfo">The beatmap info to invalidate any cached entries for.</param>
|
||||
void Invalidate(BeatmapInfo beatmapInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,12 +201,14 @@ namespace osu.Game.Beatmaps
|
||||
{
|
||||
var decoder = Decoder.GetDecoder<Storyboard>(stream);
|
||||
|
||||
var storyboardFilename = BeatmapSetInfo?.Files.FirstOrDefault(f => f.Filename.EndsWith(".osb", StringComparison.OrdinalIgnoreCase))?.Filename;
|
||||
|
||||
// todo: support loading from both set-wide storyboard *and* beatmap specific.
|
||||
if (BeatmapSetInfo?.StoryboardFile == null)
|
||||
if (string.IsNullOrEmpty(storyboardFilename))
|
||||
storyboard = decoder.Decode(stream);
|
||||
else
|
||||
{
|
||||
using (var secondaryStream = new LineBufferedReader(GetStream(BeatmapSetInfo.GetPathForFile(BeatmapSetInfo.StoryboardFile))))
|
||||
using (var secondaryStream = new LineBufferedReader(GetStream(BeatmapSetInfo.GetPathForFile(storyboardFilename))))
|
||||
storyboard = decoder.Decode(stream, secondaryStream);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace osu.Game.Database
|
||||
{
|
||||
public interface IHasOnlineID
|
||||
{
|
||||
/// <summary>
|
||||
/// The server-side ID representing this instance, if one exists.
|
||||
/// </summary>
|
||||
int? OnlineID { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Game.IO;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace osu.Game.Database
|
||||
{
|
||||
/// <summary>
|
||||
/// A usage of a file, with a local filename attached.
|
||||
/// </summary>
|
||||
public interface INamedFileUsage
|
||||
{
|
||||
/// <summary>
|
||||
/// The underlying file on disk.
|
||||
/// </summary>
|
||||
IFileInfo File { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The filename for this usage.
|
||||
/// </summary>
|
||||
string Filename { get; }
|
||||
}
|
||||
}
|
||||
@@ -115,7 +115,7 @@ namespace osu.Game.Database
|
||||
createNewTask();
|
||||
}
|
||||
|
||||
List<User> foundUsers = request.Result?.Users;
|
||||
List<User> foundUsers = request.Response?.Users;
|
||||
|
||||
if (foundUsers != null)
|
||||
{
|
||||
|
||||
@@ -6,7 +6,7 @@ using osu.Game.Database;
|
||||
|
||||
namespace osu.Game.IO
|
||||
{
|
||||
public class FileInfo : IHasPrimaryKey
|
||||
public class FileInfo : IHasPrimaryKey, IFileInfo
|
||||
{
|
||||
public int ID { get; set; }
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace osu.Game.IO
|
||||
{
|
||||
/// <summary>
|
||||
/// A representation of a tracked file.
|
||||
/// </summary>
|
||||
public interface IFileInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// SHA-256 hash of the file content.
|
||||
/// </summary>
|
||||
string Hash { get; }
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using Newtonsoft.Json;
|
||||
using osu.Framework.IO.Network;
|
||||
using osu.Framework.Logging;
|
||||
@@ -17,7 +18,11 @@ namespace osu.Game.Online.API
|
||||
{
|
||||
protected override WebRequest CreateWebRequest() => new OsuJsonWebRequest<T>(Uri);
|
||||
|
||||
public T Result { get; private set; }
|
||||
/// <summary>
|
||||
/// The deserialised response object. May be null if the request or deserialisation failed.
|
||||
/// </summary>
|
||||
[CanBeNull]
|
||||
public T Response { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Invoked on successful completion of an API request.
|
||||
@@ -27,21 +32,21 @@ namespace osu.Game.Online.API
|
||||
|
||||
protected APIRequest()
|
||||
{
|
||||
base.Success += () => Success?.Invoke(Result);
|
||||
base.Success += () => Success?.Invoke(Response);
|
||||
}
|
||||
|
||||
protected override void PostProcess()
|
||||
{
|
||||
base.PostProcess();
|
||||
Result = ((OsuJsonWebRequest<T>)WebRequest)?.ResponseObject;
|
||||
Response = ((OsuJsonWebRequest<T>)WebRequest)?.ResponseObject;
|
||||
}
|
||||
|
||||
internal void TriggerSuccess(T result)
|
||||
{
|
||||
if (Result != null)
|
||||
if (Response != null)
|
||||
throw new InvalidOperationException("Attempted to trigger success more than once");
|
||||
|
||||
Result = result;
|
||||
Response = result;
|
||||
|
||||
TriggerSuccess();
|
||||
}
|
||||
|
||||
@@ -255,6 +255,7 @@ namespace osu.Game.Online.Leaderboards
|
||||
}
|
||||
|
||||
private APIRequest getScoresRequest;
|
||||
private ScheduledDelegate getScoresRequestCallback;
|
||||
|
||||
protected abstract bool IsOnlineScope { get; }
|
||||
|
||||
@@ -282,13 +283,16 @@ namespace osu.Game.Online.Leaderboards
|
||||
getScoresRequest?.Cancel();
|
||||
getScoresRequest = null;
|
||||
|
||||
getScoresRequestCallback?.Cancel();
|
||||
getScoresRequestCallback = null;
|
||||
|
||||
pendingUpdateScores?.Cancel();
|
||||
pendingUpdateScores = Schedule(() =>
|
||||
{
|
||||
PlaceholderState = PlaceholderState.Retrieving;
|
||||
loading.Show();
|
||||
|
||||
getScoresRequest = FetchScores(scores => Schedule(() =>
|
||||
getScoresRequest = FetchScores(scores => getScoresRequestCallback = Schedule(() =>
|
||||
{
|
||||
Scores = scores.ToArray();
|
||||
PlaceholderState = Scores.Any() ? PlaceholderState.Successful : PlaceholderState.NoScores;
|
||||
@@ -297,7 +301,7 @@ namespace osu.Game.Online.Leaderboards
|
||||
if (getScoresRequest == null)
|
||||
return;
|
||||
|
||||
getScoresRequest.Failure += e => Schedule(() =>
|
||||
getScoresRequest.Failure += e => getScoresRequestCallback = Schedule(() =>
|
||||
{
|
||||
if (e is OperationCanceledException)
|
||||
return;
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace osu.Game.Overlays.Music
|
||||
{
|
||||
Padding = new MarginPadding { Left = 5 };
|
||||
|
||||
FilterTerms = item.Metadata.SearchableTerms;
|
||||
FilterTerms = item.Metadata.GetSearchableTerms();
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
|
||||
@@ -143,19 +143,27 @@ namespace osu.Game.Overlays
|
||||
switch (request)
|
||||
{
|
||||
case GetUserRankingsRequest userRequest:
|
||||
if (userRequest.Response == null)
|
||||
return null;
|
||||
|
||||
switch (userRequest.Type)
|
||||
{
|
||||
case UserRankingsType.Performance:
|
||||
return new PerformanceTable(1, userRequest.Result.Users);
|
||||
return new PerformanceTable(1, userRequest.Response.Users);
|
||||
|
||||
case UserRankingsType.Score:
|
||||
return new ScoresTable(1, userRequest.Result.Users);
|
||||
return new ScoresTable(1, userRequest.Response.Users);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
case GetCountryRankingsRequest countryRequest:
|
||||
return new CountriesTable(1, countryRequest.Result.Countries);
|
||||
{
|
||||
if (countryRequest.Response == null)
|
||||
return null;
|
||||
|
||||
return new CountriesTable(1, countryRequest.Response.Countries);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -110,10 +110,11 @@ namespace osu.Game.Rulesets.Difficulty
|
||||
private void preProcess(Mod[] mods)
|
||||
{
|
||||
playableMods = mods.Select(m => m.DeepClone()).ToArray();
|
||||
Beatmap = beatmap.GetPlayableBeatmap(ruleset.RulesetInfo, mods);
|
||||
|
||||
Beatmap = beatmap.GetPlayableBeatmap(ruleset.RulesetInfo, playableMods);
|
||||
|
||||
var track = new TrackVirtual(10000);
|
||||
mods.OfType<IApplicableToTrack>().ForEach(m => m.ApplyToTrack(track));
|
||||
playableMods.OfType<IApplicableToTrack>().ForEach(m => m.ApplyToTrack(track));
|
||||
clockRate = track.Rate;
|
||||
}
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ namespace osu.Game.Rulesets.Edit
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes <see cref="Objects.HitObject.ApplyDefaults(ControlPointInfo,BeatmapDifficulty, CancellationToken)"/>,
|
||||
/// Invokes <see cref="Objects.HitObject.ApplyDefaults(ControlPointInfo,IBeatmapDifficultyInfo,CancellationToken)"/>,
|
||||
/// refreshing <see cref="Objects.HitObject.NestedHitObjects"/> and parameters for the <see cref="HitObject"/>.
|
||||
/// </summary>
|
||||
protected void ApplyDefaultsToHitObject() => HitObject.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty);
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using osu.Game.Database;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace osu.Game.Rulesets
|
||||
{
|
||||
/// <summary>
|
||||
/// A representation of a ruleset's metadata.
|
||||
/// </summary>
|
||||
public interface IRulesetInfo : IHasOnlineID
|
||||
{
|
||||
/// <summary>
|
||||
/// The user-exposed name of this ruleset.
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// An acronym defined by the ruleset that can be used as a permanent identifier.
|
||||
/// </summary>
|
||||
string ShortName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// A string representation of this ruleset, to be used with reflection to instantiate the ruleset represented by this metadata.
|
||||
/// </summary>
|
||||
string InstantiationInfo { get; }
|
||||
|
||||
Ruleset? CreateInstance()
|
||||
{
|
||||
var type = Type.GetType(InstantiationInfo);
|
||||
|
||||
if (type == null)
|
||||
return null;
|
||||
|
||||
var ruleset = Activator.CreateInstance(type) as Ruleset;
|
||||
|
||||
// overwrite the pre-populated RulesetInfo with a potentially database attached copy.
|
||||
// TODO: figure if we still want/need this after switching to realm.
|
||||
// ruleset.RulesetInfo = this;
|
||||
|
||||
return ruleset;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -79,7 +79,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
}
|
||||
}
|
||||
|
||||
public void ReadFromDifficulty(BeatmapDifficulty difficulty)
|
||||
public void ReadFromDifficulty(IBeatmapDifficultyInfo difficulty)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
public override string Description => "Everything just got a bit harder...";
|
||||
public override Type[] IncompatibleMods => new[] { typeof(ModEasy), typeof(ModDifficultyAdjust) };
|
||||
|
||||
public void ReadFromDifficulty(BeatmapDifficulty difficulty)
|
||||
public void ReadFromDifficulty(IBeatmapDifficultyInfo difficulty)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Objects
|
||||
/// <param name="controlPointInfo">The control points.</param>
|
||||
/// <param name="difficulty">The difficulty settings to use.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
public void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty, CancellationToken cancellationToken = default)
|
||||
public void ApplyDefaults(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty, CancellationToken cancellationToken = default)
|
||||
{
|
||||
ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
|
||||
@@ -142,7 +142,7 @@ namespace osu.Game.Rulesets.Objects
|
||||
DefaultsApplied?.Invoke(this);
|
||||
}
|
||||
|
||||
protected virtual void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
protected virtual void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty)
|
||||
{
|
||||
Kiai = controlPointInfo.EffectPointAt(StartTime + control_point_leniency).KiaiMode;
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
|
||||
public double Velocity = 1;
|
||||
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty)
|
||||
{
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ using osu.Framework.Testing;
|
||||
namespace osu.Game.Rulesets
|
||||
{
|
||||
[ExcludeFromDynamicCompile]
|
||||
public class RulesetInfo : IEquatable<RulesetInfo>
|
||||
public class RulesetInfo : IEquatable<RulesetInfo>, IRulesetInfo
|
||||
{
|
||||
public int? ID { get; set; }
|
||||
|
||||
@@ -54,5 +54,11 @@ namespace osu.Game.Rulesets
|
||||
}
|
||||
|
||||
public override string ToString() => Name ?? $"{Name} ({ShortName}) ID: {ID}";
|
||||
|
||||
#region Implementation of IHasOnlineID
|
||||
|
||||
public int? OnlineID => ID;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ namespace osu.Game.Rulesets.Scoring
|
||||
.First()
|
||||
)));
|
||||
|
||||
targetMinimumHealth = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, min_health_target, mid_health_target, max_health_target);
|
||||
targetMinimumHealth = IBeatmapDifficultyInfo.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, min_health_target, mid_health_target, max_health_target);
|
||||
|
||||
// Add back a portion of the amount of HP to be drained, depending on the lenience requested.
|
||||
targetMinimumHealth += drainLenience * (1 - targetMinimumHealth);
|
||||
|
||||
@@ -86,7 +86,7 @@ namespace osu.Game.Rulesets.Scoring
|
||||
{
|
||||
foreach (var range in GetRanges())
|
||||
{
|
||||
var value = BeatmapDifficulty.DifficultyRange(difficulty, (range.Min, range.Average, range.Max));
|
||||
var value = IBeatmapDifficultyInfo.DifficultyRange(difficulty, (range.Min, range.Average, range.Max));
|
||||
|
||||
switch (range.Result)
|
||||
{
|
||||
|
||||
@@ -89,6 +89,13 @@ namespace osu.Game.Screens.Edit.Setup
|
||||
{
|
||||
public Action OnFocused;
|
||||
|
||||
protected override bool OnDragStart(DragStartEvent e)
|
||||
{
|
||||
// This text box is intended to be "read only" without actually specifying that.
|
||||
// As such we don't want to allow the user to select its content with a drag.
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override void OnFocus(FocusEvent e)
|
||||
{
|
||||
OnFocused?.Invoke();
|
||||
|
||||
@@ -66,7 +66,7 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
||||
|
||||
req.Failure += exception =>
|
||||
{
|
||||
onError?.Invoke(req.Result?.Error ?? exception.Message);
|
||||
onError?.Invoke(req.Response?.Error ?? exception.Message);
|
||||
};
|
||||
|
||||
api.Queue(req);
|
||||
|
||||
@@ -108,7 +108,7 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
difficultyIconContainer.Child = new DifficultyIcon(beatmap.Value, ruleset.Value, requiredMods) { Size = new Vector2(32) };
|
||||
|
||||
beatmapText.Clear();
|
||||
beatmapText.AddLink(Item.Beatmap.Value.ToRomanisableString(), LinkAction.OpenBeatmap, Item.Beatmap.Value.OnlineBeatmapID.ToString(), null, text =>
|
||||
beatmapText.AddLink(Item.Beatmap.Value.GetDisplayTitleRomanisable(), LinkAction.OpenBeatmap, Item.Beatmap.Value.OnlineBeatmapID.ToString(), null, text =>
|
||||
{
|
||||
text.Truncate = true;
|
||||
text.RelativeSizeAxes = Axes.X;
|
||||
|
||||
@@ -379,7 +379,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
||||
if (item.NewValue?.Beatmap.Value != null)
|
||||
{
|
||||
statusText.Text = "Currently playing ";
|
||||
beatmapText.AddLink(item.NewValue.Beatmap.Value.ToRomanisableString(),
|
||||
beatmapText.AddLink(item.NewValue.Beatmap.Value.GetDisplayTitleRomanisable(),
|
||||
LinkAction.OpenBeatmap,
|
||||
item.NewValue.Beatmap.Value.OnlineBeatmapID.ToString(),
|
||||
creationParameters: s =>
|
||||
|
||||
@@ -60,6 +60,8 @@ namespace osu.Game.Screens.Play.HUD
|
||||
Current.Value = DisplayedCount = 0;
|
||||
}
|
||||
|
||||
private Mod[] clonedMods;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours, BeatmapDifficultyCache difficultyCache)
|
||||
{
|
||||
@@ -67,8 +69,10 @@ namespace osu.Game.Screens.Play.HUD
|
||||
|
||||
if (gameplayState != null)
|
||||
{
|
||||
clonedMods = gameplayState.Mods.Select(m => m.DeepClone()).ToArray();
|
||||
|
||||
var gameplayWorkingBeatmap = new GameplayWorkingBeatmap(gameplayState.Beatmap);
|
||||
difficultyCache.GetTimedDifficultyAttributesAsync(gameplayWorkingBeatmap, gameplayState.Ruleset, gameplayState.Mods.ToArray(), loadCancellationSource.Token)
|
||||
difficultyCache.GetTimedDifficultyAttributesAsync(gameplayWorkingBeatmap, gameplayState.Ruleset, clonedMods, loadCancellationSource.Token)
|
||||
.ContinueWith(r => Schedule(() =>
|
||||
{
|
||||
timedAttributes = r.Result;
|
||||
@@ -116,7 +120,11 @@ namespace osu.Game.Screens.Play.HUD
|
||||
return;
|
||||
}
|
||||
|
||||
var calculator = gameplayState.Ruleset.CreatePerformanceCalculator(attrib, gameplayState.Score.ScoreInfo);
|
||||
// awkward but we need to make sure the true mods are not passed to PerformanceCalculator as it makes a mess of track applications.
|
||||
var scoreInfo = gameplayState.Score.ScoreInfo.DeepClone();
|
||||
scoreInfo.Mods = clonedMods;
|
||||
|
||||
var calculator = gameplayState.Ruleset.CreatePerformanceCalculator(attrib, scoreInfo);
|
||||
|
||||
Current.Value = (int)Math.Round(calculator?.Calculate() ?? 0, MidpointRounding.AwayFromZero);
|
||||
IsValid = true;
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
|
||||
if (match)
|
||||
{
|
||||
var terms = BeatmapInfo.SearchableTerms;
|
||||
var terms = BeatmapInfo.GetSearchableTerms();
|
||||
|
||||
foreach (var criteriaTerm in criteria.SearchTerms)
|
||||
match &= terms.Any(term => term.Contains(criteriaTerm, StringComparison.InvariantCultureIgnoreCase));
|
||||
|
||||
@@ -106,12 +106,12 @@ namespace osu.Game.Screens.Select.Details
|
||||
|
||||
private void updateStatistics()
|
||||
{
|
||||
BeatmapDifficulty baseDifficulty = BeatmapInfo?.BaseDifficulty;
|
||||
IBeatmapDifficultyInfo baseDifficulty = BeatmapInfo?.BaseDifficulty;
|
||||
BeatmapDifficulty adjustedDifficulty = null;
|
||||
|
||||
if (baseDifficulty != null && mods.Value.Any(m => m is IApplicableToDifficulty))
|
||||
{
|
||||
adjustedDifficulty = baseDifficulty.Clone();
|
||||
adjustedDifficulty = new BeatmapDifficulty(baseDifficulty);
|
||||
|
||||
foreach (var mod in mods.Value.OfType<IApplicableToDifficulty>())
|
||||
mod.ApplyToDifficulty(adjustedDifficulty);
|
||||
|
||||
@@ -83,8 +83,17 @@ namespace osu.Game.Tests.Visual
|
||||
protected void PushAndConfirm(Func<Screen> newScreen)
|
||||
{
|
||||
Screen screen = null;
|
||||
AddStep("Push new screen", () => Game.ScreenStack.Push(screen = newScreen()));
|
||||
AddUntilStep("Wait for new screen", () => Game.ScreenStack.CurrentScreen == screen && screen.IsLoaded);
|
||||
IScreen previousScreen = null;
|
||||
|
||||
AddStep("Push new screen", () =>
|
||||
{
|
||||
previousScreen = Game.ScreenStack.CurrentScreen;
|
||||
Game.ScreenStack.Push(screen = newScreen());
|
||||
});
|
||||
|
||||
AddUntilStep("Wait for new screen", () => screen.IsLoaded
|
||||
&& Game.ScreenStack.CurrentScreen != previousScreen
|
||||
&& previousScreen.GetChildScreen() == screen);
|
||||
}
|
||||
|
||||
protected void ConfirmAtMainMenu() => AddUntilStep("Wait for main menu", () => Game.ScreenStack.CurrentScreen is MainMenu menu && menu.IsLoaded);
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Screens;
|
||||
@@ -32,6 +33,9 @@ namespace osu.Game.Tests.Visual
|
||||
content = new Container { RelativeSizeAxes = Axes.Both },
|
||||
DialogOverlay = new DialogOverlay()
|
||||
});
|
||||
|
||||
Stack.ScreenPushed += (lastScreen, newScreen) => Logger.Log($"{nameof(ScreenTestScene)} screen changed → {newScreen}");
|
||||
Stack.ScreenExited += (lastScreen, newScreen) => Logger.Log($"{nameof(ScreenTestScene)} screen changed ← {newScreen}");
|
||||
}
|
||||
|
||||
protected void LoadScreen(OsuScreen screen) => Stack.Push(screen);
|
||||
|
||||
Reference in New Issue
Block a user