mirror of
https://github.com/ppy/osu.git
synced 2025-01-15 14:53:01 +08:00
Merge branch 'master' into move-setter-to-duration
This commit is contained in:
commit
64d1b4b11b
@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
|||||||
|
|
||||||
switch (obj)
|
switch (obj)
|
||||||
{
|
{
|
||||||
case IHasCurve curveData:
|
case IHasPathWithRepeats curveData:
|
||||||
return new JuiceStream
|
return new JuiceStream
|
||||||
{
|
{
|
||||||
StartTime = obj.StartTime,
|
StartTime = obj.StartTime,
|
||||||
|
@ -14,7 +14,7 @@ using osu.Game.Rulesets.Objects.Types;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects
|
namespace osu.Game.Rulesets.Catch.Objects
|
||||||
{
|
{
|
||||||
public class JuiceStream : CatchHitObject, IHasCurve
|
public class JuiceStream : CatchHitObject, IHasPathWithRepeats
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Positional distance that results in a duration of one second, before any speed adjustments.
|
/// Positional distance that results in a duration of one second, before any speed adjustments.
|
||||||
|
Binary file not shown.
After Width: | Height: | Size: 165 B |
Binary file not shown.
After Width: | Height: | Size: 899 B |
@ -474,7 +474,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private IList<HitSampleInfo> sampleInfoListAt(double time)
|
private IList<HitSampleInfo> sampleInfoListAt(double time)
|
||||||
{
|
{
|
||||||
if (!(HitObject is IHasCurve curveData))
|
if (!(HitObject is IHasPathWithRepeats curveData))
|
||||||
return HitObject.Samples;
|
return HitObject.Samples;
|
||||||
|
|
||||||
double segmentTime = (EndTime - HitObject.StartTime) / spanCount;
|
double segmentTime = (EndTime - HitObject.StartTime) / spanCount;
|
||||||
|
@ -52,10 +52,10 @@ namespace osu.Game.Rulesets.Mania.Skinning
|
|||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
if (leftSprite?.Height > 0)
|
if (leftSprite?.Height > 0)
|
||||||
leftSprite.Scale = new Vector2(DrawHeight / leftSprite.Height);
|
leftSprite.Scale = new Vector2(1, DrawHeight / leftSprite.Height);
|
||||||
|
|
||||||
if (rightSprite?.Height > 0)
|
if (rightSprite?.Height > 0)
|
||||||
rightSprite.Scale = new Vector2(DrawHeight / rightSprite.Height);
|
rightSprite.Scale = new Vector2(1, DrawHeight / rightSprite.Height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
|||||||
|
|
||||||
switch (original)
|
switch (original)
|
||||||
{
|
{
|
||||||
case IHasCurve curveData:
|
case IHasPathWithRepeats curveData:
|
||||||
return new Slider
|
return new Slider
|
||||||
{
|
{
|
||||||
StartTime = original.StartTime,
|
StartTime = original.StartTime,
|
||||||
|
@ -17,7 +17,7 @@ using osu.Game.Rulesets.Scoring;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Objects
|
namespace osu.Game.Rulesets.Osu.Objects
|
||||||
{
|
{
|
||||||
public class Slider : OsuHitObject, IHasCurve
|
public class Slider : OsuHitObject, IHasPathWithRepeats
|
||||||
{
|
{
|
||||||
public double EndTime => StartTime + this.SpanCount() * Path.Distance / Velocity;
|
public double EndTime => StartTime + this.SpanCount() * Path.Distance / Velocity;
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ using osu.Game.Rulesets.Scoring;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.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);
|
protected override JudgementResult CreateResult(HitObject hitObject, Judgement judgement) => new OsuJudgementResult(hitObject, judgement);
|
||||||
|
|
||||||
|
@ -114,7 +114,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
|||||||
|
|
||||||
if (!isForCurrentRuleset && tickSpacing > 0 && osuDuration < 2 * speedAdjustedBeatLength)
|
if (!isForCurrentRuleset && tickSpacing > 0 && osuDuration < 2 * speedAdjustedBeatLength)
|
||||||
{
|
{
|
||||||
List<IList<HitSampleInfo>> allSamples = obj is IHasCurve curveData ? curveData.NodeSamples : new List<IList<HitSampleInfo>>(new[] { samples });
|
List<IList<HitSampleInfo>> allSamples = obj is IHasPathWithRepeats curveData ? curveData.NodeSamples : new List<IList<HitSampleInfo>>(new[] { samples });
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
|
@ -3,9 +3,7 @@
|
|||||||
|
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using osu.Game.Audio;
|
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
@ -17,7 +15,7 @@ using osuTK;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Objects
|
namespace osu.Game.Rulesets.Taiko.Objects
|
||||||
{
|
{
|
||||||
public class DrumRoll : TaikoHitObject, IHasCurve
|
public class DrumRoll : TaikoHitObject, IHasPath
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Drum roll distance that results in a duration of 1 speed-adjusted beat length.
|
/// Drum roll distance that results in a duration of 1 speed-adjusted beat length.
|
||||||
@ -115,11 +113,7 @@ namespace osu.Game.Rulesets.Taiko.Objects
|
|||||||
|
|
||||||
double IHasDistance.Distance => Duration * Velocity;
|
double IHasDistance.Distance => Duration * Velocity;
|
||||||
|
|
||||||
int IHasRepeats.RepeatCount { get => 0; set { } }
|
SliderPath IHasPath.Path
|
||||||
|
|
||||||
List<IList<HitSampleInfo>> IHasRepeats.NodeSamples => new List<IList<HitSampleInfo>>();
|
|
||||||
|
|
||||||
SliderPath IHasCurve.Path
|
|
||||||
=> new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(1) }, ((IHasDistance)this).Distance / TaikoBeatmapConverter.LEGACY_VELOCITY_MULTIPLIER);
|
=> new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(1) }, ((IHasDistance)this).Distance / TaikoBeatmapConverter.LEGACY_VELOCITY_MULTIPLIER);
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
@ -365,7 +365,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
{
|
{
|
||||||
var hitObjects = decoder.Decode(stream).HitObjects;
|
var hitObjects = decoder.Decode(stream).HitObjects;
|
||||||
|
|
||||||
var curveData = hitObjects[0] as IHasCurve;
|
var curveData = hitObjects[0] as IHasPathWithRepeats;
|
||||||
var positionData = hitObjects[0] as IHasPosition;
|
var positionData = hitObjects[0] as IHasPosition;
|
||||||
|
|
||||||
Assert.IsNotNull(positionData);
|
Assert.IsNotNull(positionData);
|
||||||
|
@ -95,7 +95,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
{
|
{
|
||||||
var beatmap = decodeAsJson(normal);
|
var beatmap = decodeAsJson(normal);
|
||||||
|
|
||||||
var curveData = beatmap.HitObjects[0] as IHasCurve;
|
var curveData = beatmap.HitObjects[0] as IHasPathWithRepeats;
|
||||||
var positionData = beatmap.HitObjects[0] as IHasPosition;
|
var positionData = beatmap.HitObjects[0] as IHasPosition;
|
||||||
|
|
||||||
Assert.IsNotNull(positionData);
|
Assert.IsNotNull(positionData);
|
||||||
|
@ -28,6 +28,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddStep(@"locally available state", () => downloadButton.SetDownloadState(DownloadState.LocallyAvailable));
|
AddStep(@"locally available state", () => downloadButton.SetDownloadState(DownloadState.LocallyAvailable));
|
||||||
AddStep(@"not downloaded state", () => downloadButton.SetDownloadState(DownloadState.NotDownloaded));
|
AddStep(@"not downloaded state", () => downloadButton.SetDownloadState(DownloadState.NotDownloaded));
|
||||||
createButton(false);
|
createButton(false);
|
||||||
|
createButtonNoScore();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createButton(bool withReplay)
|
private void createButton(bool withReplay)
|
||||||
@ -40,6 +41,22 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
Origin = Anchor.Centre,
|
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)
|
private ScoreInfo getScoreInfo(bool replayAvailable)
|
||||||
|
@ -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),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
@ -14,10 +13,7 @@ using osu.Framework.Testing;
|
|||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Mods;
|
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Rulesets.Osu.Mods;
|
|
||||||
using osu.Game.Rulesets.Scoring;
|
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Screens.Ranking;
|
using osu.Game.Screens.Ranking;
|
||||||
using osu.Game.Screens.Ranking.Expanded;
|
using osu.Game.Screens.Ranking.Expanded;
|
||||||
@ -37,7 +33,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
|||||||
{
|
{
|
||||||
var author = new User { Username = "mapper_name" };
|
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"));
|
AddAssert("mapper name present", () => this.ChildrenOfType<OsuSpriteText>().Any(spriteText => spriteText.Text == "mapper_name"));
|
||||||
}
|
}
|
||||||
@ -45,7 +41,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestMapWithUnknownMapper()
|
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", () =>
|
AddAssert("mapped by text not present", () =>
|
||||||
this.ChildrenOfType<OsuSpriteText>().All(spriteText => !containsAny(spriteText.Text, "mapped", "by")));
|
this.ChildrenOfType<OsuSpriteText>().All(spriteText => !containsAny(spriteText.Text, "mapped", "by")));
|
||||||
@ -66,29 +62,6 @@ namespace osu.Game.Tests.Visual.Ranking
|
|||||||
return new TestWorkingBeatmap(beatmap);
|
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 bool containsAny(string text, params string[] stringsToMatch) => stringsToMatch.Any(text.Contains);
|
||||||
|
|
||||||
private class ExpandedPanelMiddleContentContainer : Container
|
private class ExpandedPanelMiddleContentContainer : Container
|
||||||
|
@ -5,8 +5,8 @@ using osu.Framework.Extensions.Color4Extensions;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Screens.Ranking.Expanded;
|
using osu.Game.Screens.Ranking.Expanded;
|
||||||
using osu.Game.Users;
|
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Ranking
|
namespace osu.Game.Tests.Visual.Ranking
|
||||||
@ -27,7 +27,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = Color4Extensions.FromHex("#444"),
|
Colour = Color4Extensions.FromHex("#444"),
|
||||||
},
|
},
|
||||||
new ExpandedPanelTopContent(new User { Id = 2, Username = "peppy" }),
|
new ExpandedPanelTopContent(new TestScoreInfo(new OsuRuleset().RulesetInfo).User),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
@ -11,13 +9,10 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Rulesets.Scoring;
|
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Screens;
|
using osu.Game.Screens;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
using osu.Game.Screens.Ranking;
|
using osu.Game.Screens.Ranking;
|
||||||
using osu.Game.Tests.Beatmaps;
|
|
||||||
using osu.Game.Users;
|
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Ranking
|
namespace osu.Game.Tests.Visual.Ranking
|
||||||
{
|
{
|
||||||
@ -41,26 +36,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
|||||||
Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmapInfo);
|
Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmapInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
private TestSoloResults createResultsScreen() => new TestSoloResults(new ScoreInfo
|
private TestSoloResults createResultsScreen() => new TestSoloResults(new TestScoreInfo(new OsuRuleset().RulesetInfo));
|
||||||
{
|
|
||||||
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",
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void ResultsWithoutPlayer()
|
public void ResultsWithoutPlayer()
|
||||||
|
@ -1,28 +1,23 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Rulesets.Mods;
|
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Rulesets.Osu.Mods;
|
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Screens.Ranking;
|
using osu.Game.Screens.Ranking;
|
||||||
using osu.Game.Tests.Beatmaps;
|
|
||||||
using osu.Game.Users;
|
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Ranking
|
namespace osu.Game.Tests.Visual.Ranking
|
||||||
{
|
{
|
||||||
public class TestSceneScorePanel : OsuTestScene
|
public class TestSceneScorePanel : OsuTestScene
|
||||||
{
|
{
|
||||||
|
private ScorePanel panel;
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestDRank()
|
public void TestDRank()
|
||||||
{
|
{
|
||||||
var score = createScore();
|
var score = new TestScoreInfo(new OsuRuleset().RulesetInfo) { Accuracy = 0.5, Rank = ScoreRank.D };
|
||||||
score.Accuracy = 0.5;
|
|
||||||
score.Rank = ScoreRank.D;
|
|
||||||
|
|
||||||
addPanelStep(score);
|
addPanelStep(score);
|
||||||
}
|
}
|
||||||
@ -30,9 +25,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestCRank()
|
public void TestCRank()
|
||||||
{
|
{
|
||||||
var score = createScore();
|
var score = new TestScoreInfo(new OsuRuleset().RulesetInfo) { Accuracy = 0.75, Rank = ScoreRank.C };
|
||||||
score.Accuracy = 0.75;
|
|
||||||
score.Rank = ScoreRank.C;
|
|
||||||
|
|
||||||
addPanelStep(score);
|
addPanelStep(score);
|
||||||
}
|
}
|
||||||
@ -40,9 +33,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestBRank()
|
public void TestBRank()
|
||||||
{
|
{
|
||||||
var score = createScore();
|
var score = new TestScoreInfo(new OsuRuleset().RulesetInfo) { Accuracy = 0.85, Rank = ScoreRank.B };
|
||||||
score.Accuracy = 0.85;
|
|
||||||
score.Rank = ScoreRank.B;
|
|
||||||
|
|
||||||
addPanelStep(score);
|
addPanelStep(score);
|
||||||
}
|
}
|
||||||
@ -50,9 +41,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestARank()
|
public void TestARank()
|
||||||
{
|
{
|
||||||
var score = createScore();
|
var score = new TestScoreInfo(new OsuRuleset().RulesetInfo) { Accuracy = 0.925, Rank = ScoreRank.A };
|
||||||
score.Accuracy = 0.925;
|
|
||||||
score.Rank = ScoreRank.A;
|
|
||||||
|
|
||||||
addPanelStep(score);
|
addPanelStep(score);
|
||||||
}
|
}
|
||||||
@ -60,9 +49,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestSRank()
|
public void TestSRank()
|
||||||
{
|
{
|
||||||
var score = createScore();
|
var score = new TestScoreInfo(new OsuRuleset().RulesetInfo) { Accuracy = 0.975, Rank = ScoreRank.S };
|
||||||
score.Accuracy = 0.975;
|
|
||||||
score.Rank = ScoreRank.S;
|
|
||||||
|
|
||||||
addPanelStep(score);
|
addPanelStep(score);
|
||||||
}
|
}
|
||||||
@ -70,9 +57,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestAlmostSSRank()
|
public void TestAlmostSSRank()
|
||||||
{
|
{
|
||||||
var score = createScore();
|
var score = new TestScoreInfo(new OsuRuleset().RulesetInfo) { Accuracy = 0.9999, Rank = ScoreRank.S };
|
||||||
score.Accuracy = 0.9999;
|
|
||||||
score.Rank = ScoreRank.S;
|
|
||||||
|
|
||||||
addPanelStep(score);
|
addPanelStep(score);
|
||||||
}
|
}
|
||||||
@ -80,9 +65,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestSSRank()
|
public void TestSSRank()
|
||||||
{
|
{
|
||||||
var score = createScore();
|
var score = new TestScoreInfo(new OsuRuleset().RulesetInfo) { Accuracy = 1, Rank = ScoreRank.X };
|
||||||
score.Accuracy = 1;
|
|
||||||
score.Rank = ScoreRank.X;
|
|
||||||
|
|
||||||
addPanelStep(score);
|
addPanelStep(score);
|
||||||
}
|
}
|
||||||
@ -90,44 +73,42 @@ namespace osu.Game.Tests.Visual.Ranking
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestAllHitResults()
|
public void TestAllHitResults()
|
||||||
{
|
{
|
||||||
var score = createScore();
|
var score = new TestScoreInfo(new OsuRuleset().RulesetInfo) { Statistics = { [HitResult.Perfect] = 350, [HitResult.Ok] = 200 } };
|
||||||
score.Statistics[HitResult.Perfect] = 350;
|
|
||||||
score.Statistics[HitResult.Ok] = 200;
|
|
||||||
|
|
||||||
addPanelStep(score);
|
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,
|
Anchor = Anchor.Centre,
|
||||||
Origin = 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 },
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
208
osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs
Normal file
208
osu.Game.Tests/Visual/Ranking/TestSceneScorePanelList.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
@ -233,9 +233,9 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
writer.Write(FormattableString.Invariant($"{(int)getObjectType(hitObject)},"));
|
writer.Write(FormattableString.Invariant($"{(int)getObjectType(hitObject)},"));
|
||||||
writer.Write(FormattableString.Invariant($"{(int)toLegacyHitSoundType(hitObject.Samples)},"));
|
writer.Write(FormattableString.Invariant($"{(int)toLegacyHitSoundType(hitObject.Samples)},"));
|
||||||
|
|
||||||
if (hitObject is IHasCurve curveData)
|
if (hitObject is IHasPath path)
|
||||||
{
|
{
|
||||||
addCurveData(writer, curveData, position);
|
addPathData(writer, path, position);
|
||||||
writer.Write(getSampleBank(hitObject.Samples, zeroBanks: true));
|
writer.Write(getSampleBank(hitObject.Samples, zeroBanks: true));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -263,7 +263,7 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
|
|
||||||
switch (hitObject)
|
switch (hitObject)
|
||||||
{
|
{
|
||||||
case IHasCurve _:
|
case IHasPath _:
|
||||||
type |= LegacyHitObjectType.Slider;
|
type |= LegacyHitObjectType.Slider;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -282,13 +282,13 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addCurveData(TextWriter writer, IHasCurve curveData, Vector2 position)
|
private void addPathData(TextWriter writer, IHasPath pathData, Vector2 position)
|
||||||
{
|
{
|
||||||
PathType? lastType = null;
|
PathType? lastType = null;
|
||||||
|
|
||||||
for (int i = 0; i < curveData.Path.ControlPoints.Count; i++)
|
for (int i = 0; i < pathData.Path.ControlPoints.Count; i++)
|
||||||
{
|
{
|
||||||
PathControlPoint point = curveData.Path.ControlPoints[i];
|
PathControlPoint point = pathData.Path.ControlPoints[i];
|
||||||
|
|
||||||
if (point.Type.Value != null)
|
if (point.Type.Value != null)
|
||||||
{
|
{
|
||||||
@ -325,13 +325,17 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
if (i != 0)
|
if (i != 0)
|
||||||
{
|
{
|
||||||
writer.Write(FormattableString.Invariant($"{position.X + point.Position.Value.X}:{position.Y + point.Position.Value.Y}"));
|
writer.Write(FormattableString.Invariant($"{position.X + point.Position.Value.X}:{position.Y + point.Position.Value.Y}"));
|
||||||
writer.Write(i != curveData.Path.ControlPoints.Count - 1 ? "|" : ",");
|
writer.Write(i != pathData.Path.ControlPoints.Count - 1 ? "|" : ",");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.Write(FormattableString.Invariant($"{curveData.RepeatCount + 1},"));
|
var curveData = pathData as IHasPathWithRepeats;
|
||||||
writer.Write(FormattableString.Invariant($"{curveData.Path.Distance},"));
|
|
||||||
|
|
||||||
|
writer.Write(FormattableString.Invariant($"{(curveData?.RepeatCount ?? 0) + 1},"));
|
||||||
|
writer.Write(FormattableString.Invariant($"{pathData.Path.Distance},"));
|
||||||
|
|
||||||
|
if (curveData != null)
|
||||||
|
{
|
||||||
for (int i = 0; i < curveData.NodeSamples.Count; i++)
|
for (int i = 0; i < curveData.NodeSamples.Count; i++)
|
||||||
{
|
{
|
||||||
writer.Write(FormattableString.Invariant($"{(int)toLegacyHitSoundType(curveData.NodeSamples[i])}"));
|
writer.Write(FormattableString.Invariant($"{(int)toLegacyHitSoundType(curveData.NodeSamples[i])}"));
|
||||||
@ -344,6 +348,7 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
writer.Write(i != curveData.NodeSamples.Count - 1 ? "|" : ",");
|
writer.Write(i != curveData.NodeSamples.Count - 1 ? "|" : ",");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void addEndTimeData(TextWriter writer, HitObject hitObject)
|
private void addEndTimeData(TextWriter writer, HitObject hitObject)
|
||||||
{
|
{
|
||||||
|
@ -27,7 +27,15 @@ namespace osu.Game.Online.API.Requests
|
|||||||
|
|
||||||
private string directionString => SortDirection == SortDirection.Descending ? @"desc" : @"asc";
|
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.query = string.IsNullOrEmpty(query) ? string.Empty : System.Uri.EscapeDataString(query);
|
||||||
this.ruleset = ruleset;
|
this.ruleset = ruleset;
|
||||||
@ -36,8 +44,8 @@ namespace osu.Game.Online.API.Requests
|
|||||||
SearchCategory = searchCategory;
|
SearchCategory = searchCategory;
|
||||||
SortCriteria = sortCriteria;
|
SortCriteria = sortCriteria;
|
||||||
SortDirection = sortDirection;
|
SortDirection = sortDirection;
|
||||||
Genre = SearchGenre.Any;
|
Genre = genre;
|
||||||
Language = SearchLanguage.Any;
|
Language = language;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override WebRequest CreateWebRequest()
|
protected override WebRequest CreateWebRequest()
|
||||||
|
@ -177,7 +177,9 @@ namespace osu.Game.Overlays.BeatmapListing
|
|||||||
lastResponse?.Cursor,
|
lastResponse?.Cursor,
|
||||||
searchControl.Category.Value,
|
searchControl.Category.Value,
|
||||||
sortControl.Current.Value,
|
sortControl.Current.Value,
|
||||||
sortControl.SortDirection.Value);
|
sortControl.SortDirection.Value,
|
||||||
|
searchControl.Genre.Value,
|
||||||
|
searchControl.Language.Value);
|
||||||
|
|
||||||
getSetsRequest.Success += response =>
|
getSetsRequest.Success += response =>
|
||||||
{
|
{
|
||||||
@ -186,6 +188,9 @@ namespace osu.Game.Overlays.BeatmapListing
|
|||||||
if (sets.Count == 0)
|
if (sets.Count == 0)
|
||||||
noMoreResults = true;
|
noMoreResults = true;
|
||||||
|
|
||||||
|
if (CurrentPage == 0)
|
||||||
|
searchControl.BeatmapSet = sets.FirstOrDefault();
|
||||||
|
|
||||||
lastResponse = response;
|
lastResponse = response;
|
||||||
getSetsRequest = null;
|
getSetsRequest = null;
|
||||||
|
|
||||||
|
@ -48,8 +48,6 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
|
|
||||||
protected ComposeBlueprintContainer BlueprintContainer { get; private set; }
|
protected ComposeBlueprintContainer BlueprintContainer { get; private set; }
|
||||||
|
|
||||||
public override Playfield Playfield => drawableRulesetWrapper.Playfield;
|
|
||||||
|
|
||||||
private DrawableEditRulesetWrapper<TObject> drawableRulesetWrapper;
|
private DrawableEditRulesetWrapper<TObject> drawableRulesetWrapper;
|
||||||
|
|
||||||
protected readonly Container LayerBelowRuleset = new Container { RelativeSizeAxes = Axes.Both };
|
protected readonly Container LayerBelowRuleset = new Container { RelativeSizeAxes = Axes.Both };
|
||||||
@ -61,7 +59,6 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
protected HitObjectComposer(Ruleset ruleset)
|
protected HitObjectComposer(Ruleset ruleset)
|
||||||
{
|
{
|
||||||
Ruleset = ruleset;
|
Ruleset = ruleset;
|
||||||
RelativeSizeAxes = Axes.Both;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
@ -137,6 +134,49 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
EditorBeatmap.SelectedHitObjects.CollectionChanged += selectionChanged;
|
EditorBeatmap.SelectedHitObjects.CollectionChanged += selectionChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
inputManager = GetContainingInputManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Playfield Playfield => drawableRulesetWrapper.Playfield;
|
||||||
|
|
||||||
|
public override IEnumerable<DrawableHitObject> HitObjects => drawableRulesetWrapper.Playfield.AllHitObjects;
|
||||||
|
|
||||||
|
public override bool CursorInPlacementArea => drawableRulesetWrapper.Playfield.ReceivePositionalInputAt(inputManager.CurrentState.Mouse.Position);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines all available composition tools, listed on the left side of the editor screen as button controls.
|
||||||
|
/// This should usually define one tool for each <see cref="HitObject"/> type used in the target ruleset.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// A "select" tool is automatically added as the first tool.
|
||||||
|
/// </remarks>
|
||||||
|
protected abstract IReadOnlyList<HitObjectCompositionTool> CompositionTools { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Construct a relevant blueprint container. This will manage hitobject selection/placement input handling and display logic.
|
||||||
|
/// </summary>
|
||||||
|
protected abstract ComposeBlueprintContainer CreateBlueprintContainer();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Construct a drawable ruleset for the provided ruleset.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Can be overridden to add editor-specific logical changes to a <see cref="Ruleset"/>'s standard <see cref="DrawableRuleset{TObject}"/>.
|
||||||
|
/// For example, hit animations or judgement logic may be changed to give a better editor user experience.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="ruleset">The ruleset used to construct its drawable counterpart.</param>
|
||||||
|
/// <param name="beatmap">The loaded beatmap.</param>
|
||||||
|
/// <param name="mods">The mods to be applied.</param>
|
||||||
|
/// <returns>An editor-relevant <see cref="DrawableRuleset{TObject}"/>.</returns>
|
||||||
|
protected virtual DrawableRuleset<TObject> CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods = null)
|
||||||
|
=> (DrawableRuleset<TObject>)ruleset.CreateDrawableRulesetWith(beatmap, mods);
|
||||||
|
|
||||||
|
#region Tool selection logic
|
||||||
|
|
||||||
protected override bool OnKeyDown(KeyDownEvent e)
|
protected override bool OnKeyDown(KeyDownEvent e)
|
||||||
{
|
{
|
||||||
if (e.Key >= Key.Number1 && e.Key <= Key.Number9)
|
if (e.Key >= Key.Number1 && e.Key <= Key.Number9)
|
||||||
@ -153,13 +193,6 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
return base.OnKeyDown(e);
|
return base.OnKeyDown(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
|
||||||
{
|
|
||||||
base.LoadComplete();
|
|
||||||
|
|
||||||
inputManager = GetContainingInputManager();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void selectionChanged(object sender, NotifyCollectionChangedEventArgs changedArgs)
|
private void selectionChanged(object sender, NotifyCollectionChangedEventArgs changedArgs)
|
||||||
{
|
{
|
||||||
if (EditorBeatmap.SelectedHitObjects.Any())
|
if (EditorBeatmap.SelectedHitObjects.Any())
|
||||||
@ -179,15 +212,9 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
EditorBeatmap.SelectedHitObjects.Clear();
|
EditorBeatmap.SelectedHitObjects.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<DrawableHitObject> HitObjects => drawableRulesetWrapper.Playfield.AllHitObjects;
|
#endregion
|
||||||
|
|
||||||
public override bool CursorInPlacementArea => drawableRulesetWrapper.Playfield.ReceivePositionalInputAt(inputManager.CurrentState.Mouse.Position);
|
#region IPlacementHandler
|
||||||
|
|
||||||
protected abstract IReadOnlyList<HitObjectCompositionTool> CompositionTools { get; }
|
|
||||||
|
|
||||||
protected abstract ComposeBlueprintContainer CreateBlueprintContainer();
|
|
||||||
|
|
||||||
protected abstract DrawableRuleset<TObject> CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods = null);
|
|
||||||
|
|
||||||
public void BeginPlacement(HitObject hitObject)
|
public void BeginPlacement(HitObject hitObject)
|
||||||
{
|
{
|
||||||
@ -209,6 +236,17 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
|
|
||||||
public void Delete(HitObject hitObject) => EditorBeatmap.Remove(hitObject);
|
public void Delete(HitObject hitObject) => EditorBeatmap.Remove(hitObject);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region IPositionSnapProvider
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieve the relevant <see cref="Playfield"/> at a specified screen-space position.
|
||||||
|
/// In cases where a ruleset doesn't require custom logic (due to nested playfields, for example)
|
||||||
|
/// this will return the ruleset's main playfield.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="screenSpacePosition">The screen-space position to query.</param>
|
||||||
|
/// <returns>The most relevant <see cref="Playfield"/>.</returns>
|
||||||
protected virtual Playfield PlayfieldAtScreenSpacePosition(Vector2 screenSpacePosition) => drawableRulesetWrapper.Playfield;
|
protected virtual Playfield PlayfieldAtScreenSpacePosition(Vector2 screenSpacePosition) => drawableRulesetWrapper.Playfield;
|
||||||
|
|
||||||
public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition)
|
public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition)
|
||||||
@ -257,8 +295,14 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
|
|
||||||
return DurationToDistance(referenceTime, snappedEndTime - referenceTime);
|
return DurationToDistance(referenceTime, snappedEndTime - referenceTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A non-generic definition of a HitObject composer class.
|
||||||
|
/// Generally used to access certain methods without requiring a generic type for <see cref="HitObjectComposer{T}" />.
|
||||||
|
/// </summary>
|
||||||
[Cached(typeof(HitObjectComposer))]
|
[Cached(typeof(HitObjectComposer))]
|
||||||
[Cached(typeof(IPositionSnapProvider))]
|
[Cached(typeof(IPositionSnapProvider))]
|
||||||
public abstract class HitObjectComposer : CompositeDrawable, IPositionSnapProvider
|
public abstract class HitObjectComposer : CompositeDrawable, IPositionSnapProvider
|
||||||
@ -268,10 +312,13 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The target ruleset's playfield.
|
||||||
|
/// </summary>
|
||||||
public abstract Playfield Playfield { get; }
|
public abstract Playfield Playfield { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// All the <see cref="DrawableHitObject"/>s.
|
/// All <see cref="DrawableHitObject"/>s in currently loaded beatmap.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract IEnumerable<DrawableHitObject> HitObjects { get; }
|
public abstract IEnumerable<DrawableHitObject> HitObjects { get; }
|
||||||
|
|
||||||
@ -280,6 +327,8 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract bool CursorInPlacementArea { get; }
|
public abstract bool CursorInPlacementArea { get; }
|
||||||
|
|
||||||
|
#region IPositionSnapProvider
|
||||||
|
|
||||||
public abstract SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition);
|
public abstract SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition);
|
||||||
|
|
||||||
public abstract float GetBeatSnapDistanceAt(double referenceTime);
|
public abstract float GetBeatSnapDistanceAt(double referenceTime);
|
||||||
@ -291,5 +340,7 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
public abstract double GetSnappedDurationFromDistance(double referenceTime, float distance);
|
public abstract double GetSnappedDurationFromDistance(double referenceTime, float distance);
|
||||||
|
|
||||||
public abstract float GetSnappedDistanceFromDistance(double referenceTime, float distance);
|
public abstract float GetSnappedDistanceFromDistance(double referenceTime, float distance);
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,10 @@ using osu.Game.Beatmaps.ControlPoints;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Objects.Legacy
|
namespace osu.Game.Rulesets.Objects.Legacy
|
||||||
{
|
{
|
||||||
internal abstract class ConvertSlider : ConvertHitObject, IHasCurve, IHasLegacyLastTickOffset
|
internal abstract class ConvertSlider : ConvertHitObject, IHasPathWithRepeats, IHasLegacyLastTickOffset,
|
||||||
|
#pragma warning disable 618
|
||||||
|
IHasCurve
|
||||||
|
#pragma warning restore 618
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Scoring distance with a speed-adjusted beat length of 1 second.
|
/// Scoring distance with a speed-adjusted beat length of 1 second.
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Objects.Types
|
namespace osu.Game.Rulesets.Objects.Types
|
||||||
{
|
{
|
||||||
/// <summary>
|
[Obsolete("Use IHasPathWithRepeats instead.")] // can be removed 20201126
|
||||||
/// A HitObject that has a curve.
|
|
||||||
/// </summary>
|
|
||||||
public interface IHasCurve : IHasDistance, IHasRepeats
|
public interface IHasCurve : IHasDistance, IHasRepeats
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -16,6 +15,8 @@ namespace osu.Game.Rulesets.Objects.Types
|
|||||||
SliderPath Path { get; }
|
SliderPath Path { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma warning disable 618
|
||||||
|
[Obsolete("Use IHasPathWithRepeats instead.")] // can be removed 20201126
|
||||||
public static class HasCurveExtensions
|
public static class HasCurveExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -50,4 +51,5 @@ namespace osu.Game.Rulesets.Objects.Types
|
|||||||
public static int SpanAt(this IHasCurve obj, double progress)
|
public static int SpanAt(this IHasCurve obj, double progress)
|
||||||
=> (int)(progress * obj.SpanCount());
|
=> (int)(progress * obj.SpanCount());
|
||||||
}
|
}
|
||||||
|
#pragma warning restore 618
|
||||||
}
|
}
|
||||||
|
13
osu.Game/Rulesets/Objects/Types/IHasPath.cs
Normal file
13
osu.Game/Rulesets/Objects/Types/IHasPath.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Objects.Types
|
||||||
|
{
|
||||||
|
public interface IHasPath : IHasDistance
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The curve.
|
||||||
|
/// </summary>
|
||||||
|
SliderPath Path { get; }
|
||||||
|
}
|
||||||
|
}
|
50
osu.Game/Rulesets/Objects/Types/IHasPathWithRepeats.cs
Normal file
50
osu.Game/Rulesets/Objects/Types/IHasPathWithRepeats.cs
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
// 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 osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Objects.Types
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A HitObject that has a curve.
|
||||||
|
/// </summary>
|
||||||
|
// ReSharper disable once RedundantExtendsListEntry
|
||||||
|
public interface IHasPathWithRepeats : IHasPath, IHasRepeats
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class HasPathWithRepeatsExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Computes the position on the curve relative to how much of the <see cref="HitObject"/> has been completed.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The curve.</param>
|
||||||
|
/// <param name="progress">[0, 1] where 0 is the start time of the <see cref="HitObject"/> and 1 is the end time of the <see cref="HitObject"/>.</param>
|
||||||
|
/// <returns>The position on the curve.</returns>
|
||||||
|
public static Vector2 CurvePositionAt(this IHasPathWithRepeats obj, double progress)
|
||||||
|
=> obj.Path.PositionAt(obj.ProgressAt(progress));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Computes the progress along the curve relative to how much of the <see cref="HitObject"/> has been completed.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The curve.</param>
|
||||||
|
/// <param name="progress">[0, 1] where 0 is the start time of the <see cref="HitObject"/> and 1 is the end time of the <see cref="HitObject"/>.</param>
|
||||||
|
/// <returns>[0, 1] where 0 is the beginning of the curve and 1 is the end of the curve.</returns>
|
||||||
|
public static double ProgressAt(this IHasPathWithRepeats obj, double progress)
|
||||||
|
{
|
||||||
|
double p = progress * obj.SpanCount() % 1;
|
||||||
|
if (obj.SpanAt(progress) % 2 == 1)
|
||||||
|
p = 1 - p;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines which span of the curve the progress point is on.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The curve.</param>
|
||||||
|
/// <param name="progress">[0, 1] where 0 is the beginning of the curve and 1 is the end of the curve.</param>
|
||||||
|
/// <returns>[0, SpanCount) where 0 is the first run.</returns>
|
||||||
|
public static int SpanAt(this IHasPathWithRepeats obj, double progress)
|
||||||
|
=> (int)(progress * obj.SpanCount());
|
||||||
|
}
|
||||||
|
}
|
@ -479,7 +479,7 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
protected override bool OnScroll(ScrollEvent e) => mouseWheelDisabled.Value && !GameplayClockContainer.IsPaused.Value;
|
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
|
#region Fail Logic
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ namespace osu.Game.Screens.Play
|
|||||||
this.Push(CreateResults(DrawableRuleset.ReplayScore.ScoreInfo));
|
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;
|
protected override ScoreInfo CreateScore() => score.ScoreInfo;
|
||||||
}
|
}
|
||||||
|
@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
@ -13,6 +14,8 @@ namespace osu.Game.Screens.Ranking
|
|||||||
{
|
{
|
||||||
public class ReplayDownloadButton : DownloadTrackingComposite<ScoreInfo, ScoreManager>
|
public class ReplayDownloadButton : DownloadTrackingComposite<ScoreInfo, ScoreManager>
|
||||||
{
|
{
|
||||||
|
public Bindable<ScoreInfo> Score => Model;
|
||||||
|
|
||||||
private DownloadButton button;
|
private DownloadButton button;
|
||||||
private ShakeContainer shakeContainer;
|
private ShakeContainer shakeContainer;
|
||||||
|
|
||||||
@ -23,7 +26,7 @@ namespace osu.Game.Screens.Ranking
|
|||||||
if (State.Value == DownloadState.LocallyAvailable)
|
if (State.Value == DownloadState.LocallyAvailable)
|
||||||
return ReplayAvailability.Local;
|
return ReplayAvailability.Local;
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(Model.Value.Hash))
|
if (!string.IsNullOrEmpty(Model.Value?.Hash))
|
||||||
return ReplayAvailability.Online;
|
return ReplayAvailability.Online;
|
||||||
|
|
||||||
return ReplayAvailability.NotAvailable;
|
return ReplayAvailability.NotAvailable;
|
||||||
|
@ -2,7 +2,9 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
@ -10,6 +12,7 @@ using osu.Framework.Graphics.Shapes;
|
|||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Screens.Backgrounds;
|
using osu.Game.Screens.Backgrounds;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
@ -17,7 +20,7 @@ using osuTK;
|
|||||||
|
|
||||||
namespace osu.Game.Screens.Ranking
|
namespace osu.Game.Screens.Ranking
|
||||||
{
|
{
|
||||||
public class ResultsScreen : OsuScreen
|
public abstract class ResultsScreen : OsuScreen
|
||||||
{
|
{
|
||||||
protected const float BACKGROUND_BLUR = 20;
|
protected const float BACKGROUND_BLUR = 20;
|
||||||
|
|
||||||
@ -28,19 +31,26 @@ namespace osu.Game.Screens.Ranking
|
|||||||
|
|
||||||
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap.Value);
|
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)]
|
[Resolved(CanBeNull = true)]
|
||||||
private Player player { get; set; }
|
private Player player { get; set; }
|
||||||
|
|
||||||
public readonly ScoreInfo Score;
|
[Resolved]
|
||||||
|
private IAPIProvider api { get; set; }
|
||||||
private readonly bool allowRetry;
|
|
||||||
|
|
||||||
private Drawable bottomPanel;
|
private Drawable bottomPanel;
|
||||||
|
private ScorePanelList panels;
|
||||||
|
|
||||||
public ResultsScreen(ScoreInfo score, bool allowRetry = true)
|
protected ResultsScreen(ScoreInfo score, bool allowRetry = true)
|
||||||
{
|
{
|
||||||
Score = score;
|
Score = score;
|
||||||
this.allowRetry = allowRetry;
|
this.allowRetry = allowRetry;
|
||||||
|
|
||||||
|
SelectedScore.Value = score;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
@ -48,17 +58,24 @@ namespace osu.Game.Screens.Ranking
|
|||||||
{
|
{
|
||||||
FillFlowContainer buttons;
|
FillFlowContainer buttons;
|
||||||
|
|
||||||
InternalChildren = new[]
|
InternalChild = new GridContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Content = new[]
|
||||||
|
{
|
||||||
|
new Drawable[]
|
||||||
{
|
{
|
||||||
new ResultsScrollContainer
|
new ResultsScrollContainer
|
||||||
{
|
{
|
||||||
Child = new ScorePanel(Score)
|
Child = panels = new ScorePanelList
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Origin = Anchor.Centre,
|
SelectedScore = { BindTarget = SelectedScore }
|
||||||
State = PanelState.Expanded
|
}
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
new[]
|
||||||
|
{
|
||||||
bottomPanel = new Container
|
bottomPanel = new Container
|
||||||
{
|
{
|
||||||
Anchor = Anchor.BottomLeft,
|
Anchor = Anchor.BottomLeft,
|
||||||
@ -82,13 +99,27 @@ namespace osu.Game.Screens.Ranking
|
|||||||
Direction = FillDirection.Horizontal,
|
Direction = FillDirection.Horizontal,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new ReplayDownloadButton(Score) { Width = 300 },
|
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)
|
if (player != null && allowRetry)
|
||||||
{
|
{
|
||||||
buttons.Add(new RetryButton { Width = 300 });
|
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)
|
public override void OnEntering(IScreen last)
|
||||||
{
|
{
|
||||||
base.OnEntering(last);
|
base.OnEntering(last);
|
||||||
@ -142,7 +194,7 @@ namespace osu.Game.Screens.Ranking
|
|||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
content.Height = Math.Max(768, DrawHeight);
|
content.Height = Math.Max(768 - TwoLayerButton.SIZE_EXTENDED.Y, DrawHeight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,9 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Colour;
|
using osu.Framework.Graphics.Colour;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
|
using osu.Game.Screens.Ranking.Contracted;
|
||||||
using osu.Game.Screens.Ranking.Expanded;
|
using osu.Game.Screens.Ranking.Expanded;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
@ -21,12 +23,12 @@ namespace osu.Game.Screens.Ranking
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Width of the panel when contracted.
|
/// Width of the panel when contracted.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const float contracted_width = 160;
|
public const float CONTRACTED_WIDTH = 130;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Height of the panel when contracted.
|
/// Height of the panel when contracted.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const float contracted_height = 320;
|
private const float contracted_height = 355;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Width of the panel when expanded.
|
/// Width of the panel when expanded.
|
||||||
@ -46,7 +48,7 @@ namespace osu.Game.Screens.Ranking
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Height of the top layer when the panel is contracted.
|
/// Height of the top layer when the panel is contracted.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const float contracted_top_layer_height = 40;
|
private const float contracted_top_layer_height = 30;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Duration for the panel to resize into its expanded/contracted size.
|
/// 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_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 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_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 event Action<PanelState> StateChanged;
|
||||||
|
public readonly ScoreInfo Score;
|
||||||
|
|
||||||
private readonly ScoreInfo score;
|
private Container content;
|
||||||
|
|
||||||
private Container topLayerContainer;
|
private Container topLayerContainer;
|
||||||
private Drawable topLayerBackground;
|
private Drawable topLayerBackground;
|
||||||
@ -89,18 +92,24 @@ namespace osu.Game.Screens.Ranking
|
|||||||
|
|
||||||
public ScorePanel(ScoreInfo score)
|
public ScorePanel(ScoreInfo score)
|
||||||
{
|
{
|
||||||
this.score = score;
|
Score = score;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
InternalChildren = new Drawable[]
|
InternalChild = content = new Container
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Size = new Vector2(40),
|
||||||
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
topLayerContainer = new Container
|
topLayerContainer = new Container
|
||||||
{
|
{
|
||||||
Name = "Top layer",
|
Name = "Top layer",
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Alpha = 0,
|
||||||
Height = 120,
|
Height = 120,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
@ -132,6 +141,7 @@ namespace osu.Game.Screens.Ranking
|
|||||||
middleLayerContentContainer = new Container { RelativeSizeAxes = Axes.Both }
|
middleLayerContentContainer = new Container { RelativeSizeAxes = Axes.Both }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,34 +184,40 @@ namespace osu.Game.Screens.Ranking
|
|||||||
|
|
||||||
private void updateState()
|
private void updateState()
|
||||||
{
|
{
|
||||||
topLayerContainer.MoveToY(0, resize_duration, Easing.OutQuint);
|
|
||||||
middleLayerContainer.MoveToY(0, resize_duration, Easing.OutQuint);
|
|
||||||
|
|
||||||
topLayerContent?.FadeOut(content_fade_duration).Expire();
|
topLayerContent?.FadeOut(content_fade_duration).Expire();
|
||||||
middleLayerContent?.FadeOut(content_fade_duration).Expire();
|
middleLayerContent?.FadeOut(content_fade_duration).Expire();
|
||||||
|
|
||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case PanelState.Expanded:
|
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);
|
topLayerBackground.FadeColour(expanded_top_layer_colour, resize_duration, Easing.OutQuint);
|
||||||
middleLayerBackground.FadeColour(expanded_middle_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));
|
topLayerContentContainer.Add(middleLayerContent = new ExpandedPanelTopContent(Score.User).With(d => d.Alpha = 0));
|
||||||
middleLayerContentContainer.Add(topLayerContent = new ExpandedPanelMiddleContent(score).With(d => d.Alpha = 0));
|
middleLayerContentContainer.Add(topLayerContent = new ExpandedPanelMiddleContent(Score).With(d => d.Alpha = 0));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PanelState.Contracted:
|
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);
|
topLayerBackground.FadeColour(contracted_top_layer_colour, resize_duration, Easing.OutQuint);
|
||||||
middleLayerBackground.FadeColour(contracted_middle_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;
|
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)
|
switch (state)
|
||||||
{
|
{
|
||||||
case PanelState.Expanded:
|
case PanelState.Expanded:
|
||||||
@ -219,5 +235,13 @@ namespace osu.Game.Screens.Ranking
|
|||||||
middleLayerContent?.FadeIn(content_fade_duration);
|
middleLayerContent?.FadeIn(content_fade_duration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override bool OnClick(ClickEvent e)
|
||||||
|
{
|
||||||
|
if (State == PanelState.Contracted)
|
||||||
|
State = PanelState.Expanded;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
185
osu.Game/Screens/Ranking/ScorePanelList.cs
Normal file
185
osu.Game/Screens/Ranking/ScorePanelList.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
32
osu.Game/Screens/Ranking/SoloResultsScreen.cs
Normal file
32
osu.Game/Screens/Ranking/SoloResultsScreen.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -37,7 +37,7 @@ namespace osu.Game.Screens.Select
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void PresentScore(ScoreInfo score) =>
|
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();
|
protected override BeatmapDetailArea CreateBeatmapDetailArea() => new PlayBeatmapDetailArea();
|
||||||
|
|
||||||
|
52
osu.Game/Tests/TestScoreInfo.cs
Normal file
52
osu.Game/Tests/TestScoreInfo.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||||
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Efnt/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Efnt/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Emp3/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Emp3/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Epng/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Epng/@EntryIndexedValue">True</s:Boolean>
|
||||||
@ -905,14 +905,17 @@ private void load()
|
|||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Catmull/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Catmull/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Drawables/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Drawables/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=gameplay/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=gameplay/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=hitobject/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=hitobjects/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=hitobjects/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=keymods/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=keymods/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Kiai/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Kiai/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Leaderboard/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Leaderboard/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Leaderboards/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Leaderboards/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Playfield/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Playfield/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=playfields/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=resampler/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=resampler/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=ruleset/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=ruleset/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=rulesets/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=rulesets/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=ruleset_0027s/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Taiko/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Taiko/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Unranked/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Unranked/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||||
|
Loading…
Reference in New Issue
Block a user