1
0
mirror of https://github.com/ppy/osu.git synced 2025-02-13 11:53:21 +08:00

Merge branch 'master' into skin/argon-song-progress-cleaner

This commit is contained in:
Bartłomiej Dach 2023-01-18 23:52:12 +01:00
commit b62ff8d644
No known key found for this signature in database
15 changed files with 612 additions and 44 deletions

View File

@ -0,0 +1,19 @@
// 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.Game.Rulesets.Mania.Mods;
using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Mania.Tests.Mods
{
public partial class TestSceneManiaModFadeIn : ModTestScene
{
protected override Ruleset CreatePlayerRuleset() => new ManiaRuleset();
[TestCase(0.5f)]
[TestCase(0.1f)]
[TestCase(0.7f)]
public void TestCoverage(float coverage) => CreateModTest(new ModTestData { Mod = new ManiaModFadeIn { Coverage = { Value = coverage } }, PassCondition = () => true });
}
}

View File

@ -0,0 +1,19 @@
// 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.Game.Rulesets.Mania.Mods;
using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Mania.Tests.Mods
{
public partial class TestSceneManiaModHidden : ModTestScene
{
protected override Ruleset CreatePlayerRuleset() => new ManiaRuleset();
[TestCase(0.5f)]
[TestCase(0.2f)]
[TestCase(0.8f)]
public void TestCoverage(float coverage) => CreateModTest(new ModTestData { Mod = new ManiaModHidden { Coverage = { Value = coverage } }, PassCondition = () => true });
}
}

View File

@ -3,6 +3,7 @@
using System;
using System.Linq;
using osu.Framework.Bindables;
using osu.Framework.Localisation;
using osu.Game.Rulesets.Mania.UI;
@ -18,5 +19,13 @@ namespace osu.Game.Rulesets.Mania.Mods
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ManiaModHidden)).ToArray();
protected override CoverExpandDirection ExpandDirection => CoverExpandDirection.AlongScroll;
public override BindableNumber<float> Coverage { get; } = new BindableFloat(0.5f)
{
Precision = 0.1f,
MinValue = 0.1f,
MaxValue = 0.7f,
Default = 0.5f,
};
}
}

View File

@ -5,6 +5,7 @@ using System;
using System.Linq;
using osu.Framework.Localisation;
using osu.Game.Rulesets.Mania.UI;
using osu.Framework.Bindables;
namespace osu.Game.Rulesets.Mania.Mods
{
@ -13,6 +14,14 @@ namespace osu.Game.Rulesets.Mania.Mods
public override LocalisableString Description => @"Keys fade out before you hit them!";
public override double ScoreMultiplier => 1;
public override BindableNumber<float> Coverage { get; } = new BindableFloat(0.5f)
{
Precision = 0.1f,
MinValue = 0.2f,
MaxValue = 0.8f,
Default = 0.5f,
};
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ManiaModFadeIn)).ToArray();
protected override CoverExpandDirection ExpandDirection => CoverExpandDirection.AgainstScroll;

View File

@ -3,8 +3,10 @@
using System;
using System.Linq;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Configuration;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods;
@ -22,6 +24,9 @@ namespace osu.Game.Rulesets.Mania.Mods
/// </summary>
protected abstract CoverExpandDirection ExpandDirection { get; }
[SettingSource("Coverage", "The proportion of playfield height that notes will be hidden for.")]
public abstract BindableNumber<float> Coverage { get; }
public virtual void ApplyToDrawableRuleset(DrawableRuleset<ManiaHitObject> drawableRuleset)
{
ManiaPlayfield maniaPlayfield = (ManiaPlayfield)drawableRuleset.Playfield;
@ -36,7 +41,7 @@ namespace osu.Game.Rulesets.Mania.Mods
{
c.RelativeSizeAxes = Axes.Both;
c.Direction = ExpandDirection;
c.Coverage = 0.5f;
c.Coverage = Coverage.Value;
}));
}
}

View File

@ -46,7 +46,9 @@ namespace osu.Game.Tests.Skins
// Covers TextElement and BeatmapInfoDrawable
"Archives/modified-default-20221102.osk",
// Covers BPM counter.
"Archives/modified-default-20221205.osk"
"Archives/modified-default-20221205.osk",
// Covers judgement counter.
"Archives/modified-default-20230117.osk"
};
/// <summary>

View File

@ -0,0 +1,144 @@
// 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.Diagnostics;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Testing;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Play.HUD.JudgementCounter;
namespace osu.Game.Tests.Visual.Gameplay
{
public partial class TestSceneJudgementCounter : OsuTestScene
{
private ScoreProcessor scoreProcessor = null!;
private JudgementTally judgementTally = null!;
private TestJudgementCounterDisplay counterDisplay = null!;
private readonly Bindable<JudgementResult> lastJudgementResult = new Bindable<JudgementResult>();
private int iteration;
[SetUpSteps]
public void SetupSteps() => AddStep("Create components", () =>
{
var ruleset = CreateRuleset();
Debug.Assert(ruleset != null);
scoreProcessor = new ScoreProcessor(ruleset);
Child = new DependencyProvidingContainer
{
RelativeSizeAxes = Axes.Both,
CachedDependencies = new (Type, object)[] { (typeof(ScoreProcessor), scoreProcessor), (typeof(Ruleset), ruleset) },
Children = new Drawable[]
{
judgementTally = new JudgementTally(),
new DependencyProvidingContainer
{
RelativeSizeAxes = Axes.Both,
CachedDependencies = new (Type, object)[] { (typeof(JudgementTally), judgementTally) },
Child = counterDisplay = new TestJudgementCounterDisplay
{
Margin = new MarginPadding { Top = 100 },
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre
}
}
},
};
});
protected override Ruleset CreateRuleset() => new ManiaRuleset();
private void applyOneJudgement(HitResult result)
{
lastJudgementResult.Value = new OsuJudgementResult(new HitObject
{
StartTime = iteration * 10000
}, new OsuJudgement())
{
Type = result,
};
scoreProcessor.ApplyResult(lastJudgementResult.Value);
iteration++;
}
[Test]
public void TestAddJudgementsToCounters()
{
AddRepeatStep("Add judgement", () => applyOneJudgement(HitResult.Great), 2);
AddRepeatStep("Add judgement", () => applyOneJudgement(HitResult.Miss), 2);
AddRepeatStep("Add judgement", () => applyOneJudgement(HitResult.Meh), 2);
}
[Test]
public void TestAddWhilstHidden()
{
AddRepeatStep("Add judgement", () => applyOneJudgement(HitResult.LargeTickHit), 2);
AddAssert("Check value added whilst hidden", () => hiddenCount() == 2);
AddStep("Show all judgements", () => counterDisplay.Mode.Value = JudgementCounterDisplay.DisplayMode.All);
}
[Test]
public void TestChangeFlowDirection()
{
AddStep("Set direction vertical", () => counterDisplay.FlowDirection.Value = Direction.Vertical);
AddStep("Set direction horizontal", () => counterDisplay.FlowDirection.Value = Direction.Horizontal);
}
[Test]
public void TestToggleJudgementNames()
{
AddStep("Hide judgement names", () => counterDisplay.ShowJudgementNames.Value = false);
AddWaitStep("wait some", 2);
AddAssert("Assert hidden", () => counterDisplay.CounterFlow.Children.First().ResultName.Alpha == 0);
AddStep("Hide judgement names", () => counterDisplay.ShowJudgementNames.Value = true);
AddWaitStep("wait some", 2);
AddAssert("Assert shown", () => counterDisplay.CounterFlow.Children.First().ResultName.Alpha == 1);
}
[Test]
public void TestHideMaxValue()
{
AddStep("Hide max judgement", () => counterDisplay.ShowMaxJudgement.Value = false);
AddWaitStep("wait some", 2);
AddAssert("Check max hidden", () => counterDisplay.CounterFlow.ChildrenOfType<JudgementCounter>().First().Alpha == 0);
AddStep("Show max judgement", () => counterDisplay.ShowMaxJudgement.Value = true);
}
[Test]
public void TestCycleDisplayModes()
{
AddStep("Show basic judgements", () => counterDisplay.Mode.Value = JudgementCounterDisplay.DisplayMode.Simple);
AddWaitStep("wait some", 2);
AddAssert("Check only basic", () => counterDisplay.CounterFlow.ChildrenOfType<JudgementCounter>().Last().Alpha == 0);
AddStep("Show normal judgements", () => counterDisplay.Mode.Value = JudgementCounterDisplay.DisplayMode.Normal);
AddStep("Show all judgements", () => counterDisplay.Mode.Value = JudgementCounterDisplay.DisplayMode.All);
AddWaitStep("wait some", 2);
AddAssert("Check all visible", () => counterDisplay.CounterFlow.ChildrenOfType<JudgementCounter>().Last().Alpha == 1);
}
private int hiddenCount()
{
var num = counterDisplay.CounterFlow.Children.First(child => child.Result.Type == HitResult.LargeTickHit);
return num.Result.ResultCount.Value;
}
private partial class TestJudgementCounterDisplay : JudgementCounterDisplay
{
public new FillFlowContainer<JudgementCounter> CounterFlow => base.CounterFlow;
}
}
}

View File

@ -580,10 +580,9 @@ namespace osu.Game.Tests.Visual.SongSelect
/// Ensures stability is maintained on different sort modes for items with equal properties.
/// </summary>
[Test]
public void TestSortingStability()
public void TestSortingStabilityDateAdded()
{
var sets = new List<BeatmapSetInfo>();
int idOffset = 0;
AddStep("Populuate beatmap sets", () =>
{
@ -593,38 +592,34 @@ namespace osu.Game.Tests.Visual.SongSelect
{
var set = TestResources.CreateTestBeatmapSetInfo();
set.DateAdded = DateTimeOffset.FromUnixTimeSeconds(i);
// only need to set the first as they are a shared reference.
var beatmap = set.Beatmaps.First();
beatmap.Metadata.Artist = $"artist {i / 2}";
beatmap.Metadata.Title = $"title {9 - i}";
beatmap.Metadata.Artist = "a";
beatmap.Metadata.Title = "b";
sets.Add(set);
}
idOffset = sets.First().OnlineID;
});
loadBeatmaps(sets);
AddStep("Sort by artist", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Artist }, false));
AddAssert("Items remain in original order", () => carousel.BeatmapSets.Select((set, index) => set.OnlineID == idOffset + index).All(b => b));
AddStep("Sort by title", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Title }, false));
AddAssert("Items are in reverse order", () => carousel.BeatmapSets.Select((set, index) => set.OnlineID == idOffset + sets.Count - index - 1).All(b => b));
AddAssert("Items remain in descending added order", () => carousel.BeatmapSets.Select(s => s.DateAdded), () => Is.Ordered.Descending);
AddStep("Sort by artist", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Artist }, false));
AddAssert("Items reset to original order", () => carousel.BeatmapSets.Select((set, index) => set.OnlineID == idOffset + index).All(b => b));
AddAssert("Items remain in descending added order", () => carousel.BeatmapSets.Select(s => s.DateAdded), () => Is.Ordered.Descending);
}
/// <summary>
/// Ensures stability is maintained on different sort modes while a new item is added to the carousel.
/// </summary>
[Test]
public void TestSortingStabilityWithNewItems()
public void TestSortingStabilityWithRemovedAndReaddedItem()
{
List<BeatmapSetInfo> sets = new List<BeatmapSetInfo>();
int idOffset = 0;
AddStep("Populuate beatmap sets", () =>
{
@ -640,16 +635,68 @@ namespace osu.Game.Tests.Visual.SongSelect
beatmap.Metadata.Artist = "same artist";
beatmap.Metadata.Title = "same title";
// testing the case where DateAdded happens to equal (quite rare).
set.DateAdded = DateTimeOffset.UnixEpoch;
sets.Add(set);
}
idOffset = sets.First().OnlineID;
});
Guid[] originalOrder = null!;
loadBeatmaps(sets);
AddStep("Sort by artist", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Artist }, false));
assertOriginalOrderMaintained();
AddAssert("Items in descending added order", () => carousel.BeatmapSets.Select(s => s.DateAdded), () => Is.Ordered.Descending);
AddStep("Save order", () => originalOrder = carousel.BeatmapSets.Select(s => s.ID).ToArray());
AddStep("Remove item", () => carousel.RemoveBeatmapSet(sets[1]));
AddStep("Re-add item", () => carousel.UpdateBeatmapSet(sets[1]));
AddAssert("Order didn't change", () => carousel.BeatmapSets.Select(s => s.ID), () => Is.EqualTo(originalOrder));
AddStep("Sort by title", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Title }, false));
AddAssert("Order didn't change", () => carousel.BeatmapSets.Select(s => s.ID), () => Is.EqualTo(originalOrder));
}
/// <summary>
/// Ensures stability is maintained on different sort modes while a new item is added to the carousel.
/// </summary>
[Test]
public void TestSortingStabilityWithNewItems()
{
List<BeatmapSetInfo> sets = new List<BeatmapSetInfo>();
AddStep("Populuate beatmap sets", () =>
{
sets.Clear();
for (int i = 0; i < 3; i++)
{
var set = TestResources.CreateTestBeatmapSetInfo(3);
// only need to set the first as they are a shared reference.
var beatmap = set.Beatmaps.First();
beatmap.Metadata.Artist = "same artist";
beatmap.Metadata.Title = "same title";
// testing the case where DateAdded happens to equal (quite rare).
set.DateAdded = DateTimeOffset.UnixEpoch;
sets.Add(set);
}
});
Guid[] originalOrder = null!;
loadBeatmaps(sets);
AddStep("Sort by artist", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Artist }, false));
AddAssert("Items in descending added order", () => carousel.BeatmapSets.Select(s => s.DateAdded), () => Is.Ordered.Descending);
AddStep("Save order", () => originalOrder = carousel.BeatmapSets.Select(s => s.ID).ToArray());
AddStep("Add new item", () =>
{
@ -661,19 +708,18 @@ namespace osu.Game.Tests.Visual.SongSelect
beatmap.Metadata.Artist = "same artist";
beatmap.Metadata.Title = "same title";
set.DateAdded = DateTimeOffset.FromUnixTimeSeconds(1);
carousel.UpdateBeatmapSet(set);
// add set to expected ordering
originalOrder = originalOrder.Prepend(set.ID).ToArray();
});
assertOriginalOrderMaintained();
AddAssert("Order didn't change", () => carousel.BeatmapSets.Select(s => s.ID), () => Is.EqualTo(originalOrder));
AddStep("Sort by title", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Title }, false));
assertOriginalOrderMaintained();
void assertOriginalOrderMaintained()
{
AddAssert("Items remain in original order",
() => carousel.BeatmapSets.Select(s => s.OnlineID), () => Is.EqualTo(carousel.BeatmapSets.Select((set, index) => idOffset + index)));
}
AddAssert("Order didn't change", () => carousel.BeatmapSets.Select(s => s.ID), () => Is.EqualTo(originalOrder));
}
[Test]

View File

@ -0,0 +1,82 @@
// 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;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Screens.Play.HUD.JudgementCounter
{
public partial class JudgementCounter : VisibilityContainer
{
public BindableBool ShowName = new BindableBool();
public Bindable<FillDirection> Direction = new Bindable<FillDirection>();
public readonly JudgementTally.JudgementCount Result;
public JudgementCounter(JudgementTally.JudgementCount result) => Result = result;
public OsuSpriteText ResultName = null!;
private FillFlowContainer flowContainer = null!;
private JudgementRollingCounter counter = null!;
[BackgroundDependencyLoader]
private void load(OsuColour colours, IBindable<RulesetInfo> ruleset)
{
AutoSizeAxes = Axes.Both;
InternalChild = flowContainer = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
counter = new JudgementRollingCounter
{
Current = Result.ResultCount
},
ResultName = new OsuSpriteText
{
Alpha = 0,
Font = OsuFont.Numeric.With(size: 8),
Text = ruleset.Value.CreateInstance().GetDisplayNameForHitResult(Result.Type)
}
}
};
var result = Result.Type;
Colour = result.IsBasic() ? colours.ForHitResult(Result.Type) : !result.IsBonus() ? colours.PurpleLight : colours.PurpleLighter;
}
protected override void LoadComplete()
{
ShowName.BindValueChanged(value =>
ResultName.FadeTo(value.NewValue ? 1 : 0, JudgementCounterDisplay.TRANSFORM_DURATION, Easing.OutQuint), true);
Direction.BindValueChanged(direction =>
{
flowContainer.Direction = direction.NewValue;
changeAnchor(direction.NewValue == FillDirection.Vertical ? Anchor.TopLeft : Anchor.BottomLeft);
void changeAnchor(Anchor anchor) => counter.Anchor = ResultName.Anchor = counter.Origin = ResultName.Origin = anchor;
}, true);
base.LoadComplete();
}
protected override void PopIn() => this.FadeIn(JudgementCounterDisplay.TRANSFORM_DURATION, Easing.OutQuint);
protected override void PopOut() => this.FadeOut(100);
private sealed partial class JudgementRollingCounter : RollingCounter<int>
{
protected override OsuSpriteText CreateSpriteText()
=> base.CreateSpriteText().With(s => s.Font = s.Font.With(fixedWidth: true, size: 16));
}
}
}

View File

@ -0,0 +1,139 @@
// 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.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Configuration;
using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning;
using osuTK;
namespace osu.Game.Screens.Play.HUD.JudgementCounter
{
public partial class JudgementCounterDisplay : CompositeDrawable, ISkinnableDrawable
{
public const int TRANSFORM_DURATION = 250;
public bool UsesFixedAnchor { get; set; }
[SettingSource("Display mode")]
public Bindable<DisplayMode> Mode { get; set; } = new Bindable<DisplayMode>();
[SettingSource("Counter direction")]
public Bindable<Direction> FlowDirection { get; set; } = new Bindable<Direction>();
[SettingSource("Show judgement names")]
public BindableBool ShowJudgementNames { get; set; } = new BindableBool(true);
[SettingSource("Show max judgement")]
public BindableBool ShowMaxJudgement { get; set; } = new BindableBool(true);
[Resolved]
private JudgementTally tally { get; set; } = null!;
protected FillFlowContainer<JudgementCounter> CounterFlow = null!;
[BackgroundDependencyLoader]
private void load()
{
AutoSizeAxes = Axes.Both;
InternalChild = CounterFlow = new FillFlowContainer<JudgementCounter>
{
Direction = getFillDirection(FlowDirection.Value),
Spacing = new Vector2(10),
AutoSizeAxes = Axes.Both
};
foreach (var result in tally.Results)
CounterFlow.Add(createCounter(result));
}
protected override void LoadComplete()
{
base.LoadComplete();
FlowDirection.BindValueChanged(direction =>
{
var convertedDirection = getFillDirection(direction.NewValue);
CounterFlow.Direction = convertedDirection;
foreach (var counter in CounterFlow.Children)
counter.Direction.Value = convertedDirection;
}, true);
Mode.BindValueChanged(_ => updateMode(), true);
ShowMaxJudgement.BindValueChanged(value =>
{
var firstChild = CounterFlow.Children.FirstOrDefault();
firstChild.FadeTo(value.NewValue ? 1 : 0, TRANSFORM_DURATION, Easing.OutQuint);
}, true);
}
private void updateMode()
{
foreach (var counter in CounterFlow.Children)
{
if (shouldShow(counter))
counter.Show();
else
counter.Hide();
}
bool shouldShow(JudgementCounter counter)
{
if (counter.Result.Type.IsBasic())
return true;
switch (Mode.Value)
{
case DisplayMode.Simple:
return false;
case DisplayMode.Normal:
return !counter.Result.Type.IsBonus();
case DisplayMode.All:
return true;
default:
throw new ArgumentOutOfRangeException();
}
}
}
private FillDirection getFillDirection(Direction flow)
{
switch (flow)
{
case Direction.Horizontal:
return FillDirection.Horizontal;
case Direction.Vertical:
return FillDirection.Vertical;
default:
throw new ArgumentOutOfRangeException(nameof(flow), flow, @"Unsupported direction");
}
}
private JudgementCounter createCounter(JudgementTally.JudgementCount info) =>
new JudgementCounter(info)
{
State = { Value = Visibility.Hidden },
ShowName = { BindTarget = ShowJudgementNames }
};
public enum DisplayMode
{
Simple,
Normal,
All
}
}
}

View File

@ -0,0 +1,60 @@
// 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.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Screens.Play.HUD.JudgementCounter
{
/// <summary>
/// Keeps track of judgements for a current play session, exposing bindable counts which can
/// be used for display purposes.
/// </summary>
public partial class JudgementTally : CompositeDrawable
{
[Resolved]
private ScoreProcessor scoreProcessor { get; set; } = null!;
public List<JudgementCount> Results = new List<JudgementCount>();
[BackgroundDependencyLoader]
private void load(IBindable<RulesetInfo> ruleset)
{
foreach (var result in ruleset.Value.CreateInstance().GetHitResults())
{
Results.Add(new JudgementCount
{
Type = result.result,
ResultCount = new BindableInt()
});
}
}
protected override void LoadComplete()
{
base.LoadComplete();
scoreProcessor.NewJudgement += judgement => updateCount(judgement, false);
scoreProcessor.JudgementReverted += judgement => updateCount(judgement, true);
}
private void updateCount(JudgementResult judgement, bool revert)
{
foreach (JudgementCount result in Results.Where(result => result.Type == judgement.Type))
result.ResultCount.Value = revert ? result.ResultCount.Value - 1 : result.ResultCount.Value + 1;
}
public struct JudgementCount
{
public HitResult Type { get; set; }
public BindableInt ResultCount { get; set; }
}
}
}

View File

@ -22,6 +22,7 @@ using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.Play.HUD;
using osu.Game.Screens.Play.HUD.ClicksPerSecond;
using osu.Game.Screens.Play.HUD.JudgementCounter;
using osu.Game.Skinning;
using osuTK;
using osu.Game.Localisation;
@ -59,6 +60,9 @@ namespace osu.Game.Screens.Play
[Cached]
private readonly ClicksPerSecondCalculator clicksPerSecondCalculator;
[Cached]
private readonly JudgementTally tally;
public Bindable<bool> ShowHealthBar = new Bindable<bool>(true);
private readonly DrawableRuleset drawableRuleset;
@ -104,6 +108,8 @@ namespace osu.Game.Screens.Play
Children = new Drawable[]
{
CreateFailingLayer(),
//Needs to be initialized before skinnable drawables.
tally = new JudgementTally(),
mainComponents = new MainComponentsContainer
{
AlwaysPresent = true,

View File

@ -61,50 +61,75 @@ namespace osu.Game.Screens.Select.Carousel
if (!(other is CarouselBeatmapSet otherSet))
return base.CompareTo(criteria, other);
int comparison = 0;
switch (criteria.Sort)
{
default:
case SortMode.Artist:
return string.Compare(BeatmapSet.Metadata.Artist, otherSet.BeatmapSet.Metadata.Artist, StringComparison.OrdinalIgnoreCase);
comparison = string.Compare(BeatmapSet.Metadata.Artist, otherSet.BeatmapSet.Metadata.Artist, StringComparison.OrdinalIgnoreCase);
break;
case SortMode.Title:
return string.Compare(BeatmapSet.Metadata.Title, otherSet.BeatmapSet.Metadata.Title, StringComparison.OrdinalIgnoreCase);
comparison = string.Compare(BeatmapSet.Metadata.Title, otherSet.BeatmapSet.Metadata.Title, StringComparison.OrdinalIgnoreCase);
break;
case SortMode.Author:
return string.Compare(BeatmapSet.Metadata.Author.Username, otherSet.BeatmapSet.Metadata.Author.Username, StringComparison.OrdinalIgnoreCase);
comparison = string.Compare(BeatmapSet.Metadata.Author.Username, otherSet.BeatmapSet.Metadata.Author.Username, StringComparison.OrdinalIgnoreCase);
break;
case SortMode.Source:
return string.Compare(BeatmapSet.Metadata.Source, otherSet.BeatmapSet.Metadata.Source, StringComparison.OrdinalIgnoreCase);
comparison = string.Compare(BeatmapSet.Metadata.Source, otherSet.BeatmapSet.Metadata.Source, StringComparison.OrdinalIgnoreCase);
break;
case SortMode.DateAdded:
return otherSet.BeatmapSet.DateAdded.CompareTo(BeatmapSet.DateAdded);
comparison = otherSet.BeatmapSet.DateAdded.CompareTo(BeatmapSet.DateAdded);
break;
case SortMode.DateRanked:
// Beatmaps which have no ranked date should already be filtered away in this mode.
if (BeatmapSet.DateRanked == null || otherSet.BeatmapSet.DateRanked == null)
return 0;
break;
return otherSet.BeatmapSet.DateRanked.Value.CompareTo(BeatmapSet.DateRanked.Value);
comparison = otherSet.BeatmapSet.DateRanked.Value.CompareTo(BeatmapSet.DateRanked.Value);
break;
case SortMode.LastPlayed:
return -compareUsingAggregateMax(otherSet, b => (b.LastPlayed ?? DateTimeOffset.MinValue).ToUnixTimeSeconds());
comparison = -compareUsingAggregateMax(otherSet, b => (b.LastPlayed ?? DateTimeOffset.MinValue).ToUnixTimeSeconds());
break;
case SortMode.BPM:
return compareUsingAggregateMax(otherSet, b => b.BPM);
comparison = compareUsingAggregateMax(otherSet, b => b.BPM);
break;
case SortMode.Length:
return compareUsingAggregateMax(otherSet, b => b.Length);
comparison = compareUsingAggregateMax(otherSet, b => b.Length);
break;
case SortMode.Difficulty:
return compareUsingAggregateMax(otherSet, b => b.StarRating);
comparison = compareUsingAggregateMax(otherSet, b => b.StarRating);
break;
case SortMode.DateSubmitted:
// Beatmaps which have no submitted date should already be filtered away in this mode.
if (BeatmapSet.DateSubmitted == null || otherSet.BeatmapSet.DateSubmitted == null)
return 0;
break;
return otherSet.BeatmapSet.DateSubmitted.Value.CompareTo(BeatmapSet.DateSubmitted.Value);
comparison = otherSet.BeatmapSet.DateSubmitted.Value.CompareTo(BeatmapSet.DateSubmitted.Value);
break;
}
if (comparison != 0) return comparison;
// If the initial sort could not differentiate, attempt to use DateAdded to order sets in a stable fashion.
// The directionality of this matches the current SortMode.DateAdded, but we may want to reconsider if that becomes a user decision (ie. asc / desc).
comparison = otherSet.BeatmapSet.DateAdded.CompareTo(BeatmapSet.DateAdded);
if (comparison != 0) return comparison;
// If DateAdded fails to break the tie, fallback to our internal GUID for stability.
// This basically means it's a stable random sort.
return otherSet.BeatmapSet.ID.CompareTo(BeatmapSet.ID);
}
/// <summary>

View File

@ -65,11 +65,14 @@ namespace osu.Game.Skinning
default:
this.ScaleTo(0.6f).Then()
.ScaleTo(1.1f, fade_in_length * 0.8f).Then()
// this is actually correct to match stable; there were overlapping transforms.
.ScaleTo(0.9f).Delay(fade_in_length * 0.2f)
.ScaleTo(1.1f).ScaleTo(0.9f, fade_in_length * 0.2f).Then()
.ScaleTo(0.95f).ScaleTo(finalScale, fade_in_length * 0.2f);
.ScaleTo(1.1f, fade_in_length * 0.8f).Then() // t = 0.8
.Delay(fade_in_length * 0.2f) // t = 1.0
.ScaleTo(0.9f, fade_in_length * 0.2f).Then() // t = 1.2
// stable dictates scale of 0.9->1 over time 1.0 to 1.4, but we are already at 1.2.
// so we need to force the current value to be correct at 1.2 (0.95) then complete the
// second half of the transform.
.ScaleTo(0.95f).ScaleTo(finalScale, fade_in_length * 0.2f); // t = 1.4
break;
}
}