Merge branch 'master' into i-has-curve-with-repeats

This commit is contained in:
Dean Herbert 2020-05-31 22:30:18 +09:00
commit 0ef971be28
48 changed files with 1137 additions and 282 deletions

View File

@ -52,6 +52,6 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.512.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.525.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.528.0" />
</ItemGroup>
</Project>

View File

@ -8,7 +8,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Timing;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Mania.Edit;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods;
@ -19,8 +18,7 @@ using osuTK.Graphics;
namespace osu.Game.Rulesets.Mania.Tests
{
[Cached(Type = typeof(IManiaHitObjectComposer))]
public abstract class ManiaPlacementBlueprintTestScene : PlacementBlueprintTestScene, IManiaHitObjectComposer
public abstract class ManiaPlacementBlueprintTestScene : PlacementBlueprintTestScene
{
private readonly Column column;

View File

@ -4,15 +4,13 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Timing;
using osu.Game.Rulesets.Mania.Edit;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Tests.Visual;
using osuTK.Graphics;
namespace osu.Game.Rulesets.Mania.Tests
{
[Cached(Type = typeof(IManiaHitObjectComposer))]
public abstract class ManiaSelectionBlueprintTestScene : SelectionBlueprintTestScene, IManiaHitObjectComposer
public abstract class ManiaSelectionBlueprintTestScene : SelectionBlueprintTestScene
{
[Cached(Type = typeof(IAdjustableClock))]
private readonly IAdjustableClock clock = new StopwatchClock();

View File

@ -2,23 +2,27 @@
// 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.Graphics;
using osu.Framework.Input.Events;
using osu.Framework.Timing;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Configuration;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Edit;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Screens.Edit;
using osu.Game.Tests.Visual;
using osuTK;
namespace osu.Game.Rulesets.Mania.Tests
{
[Cached(typeof(IManiaHitObjectComposer))]
public class TestSceneManiaBeatSnapGrid : EditorClockTestScene, IManiaHitObjectComposer
public class TestSceneManiaBeatSnapGrid : EditorClockTestScene
{
[Cached(typeof(IScrollingInfo))]
private ScrollingTestContainer.TestScrollingInfo scrollingInfo = new ScrollingTestContainer.TestScrollingInfo();
@ -50,7 +54,10 @@ namespace osu.Game.Rulesets.Mania.Tests
{
Clock = new FramedClock(new StopwatchClock())
},
beatSnapGrid = new ManiaBeatSnapGrid()
new TestHitObjectComposer(Playfield)
{
Child = beatSnapGrid = new ManiaBeatSnapGrid()
}
};
}
@ -67,4 +74,51 @@ namespace osu.Game.Rulesets.Mania.Tests
public ManiaPlayfield Playfield { get; }
}
public class TestHitObjectComposer : HitObjectComposer
{
public override Playfield Playfield { get; }
public override IEnumerable<DrawableHitObject> HitObjects => Enumerable.Empty<DrawableHitObject>();
public override bool CursorInPlacementArea => false;
public TestHitObjectComposer(Playfield playfield)
{
Playfield = playfield;
}
public Drawable Child
{
set => InternalChild = value;
}
public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition)
{
throw new System.NotImplementedException();
}
public override float GetBeatSnapDistanceAt(double referenceTime)
{
throw new System.NotImplementedException();
}
public override float DurationToDistance(double referenceTime, double duration)
{
throw new System.NotImplementedException();
}
public override double DistanceToDuration(double referenceTime, float distance)
{
throw new System.NotImplementedException();
}
public override double GetSnappedDurationFromDistance(double referenceTime, float distance)
{
throw new System.NotImplementedException();
}
public override float GetSnappedDistanceFromDistance(double referenceTime, float distance)
{
throw new System.NotImplementedException();
}
}
}

View File

@ -20,9 +20,6 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
private readonly EditNotePiece headPiece;
private readonly EditNotePiece tailPiece;
[Resolved]
private IManiaHitObjectComposer composer { get; set; }
[Resolved]
private IScrollingInfo scrollingInfo { get; set; }

View File

@ -18,9 +18,6 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
[Resolved]
private IScrollingInfo scrollingInfo { get; set; }
[Resolved]
private IManiaHitObjectComposer composer { get; set; }
protected ManiaSelectionBlueprint(DrawableHitObject drawableObject)
: base(drawableObject)
{

View File

@ -1,12 +0,0 @@
// 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.Rulesets.Mania.UI;
namespace osu.Game.Rulesets.Mania.Edit
{
public interface IManiaHitObjectComposer
{
ManiaPlayfield Playfield { get; }
}
}

View File

@ -11,6 +11,8 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI.Scrolling;
@ -63,9 +65,9 @@ namespace osu.Game.Rulesets.Mania.Edit
private (double start, double end)? selectionTimeRange;
[BackgroundDependencyLoader]
private void load(IManiaHitObjectComposer composer)
private void load(HitObjectComposer composer)
{
foreach (var stage in composer.Playfield.Stages)
foreach (var stage in ((ManiaPlayfield)composer.Playfield).Stages)
{
foreach (var column in stage.Columns)
{

View File

@ -20,8 +20,7 @@ using osuTK;
namespace osu.Game.Rulesets.Mania.Edit
{
[Cached(Type = typeof(IManiaHitObjectComposer))]
public class ManiaHitObjectComposer : HitObjectComposer<ManiaHitObject>, IManiaHitObjectComposer
public class ManiaHitObjectComposer : HitObjectComposer<ManiaHitObject>
{
private DrawableManiaEditRuleset drawableRuleset;
private ManiaBeatSnapGrid beatSnapGrid;
@ -50,7 +49,7 @@ namespace osu.Game.Rulesets.Mania.Edit
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
=> dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
public ManiaPlayfield Playfield => ((ManiaPlayfield)drawableRuleset.Playfield);
public new ManiaPlayfield Playfield => ((ManiaPlayfield)drawableRuleset.Playfield);
public IScrollingInfo ScrollingInfo => drawableRuleset.ScrollingInfo;

View File

@ -4,6 +4,7 @@
using System;
using System.Linq;
using osu.Framework.Allocation;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Mania.Edit.Blueprints;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.UI.Scrolling;
@ -17,7 +18,7 @@ namespace osu.Game.Rulesets.Mania.Edit
private IScrollingInfo scrollingInfo { get; set; }
[Resolved]
private IManiaHitObjectComposer composer { get; set; }
private HitObjectComposer composer { get; set; }
public override bool HandleMovement(MoveSelectionEvent moveEvent)
{
@ -31,7 +32,9 @@ namespace osu.Game.Rulesets.Mania.Edit
private void performColumnMovement(int lastColumn, MoveSelectionEvent moveEvent)
{
var currentColumn = composer.Playfield.GetColumnByPosition(moveEvent.ScreenSpacePosition);
var maniaPlayfield = ((ManiaHitObjectComposer)composer).Playfield;
var currentColumn = maniaPlayfield.GetColumnByPosition(moveEvent.ScreenSpacePosition);
if (currentColumn == null)
return;
@ -50,7 +53,7 @@ namespace osu.Game.Rulesets.Mania.Edit
maxColumn = obj.Column;
}
columnDelta = Math.Clamp(columnDelta, -minColumn, composer.Playfield.TotalColumns - 1 - maxColumn);
columnDelta = Math.Clamp(columnDelta, -minColumn, maniaPlayfield.TotalColumns - 1 - maxColumn);
foreach (var obj in SelectedHitObjects.OfType<ManiaHitObject>())
obj.Column += columnDelta;

View File

@ -12,6 +12,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints
{
protected new T HitObject => (T)DrawableObject.HitObject;
protected override bool AlwaysShowWhenSelected => true;
protected OsuSelectionBlueprint(DrawableHitObject drawableObject)
: base(drawableObject)
{

View File

@ -8,7 +8,7 @@ using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Scoring
{
internal class OsuScoreProcessor : ScoreProcessor
public class OsuScoreProcessor : ScoreProcessor
{
protected override JudgementResult CreateResult(HitObject hitObject, Judgement judgement) => new OsuJudgementResult(hitObject, judgement);

View File

@ -156,7 +156,7 @@ namespace osu.Game.Tests.Beatmaps.IO
var manager = osu.Dependencies.Get<BeatmapManager>();
// ReSharper disable once AccessToModifiedClosure
manager.ItemAdded.BindValueChanged(_ => Interlocked.Increment(ref itemAddRemoveFireCount));
manager.ItemUpdated.BindValueChanged(_ => Interlocked.Increment(ref itemAddRemoveFireCount));
manager.ItemRemoved.BindValueChanged(_ => Interlocked.Increment(ref itemAddRemoveFireCount));
var imported = await LoadOszIntoOsu(osu);
@ -166,7 +166,7 @@ namespace osu.Game.Tests.Beatmaps.IO
imported.Hash += "-changed";
manager.Update(imported);
Assert.AreEqual(0, itemAddRemoveFireCount -= 2);
Assert.AreEqual(0, itemAddRemoveFireCount -= 1);
checkBeatmapSetCount(osu, 1);
checkBeatmapCount(osu, 12);

View File

@ -28,6 +28,7 @@ namespace osu.Game.Tests.Visual.Gameplay
AddStep(@"locally available state", () => downloadButton.SetDownloadState(DownloadState.LocallyAvailable));
AddStep(@"not downloaded state", () => downloadButton.SetDownloadState(DownloadState.NotDownloaded));
createButton(false);
createButtonNoScore();
}
private void createButton(bool withReplay)
@ -40,6 +41,22 @@ namespace osu.Game.Tests.Visual.Gameplay
Origin = Anchor.Centre,
};
});
AddUntilStep("wait for load", () => downloadButton.IsLoaded);
}
private void createButtonNoScore()
{
AddStep("create button with null score", () =>
{
Child = downloadButton = new TestReplayDownloadButton(null)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
};
});
AddUntilStep("wait for load", () => downloadButton.IsLoaded);
}
private ScoreInfo getScoreInfo(bool replayAvailable)

View File

@ -0,0 +1,61 @@
// 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.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu;
using osu.Game.Scoring;
using osu.Game.Screens.Ranking;
using osu.Game.Screens.Ranking.Contracted;
using osuTK;
namespace osu.Game.Tests.Visual.Ranking
{
public class TestSceneContractedPanelMiddleContent : OsuTestScene
{
[Resolved]
private RulesetStore rulesetStore { get; set; }
[Test]
public void TestShowPanel()
{
AddStep("show example score", () => showPanel(CreateWorkingBeatmap(CreateBeatmap(new OsuRuleset().RulesetInfo)), new TestScoreInfo(new OsuRuleset().RulesetInfo)));
}
private void showPanel(WorkingBeatmap workingBeatmap, ScoreInfo score)
{
Child = new ContractedPanelMiddleContentContainer(workingBeatmap, score);
}
private class ContractedPanelMiddleContentContainer : Container
{
[Cached]
private Bindable<WorkingBeatmap> workingBeatmap { get; set; }
public ContractedPanelMiddleContentContainer(WorkingBeatmap beatmap, ScoreInfo score)
{
workingBeatmap = new Bindable<WorkingBeatmap>(beatmap);
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
Size = new Vector2(ScorePanel.CONTRACTED_WIDTH, 460);
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4Extensions.FromHex("#353535"),
},
new ContractedPanelMiddleContent(score),
};
}
}
}
}

View File

@ -1,7 +1,6 @@
// 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 NUnit.Framework;
using osu.Framework.Allocation;
@ -14,10 +13,7 @@ using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using osu.Game.Screens.Ranking;
using osu.Game.Screens.Ranking.Expanded;
@ -37,7 +33,7 @@ namespace osu.Game.Tests.Visual.Ranking
{
var author = new User { Username = "mapper_name" };
AddStep("show example score", () => showPanel(createTestBeatmap(author), createTestScore()));
AddStep("show example score", () => showPanel(createTestBeatmap(author), new TestScoreInfo(new OsuRuleset().RulesetInfo)));
AddAssert("mapper name present", () => this.ChildrenOfType<OsuSpriteText>().Any(spriteText => spriteText.Text == "mapper_name"));
}
@ -45,7 +41,7 @@ namespace osu.Game.Tests.Visual.Ranking
[Test]
public void TestMapWithUnknownMapper()
{
AddStep("show example score", () => showPanel(createTestBeatmap(null), createTestScore()));
AddStep("show example score", () => showPanel(createTestBeatmap(null), new TestScoreInfo(new OsuRuleset().RulesetInfo)));
AddAssert("mapped by text not present", () =>
this.ChildrenOfType<OsuSpriteText>().All(spriteText => !containsAny(spriteText.Text, "mapped", "by")));
@ -66,29 +62,6 @@ namespace osu.Game.Tests.Visual.Ranking
return new TestWorkingBeatmap(beatmap);
}
private ScoreInfo createTestScore() => new ScoreInfo
{
User = new User
{
Id = 2,
Username = "peppy",
},
Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo,
Mods = new Mod[] { new OsuModHardRock(), new OsuModDoubleTime() },
TotalScore = 999999,
Accuracy = 0.95,
MaxCombo = 999,
Rank = ScoreRank.S,
Date = DateTimeOffset.Now,
Statistics =
{
{ HitResult.Miss, 1 },
{ HitResult.Meh, 50 },
{ HitResult.Good, 100 },
{ HitResult.Great, 300 },
}
};
private bool containsAny(string text, params string[] stringsToMatch) => stringsToMatch.Any(text.Contains);
private class ExpandedPanelMiddleContentContainer : Container

View File

@ -5,8 +5,8 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Osu;
using osu.Game.Screens.Ranking.Expanded;
using osu.Game.Users;
using osuTK;
namespace osu.Game.Tests.Visual.Ranking
@ -27,7 +27,7 @@ namespace osu.Game.Tests.Visual.Ranking
RelativeSizeAxes = Axes.Both,
Colour = Color4Extensions.FromHex("#444"),
},
new ExpandedPanelTopContent(new User { Id = 2, Username = "peppy" }),
new ExpandedPanelTopContent(new TestScoreInfo(new OsuRuleset().RulesetInfo).User),
}
};
}

View File

@ -1,8 +1,6 @@
// 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 NUnit.Framework;
using osu.Framework.Allocation;
@ -11,13 +9,10 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Screens;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using osu.Game.Screens;
using osu.Game.Screens.Play;
using osu.Game.Screens.Ranking;
using osu.Game.Tests.Beatmaps;
using osu.Game.Users;
namespace osu.Game.Tests.Visual.Ranking
{
@ -41,26 +36,7 @@ namespace osu.Game.Tests.Visual.Ranking
Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmapInfo);
}
private TestSoloResults createResultsScreen() => new TestSoloResults(new ScoreInfo
{
TotalScore = 2845370,
Accuracy = 0.98,
MaxCombo = 123,
Rank = ScoreRank.A,
Date = DateTimeOffset.Now,
Statistics = new Dictionary<HitResult, int>
{
{ HitResult.Great, 50 },
{ HitResult.Good, 20 },
{ HitResult.Meh, 50 },
{ HitResult.Miss, 1 }
},
Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo,
User = new User
{
Username = "peppy",
}
});
private TestSoloResults createResultsScreen() => new TestSoloResults(new TestScoreInfo(new OsuRuleset().RulesetInfo));
[Test]
public void ResultsWithoutPlayer()

View File

@ -1,28 +1,23 @@
// 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 NUnit.Framework;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using osu.Game.Screens.Ranking;
using osu.Game.Tests.Beatmaps;
using osu.Game.Users;
namespace osu.Game.Tests.Visual.Ranking
{
public class TestSceneScorePanel : OsuTestScene
{
private ScorePanel panel;
[Test]
public void TestDRank()
{
var score = createScore();
score.Accuracy = 0.5;
score.Rank = ScoreRank.D;
var score = new TestScoreInfo(new OsuRuleset().RulesetInfo) { Accuracy = 0.5, Rank = ScoreRank.D };
addPanelStep(score);
}
@ -30,9 +25,7 @@ namespace osu.Game.Tests.Visual.Ranking
[Test]
public void TestCRank()
{
var score = createScore();
score.Accuracy = 0.75;
score.Rank = ScoreRank.C;
var score = new TestScoreInfo(new OsuRuleset().RulesetInfo) { Accuracy = 0.75, Rank = ScoreRank.C };
addPanelStep(score);
}
@ -40,9 +33,7 @@ namespace osu.Game.Tests.Visual.Ranking
[Test]
public void TestBRank()
{
var score = createScore();
score.Accuracy = 0.85;
score.Rank = ScoreRank.B;
var score = new TestScoreInfo(new OsuRuleset().RulesetInfo) { Accuracy = 0.85, Rank = ScoreRank.B };
addPanelStep(score);
}
@ -50,9 +41,7 @@ namespace osu.Game.Tests.Visual.Ranking
[Test]
public void TestARank()
{
var score = createScore();
score.Accuracy = 0.925;
score.Rank = ScoreRank.A;
var score = new TestScoreInfo(new OsuRuleset().RulesetInfo) { Accuracy = 0.925, Rank = ScoreRank.A };
addPanelStep(score);
}
@ -60,9 +49,7 @@ namespace osu.Game.Tests.Visual.Ranking
[Test]
public void TestSRank()
{
var score = createScore();
score.Accuracy = 0.975;
score.Rank = ScoreRank.S;
var score = new TestScoreInfo(new OsuRuleset().RulesetInfo) { Accuracy = 0.975, Rank = ScoreRank.S };
addPanelStep(score);
}
@ -70,9 +57,7 @@ namespace osu.Game.Tests.Visual.Ranking
[Test]
public void TestAlmostSSRank()
{
var score = createScore();
score.Accuracy = 0.9999;
score.Rank = ScoreRank.S;
var score = new TestScoreInfo(new OsuRuleset().RulesetInfo) { Accuracy = 0.9999, Rank = ScoreRank.S };
addPanelStep(score);
}
@ -80,9 +65,7 @@ namespace osu.Game.Tests.Visual.Ranking
[Test]
public void TestSSRank()
{
var score = createScore();
score.Accuracy = 1;
score.Rank = ScoreRank.X;
var score = new TestScoreInfo(new OsuRuleset().RulesetInfo) { Accuracy = 1, Rank = ScoreRank.X };
addPanelStep(score);
}
@ -90,44 +73,42 @@ namespace osu.Game.Tests.Visual.Ranking
[Test]
public void TestAllHitResults()
{
var score = createScore();
score.Statistics[HitResult.Perfect] = 350;
score.Statistics[HitResult.Ok] = 200;
var score = new TestScoreInfo(new OsuRuleset().RulesetInfo) { Statistics = { [HitResult.Perfect] = 350, [HitResult.Ok] = 200 } };
addPanelStep(score);
}
private void addPanelStep(ScoreInfo score) => AddStep("add panel", () =>
[Test]
public void TestContractedPanel()
{
Child = new ScorePanel(score)
var score = new TestScoreInfo(new OsuRuleset().RulesetInfo) { Accuracy = 0.925, Rank = ScoreRank.A };
addPanelStep(score, PanelState.Contracted);
}
[Test]
public void TestExpandAndContract()
{
var score = new TestScoreInfo(new OsuRuleset().RulesetInfo) { Accuracy = 0.925, Rank = ScoreRank.A };
addPanelStep(score, PanelState.Contracted);
AddWaitStep("wait for transition", 10);
AddStep("expand panel", () => panel.State = PanelState.Expanded);
AddWaitStep("wait for transition", 10);
AddStep("contract panel", () => panel.State = PanelState.Contracted);
AddWaitStep("wait for transition", 10);
}
private void addPanelStep(ScoreInfo score, PanelState state = PanelState.Expanded) => AddStep("add panel", () =>
{
Child = panel = new ScorePanel(score)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
State = PanelState.Expanded
State = state
};
});
private ScoreInfo createScore() => new ScoreInfo
{
User = new User
{
Id = 2,
Username = "peppy",
},
Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo,
Mods = new Mod[] { new OsuModHardRock(), new OsuModDoubleTime() },
TotalScore = 2845370,
Accuracy = 0.95,
MaxCombo = 999,
Rank = ScoreRank.S,
Date = DateTimeOffset.Now,
Statistics =
{
{ HitResult.Miss, 1 },
{ HitResult.Meh, 50 },
{ HitResult.Good, 100 },
{ HitResult.Great, 300 },
}
};
}
}

View File

@ -0,0 +1,208 @@
// 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 NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Testing;
using osu.Framework.Utils;
using osu.Game.Rulesets.Osu;
using osu.Game.Scoring;
using osu.Game.Screens.Ranking;
using osuTK.Input;
namespace osu.Game.Tests.Visual.Ranking
{
public class TestSceneScorePanelList : OsuManualInputManagerTestScene
{
private ScorePanelList list;
[Test]
public void TestEmptyList()
{
createListStep(() => new ScorePanelList());
}
[Test]
public void TestEmptyListWithSelectedScore()
{
createListStep(() => new ScorePanelList
{
SelectedScore = { Value = new TestScoreInfo(new OsuRuleset().RulesetInfo) }
});
}
[Test]
public void TestAddPanelAfterSelectingScore()
{
var score = new TestScoreInfo(new OsuRuleset().RulesetInfo);
createListStep(() => new ScorePanelList
{
SelectedScore = { Value = score }
});
AddStep("add panel", () => list.AddScore(score));
assertScoreState(score, true);
assertExpandedPanelCentred();
}
[Test]
public void TestAddPanelBeforeSelectingScore()
{
var score = new TestScoreInfo(new OsuRuleset().RulesetInfo);
createListStep(() => new ScorePanelList());
AddStep("add panel", () => list.AddScore(score));
assertScoreState(score, false);
assertFirstPanelCentred();
AddStep("select score", () => list.SelectedScore.Value = score);
assertScoreState(score, true);
assertExpandedPanelCentred();
}
[Test]
public void TestAddManyNonExpandedPanels()
{
createListStep(() => new ScorePanelList());
AddStep("add many scores", () =>
{
for (int i = 0; i < 20; i++)
list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo));
});
assertFirstPanelCentred();
}
[Test]
public void TestAddManyScoresAfterExpandedPanel()
{
var initialScore = new TestScoreInfo(new OsuRuleset().RulesetInfo);
createListStep(() => new ScorePanelList());
AddStep("add initial panel and select", () =>
{
list.AddScore(initialScore);
list.SelectedScore.Value = initialScore;
});
AddStep("add many scores", () =>
{
for (int i = 0; i < 20; i++)
list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo) { TotalScore = initialScore.TotalScore - i - 1 });
});
assertScoreState(initialScore, true);
assertExpandedPanelCentred();
}
[Test]
public void TestAddManyScoresBeforeExpandedPanel()
{
var initialScore = new TestScoreInfo(new OsuRuleset().RulesetInfo);
createListStep(() => new ScorePanelList());
AddStep("add initial panel and select", () =>
{
list.AddScore(initialScore);
list.SelectedScore.Value = initialScore;
});
AddStep("add scores", () =>
{
for (int i = 0; i < 20; i++)
list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo) { TotalScore = initialScore.TotalScore + i + 1 });
});
assertScoreState(initialScore, true);
assertExpandedPanelCentred();
}
[Test]
public void TestAddManyPanelsOnBothSidesOfExpandedPanel()
{
var initialScore = new TestScoreInfo(new OsuRuleset().RulesetInfo);
createListStep(() => new ScorePanelList());
AddStep("add initial panel and select", () =>
{
list.AddScore(initialScore);
list.SelectedScore.Value = initialScore;
});
AddStep("add scores after", () =>
{
for (int i = 0; i < 20; i++)
list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo) { TotalScore = initialScore.TotalScore - i - 1 });
for (int i = 0; i < 20; i++)
list.AddScore(new TestScoreInfo(new OsuRuleset().RulesetInfo) { TotalScore = initialScore.TotalScore + i + 1 });
});
assertScoreState(initialScore, true);
assertExpandedPanelCentred();
}
[Test]
public void TestSelectMultipleScores()
{
var firstScore = new TestScoreInfo(new OsuRuleset().RulesetInfo);
var secondScore = new TestScoreInfo(new OsuRuleset().RulesetInfo);
createListStep(() => new ScorePanelList());
AddStep("add scores and select first", () =>
{
list.AddScore(firstScore);
list.AddScore(secondScore);
list.SelectedScore.Value = firstScore;
});
assertScoreState(firstScore, true);
assertScoreState(secondScore, false);
AddStep("select second score", () =>
{
InputManager.MoveMouseTo(list.ChildrenOfType<ScorePanel>().Single(p => p.Score == secondScore));
InputManager.Click(MouseButton.Left);
});
assertScoreState(firstScore, false);
assertScoreState(secondScore, true);
assertExpandedPanelCentred();
}
private void createListStep(Func<ScorePanelList> creationFunc)
{
AddStep("create list", () => Child = list = creationFunc().With(d =>
{
d.Anchor = Anchor.Centre;
d.Origin = Anchor.Centre;
}));
AddUntilStep("wait for load", () => list.IsLoaded);
}
private void assertExpandedPanelCentred() => AddUntilStep("expanded panel centred", () =>
{
var expandedPanel = list.ChildrenOfType<ScorePanel>().Single(p => p.State == PanelState.Expanded);
return Precision.AlmostEquals(expandedPanel.ScreenSpaceDrawQuad.Centre.X, list.ScreenSpaceDrawQuad.Centre.X, 1);
});
private void assertFirstPanelCentred()
=> AddUntilStep("first panel centred", () => Precision.AlmostEquals(list.ChildrenOfType<ScorePanel>().First().ScreenSpaceDrawQuad.Centre.X, list.ScreenSpaceDrawQuad.Centre.X, 1));
private void assertScoreState(ScoreInfo score, bool expanded)
=> AddUntilStep($"score expanded = {expanded}", () => (list.ChildrenOfType<ScorePanel>().Single(p => p.Score == score).State == PanelState.Expanded) == expanded);
}
}

View File

@ -55,12 +55,12 @@ namespace osu.Game.Database
public Action<Notification> PostNotification { protected get; set; }
/// <summary>
/// Fired when a new <typeparamref name="TModel"/> becomes available in the database.
/// Fired when a new or updated <typeparamref name="TModel"/> becomes available in the database.
/// This is not guaranteed to run on the update thread.
/// </summary>
public IBindable<WeakReference<TModel>> ItemAdded => itemAdded;
public IBindable<WeakReference<TModel>> ItemUpdated => itemUpdated;
private readonly Bindable<WeakReference<TModel>> itemAdded = new Bindable<WeakReference<TModel>>();
private readonly Bindable<WeakReference<TModel>> itemUpdated = new Bindable<WeakReference<TModel>>();
/// <summary>
/// Fired when a <typeparamref name="TModel"/> is removed from the database.
@ -90,7 +90,7 @@ namespace osu.Game.Database
ContextFactory = contextFactory;
ModelStore = modelStore;
ModelStore.ItemAdded += item => handleEvent(() => itemAdded.Value = new WeakReference<TModel>(item));
ModelStore.ItemUpdated += item => handleEvent(() => itemUpdated.Value = new WeakReference<TModel>(item));
ModelStore.ItemRemoved += item => handleEvent(() => itemRemoved.Value = new WeakReference<TModel>(item));
exportStorage = storage.GetStorageForDirectory("exports");

View File

@ -13,7 +13,7 @@ namespace osu.Game.Database
public interface IModelManager<TModel>
where TModel : class
{
IBindable<WeakReference<TModel>> ItemAdded { get; }
IBindable<WeakReference<TModel>> ItemUpdated { get; }
IBindable<WeakReference<TModel>> ItemRemoved { get; }
}

View File

@ -16,7 +16,14 @@ namespace osu.Game.Database
public abstract class MutableDatabaseBackedStore<T> : DatabaseBackedStore
where T : class, IHasPrimaryKey, ISoftDelete
{
public event Action<T> ItemAdded;
/// <summary>
/// Fired when an item was added or updated.
/// </summary>
public event Action<T> ItemUpdated;
/// <summary>
/// Fired when an item was removed.
/// </summary>
public event Action<T> ItemRemoved;
protected MutableDatabaseBackedStore(IDatabaseContextFactory contextFactory, Storage storage = null)
@ -41,7 +48,7 @@ namespace osu.Game.Database
context.Attach(item);
}
ItemAdded?.Invoke(item);
ItemUpdated?.Invoke(item);
}
/// <summary>
@ -53,8 +60,7 @@ namespace osu.Game.Database
using (var usage = ContextFactory.GetForWrite())
usage.Context.Update(item);
ItemRemoved?.Invoke(item);
ItemAdded?.Invoke(item);
ItemUpdated?.Invoke(item);
}
/// <summary>
@ -91,7 +97,7 @@ namespace osu.Game.Database
item.DeletePending = false;
}
ItemAdded?.Invoke(item);
ItemUpdated?.Invoke(item);
return true;
}

View File

@ -27,7 +27,15 @@ namespace osu.Game.Online.API.Requests
private string directionString => SortDirection == SortDirection.Descending ? @"desc" : @"asc";
public SearchBeatmapSetsRequest(string query, RulesetInfo ruleset, Cursor cursor = null, SearchCategory searchCategory = SearchCategory.Any, SortCriteria sortCriteria = SortCriteria.Ranked, SortDirection sortDirection = SortDirection.Descending)
public SearchBeatmapSetsRequest(
string query,
RulesetInfo ruleset,
Cursor cursor = null,
SearchCategory searchCategory = SearchCategory.Any,
SortCriteria sortCriteria = SortCriteria.Ranked,
SortDirection sortDirection = SortDirection.Descending,
SearchGenre genre = SearchGenre.Any,
SearchLanguage language = SearchLanguage.Any)
{
this.query = string.IsNullOrEmpty(query) ? string.Empty : System.Uri.EscapeDataString(query);
this.ruleset = ruleset;
@ -36,8 +44,8 @@ namespace osu.Game.Online.API.Requests
SearchCategory = searchCategory;
SortCriteria = sortCriteria;
SortDirection = sortDirection;
Genre = SearchGenre.Any;
Language = SearchLanguage.Any;
Genre = genre;
Language = language;
}
protected override WebRequest CreateWebRequest()

View File

@ -34,7 +34,7 @@ namespace osu.Game.Online
Model.Value = model;
}
private IBindable<WeakReference<TModel>> managerAdded;
private IBindable<WeakReference<TModel>> managedUpdated;
private IBindable<WeakReference<TModel>> managerRemoved;
private IBindable<WeakReference<ArchiveDownloadRequest<TModel>>> managerDownloadBegan;
private IBindable<WeakReference<ArchiveDownloadRequest<TModel>>> managerDownloadFailed;
@ -56,8 +56,8 @@ namespace osu.Game.Online
managerDownloadBegan.BindValueChanged(downloadBegan);
managerDownloadFailed = manager.DownloadFailed.GetBoundCopy();
managerDownloadFailed.BindValueChanged(downloadFailed);
managerAdded = manager.ItemAdded.GetBoundCopy();
managerAdded.BindValueChanged(itemAdded);
managedUpdated = manager.ItemUpdated.GetBoundCopy();
managedUpdated.BindValueChanged(itemUpdated);
managerRemoved = manager.ItemRemoved.GetBoundCopy();
managerRemoved.BindValueChanged(itemRemoved);
}
@ -128,7 +128,7 @@ namespace osu.Game.Online
private void onRequestFailure(Exception e) => Schedule(() => attachDownload(null));
private void itemAdded(ValueChangedEvent<WeakReference<TModel>> weakItem)
private void itemUpdated(ValueChangedEvent<WeakReference<TModel>> weakItem)
{
if (weakItem.NewValue.TryGetTarget(out var item))
setDownloadStateFromManager(item, DownloadState.LocallyAvailable);

View File

@ -192,7 +192,7 @@ namespace osu.Game
ScoreManager.Delete(getBeatmapScores(item), true);
});
BeatmapManager.ItemAdded.BindValueChanged(i =>
BeatmapManager.ItemUpdated.BindValueChanged(i =>
{
if (i.NewValue.TryGetTarget(out var item))
ScoreManager.Undelete(getBeatmapScores(item), true);
@ -229,8 +229,8 @@ namespace osu.Game
FileStore.Cleanup();
if (API is APIAccess apiAcces)
AddInternal(apiAcces);
if (API is APIAccess apiAccess)
AddInternal(apiAccess);
AddInternal(RulesetConfigCache);
GlobalActionContainer globalBinding;

View File

@ -177,7 +177,9 @@ namespace osu.Game.Overlays.BeatmapListing
lastResponse?.Cursor,
searchControl.Category.Value,
sortControl.Current.Value,
sortControl.SortDirection.Value);
sortControl.SortDirection.Value,
searchControl.Genre.Value,
searchControl.Language.Value);
getSetsRequest.Success += response =>
{
@ -186,6 +188,9 @@ namespace osu.Game.Overlays.BeatmapListing
if (sets.Count == 0)
noMoreResults = true;
if (CurrentPage == 0)
searchControl.BeatmapSet = sets.FirstOrDefault();
lastResponse = response;
getSetsRequest = null;

View File

@ -60,14 +60,14 @@ namespace osu.Game.Overlays
[Resolved(canBeNull: true)]
private OnScreenDisplay onScreenDisplay { get; set; }
private IBindable<WeakReference<BeatmapSetInfo>> managerAdded;
private IBindable<WeakReference<BeatmapSetInfo>> managerUpdated;
private IBindable<WeakReference<BeatmapSetInfo>> managerRemoved;
[BackgroundDependencyLoader]
private void load()
{
managerAdded = beatmaps.ItemAdded.GetBoundCopy();
managerAdded.BindValueChanged(beatmapAdded);
managerUpdated = beatmaps.ItemUpdated.GetBoundCopy();
managerUpdated.BindValueChanged(beatmapUpdated);
managerRemoved = beatmaps.ItemRemoved.GetBoundCopy();
managerRemoved.BindValueChanged(beatmapRemoved);
@ -98,14 +98,14 @@ namespace osu.Game.Overlays
/// </summary>
public bool IsPlaying => current?.Track.IsRunning ?? false;
private void beatmapAdded(ValueChangedEvent<WeakReference<BeatmapSetInfo>> weakSet)
private void beatmapUpdated(ValueChangedEvent<WeakReference<BeatmapSetInfo>> weakSet)
{
if (weakSet.NewValue.TryGetTarget(out var set))
{
Schedule(() =>
{
if (!beatmapSets.Contains(set))
beatmapSets.Add(set);
beatmapSets.Remove(set);
beatmapSets.Add(set);
});
}
}

View File

@ -32,7 +32,7 @@ namespace osu.Game.Overlays.Settings.Sections
[Resolved]
private SkinManager skins { get; set; }
private IBindable<WeakReference<SkinInfo>> managerAdded;
private IBindable<WeakReference<SkinInfo>> managerUpdated;
private IBindable<WeakReference<SkinInfo>> managerRemoved;
[BackgroundDependencyLoader]
@ -73,8 +73,8 @@ namespace osu.Game.Overlays.Settings.Sections
},
};
managerAdded = skins.ItemAdded.GetBoundCopy();
managerAdded.BindValueChanged(itemAdded);
managerUpdated = skins.ItemUpdated.GetBoundCopy();
managerUpdated.BindValueChanged(itemUpdated);
managerRemoved = skins.ItemRemoved.GetBoundCopy();
managerRemoved.BindValueChanged(itemRemoved);
@ -92,10 +92,10 @@ namespace osu.Game.Overlays.Settings.Sections
dropdownBindable.BindValueChanged(skin => configBindable.Value = skin.NewValue.ID);
}
private void itemAdded(ValueChangedEvent<WeakReference<SkinInfo>> weakItem)
private void itemUpdated(ValueChangedEvent<WeakReference<SkinInfo>> weakItem)
{
if (weakItem.NewValue.TryGetTarget(out var item))
Schedule(() => skinDropdown.Items = skinDropdown.Items.Append(item).ToArray());
Schedule(() => skinDropdown.Items = skinDropdown.Items.Where(i => !i.Equals(item)).Append(item).ToArray());
}
private void itemRemoved(ValueChangedEvent<WeakReference<SkinInfo>> weakItem)

View File

@ -48,6 +48,8 @@ namespace osu.Game.Rulesets.Edit
protected ComposeBlueprintContainer BlueprintContainer { get; private set; }
public override Playfield Playfield => drawableRulesetWrapper.Playfield;
private DrawableEditRulesetWrapper<TObject> drawableRulesetWrapper;
protected readonly Container LayerBelowRuleset = new Container { RelativeSizeAxes = Axes.Both };
@ -102,6 +104,7 @@ namespace osu.Game.Rulesets.Edit
{
Name = "Content",
RelativeSizeAxes = Axes.Both,
Masking = true,
Children = new Drawable[]
{
// layers below playfield
@ -260,11 +263,13 @@ namespace osu.Game.Rulesets.Edit
[Cached(typeof(IPositionSnapProvider))]
public abstract class HitObjectComposer : CompositeDrawable, IPositionSnapProvider
{
internal HitObjectComposer()
protected HitObjectComposer()
{
RelativeSizeAxes = Axes.Both;
}
public abstract Playfield Playfield { get; }
/// <summary>
/// All the <see cref="DrawableHitObject"/>s.
/// </summary>

View File

@ -15,7 +15,12 @@ namespace osu.Game.Rulesets.Edit
/// </summary>
public readonly DrawableHitObject DrawableObject;
protected override bool ShouldBeAlive => (DrawableObject.IsAlive && DrawableObject.IsPresent) || State == SelectionState.Selected;
/// <summary>
/// Whether the blueprint should be shown even when the <see cref="DrawableObject"/> is not alive.
/// </summary>
protected virtual bool AlwaysShowWhenSelected => false;
protected override bool ShouldBeAlive => (DrawableObject.IsAlive && DrawableObject.IsPresent) || (AlwaysShowWhenSelected && State == SelectionState.Selected);
protected OverlaySelectionBlueprint(DrawableHitObject drawableObject)
: base(drawableObject.HitObject)

View File

@ -44,6 +44,8 @@ namespace osu.Game.Screens.Edit.Compose.Components
private readonly BindableList<HitObject> selectedHitObjects = new BindableList<HitObject>();
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
[Resolved(canBeNull: true)]
private IPositionSnapProvider snapProvider { get; set; }

View File

@ -32,14 +32,14 @@ namespace osu.Game.Screens.Multi.Match.Components
Text = "Start";
}
private IBindable<WeakReference<BeatmapSetInfo>> managerAdded;
private IBindable<WeakReference<BeatmapSetInfo>> managerUpdated;
private IBindable<WeakReference<BeatmapSetInfo>> managerRemoved;
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
managerAdded = beatmaps.ItemAdded.GetBoundCopy();
managerAdded.BindValueChanged(beatmapAdded);
managerUpdated = beatmaps.ItemUpdated.GetBoundCopy();
managerUpdated.BindValueChanged(beatmapUpdated);
managerRemoved = beatmaps.ItemRemoved.GetBoundCopy();
managerRemoved.BindValueChanged(beatmapRemoved);
@ -61,7 +61,7 @@ namespace osu.Game.Screens.Multi.Match.Components
hasBeatmap = beatmaps.QueryBeatmap(b => b.OnlineBeatmapID == beatmapId) != null;
}
private void beatmapAdded(ValueChangedEvent<WeakReference<BeatmapSetInfo>> weakSet)
private void beatmapUpdated(ValueChangedEvent<WeakReference<BeatmapSetInfo>> weakSet)
{
if (weakSet.NewValue.TryGetTarget(out var set))
{

View File

@ -50,7 +50,7 @@ namespace osu.Game.Screens.Multi.Match
private LeaderboardChatDisplay leaderboardChatDisplay;
private MatchSettingsOverlay settingsOverlay;
private IBindable<WeakReference<BeatmapSetInfo>> managerAdded;
private IBindable<WeakReference<BeatmapSetInfo>> managerUpdated;
public MatchSubScreen(Room room)
{
@ -183,8 +183,8 @@ namespace osu.Game.Screens.Multi.Match
SelectedItem.BindValueChanged(_ => Scheduler.AddOnce(selectedItemChanged));
SelectedItem.Value = playlist.FirstOrDefault();
managerAdded = beatmapManager.ItemAdded.GetBoundCopy();
managerAdded.BindValueChanged(beatmapAdded);
managerUpdated = beatmapManager.ItemUpdated.GetBoundCopy();
managerUpdated.BindValueChanged(beatmapUpdated);
}
public override bool OnExiting(IScreen next)
@ -217,7 +217,7 @@ namespace osu.Game.Screens.Multi.Match
Beatmap.Value = beatmapManager.GetWorkingBeatmap(localBeatmap);
}
private void beatmapAdded(ValueChangedEvent<WeakReference<BeatmapSetInfo>> weakSet)
private void beatmapUpdated(ValueChangedEvent<WeakReference<BeatmapSetInfo>> weakSet)
{
Schedule(() =>
{

View File

@ -479,7 +479,7 @@ namespace osu.Game.Screens.Play
protected override bool OnScroll(ScrollEvent e) => mouseWheelDisabled.Value && !GameplayClockContainer.IsPaused.Value;
protected virtual ResultsScreen CreateResults(ScoreInfo score) => new ResultsScreen(score);
protected virtual ResultsScreen CreateResults(ScoreInfo score) => new SoloResultsScreen(score);
#region Fail Logic

View File

@ -30,7 +30,7 @@ namespace osu.Game.Screens.Play
this.Push(CreateResults(DrawableRuleset.ReplayScore.ScoreInfo));
}
protected override ResultsScreen CreateResults(ScoreInfo score) => new ResultsScreen(score, false);
protected override ResultsScreen CreateResults(ScoreInfo score) => new SoloResultsScreen(score, false);
protected override ScoreInfo CreateScore() => score.ScoreInfo;
}

View File

@ -0,0 +1,222 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Extensions;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.Leaderboards;
using osu.Game.Scoring;
using osu.Game.Screens.Play.HUD;
using osu.Game.Users;
using osu.Game.Users.Drawables;
using osu.Game.Utils;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Screens.Ranking.Contracted
{
/// <summary>
/// The content that appears in the middle of a contracted <see cref="ScorePanel"/>.
/// </summary>
public class ContractedPanelMiddleContent : CompositeDrawable
{
private readonly ScoreInfo score;
/// <summary>
/// Creates a new <see cref="ContractedPanelMiddleContent"/>.
/// </summary>
/// <param name="score">The <see cref="ScoreInfo"/> to display.</param>
public ContractedPanelMiddleContent(ScoreInfo score)
{
this.score = score;
RelativeSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load()
{
InternalChild = new GridContainer
{
RelativeSizeAxes = Axes.Both,
Content = new[]
{
new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
Masking = true,
CornerExponent = 2.5f,
CornerRadius = 20,
EdgeEffect = new EdgeEffectParameters
{
Colour = Color4.Black.Opacity(0.25f),
Type = EdgeEffectType.Shadow,
Radius = 1,
Offset = new Vector2(0, 4)
},
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4Extensions.FromHex("444")
},
new UserCoverBackground
{
RelativeSizeAxes = Axes.Both,
User = score.User,
Colour = ColourInfo.GradientVertical(Color4.White.Opacity(0.5f), Color4Extensions.FromHex("#444").Opacity(0))
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding(10),
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 10),
Children = new Drawable[]
{
new UpdateableAvatar(score.User)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Size = new Vector2(110),
Masking = true,
CornerExponent = 2.5f,
CornerRadius = 20,
EdgeEffect = new EdgeEffectParameters
{
Colour = Color4.Black.Opacity(0.25f),
Type = EdgeEffectType.Shadow,
Radius = 8,
Offset = new Vector2(0, 4),
}
},
new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = score.UserString,
Font = OsuFont.GetFont(size: 16, weight: FontWeight.SemiBold)
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 5),
ChildrenEnumerable = score.SortedStatistics.Select(s => createStatistic(s.Key.GetDescription(), s.Value.ToString()))
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Margin = new MarginPadding { Top = 10 },
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 5),
Children = new[]
{
createStatistic("Max Combo", $"x{score.MaxCombo}"),
createStatistic("Accuracy", $"{score.Accuracy.FormatAccuracy()}"),
}
},
new ModDisplay
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
AutoSizeAxes = Axes.Both,
ExpansionMode = ExpansionMode.AlwaysExpanded,
DisplayUnrankedText = false,
Current = { Value = score.Mods },
Scale = new Vector2(0.5f),
}
}
}
}
},
},
new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Vertical = 5 },
Child = new GridContainer
{
RelativeSizeAxes = Axes.Both,
Content = new[]
{
new Drawable[]
{
new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Text = score.TotalScore.ToString("N0"),
Font = OsuFont.GetFont(size: 20, weight: FontWeight.Medium, fixedWidth: true),
Spacing = new Vector2(-1, 0)
},
},
new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = 2 },
Child = new DrawableRank(score.Rank)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
}
}
},
},
RowDimensions = new[]
{
new Dimension(GridSizeMode.AutoSize),
}
}
}
},
},
RowDimensions = new[]
{
new Dimension(),
new Dimension(GridSizeMode.Absolute, 45),
}
};
}
private Drawable createStatistic(string key, string value) => new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Text = key,
Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold)
},
new OsuSpriteText
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Text = value,
Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold),
Colour = Color4Extensions.FromHex("#FFDD55")
}
}
};
}
}

View File

@ -2,6 +2,7 @@
// 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.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
@ -13,6 +14,8 @@ namespace osu.Game.Screens.Ranking
{
public class ReplayDownloadButton : DownloadTrackingComposite<ScoreInfo, ScoreManager>
{
public Bindable<ScoreInfo> Score => Model;
private DownloadButton button;
private ShakeContainer shakeContainer;
@ -23,7 +26,7 @@ namespace osu.Game.Screens.Ranking
if (State.Value == DownloadState.LocallyAvailable)
return ReplayAvailability.Local;
if (!string.IsNullOrEmpty(Model.Value.Hash))
if (!string.IsNullOrEmpty(Model.Value?.Hash))
return ReplayAvailability.Online;
return ReplayAvailability.NotAvailable;

View File

@ -2,7 +2,9 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@ -10,6 +12,7 @@ using osu.Framework.Graphics.Shapes;
using osu.Framework.Screens;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using osu.Game.Scoring;
using osu.Game.Screens.Backgrounds;
using osu.Game.Screens.Play;
@ -17,7 +20,7 @@ using osuTK;
namespace osu.Game.Screens.Ranking
{
public class ResultsScreen : OsuScreen
public abstract class ResultsScreen : OsuScreen
{
protected const float BACKGROUND_BLUR = 20;
@ -28,19 +31,26 @@ namespace osu.Game.Screens.Ranking
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap.Value);
public readonly Bindable<ScoreInfo> SelectedScore = new Bindable<ScoreInfo>();
public readonly ScoreInfo Score;
private readonly bool allowRetry;
[Resolved(CanBeNull = true)]
private Player player { get; set; }
public readonly ScoreInfo Score;
private readonly bool allowRetry;
[Resolved]
private IAPIProvider api { get; set; }
private Drawable bottomPanel;
private ScorePanelList panels;
public ResultsScreen(ScoreInfo score, bool allowRetry = true)
protected ResultsScreen(ScoreInfo score, bool allowRetry = true)
{
Score = score;
this.allowRetry = allowRetry;
SelectedScore.Value = score;
}
[BackgroundDependencyLoader]
@ -48,47 +58,68 @@ namespace osu.Game.Screens.Ranking
{
FillFlowContainer buttons;
InternalChildren = new[]
InternalChild = new GridContainer
{
new ResultsScrollContainer
RelativeSizeAxes = Axes.Both,
Content = new[]
{
Child = new ScorePanel(Score)
new Drawable[]
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
State = PanelState.Expanded
new ResultsScrollContainer
{
Child = panels = new ScorePanelList
{
RelativeSizeAxes = Axes.Both,
SelectedScore = { BindTarget = SelectedScore }
}
}
},
},
bottomPanel = new Container
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X,
Height = TwoLayerButton.SIZE_EXTENDED.Y,
Alpha = 0,
Children = new Drawable[]
new[]
{
new Box
bottomPanel = new Container
{
RelativeSizeAxes = Axes.Both,
Colour = Color4Extensions.FromHex("#333")
},
buttons = new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Both,
Spacing = new Vector2(5),
Direction = FillDirection.Horizontal,
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X,
Height = TwoLayerButton.SIZE_EXTENDED.Y,
Alpha = 0,
Children = new Drawable[]
{
new ReplayDownloadButton(Score) { Width = 300 },
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4Extensions.FromHex("#333")
},
buttons = new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Both,
Spacing = new Vector2(5),
Direction = FillDirection.Horizontal,
Children = new Drawable[]
{
new ReplayDownloadButton(null)
{
Score = { BindTarget = SelectedScore },
Width = 300
},
}
}
}
}
}
},
RowDimensions = new[]
{
new Dimension(),
new Dimension(GridSizeMode.AutoSize)
}
};
if (Score != null)
panels.AddScore(Score);
if (player != null && allowRetry)
{
buttons.Add(new RetryButton { Width = 300 });
@ -105,6 +136,27 @@ namespace osu.Game.Screens.Ranking
}
}
protected override void LoadComplete()
{
base.LoadComplete();
var req = FetchScores(scores => Schedule(() =>
{
foreach (var s in scores)
panels.AddScore(s);
}));
if (req != null)
api.Queue(req);
}
/// <summary>
/// Performs a fetch/refresh of scores to be displayed.
/// </summary>
/// <param name="scoresCallback">A callback which should be called when fetching is completed. Scheduling is not required.</param>
/// <returns>An <see cref="APIRequest"/> responsible for the fetch operation. This will be queued and performed automatically.</returns>
protected virtual APIRequest FetchScores(Action<IEnumerable<ScoreInfo>> scoresCallback) => null;
public override void OnEntering(IScreen last)
{
base.OnEntering(last);
@ -142,7 +194,7 @@ namespace osu.Game.Screens.Ranking
protected override void Update()
{
base.Update();
content.Height = Math.Max(768, DrawHeight);
content.Height = Math.Max(768 - TwoLayerButton.SIZE_EXTENDED.Y, DrawHeight);
}
}
}

View File

@ -9,7 +9,9 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events;
using osu.Game.Scoring;
using osu.Game.Screens.Ranking.Contracted;
using osu.Game.Screens.Ranking.Expanded;
using osuTK;
using osuTK.Graphics;
@ -21,12 +23,12 @@ namespace osu.Game.Screens.Ranking
/// <summary>
/// Width of the panel when contracted.
/// </summary>
private const float contracted_width = 160;
public const float CONTRACTED_WIDTH = 130;
/// <summary>
/// Height of the panel when contracted.
/// </summary>
private const float contracted_height = 320;
private const float contracted_height = 355;
/// <summary>
/// Width of the panel when expanded.
@ -46,7 +48,7 @@ namespace osu.Game.Screens.Ranking
/// <summary>
/// Height of the top layer when the panel is contracted.
/// </summary>
private const float contracted_top_layer_height = 40;
private const float contracted_top_layer_height = 30;
/// <summary>
/// Duration for the panel to resize into its expanded/contracted size.
@ -71,11 +73,12 @@ namespace osu.Game.Screens.Ranking
private static readonly ColourInfo expanded_top_layer_colour = ColourInfo.GradientVertical(Color4Extensions.FromHex("#444"), Color4Extensions.FromHex("#333"));
private static readonly ColourInfo expanded_middle_layer_colour = ColourInfo.GradientVertical(Color4Extensions.FromHex("#555"), Color4Extensions.FromHex("#333"));
private static readonly Color4 contracted_top_layer_colour = Color4Extensions.FromHex("#353535");
private static readonly Color4 contracted_middle_layer_colour = Color4Extensions.FromHex("#444");
private static readonly Color4 contracted_middle_layer_colour = Color4Extensions.FromHex("#353535");
public event Action<PanelState> StateChanged;
public readonly ScoreInfo Score;
private readonly ScoreInfo score;
private Container content;
private Container topLayerContainer;
private Drawable topLayerBackground;
@ -89,47 +92,54 @@ namespace osu.Game.Screens.Ranking
public ScorePanel(ScoreInfo score)
{
this.score = score;
Score = score;
}
[BackgroundDependencyLoader]
private void load()
{
InternalChildren = new Drawable[]
InternalChild = content = new Container
{
topLayerContainer = new Container
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(40),
Children = new Drawable[]
{
Name = "Top layer",
RelativeSizeAxes = Axes.X,
Height = 120,
Children = new Drawable[]
topLayerContainer = new Container
{
new Container
Name = "Top layer",
RelativeSizeAxes = Axes.X,
Alpha = 0,
Height = 120,
Children = new Drawable[]
{
RelativeSizeAxes = Axes.Both,
CornerRadius = 20,
CornerExponent = 2.5f,
Masking = true,
Child = topLayerBackground = new Box { RelativeSizeAxes = Axes.Both }
},
topLayerContentContainer = new Container { RelativeSizeAxes = Axes.Both }
}
},
middleLayerContainer = new Container
{
Name = "Middle layer",
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
new Container
{
RelativeSizeAxes = Axes.Both,
CornerRadius = 20,
CornerExponent = 2.5f,
Masking = true,
Child = topLayerBackground = new Box { RelativeSizeAxes = Axes.Both }
},
topLayerContentContainer = new Container { RelativeSizeAxes = Axes.Both }
}
},
middleLayerContainer = new Container
{
new Container
Name = "Middle layer",
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
RelativeSizeAxes = Axes.Both,
CornerRadius = 20,
CornerExponent = 2.5f,
Masking = true,
Child = middleLayerBackground = new Box { RelativeSizeAxes = Axes.Both }
},
middleLayerContentContainer = new Container { RelativeSizeAxes = Axes.Both }
new Container
{
RelativeSizeAxes = Axes.Both,
CornerRadius = 20,
CornerExponent = 2.5f,
Masking = true,
Child = middleLayerBackground = new Box { RelativeSizeAxes = Axes.Both }
},
middleLayerContentContainer = new Container { RelativeSizeAxes = Axes.Both }
}
}
}
};
@ -174,34 +184,40 @@ namespace osu.Game.Screens.Ranking
private void updateState()
{
topLayerContainer.MoveToY(0, resize_duration, Easing.OutQuint);
middleLayerContainer.MoveToY(0, resize_duration, Easing.OutQuint);
topLayerContent?.FadeOut(content_fade_duration).Expire();
middleLayerContent?.FadeOut(content_fade_duration).Expire();
switch (state)
{
case PanelState.Expanded:
this.ResizeTo(new Vector2(EXPANDED_WIDTH, expanded_height), resize_duration, Easing.OutQuint);
Size = new Vector2(EXPANDED_WIDTH, expanded_height);
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(middleLayerContent = new ExpandedPanelTopContent(Score.User).With(d => d.Alpha = 0));
middleLayerContentContainer.Add(topLayerContent = new ExpandedPanelMiddleContent(Score).With(d => d.Alpha = 0));
break;
case PanelState.Contracted:
this.ResizeTo(new Vector2(contracted_width, contracted_height), resize_duration, Easing.OutQuint);
Size = new Vector2(CONTRACTED_WIDTH, contracted_height);
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));
break;
}
using (BeginDelayedSequence(resize_duration + top_layer_expand_delay, true))
content.ResizeTo(Size, resize_duration, Easing.OutQuint);
bool topLayerExpanded = topLayerContainer.Y < 0;
// If the top layer was already expanded, then we don't need to wait for the resize and can instead transform immediately. This looks better when changing the panel state.
using (BeginDelayedSequence(topLayerExpanded ? 0 : resize_duration + top_layer_expand_delay, true))
{
topLayerContainer.FadeIn();
switch (state)
{
case PanelState.Expanded:
@ -219,5 +235,13 @@ namespace osu.Game.Screens.Ranking
middleLayerContent?.FadeIn(content_fade_duration);
}
}
protected override bool OnClick(ClickEvent e)
{
if (State == PanelState.Contracted)
State = PanelState.Expanded;
return true;
}
}
}

View File

@ -0,0 +1,185 @@
// 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.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.Containers;
using osu.Game.Scoring;
using osuTK;
namespace osu.Game.Screens.Ranking
{
public class ScorePanelList : CompositeDrawable
{
/// <summary>
/// Normal spacing between all panels.
/// </summary>
private const float panel_spacing = 5;
/// <summary>
/// Spacing around both sides of the expanded panel. This is added on top of <see cref="panel_spacing"/>.
/// </summary>
private const float expanded_panel_spacing = 15;
public readonly Bindable<ScoreInfo> SelectedScore = new Bindable<ScoreInfo>();
private readonly Flow flow;
private readonly Scroll scroll;
private ScorePanel expandedPanel;
public ScorePanelList()
{
RelativeSizeAxes = Axes.Both;
InternalChild = scroll = new Scroll
{
RelativeSizeAxes = Axes.Both,
Child = flow = new Flow
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(panel_spacing, 0),
AutoSizeAxes = Axes.Both,
}
};
}
protected override void LoadComplete()
{
base.LoadComplete();
SelectedScore.BindValueChanged(selectedScoreChanged, true);
}
/// <summary>
/// Adds a <see cref="ScoreInfo"/> to this list.
/// </summary>
/// <param name="score">The <see cref="ScoreInfo"/> to add.</param>
public void AddScore(ScoreInfo score)
{
flow.Add(new ScorePanel(score)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
}.With(p =>
{
p.StateChanged += s =>
{
if (s == PanelState.Expanded)
SelectedScore.Value = p.Score;
};
}));
if (SelectedScore.Value == score)
selectedScoreChanged(new ValueChangedEvent<ScoreInfo>(SelectedScore.Value, SelectedScore.Value));
else
{
// We want the scroll position to remain relative to the expanded panel. When a new panel is added after the expanded panel, nothing needs to be done.
// But when a panel is added before the expanded panel, we need to offset the scroll position by the width of the new panel.
if (expandedPanel != null && flow.GetPanelIndex(score) < flow.GetPanelIndex(expandedPanel.Score))
{
// A somewhat hacky property is used here because we need to:
// 1) Scroll after the scroll container's visible range is updated.
// 2) Scroll before the scroll container's scroll position is updated.
// Without this, we would have a 1-frame positioning error which looks very jarring.
scroll.InstantScrollTarget = (scroll.InstantScrollTarget ?? scroll.Target) + ScorePanel.CONTRACTED_WIDTH + panel_spacing;
}
}
}
/// <summary>
/// Brings a <see cref="ScoreInfo"/> to the centre of the screen and expands it.
/// </summary>
/// <param name="score">The <see cref="ScoreInfo"/> to present.</param>
private void selectedScoreChanged(ValueChangedEvent<ScoreInfo> score)
{
// Contract the old panel.
foreach (var p in flow.Where(p => p.Score == score.OldValue))
{
p.State = PanelState.Contracted;
p.Margin = new MarginPadding();
}
// Find the panel corresponding to the new score.
expandedPanel = flow.SingleOrDefault(p => p.Score == score.NewValue);
if (expandedPanel == null)
return;
// Expand the new panel.
expandedPanel.State = PanelState.Expanded;
expandedPanel.Margin = new MarginPadding { Horizontal = expanded_panel_spacing };
// Scroll to the new panel. This is done manually since we need:
// 1) To scroll after the scroll container's visible range is updated.
// 2) To account for the centre anchor/origins of panels.
// In the end, it's easier to compute the scroll position manually.
float scrollOffset = flow.GetPanelIndex(expandedPanel.Score) * (ScorePanel.CONTRACTED_WIDTH + panel_spacing);
scroll.ScrollTo(scrollOffset);
}
protected override void Update()
{
base.Update();
float offset = DrawWidth / 2f;
// Add padding to both sides such that the centre of an expanded panel on either side is in the middle of the screen.
if (SelectedScore.Value != null)
{
// The expanded panel has extra padding applied to it, so it needs to be included into the offset.
offset -= ScorePanel.EXPANDED_WIDTH / 2f + expanded_panel_spacing;
}
else
offset -= ScorePanel.CONTRACTED_WIDTH / 2f;
flow.Padding = new MarginPadding { Horizontal = offset };
}
private class Flow : FillFlowContainer<ScorePanel>
{
public override IEnumerable<Drawable> FlowingChildren => applySorting(AliveInternalChildren);
public int GetPanelIndex(ScoreInfo score) => applySorting(Children).TakeWhile(s => s.Score != score).Count();
private IEnumerable<ScorePanel> applySorting(IEnumerable<Drawable> drawables) => drawables.OfType<ScorePanel>()
.OrderByDescending(s => s.Score.TotalScore)
.ThenBy(s => s.Score.OnlineScoreID);
}
private class Scroll : OsuScrollContainer
{
public new float Target => base.Target;
public Scroll()
: base(Direction.Horizontal)
{
}
/// <summary>
/// The target that will be scrolled to instantaneously next frame.
/// </summary>
public float? InstantScrollTarget;
protected override void UpdateAfterChildren()
{
if (InstantScrollTarget != null)
{
ScrollTo(InstantScrollTarget.Value, false);
InstantScrollTarget = null;
}
base.UpdateAfterChildren();
}
public override bool HandlePositionalInput => false;
public override bool HandleNonPositionalInput => false;
}
}
}

View File

@ -0,0 +1,32 @@
// 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.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Rulesets;
using osu.Game.Scoring;
namespace osu.Game.Screens.Ranking
{
public class SoloResultsScreen : ResultsScreen
{
[Resolved]
private RulesetStore rulesets { get; set; }
public SoloResultsScreen(ScoreInfo score, bool allowRetry = true)
: base(score, allowRetry)
{
}
protected override APIRequest FetchScores(Action<IEnumerable<ScoreInfo>> scoresCallback)
{
var req = new GetScoresRequest(Score.Beatmap, Score.Ruleset);
req.Success += r => scoresCallback?.Invoke(r.Scores.Where(s => s.OnlineScoreID != Score.OnlineScoreID).Select(s => s.CreateScoreInfo(rulesets)));
return req;
}
}
}

View File

@ -131,7 +131,7 @@ namespace osu.Game.Screens.Select
private CarouselRoot root;
private IBindable<WeakReference<BeatmapSetInfo>> itemAdded;
private IBindable<WeakReference<BeatmapSetInfo>> itemUpdated;
private IBindable<WeakReference<BeatmapSetInfo>> itemRemoved;
private IBindable<WeakReference<BeatmapInfo>> itemHidden;
private IBindable<WeakReference<BeatmapInfo>> itemRestored;
@ -166,8 +166,8 @@ namespace osu.Game.Screens.Select
RightClickScrollingEnabled.ValueChanged += enabled => scroll.RightMouseScrollbar = enabled.NewValue;
RightClickScrollingEnabled.TriggerChange();
itemAdded = beatmaps.ItemAdded.GetBoundCopy();
itemAdded.BindValueChanged(beatmapAdded);
itemUpdated = beatmaps.ItemUpdated.GetBoundCopy();
itemUpdated.BindValueChanged(beatmapUpdated);
itemRemoved = beatmaps.ItemRemoved.GetBoundCopy();
itemRemoved.BindValueChanged(beatmapRemoved);
itemHidden = beatmaps.BeatmapHidden.GetBoundCopy();
@ -582,7 +582,7 @@ namespace osu.Game.Screens.Select
RemoveBeatmapSet(item);
}
private void beatmapAdded(ValueChangedEvent<WeakReference<BeatmapSetInfo>> weakItem)
private void beatmapUpdated(ValueChangedEvent<WeakReference<BeatmapSetInfo>> weakItem)
{
if (weakItem.NewValue.TryGetTarget(out var item))
UpdateBeatmapSet(item);

View File

@ -28,7 +28,7 @@ namespace osu.Game.Screens.Select.Carousel
[Resolved]
private IAPIProvider api { get; set; }
private IBindable<WeakReference<ScoreInfo>> itemAdded;
private IBindable<WeakReference<ScoreInfo>> itemUpdated;
private IBindable<WeakReference<ScoreInfo>> itemRemoved;
public TopLocalRank(BeatmapInfo beatmap)
@ -40,8 +40,8 @@ namespace osu.Game.Screens.Select.Carousel
[BackgroundDependencyLoader]
private void load()
{
itemAdded = scores.ItemAdded.GetBoundCopy();
itemAdded.BindValueChanged(scoreChanged);
itemUpdated = scores.ItemUpdated.GetBoundCopy();
itemUpdated.BindValueChanged(scoreChanged);
itemRemoved = scores.ItemRemoved.GetBoundCopy();
itemRemoved.BindValueChanged(scoreChanged);

View File

@ -37,7 +37,7 @@ namespace osu.Game.Screens.Select
}
protected void PresentScore(ScoreInfo score) =>
FinaliseSelection(score.Beatmap, score.Ruleset, () => this.Push(new ResultsScreen(score)));
FinaliseSelection(score.Beatmap, score.Ruleset, () => this.Push(new SoloResultsScreen(score)));
protected override BeatmapDetailArea CreateBeatmapDetailArea() => new PlayBeatmapDetailArea();

View File

@ -0,0 +1,52 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using osu.Game.Tests.Beatmaps;
using osu.Game.Users;
namespace osu.Game.Tests
{
public class TestScoreInfo : ScoreInfo
{
public TestScoreInfo(RulesetInfo ruleset)
{
User = new User
{
Id = 2,
Username = "peppy",
CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
};
Beatmap = new TestBeatmap(ruleset).BeatmapInfo;
Ruleset = ruleset;
RulesetID = ruleset.ID ?? 0;
Mods = new Mod[] { new TestModHardRock(), new TestModDoubleTime() };
TotalScore = 2845370;
Accuracy = 0.95;
MaxCombo = 999;
Rank = ScoreRank.S;
Date = DateTimeOffset.Now;
Statistics[HitResult.Miss] = 1;
Statistics[HitResult.Meh] = 50;
Statistics[HitResult.Good] = 100;
Statistics[HitResult.Great] = 300;
}
private class TestModHardRock : ModHardRock
{
public override double ScoreMultiplier => 1;
}
private class TestModDoubleTime : ModDoubleTime
{
public override double ScoreMultiplier => 1;
}
}
}

View File

@ -24,7 +24,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="ppy.osu.Framework" Version="2020.525.0" />
<PackageReference Include="ppy.osu.Framework" Version="2020.528.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.512.0" />
<PackageReference Include="Sentry" Version="2.1.1" />
<PackageReference Include="SharpCompress" Version="0.25.1" />

View File

@ -70,7 +70,7 @@
<Reference Include="System.Net.Http" />
</ItemGroup>
<ItemGroup Label="Package References">
<PackageReference Include="ppy.osu.Framework.iOS" Version="2020.525.0" />
<PackageReference Include="ppy.osu.Framework.iOS" Version="2020.528.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.512.0" />
</ItemGroup>
<!-- Xamarin.iOS does not automatically handle transitive dependencies from NuGet packages. -->
@ -80,7 +80,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="ppy.osu.Framework" Version="2020.525.0" />
<PackageReference Include="ppy.osu.Framework" Version="2020.528.0" />
<PackageReference Include="SharpCompress" Version="0.25.1" />
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="SharpRaven" Version="2.4.0" />