mirror of
https://github.com/osukey/osukey.git
synced 2025-05-30 09:57:21 +09:00
Merge branch 'master' into multi-queueing-modes
This commit is contained in:
commit
35a5182ebf
2
.github/dependabot.yml
vendored
2
.github/dependabot.yml
vendored
@ -5,7 +5,7 @@ updates:
|
|||||||
schedule:
|
schedule:
|
||||||
interval: monthly
|
interval: monthly
|
||||||
time: "17:00"
|
time: "17:00"
|
||||||
open-pull-requests-limit: 99
|
open-pull-requests-limit: 0 # disabled until https://github.com/dependabot/dependabot-core/issues/369 is resolved.
|
||||||
ignore:
|
ignore:
|
||||||
- dependency-name: Microsoft.EntityFrameworkCore.Design
|
- dependency-name: Microsoft.EntityFrameworkCore.Design
|
||||||
versions:
|
versions:
|
||||||
|
@ -0,0 +1,91 @@
|
|||||||
|
// 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.Framework.Utils;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Edit;
|
||||||
|
using osu.Game.Rulesets.Taiko.Beatmaps;
|
||||||
|
using osu.Game.Screens.Edit;
|
||||||
|
using osu.Game.Screens.Edit.Setup;
|
||||||
|
using osu.Game.Screens.Menu;
|
||||||
|
using osu.Game.Screens.Select;
|
||||||
|
using osu.Game.Tests.Visual;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Taiko.Tests.Editor
|
||||||
|
{
|
||||||
|
public class TestSceneEditorSaving : OsuGameTestScene
|
||||||
|
{
|
||||||
|
private Screens.Edit.Editor editor => Game.ChildrenOfType<Screens.Edit.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.
|
||||||
|
/// Emphasis is placed on <see cref="BeatmapDifficulty.SliderMultiplier"/>, since taiko has special handling for it to keep compatibility with stable.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestNewBeatmapSaveThenLoad()
|
||||||
|
{
|
||||||
|
AddStep("set default beatmap", () => Game.Beatmap.SetDefault());
|
||||||
|
AddStep("set taiko ruleset", () => Ruleset.Value = new TaikoRuleset().RulesetInfo);
|
||||||
|
|
||||||
|
PushAndConfirm(() => new EditorLoader());
|
||||||
|
|
||||||
|
AddUntilStep("wait for editor load", () => editor?.IsLoaded == true);
|
||||||
|
|
||||||
|
AddUntilStep("wait for metadata screen load", () => editor.ChildrenOfType<MetadataSection>().FirstOrDefault()?.IsLoaded == true);
|
||||||
|
|
||||||
|
// We intentionally switch away from the metadata screen, else there is a feedback loop with the textbox handling which causes metadata changes below to get overwritten.
|
||||||
|
|
||||||
|
AddStep("Enter compose mode", () => InputManager.Key(Key.F1));
|
||||||
|
AddUntilStep("Wait for compose mode load", () => editor.ChildrenOfType<HitObjectComposer>().FirstOrDefault()?.IsLoaded == true);
|
||||||
|
|
||||||
|
AddStep("Set slider multiplier", () => editorBeatmap.Difficulty.SliderMultiplier = 2);
|
||||||
|
AddStep("Set artist and title", () =>
|
||||||
|
{
|
||||||
|
editorBeatmap.BeatmapInfo.Metadata.Artist = "artist";
|
||||||
|
editorBeatmap.BeatmapInfo.Metadata.Title = "title";
|
||||||
|
});
|
||||||
|
AddStep("Set difficulty name", () => editorBeatmap.BeatmapInfo.Version = "difficulty");
|
||||||
|
|
||||||
|
checkMutations();
|
||||||
|
|
||||||
|
AddStep("Save", () => InputManager.Keys(PlatformAction.Save));
|
||||||
|
|
||||||
|
checkMutations();
|
||||||
|
|
||||||
|
AddStep("Exit", () => 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);
|
||||||
|
|
||||||
|
checkMutations();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkMutations()
|
||||||
|
{
|
||||||
|
AddAssert("Beatmap has correct slider multiplier", () =>
|
||||||
|
{
|
||||||
|
// we can only assert value correctness on TaikoMultiplierAppliedDifficulty, because that is the final difficulty converted taiko beatmaps use.
|
||||||
|
// therefore, ensure that we have that difficulty type by calling .CopyFrom(), which is a no-op if the type is already correct.
|
||||||
|
var taikoDifficulty = new TaikoBeatmapConverter.TaikoMultiplierAppliedDifficulty();
|
||||||
|
taikoDifficulty.CopyFrom(editorBeatmap.Difficulty);
|
||||||
|
return Precision.AlmostEquals(taikoDifficulty.SliderMultiplier, 2);
|
||||||
|
});
|
||||||
|
AddAssert("Beatmap has correct metadata", () => editorBeatmap.BeatmapInfo.Metadata.Artist == "artist" && editorBeatmap.BeatmapInfo.Metadata.Title == "title");
|
||||||
|
AddAssert("Beatmap has correct difficulty name", () => editorBeatmap.BeatmapInfo.Version == "difficulty");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -191,7 +191,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
|||||||
|
|
||||||
protected override Beatmap<TaikoHitObject> CreateBeatmap() => new TaikoBeatmap();
|
protected override Beatmap<TaikoHitObject> CreateBeatmap() => new TaikoBeatmap();
|
||||||
|
|
||||||
private class TaikoMultiplierAppliedDifficulty : BeatmapDifficulty
|
internal class TaikoMultiplierAppliedDifficulty : BeatmapDifficulty
|
||||||
{
|
{
|
||||||
public TaikoMultiplierAppliedDifficulty(IBeatmapDifficultyInfo difficulty)
|
public TaikoMultiplierAppliedDifficulty(IBeatmapDifficultyInfo difficulty)
|
||||||
{
|
{
|
||||||
@ -209,7 +209,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
|||||||
{
|
{
|
||||||
base.CopyTo(other);
|
base.CopyTo(other);
|
||||||
if (!(other is TaikoMultiplierAppliedDifficulty))
|
if (!(other is TaikoMultiplierAppliedDifficulty))
|
||||||
SliderMultiplier /= LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER;
|
other.SliderMultiplier /= LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void CopyFrom(IBeatmapDifficultyInfo other)
|
public override void CopyFrom(IBeatmapDifficultyInfo other)
|
||||||
|
@ -9,8 +9,11 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.Drawables.Cards;
|
using osu.Game.Beatmaps.Drawables.Cards;
|
||||||
|
using osu.Game.Online.API;
|
||||||
|
using osu.Game.Online.API.Requests;
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -20,6 +23,8 @@ namespace osu.Game.Tests.Visual.Beatmaps
|
|||||||
{
|
{
|
||||||
public class TestSceneBeatmapCard : OsuTestScene
|
public class TestSceneBeatmapCard : OsuTestScene
|
||||||
{
|
{
|
||||||
|
private DummyAPIAccess dummyAPI => (DummyAPIAccess)API;
|
||||||
|
|
||||||
private APIBeatmapSet[] testCases;
|
private APIBeatmapSet[] testCases;
|
||||||
|
|
||||||
#region Test case generation
|
#region Test case generation
|
||||||
@ -164,6 +169,19 @@ namespace osu.Game.Tests.Visual.Beatmaps
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
[SetUpSteps]
|
||||||
|
public void SetUpSteps()
|
||||||
|
{
|
||||||
|
AddStep("register request handling", () => dummyAPI.HandleRequest = request =>
|
||||||
|
{
|
||||||
|
if (!(request is PostBeatmapFavouriteRequest))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
request.TriggerSuccess();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private Drawable createContent(OverlayColourScheme colourScheme, Func<APIBeatmapSet, Drawable> creationFunc)
|
private Drawable createContent(OverlayColourScheme colourScheme, Func<APIBeatmapSet, Drawable> creationFunc)
|
||||||
{
|
{
|
||||||
var colourProvider = new OverlayColourProvider(colourScheme);
|
var colourProvider = new OverlayColourProvider(colourScheme);
|
||||||
|
@ -0,0 +1,86 @@
|
|||||||
|
// 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.Allocation;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Beatmaps.Drawables.Cards.Buttons;
|
||||||
|
using osu.Game.Online.API;
|
||||||
|
using osu.Game.Online.API.Requests;
|
||||||
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Resources.Localisation.Web;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Beatmaps
|
||||||
|
{
|
||||||
|
public class TestSceneBeatmapCardFavouriteButton : OsuManualInputManagerTestScene
|
||||||
|
{
|
||||||
|
private DummyAPIAccess dummyAPI => (DummyAPIAccess)API;
|
||||||
|
|
||||||
|
[Cached]
|
||||||
|
private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestInitialState([Values] bool favourited)
|
||||||
|
{
|
||||||
|
APIBeatmapSet beatmapSetInfo = null;
|
||||||
|
FavouriteButton button = null;
|
||||||
|
|
||||||
|
AddStep("create beatmap set", () =>
|
||||||
|
{
|
||||||
|
beatmapSetInfo = CreateAPIBeatmapSet(Ruleset.Value);
|
||||||
|
beatmapSetInfo.HasFavourited = favourited;
|
||||||
|
});
|
||||||
|
AddStep("create button", () => Child = button = new FavouriteButton(beatmapSetInfo) { Scale = new Vector2(2) });
|
||||||
|
|
||||||
|
assertCorrectIcon(favourited);
|
||||||
|
AddAssert("correct tooltip text", () => button.TooltipText == (favourited ? BeatmapsetsStrings.ShowDetailsUnfavourite : BeatmapsetsStrings.ShowDetailsFavourite));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestRequestHandling()
|
||||||
|
{
|
||||||
|
APIBeatmapSet beatmapSetInfo = null;
|
||||||
|
FavouriteButton button = null;
|
||||||
|
BeatmapFavouriteAction? lastRequestAction = null;
|
||||||
|
|
||||||
|
AddStep("create beatmap set", () => beatmapSetInfo = CreateAPIBeatmapSet(Ruleset.Value));
|
||||||
|
AddStep("create button", () => Child = button = new FavouriteButton(beatmapSetInfo) { Scale = new Vector2(2) });
|
||||||
|
|
||||||
|
assertCorrectIcon(false);
|
||||||
|
|
||||||
|
AddStep("register request handling", () => dummyAPI.HandleRequest = request =>
|
||||||
|
{
|
||||||
|
if (!(request is PostBeatmapFavouriteRequest favouriteRequest))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
lastRequestAction = favouriteRequest.Action;
|
||||||
|
request.TriggerSuccess();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("click icon", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(button);
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
});
|
||||||
|
AddUntilStep("favourite request sent", () => lastRequestAction == BeatmapFavouriteAction.Favourite);
|
||||||
|
assertCorrectIcon(true);
|
||||||
|
|
||||||
|
AddStep("click icon", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(button);
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
});
|
||||||
|
AddUntilStep("unfavourite request sent", () => lastRequestAction == BeatmapFavouriteAction.UnFavourite);
|
||||||
|
assertCorrectIcon(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertCorrectIcon(bool favourited) => AddAssert("icon correct",
|
||||||
|
() => this.ChildrenOfType<SpriteIcon>().Single().Icon.Equals(favourited ? FontAwesome.Solid.Heart : FontAwesome.Regular.Heart));
|
||||||
|
}
|
||||||
|
}
|
@ -13,10 +13,17 @@ namespace osu.Game.Tests.Visual.Components
|
|||||||
{
|
{
|
||||||
public class TestScenePreviewTrackManager : OsuTestScene, IPreviewTrackOwner
|
public class TestScenePreviewTrackManager : OsuTestScene, IPreviewTrackOwner
|
||||||
{
|
{
|
||||||
private readonly TestPreviewTrackManager trackManager = new TestPreviewTrackManager();
|
private readonly IAdjustableAudioComponent gameTrackAudio = new AudioAdjustments();
|
||||||
|
|
||||||
|
private readonly TestPreviewTrackManager trackManager;
|
||||||
|
|
||||||
private AudioManager audio;
|
private AudioManager audio;
|
||||||
|
|
||||||
|
public TestScenePreviewTrackManager()
|
||||||
|
{
|
||||||
|
trackManager = new TestPreviewTrackManager(gameTrackAudio);
|
||||||
|
}
|
||||||
|
|
||||||
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
||||||
{
|
{
|
||||||
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
|
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
|
||||||
@ -151,19 +158,19 @@ namespace osu.Game.Tests.Visual.Components
|
|||||||
audio.VolumeTrack.Value = 1;
|
audio.VolumeTrack.Value = 1;
|
||||||
});
|
});
|
||||||
|
|
||||||
AddAssert("game not muted", () => audio.Tracks.AggregateVolume.Value != 0);
|
AddAssert("game not muted", () => gameTrackAudio.AggregateVolume.Value != 0);
|
||||||
|
|
||||||
AddStep("get track", () => Add(owner = new TestTrackOwner(track = getTrack())));
|
AddStep("get track", () => Add(owner = new TestTrackOwner(track = getTrack())));
|
||||||
AddUntilStep("wait loaded", () => track.IsLoaded);
|
AddUntilStep("wait loaded", () => track.IsLoaded);
|
||||||
AddStep("start track", () => track.Start());
|
AddStep("start track", () => track.Start());
|
||||||
AddAssert("game is muted", () => audio.Tracks.AggregateVolume.Value == 0);
|
AddAssert("game is muted", () => gameTrackAudio.AggregateVolume.Value == 0);
|
||||||
|
|
||||||
if (stopAnyPlaying)
|
if (stopAnyPlaying)
|
||||||
AddStep("stop any playing", () => trackManager.StopAnyPlaying(owner));
|
AddStep("stop any playing", () => trackManager.StopAnyPlaying(owner));
|
||||||
else
|
else
|
||||||
AddStep("stop track", () => track.Stop());
|
AddStep("stop track", () => track.Stop());
|
||||||
|
|
||||||
AddAssert("game not muted", () => audio.Tracks.AggregateVolume.Value != 0);
|
AddAssert("game not muted", () => gameTrackAudio.AggregateVolume.Value != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -224,6 +231,11 @@ namespace osu.Game.Tests.Visual.Components
|
|||||||
|
|
||||||
public new PreviewTrack CurrentTrack => base.CurrentTrack;
|
public new PreviewTrack CurrentTrack => base.CurrentTrack;
|
||||||
|
|
||||||
|
public TestPreviewTrackManager(IAdjustableAudioComponent mainTrackAdjustments)
|
||||||
|
: base(mainTrackAdjustments)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
protected override TrackManagerPreviewTrack CreatePreviewTrack(IBeatmapSetInfo beatmapSetInfo, ITrackStore trackStore) => new TestPreviewTrack(beatmapSetInfo, trackStore);
|
protected override TrackManagerPreviewTrack CreatePreviewTrack(IBeatmapSetInfo beatmapSetInfo, ITrackStore trackStore) => new TestPreviewTrack(beatmapSetInfo, trackStore);
|
||||||
|
|
||||||
public override bool UpdateSubTree()
|
public override bool UpdateSubTree()
|
||||||
|
@ -0,0 +1,98 @@
|
|||||||
|
// 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 NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Rulesets.Judgements;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.Scoring;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osu.Game.Screens.Play.HUD;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
|
{
|
||||||
|
public class TestSceneUnstableRateCounter : OsuTestScene
|
||||||
|
{
|
||||||
|
[Cached(typeof(ScoreProcessor))]
|
||||||
|
private TestScoreProcessor scoreProcessor = new TestScoreProcessor();
|
||||||
|
|
||||||
|
private readonly OsuHitWindows hitWindows = new OsuHitWindows();
|
||||||
|
|
||||||
|
private UnstableRateCounter counter;
|
||||||
|
|
||||||
|
private double prev;
|
||||||
|
|
||||||
|
[SetUpSteps]
|
||||||
|
public void SetUp()
|
||||||
|
{
|
||||||
|
AddStep("Reset Score Processor", () => scoreProcessor.Reset());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestBasic()
|
||||||
|
{
|
||||||
|
AddStep("Create Display", recreateDisplay);
|
||||||
|
|
||||||
|
// Needs multiples 2 by the nature of UR, and went for 4 to be safe.
|
||||||
|
// Creates a 250 UR by placing a +25ms then a -25ms judgement, which then results in a 250 UR
|
||||||
|
AddRepeatStep("Set UR to 250", () => applyJudgement(25, true), 4);
|
||||||
|
|
||||||
|
AddUntilStep("UR = 250", () => counter.Current.Value == 250.0);
|
||||||
|
|
||||||
|
AddRepeatStep("Revert UR", () =>
|
||||||
|
{
|
||||||
|
scoreProcessor.RevertResult(
|
||||||
|
new JudgementResult(new HitCircle { HitWindows = hitWindows }, new Judgement())
|
||||||
|
{
|
||||||
|
TimeOffset = 25,
|
||||||
|
Type = HitResult.Perfect,
|
||||||
|
});
|
||||||
|
}, 4);
|
||||||
|
|
||||||
|
AddUntilStep("UR is 0", () => counter.Current.Value == 0.0);
|
||||||
|
AddUntilStep("Counter is invalid", () => counter.Child.Alpha == 0.3f);
|
||||||
|
|
||||||
|
//Sets a UR of 0 by creating 10 10ms offset judgements. Since average = offset, UR = 0
|
||||||
|
AddRepeatStep("Set UR to 0", () => applyJudgement(10, false), 10);
|
||||||
|
//Applies a UR of 100 by creating 10 -10ms offset judgements. At the 10th judgement, offset should be 100.
|
||||||
|
AddRepeatStep("Bring UR to 100", () => applyJudgement(-10, false), 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void recreateDisplay()
|
||||||
|
{
|
||||||
|
Clear();
|
||||||
|
|
||||||
|
Add(counter = new UnstableRateCounter
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Scale = new Vector2(5),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyJudgement(double offsetMs, bool alt)
|
||||||
|
{
|
||||||
|
double placement = offsetMs;
|
||||||
|
|
||||||
|
if (alt)
|
||||||
|
{
|
||||||
|
placement = prev > 0 ? -offsetMs : offsetMs;
|
||||||
|
prev = placement;
|
||||||
|
}
|
||||||
|
|
||||||
|
scoreProcessor.ApplyResult(new JudgementResult(new HitCircle { HitWindows = hitWindows }, new Judgement())
|
||||||
|
{
|
||||||
|
TimeOffset = placement,
|
||||||
|
Type = HitResult.Perfect,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestScoreProcessor : ScoreProcessor
|
||||||
|
{
|
||||||
|
public void Reset() => base.Reset(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +1,8 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
using osu.Framework.Audio.Mixing;
|
|
||||||
using osu.Framework.Audio.Track;
|
using osu.Framework.Audio.Track;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -19,27 +14,26 @@ namespace osu.Game.Audio
|
|||||||
{
|
{
|
||||||
public class PreviewTrackManager : Component
|
public class PreviewTrackManager : Component
|
||||||
{
|
{
|
||||||
|
private readonly IAdjustableAudioComponent mainTrackAdjustments;
|
||||||
|
|
||||||
private readonly BindableDouble muteBindable = new BindableDouble();
|
private readonly BindableDouble muteBindable = new BindableDouble();
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private AudioManager audio { get; set; }
|
private AudioManager audio { get; set; }
|
||||||
|
|
||||||
private PreviewTrackStore trackStore;
|
private ITrackStore trackStore;
|
||||||
|
|
||||||
protected TrackManagerPreviewTrack CurrentTrack;
|
protected TrackManagerPreviewTrack CurrentTrack;
|
||||||
|
|
||||||
private readonly BindableNumber<double> globalTrackVolumeAdjust = new BindableNumber<double>(OsuGameBase.GLOBAL_TRACK_VOLUME_ADJUST);
|
public PreviewTrackManager(IAdjustableAudioComponent mainTrackAdjustments)
|
||||||
|
{
|
||||||
|
this.mainTrackAdjustments = mainTrackAdjustments;
|
||||||
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(AudioManager audioManager)
|
private void load(AudioManager audioManager)
|
||||||
{
|
{
|
||||||
// this is a temporary solution to get around muting ourselves.
|
trackStore = audioManager.GetTrackStore(new OnlineStore());
|
||||||
// todo: update this once we have a BackgroundTrackManager or similar.
|
|
||||||
trackStore = new PreviewTrackStore(audioManager.TrackMixer, new OnlineStore());
|
|
||||||
|
|
||||||
audio.AddItem(trackStore);
|
|
||||||
trackStore.AddAdjustment(AdjustableProperty.Volume, globalTrackVolumeAdjust);
|
|
||||||
trackStore.AddAdjustment(AdjustableProperty.Volume, audio.VolumeTrack);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -55,7 +49,7 @@ namespace osu.Game.Audio
|
|||||||
{
|
{
|
||||||
CurrentTrack?.Stop();
|
CurrentTrack?.Stop();
|
||||||
CurrentTrack = track;
|
CurrentTrack = track;
|
||||||
audio.Tracks.AddAdjustment(AdjustableProperty.Volume, muteBindable);
|
mainTrackAdjustments.AddAdjustment(AdjustableProperty.Volume, muteBindable);
|
||||||
});
|
});
|
||||||
|
|
||||||
track.Stopped += () => Schedule(() =>
|
track.Stopped += () => Schedule(() =>
|
||||||
@ -64,7 +58,7 @@ namespace osu.Game.Audio
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
CurrentTrack = null;
|
CurrentTrack = null;
|
||||||
audio.Tracks.RemoveAdjustment(AdjustableProperty.Volume, muteBindable);
|
mainTrackAdjustments.RemoveAdjustment(AdjustableProperty.Volume, muteBindable);
|
||||||
});
|
});
|
||||||
|
|
||||||
return track;
|
return track;
|
||||||
@ -116,52 +110,5 @@ namespace osu.Game.Audio
|
|||||||
|
|
||||||
protected override Track GetTrack() => trackManager.Get($"https://b.ppy.sh/preview/{beatmapSetInfo.OnlineID}.mp3");
|
protected override Track GetTrack() => trackManager.Get($"https://b.ppy.sh/preview/{beatmapSetInfo.OnlineID}.mp3");
|
||||||
}
|
}
|
||||||
|
|
||||||
private class PreviewTrackStore : AudioCollectionManager<AdjustableAudioComponent>, ITrackStore
|
|
||||||
{
|
|
||||||
private readonly AudioMixer defaultMixer;
|
|
||||||
private readonly IResourceStore<byte[]> store;
|
|
||||||
|
|
||||||
internal PreviewTrackStore(AudioMixer defaultMixer, IResourceStore<byte[]> store)
|
|
||||||
{
|
|
||||||
this.defaultMixer = defaultMixer;
|
|
||||||
this.store = store;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Track GetVirtual(double length = double.PositiveInfinity)
|
|
||||||
{
|
|
||||||
if (IsDisposed) throw new ObjectDisposedException($"Cannot retrieve items for an already disposed {nameof(PreviewTrackStore)}");
|
|
||||||
|
|
||||||
var track = new TrackVirtual(length);
|
|
||||||
AddItem(track);
|
|
||||||
return track;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Track Get(string name)
|
|
||||||
{
|
|
||||||
if (IsDisposed) throw new ObjectDisposedException($"Cannot retrieve items for an already disposed {nameof(PreviewTrackStore)}");
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(name)) return null;
|
|
||||||
|
|
||||||
var dataStream = store.GetStream(name);
|
|
||||||
|
|
||||||
if (dataStream == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
// Todo: This is quite unsafe. TrackBass shouldn't be exposed as public.
|
|
||||||
Track track = new TrackBass(dataStream);
|
|
||||||
|
|
||||||
defaultMixer.Add(track);
|
|
||||||
AddItem(track);
|
|
||||||
|
|
||||||
return track;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<Track> GetAsync(string name) => Task.Run(() => Get(name));
|
|
||||||
|
|
||||||
public Stream GetStream(string name) => store.GetStream(name);
|
|
||||||
|
|
||||||
public IEnumerable<string> GetAvailableResources() => store.GetAvailableResources();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,8 @@ using System.Threading;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
|
using osu.Framework.Audio.Mixing;
|
||||||
|
using osu.Framework.Audio.Track;
|
||||||
using osu.Framework.IO.Stores;
|
using osu.Framework.IO.Stores;
|
||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
@ -30,18 +32,23 @@ namespace osu.Game.Beatmaps
|
|||||||
[ExcludeFromDynamicCompile]
|
[ExcludeFromDynamicCompile]
|
||||||
public class BeatmapManager : IModelDownloader<IBeatmapSetInfo>, IModelManager<BeatmapSetInfo>, IModelFileManager<BeatmapSetInfo, BeatmapSetFileInfo>, IModelImporter<BeatmapSetInfo>, IWorkingBeatmapCache, IDisposable
|
public class BeatmapManager : IModelDownloader<IBeatmapSetInfo>, IModelManager<BeatmapSetInfo>, IModelFileManager<BeatmapSetInfo, BeatmapSetFileInfo>, IModelImporter<BeatmapSetInfo>, IWorkingBeatmapCache, IDisposable
|
||||||
{
|
{
|
||||||
|
public ITrackStore BeatmapTrackStore { get; }
|
||||||
|
|
||||||
private readonly BeatmapModelManager beatmapModelManager;
|
private readonly BeatmapModelManager beatmapModelManager;
|
||||||
private readonly BeatmapModelDownloader beatmapModelDownloader;
|
private readonly BeatmapModelDownloader beatmapModelDownloader;
|
||||||
|
|
||||||
private readonly WorkingBeatmapCache workingBeatmapCache;
|
private readonly WorkingBeatmapCache workingBeatmapCache;
|
||||||
private readonly BeatmapOnlineLookupQueue onlineBeatmapLookupQueue;
|
private readonly BeatmapOnlineLookupQueue onlineBeatmapLookupQueue;
|
||||||
|
|
||||||
public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, IResourceStore<byte[]> resources, GameHost host = null,
|
public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, IResourceStore<byte[]> gameResources, GameHost host = null, WorkingBeatmap defaultBeatmap = null, bool performOnlineLookups = false, AudioMixer mainTrackMixer = null)
|
||||||
WorkingBeatmap defaultBeatmap = null, bool performOnlineLookups = false)
|
|
||||||
{
|
{
|
||||||
|
var userResources = new FileStore(contextFactory, storage).Store;
|
||||||
|
|
||||||
|
BeatmapTrackStore = audioManager.GetTrackStore(userResources);
|
||||||
|
|
||||||
beatmapModelManager = CreateBeatmapModelManager(storage, contextFactory, rulesets, api, host);
|
beatmapModelManager = CreateBeatmapModelManager(storage, contextFactory, rulesets, api, host);
|
||||||
beatmapModelDownloader = CreateBeatmapModelDownloader(beatmapModelManager, api, host);
|
beatmapModelDownloader = CreateBeatmapModelDownloader(beatmapModelManager, api, host);
|
||||||
workingBeatmapCache = CreateWorkingBeatmapCache(audioManager, resources, new FileStore(contextFactory, storage).Store, defaultBeatmap, host);
|
workingBeatmapCache = CreateWorkingBeatmapCache(audioManager, gameResources, userResources, defaultBeatmap, host);
|
||||||
|
|
||||||
workingBeatmapCache.BeatmapManager = beatmapModelManager;
|
workingBeatmapCache.BeatmapManager = beatmapModelManager;
|
||||||
beatmapModelManager.WorkingBeatmapCache = workingBeatmapCache;
|
beatmapModelManager.WorkingBeatmapCache = workingBeatmapCache;
|
||||||
@ -58,8 +65,10 @@ namespace osu.Game.Beatmaps
|
|||||||
return new BeatmapModelDownloader(modelManager, api, host);
|
return new BeatmapModelDownloader(modelManager, api, host);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual WorkingBeatmapCache CreateWorkingBeatmapCache(AudioManager audioManager, IResourceStore<byte[]> resources, IResourceStore<byte[]> storage, WorkingBeatmap defaultBeatmap, GameHost host) =>
|
protected virtual WorkingBeatmapCache CreateWorkingBeatmapCache(AudioManager audioManager, IResourceStore<byte[]> resources, IResourceStore<byte[]> storage, WorkingBeatmap defaultBeatmap, GameHost host)
|
||||||
new WorkingBeatmapCache(audioManager, resources, storage, defaultBeatmap, host);
|
{
|
||||||
|
return new WorkingBeatmapCache(BeatmapTrackStore, audioManager, resources, storage, defaultBeatmap, host);
|
||||||
|
}
|
||||||
|
|
||||||
protected virtual BeatmapModelManager CreateBeatmapModelManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, GameHost host) =>
|
protected virtual BeatmapModelManager CreateBeatmapModelManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, GameHost host) =>
|
||||||
new BeatmapModelManager(storage, contextFactory, rulesets, host);
|
new BeatmapModelManager(storage, contextFactory, rulesets, host);
|
||||||
|
@ -189,7 +189,11 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
// Difficulty settings must be copied first due to the clone in `Beatmap<>.BeatmapInfo_Set`.
|
// Difficulty settings must be copied first due to the clone in `Beatmap<>.BeatmapInfo_Set`.
|
||||||
// This should hopefully be temporary, assuming said clone is eventually removed.
|
// This should hopefully be temporary, assuming said clone is eventually removed.
|
||||||
beatmapInfo.BaseDifficulty.CopyFrom(beatmapContent.Difficulty);
|
|
||||||
|
// Warning: The directionality here is important. Changes have to be copied *from* beatmapContent (which comes from editor and is being saved)
|
||||||
|
// *to* the beatmapInfo (which is a database model and needs to receive values without the taiko slider velocity multiplier for correct operation).
|
||||||
|
// CopyTo() will undo such adjustments, while CopyFrom() will not.
|
||||||
|
beatmapContent.Difficulty.CopyTo(beatmapInfo.BaseDifficulty);
|
||||||
|
|
||||||
// All changes to metadata are made in the provided beatmapInfo, so this should be copied to the `IBeatmap` before encoding.
|
// All changes to metadata are made in the provided beatmapInfo, so this should be copied to the `IBeatmap` before encoding.
|
||||||
beatmapContent.BeatmapInfo = beatmapInfo;
|
beatmapContent.BeatmapInfo = beatmapInfo;
|
||||||
|
@ -3,12 +3,14 @@
|
|||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
|
using osu.Game.Beatmaps.Drawables.Cards.Buttons;
|
||||||
using osu.Game.Beatmaps.Drawables.Cards.Statistics;
|
using osu.Game.Beatmaps.Drawables.Cards.Statistics;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
@ -21,6 +23,7 @@ using osuTK;
|
|||||||
using osu.Game.Overlays.BeatmapListing.Panels;
|
using osu.Game.Overlays.BeatmapListing.Panels;
|
||||||
using osu.Game.Resources.Localisation.Web;
|
using osu.Game.Resources.Localisation.Web;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
using DownloadButton = osu.Game.Beatmaps.Drawables.Cards.Buttons.DownloadButton;
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps.Drawables.Cards
|
namespace osu.Game.Beatmaps.Drawables.Cards
|
||||||
{
|
{
|
||||||
@ -33,9 +36,12 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
private const float corner_radius = 10;
|
private const float corner_radius = 10;
|
||||||
|
|
||||||
private readonly APIBeatmapSet beatmapSet;
|
private readonly APIBeatmapSet beatmapSet;
|
||||||
|
private readonly Bindable<BeatmapSetFavouriteState> favouriteState;
|
||||||
|
|
||||||
private UpdateableOnlineBeatmapSetCover leftCover;
|
private UpdateableOnlineBeatmapSetCover leftCover;
|
||||||
private FillFlowContainer iconArea;
|
private FillFlowContainer leftIconArea;
|
||||||
|
|
||||||
|
private Container rightButtonArea;
|
||||||
|
|
||||||
private Container mainContent;
|
private Container mainContent;
|
||||||
private BeatmapCardContentBackground mainContentBackground;
|
private BeatmapCardContentBackground mainContentBackground;
|
||||||
@ -51,6 +57,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
: base(HoverSampleSet.Submit)
|
: base(HoverSampleSet.Submit)
|
||||||
{
|
{
|
||||||
this.beatmapSet = beatmapSet;
|
this.beatmapSet = beatmapSet;
|
||||||
|
favouriteState = new Bindable<BeatmapSetFavouriteState>(new BeatmapSetFavouriteState(beatmapSet.HasFavourited, beatmapSet.FavouriteCount));
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
@ -79,7 +86,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
OnlineInfo = beatmapSet
|
OnlineInfo = beatmapSet
|
||||||
},
|
},
|
||||||
iconArea = new FillFlowContainer
|
leftIconArea = new FillFlowContainer
|
||||||
{
|
{
|
||||||
Margin = new MarginPadding(5),
|
Margin = new MarginPadding(5),
|
||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
@ -88,6 +95,27 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
rightButtonArea = new Container
|
||||||
|
{
|
||||||
|
Name = @"Right (button) area",
|
||||||
|
Width = 30,
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
Origin = Anchor.TopRight,
|
||||||
|
Anchor = Anchor.TopRight,
|
||||||
|
Child = new FillFlowContainer<BeatmapCardIconButton>
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
Spacing = new Vector2(0, 14),
|
||||||
|
Children = new BeatmapCardIconButton[]
|
||||||
|
{
|
||||||
|
new FavouriteButton(beatmapSet) { Current = favouriteState },
|
||||||
|
new DownloadButton(beatmapSet)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
mainContent = new Container
|
mainContent = new Container
|
||||||
{
|
{
|
||||||
Name = @"Main content",
|
Name = @"Main content",
|
||||||
@ -226,10 +254,10 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (beatmapSet.HasVideo)
|
if (beatmapSet.HasVideo)
|
||||||
iconArea.Add(new IconPill(FontAwesome.Solid.Film));
|
leftIconArea.Add(new IconPill(FontAwesome.Solid.Film));
|
||||||
|
|
||||||
if (beatmapSet.HasStoryboard)
|
if (beatmapSet.HasStoryboard)
|
||||||
iconArea.Add(new IconPill(FontAwesome.Solid.Image));
|
leftIconArea.Add(new IconPill(FontAwesome.Solid.Image));
|
||||||
|
|
||||||
if (beatmapSet.HasExplicitContent)
|
if (beatmapSet.HasExplicitContent)
|
||||||
{
|
{
|
||||||
@ -287,7 +315,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
if (beatmapSet.HypeStatus != null && beatmapSet.NominationStatus != null)
|
if (beatmapSet.HypeStatus != null && beatmapSet.NominationStatus != null)
|
||||||
yield return new NominationsStatistic(beatmapSet.NominationStatus);
|
yield return new NominationsStatistic(beatmapSet.NominationStatus);
|
||||||
|
|
||||||
yield return new FavouritesStatistic(beatmapSet);
|
yield return new FavouritesStatistic(beatmapSet) { Current = favouriteState };
|
||||||
yield return new PlayCountStatistic(beatmapSet);
|
yield return new PlayCountStatistic(beatmapSet);
|
||||||
|
|
||||||
var dateStatistic = BeatmapCardDateStatistic.CreateFor(beatmapSet);
|
var dateStatistic = BeatmapCardDateStatistic.CreateFor(beatmapSet);
|
||||||
@ -306,6 +334,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards
|
|||||||
|
|
||||||
leftCover.FadeColour(IsHovered ? OsuColour.Gray(0.2f) : Color4.White, TRANSITION_DURATION, Easing.OutQuint);
|
leftCover.FadeColour(IsHovered ? OsuColour.Gray(0.2f) : Color4.White, TRANSITION_DURATION, Easing.OutQuint);
|
||||||
statisticsContainer.FadeTo(IsHovered ? 1 : 0, TRANSITION_DURATION, Easing.OutQuint);
|
statisticsContainer.FadeTo(IsHovered ? 1 : 0, TRANSITION_DURATION, Easing.OutQuint);
|
||||||
|
rightButtonArea.FadeTo(IsHovered ? 1 : 0, TRANSITION_DURATION, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
// 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.Beatmaps.Drawables.Cards.Buttons;
|
||||||
|
using osu.Game.Beatmaps.Drawables.Cards.Statistics;
|
||||||
|
|
||||||
|
namespace osu.Game.Beatmaps.Drawables.Cards
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Stores the current favourite state of a beatmap set.
|
||||||
|
/// Used to coordinate between <see cref="FavouriteButton"/> and <see cref="FavouritesStatistic"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly struct BeatmapSetFavouriteState
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the currently logged-in user has favourited this beatmap.
|
||||||
|
/// </summary>
|
||||||
|
public bool Favourited { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The number of favourites that the beatmap set has received, including the currently logged-in user.
|
||||||
|
/// </summary>
|
||||||
|
public int FavouriteCount { get; }
|
||||||
|
|
||||||
|
public BeatmapSetFavouriteState(bool favourited, int favouriteCount)
|
||||||
|
{
|
||||||
|
Favourited = favourited;
|
||||||
|
FavouriteCount = favouriteCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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 osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Beatmaps.Drawables.Cards.Buttons
|
||||||
|
{
|
||||||
|
public abstract class BeatmapCardIconButton : OsuHoverContainer
|
||||||
|
{
|
||||||
|
protected readonly SpriteIcon Icon;
|
||||||
|
|
||||||
|
private float size;
|
||||||
|
|
||||||
|
public new float Size
|
||||||
|
{
|
||||||
|
get => size;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
size = value;
|
||||||
|
Icon.Size = new Vector2(size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected BeatmapCardIconButton()
|
||||||
|
{
|
||||||
|
Add(Icon = new SpriteIcon());
|
||||||
|
|
||||||
|
AutoSizeAxes = Axes.Both;
|
||||||
|
Size = 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OverlayColourProvider colourProvider)
|
||||||
|
{
|
||||||
|
Anchor = Origin = Anchor.Centre;
|
||||||
|
|
||||||
|
IdleColour = colourProvider.Light1;
|
||||||
|
HoverColour = colourProvider.Content1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
osu.Game/Beatmaps/Drawables/Cards/Buttons/DownloadButton.cs
Normal file
18
osu.Game/Beatmaps/Drawables/Cards/Buttons/DownloadButton.cs
Normal file
@ -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.
|
||||||
|
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
|
|
||||||
|
namespace osu.Game.Beatmaps.Drawables.Cards.Buttons
|
||||||
|
{
|
||||||
|
public class DownloadButton : BeatmapCardIconButton
|
||||||
|
{
|
||||||
|
public DownloadButton(APIBeatmapSet beatmapSet)
|
||||||
|
{
|
||||||
|
Icon.Icon = FontAwesome.Solid.FileDownload;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: implement behaviour
|
||||||
|
}
|
||||||
|
}
|
86
osu.Game/Beatmaps/Drawables/Cards/Buttons/FavouriteButton.cs
Normal file
86
osu.Game/Beatmaps/Drawables/Cards/Buttons/FavouriteButton.cs
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
// 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.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
|
using osu.Framework.Logging;
|
||||||
|
using osu.Game.Online.API;
|
||||||
|
using osu.Game.Online.API.Requests;
|
||||||
|
using osu.Game.Resources.Localisation.Web;
|
||||||
|
|
||||||
|
namespace osu.Game.Beatmaps.Drawables.Cards.Buttons
|
||||||
|
{
|
||||||
|
public class FavouriteButton : BeatmapCardIconButton, IHasCurrentValue<BeatmapSetFavouriteState>
|
||||||
|
{
|
||||||
|
private readonly BindableWithCurrent<BeatmapSetFavouriteState> current;
|
||||||
|
|
||||||
|
public Bindable<BeatmapSetFavouriteState> Current
|
||||||
|
{
|
||||||
|
get => current.Current;
|
||||||
|
set => current.Current = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly APIBeatmapSet beatmapSet;
|
||||||
|
|
||||||
|
private PostBeatmapFavouriteRequest favouriteRequest;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private IAPIProvider api { get; set; }
|
||||||
|
|
||||||
|
public FavouriteButton(APIBeatmapSet beatmapSet)
|
||||||
|
{
|
||||||
|
current = new BindableWithCurrent<BeatmapSetFavouriteState>(new BeatmapSetFavouriteState(beatmapSet.HasFavourited, beatmapSet.FavouriteCount));
|
||||||
|
this.beatmapSet = beatmapSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
Action = toggleFavouriteStatus;
|
||||||
|
current.BindValueChanged(_ => updateState(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void toggleFavouriteStatus()
|
||||||
|
{
|
||||||
|
var actionType = current.Value.Favourited ? BeatmapFavouriteAction.UnFavourite : BeatmapFavouriteAction.Favourite;
|
||||||
|
|
||||||
|
favouriteRequest?.Cancel();
|
||||||
|
favouriteRequest = new PostBeatmapFavouriteRequest(beatmapSet.OnlineID, actionType);
|
||||||
|
|
||||||
|
Enabled.Value = false;
|
||||||
|
favouriteRequest.Success += () =>
|
||||||
|
{
|
||||||
|
bool favourited = actionType == BeatmapFavouriteAction.Favourite;
|
||||||
|
|
||||||
|
current.Value = new BeatmapSetFavouriteState(favourited, current.Value.FavouriteCount + (favourited ? 1 : -1));
|
||||||
|
|
||||||
|
Enabled.Value = true;
|
||||||
|
};
|
||||||
|
favouriteRequest.Failure += e =>
|
||||||
|
{
|
||||||
|
Logger.Error(e, $"Failed to {actionType.ToString().ToLower()} beatmap: {e.Message}");
|
||||||
|
Enabled.Value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
api.Queue(favouriteRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateState()
|
||||||
|
{
|
||||||
|
if (current.Value.Favourited)
|
||||||
|
{
|
||||||
|
Icon.Icon = FontAwesome.Solid.Heart;
|
||||||
|
TooltipText = BeatmapsetsStrings.ShowDetailsUnfavourite;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Icon.Icon = FontAwesome.Regular.Heart;
|
||||||
|
TooltipText = BeatmapsetsStrings.ShowDetailsFavourite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,8 +2,10 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using Humanizer;
|
using Humanizer;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions.LocalisationExtensions;
|
using osu.Framework.Extensions.LocalisationExtensions;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Game.Resources.Localisation.Web;
|
using osu.Game.Resources.Localisation.Web;
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps.Drawables.Cards.Statistics
|
namespace osu.Game.Beatmaps.Drawables.Cards.Statistics
|
||||||
@ -11,13 +13,32 @@ namespace osu.Game.Beatmaps.Drawables.Cards.Statistics
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Shows the number of favourites that a beatmap set has received.
|
/// Shows the number of favourites that a beatmap set has received.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class FavouritesStatistic : BeatmapCardStatistic
|
public class FavouritesStatistic : BeatmapCardStatistic, IHasCurrentValue<BeatmapSetFavouriteState>
|
||||||
{
|
{
|
||||||
|
private readonly BindableWithCurrent<BeatmapSetFavouriteState> current;
|
||||||
|
|
||||||
|
public Bindable<BeatmapSetFavouriteState> Current
|
||||||
|
{
|
||||||
|
get => current.Current;
|
||||||
|
set => current.Current = value;
|
||||||
|
}
|
||||||
|
|
||||||
public FavouritesStatistic(IBeatmapSetOnlineInfo onlineInfo)
|
public FavouritesStatistic(IBeatmapSetOnlineInfo onlineInfo)
|
||||||
{
|
{
|
||||||
Icon = onlineInfo.HasFavourited ? FontAwesome.Solid.Heart : FontAwesome.Regular.Heart;
|
current = new BindableWithCurrent<BeatmapSetFavouriteState>(new BeatmapSetFavouriteState(onlineInfo.HasFavourited, onlineInfo.FavouriteCount));
|
||||||
Text = onlineInfo.FavouriteCount.ToMetric(decimals: 1);
|
}
|
||||||
TooltipText = BeatmapsStrings.PanelFavourites(onlineInfo.FavouriteCount.ToLocalisableString(@"N0"));
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
current.BindValueChanged(_ => updateState(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateState()
|
||||||
|
{
|
||||||
|
Icon = current.Value.Favourited ? FontAwesome.Solid.Heart : FontAwesome.Regular.Heart;
|
||||||
|
Text = current.Value.FavouriteCount.ToMetric(decimals: 1);
|
||||||
|
TooltipText = BeatmapsStrings.PanelFavourites(current.Value.FavouriteCount.ToLocalisableString(@"N0"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ namespace osu.Game.Beatmaps
|
|||||||
[CanBeNull]
|
[CanBeNull]
|
||||||
private readonly GameHost host;
|
private readonly GameHost host;
|
||||||
|
|
||||||
public WorkingBeatmapCache([NotNull] AudioManager audioManager, IResourceStore<byte[]> resources, IResourceStore<byte[]> files, WorkingBeatmap defaultBeatmap = null, GameHost host = null)
|
public WorkingBeatmapCache(ITrackStore trackStore, AudioManager audioManager, IResourceStore<byte[]> resources, IResourceStore<byte[]> files, WorkingBeatmap defaultBeatmap = null, GameHost host = null)
|
||||||
{
|
{
|
||||||
DefaultBeatmap = defaultBeatmap;
|
DefaultBeatmap = defaultBeatmap;
|
||||||
|
|
||||||
@ -50,7 +50,7 @@ namespace osu.Game.Beatmaps
|
|||||||
this.host = host;
|
this.host = host;
|
||||||
this.files = files;
|
this.files = files;
|
||||||
largeTextureStore = new LargeTextureStore(host?.CreateTextureLoaderStore(files));
|
largeTextureStore = new LargeTextureStore(host?.CreateTextureLoaderStore(files));
|
||||||
trackStore = audioManager.GetTrackStore(files);
|
this.trackStore = trackStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Invalidate(BeatmapSetInfo info)
|
public void Invalidate(BeatmapSetInfo info)
|
||||||
|
@ -209,7 +209,13 @@ namespace osu.Game.Database
|
|||||||
|
|
||||||
case 9:
|
case 9:
|
||||||
// Pretty pointless to do this as beatmaps aren't really loaded via realm yet, but oh well.
|
// Pretty pointless to do this as beatmaps aren't really loaded via realm yet, but oh well.
|
||||||
var oldMetadata = migration.OldRealm.DynamicApi.All(getMappedOrOriginalName(typeof(RealmBeatmapMetadata)));
|
string metadataClassName = getMappedOrOriginalName(typeof(RealmBeatmapMetadata));
|
||||||
|
|
||||||
|
// May be coming from a version before `RealmBeatmapMetadata` existed.
|
||||||
|
if (!migration.OldRealm.Schema.TryFindObjectSchema(metadataClassName, out _))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var oldMetadata = migration.OldRealm.DynamicApi.All(metadataClassName);
|
||||||
var newMetadata = migration.NewRealm.All<RealmBeatmapMetadata>();
|
var newMetadata = migration.NewRealm.All<RealmBeatmapMetadata>();
|
||||||
|
|
||||||
int metadataCount = newMetadata.Count();
|
int metadataCount = newMetadata.Count();
|
||||||
|
@ -8,20 +8,21 @@ namespace osu.Game.Online.API.Requests
|
|||||||
{
|
{
|
||||||
public class PostBeatmapFavouriteRequest : APIRequest
|
public class PostBeatmapFavouriteRequest : APIRequest
|
||||||
{
|
{
|
||||||
|
public readonly BeatmapFavouriteAction Action;
|
||||||
|
|
||||||
private readonly int id;
|
private readonly int id;
|
||||||
private readonly BeatmapFavouriteAction action;
|
|
||||||
|
|
||||||
public PostBeatmapFavouriteRequest(int id, BeatmapFavouriteAction action)
|
public PostBeatmapFavouriteRequest(int id, BeatmapFavouriteAction action)
|
||||||
{
|
{
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.action = action;
|
Action = action;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override WebRequest CreateWebRequest()
|
protected override WebRequest CreateWebRequest()
|
||||||
{
|
{
|
||||||
var req = base.CreateWebRequest();
|
var req = base.CreateWebRequest();
|
||||||
req.Method = HttpMethod.Post;
|
req.Method = HttpMethod.Post;
|
||||||
req.AddParameter(@"action", action.ToString().ToLowerInvariant());
|
req.AddParameter(@"action", Action.ToString().ToLowerInvariant());
|
||||||
return req;
|
return req;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ namespace osu.Game
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The maximum volume at which audio tracks should playback. This can be set lower than 1 to create some head-room for sound effects.
|
/// The maximum volume at which audio tracks should playback. This can be set lower than 1 to create some head-room for sound effects.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal const double GLOBAL_TRACK_VOLUME_ADJUST = 0.8;
|
private const double global_track_volume_adjust = 0.8;
|
||||||
|
|
||||||
public bool UseDevelopmentServer { get; }
|
public bool UseDevelopmentServer { get; }
|
||||||
|
|
||||||
@ -159,7 +159,7 @@ namespace osu.Game
|
|||||||
|
|
||||||
private Bindable<bool> fpsDisplayVisible;
|
private Bindable<bool> fpsDisplayVisible;
|
||||||
|
|
||||||
private readonly BindableNumber<double> globalTrackVolumeAdjust = new BindableNumber<double>(GLOBAL_TRACK_VOLUME_ADJUST);
|
private readonly BindableNumber<double> globalTrackVolumeAdjust = new BindableNumber<double>(global_track_volume_adjust);
|
||||||
|
|
||||||
private RealmRulesetStore realmRulesetStore;
|
private RealmRulesetStore realmRulesetStore;
|
||||||
|
|
||||||
@ -315,7 +315,7 @@ namespace osu.Game
|
|||||||
dependencies.Cache(globalBindings);
|
dependencies.Cache(globalBindings);
|
||||||
|
|
||||||
PreviewTrackManager previewTrackManager;
|
PreviewTrackManager previewTrackManager;
|
||||||
dependencies.Cache(previewTrackManager = new PreviewTrackManager());
|
dependencies.Cache(previewTrackManager = new PreviewTrackManager(BeatmapManager.BeatmapTrackStore));
|
||||||
Add(previewTrackManager);
|
Add(previewTrackManager);
|
||||||
|
|
||||||
AddInternal(MusicController = new MusicController());
|
AddInternal(MusicController = new MusicController());
|
||||||
|
@ -153,12 +153,12 @@ namespace osu.Game.Overlays.Settings.Sections.Input
|
|||||||
new CancelButton { Action = finalise },
|
new CancelButton { Action = finalise },
|
||||||
new ClearButton { Action = clear },
|
new ClearButton { Action = clear },
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
|
new HoverClickSounds()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
new HoverClickSounds()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach (var b in bindings)
|
foreach (var b in bindings)
|
||||||
|
147
osu.Game/Screens/Play/HUD/UnstableRateCounter.cs
Normal file
147
osu.Game/Screens/Play/HUD/UnstableRateCounter.cs
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
// 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 System.Linq;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Rulesets.Judgements;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Play.HUD
|
||||||
|
{
|
||||||
|
public class UnstableRateCounter : RollingCounter<int>, ISkinnableDrawable
|
||||||
|
{
|
||||||
|
public bool UsesFixedAnchor { get; set; }
|
||||||
|
|
||||||
|
protected override double RollingDuration => 750;
|
||||||
|
|
||||||
|
private const float alpha_when_invalid = 0.3f;
|
||||||
|
private readonly Bindable<bool> valid = new Bindable<bool>();
|
||||||
|
|
||||||
|
private readonly List<double> hitOffsets = new List<double>();
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private ScoreProcessor scoreProcessor { get; set; }
|
||||||
|
|
||||||
|
public UnstableRateCounter()
|
||||||
|
{
|
||||||
|
Current.Value = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours, BeatmapDifficultyCache difficultyCache)
|
||||||
|
{
|
||||||
|
Colour = colours.BlueLighter;
|
||||||
|
valid.BindValueChanged(e =>
|
||||||
|
DrawableCount.FadeTo(e.NewValue ? 1 : alpha_when_invalid, 1000, Easing.OutQuint));
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool changesUnstableRate(JudgementResult judgement)
|
||||||
|
=> !(judgement.HitObject.HitWindows is HitWindows.EmptyHitWindows) && judgement.IsHit;
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
scoreProcessor.NewJudgement += onJudgementAdded;
|
||||||
|
scoreProcessor.JudgementReverted += onJudgementReverted;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onJudgementAdded(JudgementResult judgement)
|
||||||
|
{
|
||||||
|
if (!changesUnstableRate(judgement)) return;
|
||||||
|
|
||||||
|
hitOffsets.Add(judgement.TimeOffset);
|
||||||
|
updateDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onJudgementReverted(JudgementResult judgement)
|
||||||
|
{
|
||||||
|
if (judgement.FailedAtJudgement || !changesUnstableRate(judgement)) return;
|
||||||
|
|
||||||
|
hitOffsets.RemoveAt(hitOffsets.Count - 1);
|
||||||
|
updateDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateDisplay()
|
||||||
|
{
|
||||||
|
// At Count = 0, we get NaN, While we are allowing count = 1, it will be 0 since average = offset.
|
||||||
|
if (hitOffsets.Count > 0)
|
||||||
|
{
|
||||||
|
double mean = hitOffsets.Average();
|
||||||
|
double squares = hitOffsets.Select(offset => Math.Pow(offset - mean, 2)).Sum();
|
||||||
|
Current.Value = (int)(Math.Sqrt(squares / hitOffsets.Count) * 10);
|
||||||
|
valid.Value = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Current.Value = 0;
|
||||||
|
valid.Value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override IHasText CreateText() => new TextComponent
|
||||||
|
{
|
||||||
|
Alpha = alpha_when_invalid,
|
||||||
|
};
|
||||||
|
|
||||||
|
protected override void Dispose(bool isDisposing)
|
||||||
|
{
|
||||||
|
base.Dispose(isDisposing);
|
||||||
|
|
||||||
|
if (scoreProcessor == null) return;
|
||||||
|
|
||||||
|
scoreProcessor.NewJudgement -= onJudgementAdded;
|
||||||
|
scoreProcessor.JudgementReverted -= onJudgementReverted;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TextComponent : CompositeDrawable, IHasText
|
||||||
|
{
|
||||||
|
public LocalisableString Text
|
||||||
|
{
|
||||||
|
get => text.Text;
|
||||||
|
set => text.Text = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly OsuSpriteText text;
|
||||||
|
|
||||||
|
public TextComponent()
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
InternalChild = new FillFlowContainer
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
text = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
Font = OsuFont.Numeric.With(size: 16, fixedWidth: true)
|
||||||
|
},
|
||||||
|
new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
Font = OsuFont.Numeric.With(size: 8, fixedWidth: true),
|
||||||
|
Text = "UR",
|
||||||
|
Padding = new MarginPadding { Bottom = 1.5f },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -137,7 +137,7 @@ namespace osu.Game.Tests.Visual
|
|||||||
private readonly TestBeatmapManager testBeatmapManager;
|
private readonly TestBeatmapManager testBeatmapManager;
|
||||||
|
|
||||||
public TestWorkingBeatmapCache(TestBeatmapManager testBeatmapManager, AudioManager audioManager, IResourceStore<byte[]> resourceStore, IResourceStore<byte[]> storage, WorkingBeatmap defaultBeatmap, GameHost gameHost)
|
public TestWorkingBeatmapCache(TestBeatmapManager testBeatmapManager, AudioManager audioManager, IResourceStore<byte[]> resourceStore, IResourceStore<byte[]> storage, WorkingBeatmap defaultBeatmap, GameHost gameHost)
|
||||||
: base(audioManager, resourceStore, storage, defaultBeatmap, gameHost)
|
: base(testBeatmapManager.BeatmapTrackStore, audioManager, resourceStore, storage, defaultBeatmap, gameHost)
|
||||||
{
|
{
|
||||||
this.testBeatmapManager = testBeatmapManager;
|
this.testBeatmapManager = testBeatmapManager;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user