diff --git a/osu.Game.Rulesets.Osu.Tests/OsuSkinnableTestScene.cs b/osu.Game.Rulesets.Osu.Tests/OsuSkinnableTestScene.cs index a0a38fc47b..cad98185ce 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuSkinnableTestScene.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuSkinnableTestScene.cs @@ -1,12 +1,27 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Game.Tests.Visual; namespace osu.Game.Rulesets.Osu.Tests { public abstract class OsuSkinnableTestScene : SkinnableTestScene { + private Container content; + + protected override Container Content + { + get + { + if (content == null) + base.Content.Add(content = new OsuInputManager(new RulesetInfo { ID = 0 })); + + return content; + } + } + protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); } } diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs index a9404f665a..6a689a1f80 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; @@ -26,19 +25,6 @@ namespace osu.Game.Rulesets.Osu.Tests [TestFixture] public class TestSceneSlider : OsuSkinnableTestScene { - private Container content; - - protected override Container Content - { - get - { - if (content == null) - base.Content.Add(content = new OsuInputManager(new RulesetInfo { ID = 0 })); - - return content; - } - } - private int depthIndex; public TestSceneSlider() diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs index 65b338882e..b57561f3e1 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs @@ -19,8 +19,6 @@ namespace osu.Game.Rulesets.Osu.Tests public TestSceneSpinner() { - // base.Content.Add(content = new OsuInputManager(new RulesetInfo { ID = 0 })); - AddStep("Miss Big", () => SetContents(() => testSingle(2))); AddStep("Miss Medium", () => SetContents(() => testSingle(5))); AddStep("Miss Small", () => SetContents(() => testSingle(7))); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs index 2644f425a0..dfb692eba9 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs @@ -29,7 +29,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces private SpinnerTicks ticks; - private int completeTick; + private int wholeRotationCount; + private SpinnerFill fill; private Container mainContainer; private SpinnerCentreLayer centre; @@ -95,6 +96,33 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces drawableSpinner.State.BindValueChanged(updateStateTransforms, true); } + protected override void Update() + { + base.Update(); + + if (drawableSpinner.RotationTracker.Complete.Value) + { + if (checkNewRotationCount) + { + fill.FinishTransforms(false, nameof(Alpha)); + fill + .FadeTo(tracking_alpha + 0.2f, 60, Easing.OutExpo) + .Then() + .FadeTo(tracking_alpha, 250, Easing.OutQuint); + } + } + else + { + fill.Alpha = (float)Interpolation.Damp(fill.Alpha, drawableSpinner.RotationTracker.Tracking ? tracking_alpha : idle_alpha, 0.98f, (float)Math.Abs(Clock.ElapsedFrameTime)); + } + + const float initial_scale = 0.2f; + float targetScale = initial_scale + (1 - initial_scale) * drawableSpinner.Progress; + + fill.Scale = new Vector2((float)Interpolation.Lerp(fill.Scale.X, targetScale, Math.Clamp(Math.Abs(Time.Elapsed) / 100, 0, 1))); + mainContainer.Rotation = drawableSpinner.RotationTracker.Rotation; + } + private void updateStateTransforms(ValueChangedEvent state) { centre.ScaleTo(0); @@ -145,26 +173,17 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces centre.FadeAccent(colour, duration); } - private bool updateCompleteTick() => completeTick != (completeTick = (int)(drawableSpinner.RotationTracker.CumulativeRotation / 360)); - - protected override void Update() + private bool checkNewRotationCount { - base.Update(); - - if (drawableSpinner.RotationTracker.Complete.Value && updateCompleteTick()) + get { - fill.FinishTransforms(false, nameof(Alpha)); - fill - .FadeTo(tracking_alpha + 0.2f, 60, Easing.OutExpo) - .Then() - .FadeTo(tracking_alpha, 250, Easing.OutQuint); + int rotations = (int)(drawableSpinner.RotationTracker.CumulativeRotation / 360); + + if (wholeRotationCount == rotations) return false; + + wholeRotationCount = rotations; + return true; } - - const float initial_scale = 0.2f; - float targetScale = initial_scale + (1 - initial_scale) * drawableSpinner.Progress; - - fill.Scale = new Vector2((float)Interpolation.Lerp(fill.Scale.X, targetScale, Math.Clamp(Math.Abs(Time.Elapsed) / 100, 0, 1))); - mainContainer.Rotation = drawableSpinner.RotationTracker.Rotation; } } } diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs index 618cb0e9ad..72bc3ddc9a 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs @@ -93,8 +93,7 @@ namespace osu.Game.Rulesets.Osu.Skinning spinningMiddle.Rotation = discTop.Rotation = drawableSpinner.RotationTracker.Rotation; discBottom.Rotation = discTop.Rotation / 3; - Scale = new Vector2(final_scale * 0.8f - + (float)Interpolation.ApplyEasing(Easing.Out, drawableSpinner.Progress) * (final_scale * 0.2f)); + Scale = new Vector2(final_scale * (0.8f + (float)Interpolation.ApplyEasing(Easing.Out, drawableSpinner.Progress) * 0.2f)); } } } diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs index 44f431d6f7..81d1d05b66 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs @@ -104,9 +104,11 @@ namespace osu.Game.Rulesets.Osu.Skinning }; case OsuSkinComponents.SpinnerBody: - if (Source.GetTexture("spinner-top") != null) + bool hasBackground = Source.GetTexture("spinner-background") != null; + + if (Source.GetTexture("spinner-top") != null && !hasBackground) return new LegacyNewStyleSpinner(); - else if (Source.GetTexture("spinner-background") != null) + else if (hasBackground) return new LegacyOldStyleSpinner(); return null; diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs index 628d08a314..03fd2b968c 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs @@ -244,6 +244,7 @@ namespace osu.Game.Tests.Visual.Multiplayer EndedAt = userScore.Date, Passed = userScore.Passed, Rank = userScore.Rank, + Position = 200, MaxCombo = userScore.MaxCombo, TotalScore = userScore.TotalScore, User = userScore.User, diff --git a/osu.Game/Online/Multiplayer/MultiplayerScore.cs b/osu.Game/Online/Multiplayer/MultiplayerScore.cs index 1793ba72ef..8191003aad 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerScore.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerScore.cs @@ -80,7 +80,8 @@ namespace osu.Game.Online.Multiplayer Date = EndedAt, Hash = string.Empty, // todo: temporary? Rank = Rank, - Mods = Mods?.Select(m => m.ToMod(rulesetInstance)).ToArray() ?? Array.Empty() + Mods = Mods?.Select(m => m.ToMod(rulesetInstance)).ToArray() ?? Array.Empty(), + Position = Position, }; return scoreInfo; diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 84c0d5b54e..efcf1737c9 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -179,6 +179,13 @@ namespace osu.Game.Scoring [JsonIgnore] public bool DeletePending { get; set; } + /// + /// The position of this score, starting at 1. + /// + [NotMapped] + [JsonProperty("position")] + public int? Position { get; set; } + [Serializable] protected class DeserializedMod : IMod { diff --git a/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs b/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs index e212bd4a82..8da6a530a8 100644 --- a/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs +++ b/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs @@ -84,12 +84,18 @@ namespace osu.Game.Screens.Multi.Ranking { allScores.AddRange(userScore.ScoresAround.Higher.Scores); higherScores = userScore.ScoresAround.Higher; + + Debug.Assert(userScore.Position != null); + setPositions(higherScores, userScore.Position.Value, -1); } if (userScore.ScoresAround?.Lower != null) { allScores.AddRange(userScore.ScoresAround.Lower.Scores); lowerScores = userScore.ScoresAround.Lower; + + Debug.Assert(userScore.Position != null); + setPositions(lowerScores, userScore.Position.Value, 1); } performSuccessCallback(scoresCallback, allScores); @@ -134,9 +140,15 @@ namespace osu.Game.Screens.Multi.Ranking indexReq.Success += r => { if (pivot == lowerScores) + { lowerScores = r; + setPositions(r, pivot, 1); + } else + { higherScores = r; + setPositions(r, pivot, -1); + } performSuccessCallback(scoresCallback, r.Scores, r); }; @@ -183,6 +195,30 @@ namespace osu.Game.Screens.Multi.Ranking LeftSpinner.Hide(); } + /// + /// Applies positions to all s referenced to a given pivot. + /// + /// The to set positions on. + /// The pivot. + /// The amount to increment the pivot position by for each in . + private void setPositions([NotNull] MultiplayerScores scores, [CanBeNull] MultiplayerScores pivot, int increment) + => setPositions(scores, pivot?.Scores[^1].Position ?? 0, increment); + + /// + /// Applies positions to all s referenced to a given pivot. + /// + /// The to set positions on. + /// The pivot position. + /// The amount to increment the pivot position by for each in . + private void setPositions([NotNull] MultiplayerScores scores, int pivotPosition, int increment) + { + foreach (var s in scores.Scores) + { + pivotPosition += increment; + s.Position = pivotPosition; + } + } + private class PanelListLoadingSpinner : LoadingSpinner { private readonly ScorePanelList list; diff --git a/osu.Game/Screens/Ranking/Contracted/ContractedPanelTopContent.cs b/osu.Game/Screens/Ranking/Contracted/ContractedPanelTopContent.cs new file mode 100644 index 0000000000..0935ee7fb2 --- /dev/null +++ b/osu.Game/Screens/Ranking/Contracted/ContractedPanelTopContent.cs @@ -0,0 +1,37 @@ +// Copyright (c) ppy Pty Ltd . 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.Containers; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Scoring; + +namespace osu.Game.Screens.Ranking.Contracted +{ + public class ContractedPanelTopContent : CompositeDrawable + { + private readonly ScoreInfo score; + + public ContractedPanelTopContent(ScoreInfo score) + { + this.score = score; + + RelativeSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader] + private void load() + { + InternalChild = new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Y = 6, + Text = score.Position != null ? $"#{score.Position}" : string.Empty, + Font = OsuFont.GetFont(size: 18, weight: FontWeight.Bold) + }; + } + } +} diff --git a/osu.Game/Screens/Ranking/ScorePanel.cs b/osu.Game/Screens/Ranking/ScorePanel.cs index 24d193e9a7..1904da7094 100644 --- a/osu.Game/Screens/Ranking/ScorePanel.cs +++ b/osu.Game/Screens/Ranking/ScorePanel.cs @@ -203,8 +203,8 @@ namespace osu.Game.Screens.Ranking topLayerBackground.FadeColour(expanded_top_layer_colour, resize_duration, Easing.OutQuint); middleLayerBackground.FadeColour(expanded_middle_layer_colour, resize_duration, Easing.OutQuint); - topLayerContentContainer.Add(middleLayerContent = new ExpandedPanelTopContent(Score.User).With(d => d.Alpha = 0)); - middleLayerContentContainer.Add(topLayerContent = new ExpandedPanelMiddleContent(Score).With(d => d.Alpha = 0)); + topLayerContentContainer.Add(topLayerContent = new ExpandedPanelTopContent(Score.User).With(d => d.Alpha = 0)); + middleLayerContentContainer.Add(middleLayerContent = new ExpandedPanelMiddleContent(Score).With(d => d.Alpha = 0)); break; case PanelState.Contracted: @@ -213,7 +213,8 @@ namespace osu.Game.Screens.Ranking topLayerBackground.FadeColour(contracted_top_layer_colour, resize_duration, Easing.OutQuint); middleLayerBackground.FadeColour(contracted_middle_layer_colour, resize_duration, Easing.OutQuint); - middleLayerContentContainer.Add(topLayerContent = new ContractedPanelMiddleContent(Score).With(d => d.Alpha = 0)); + topLayerContentContainer.Add(topLayerContent = new ContractedPanelTopContent(Score).With(d => d.Alpha = 0)); + middleLayerContentContainer.Add(middleLayerContent = new ContractedPanelMiddleContent(Score).With(d => d.Alpha = 0)); break; } diff --git a/osu.Game/Tests/TestScoreInfo.cs b/osu.Game/Tests/TestScoreInfo.cs index 1193a29d70..31cced6ce4 100644 --- a/osu.Game/Tests/TestScoreInfo.cs +++ b/osu.Game/Tests/TestScoreInfo.cs @@ -37,6 +37,8 @@ namespace osu.Game.Tests Statistics[HitResult.Meh] = 50; Statistics[HitResult.Good] = 100; Statistics[HitResult.Great] = 300; + + Position = 1; } private class TestModHardRock : ModHardRock