mirror of
https://github.com/ppy/osu.git
synced 2025-03-23 01:37:31 +08:00
Merge branch 'master' into remove_obsolete_star_colours
This commit is contained in:
commit
e416bdd06b
@ -1,6 +1,6 @@
|
||||
clone_depth: 1
|
||||
version: '{build}'
|
||||
image: Visual Studio 2019
|
||||
image: Visual Studio 2022
|
||||
test: off
|
||||
skip_non_tags: true
|
||||
configuration: Release
|
||||
@ -83,4 +83,4 @@ artifacts:
|
||||
|
||||
deploy:
|
||||
- provider: Environment
|
||||
name: nuget
|
||||
name: nuget
|
||||
|
@ -10,7 +10,7 @@
|
||||
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.1219.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.1226.0" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<!-- Fody does not handle Android build well, and warns when unchanged.
|
||||
|
@ -1,193 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Skinning.Argon
|
||||
{
|
||||
public partial class ArgonJudgementPiece : CompositeDrawable, IAnimatableJudgement
|
||||
{
|
||||
protected readonly HitResult Result;
|
||||
|
||||
protected SpriteText JudgementText { get; private set; } = null!;
|
||||
|
||||
private RingExplosion? ringExplosion;
|
||||
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; } = null!;
|
||||
|
||||
public ArgonJudgementPiece(HitResult result)
|
||||
{
|
||||
Result = result;
|
||||
Origin = Anchor.Centre;
|
||||
Y = 160;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
JudgementText = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Text = Result.GetDescription().ToUpperInvariant(),
|
||||
Colour = colours.ForHitResult(Result),
|
||||
Blending = BlendingParameters.Additive,
|
||||
Spacing = new Vector2(10, 0),
|
||||
Font = OsuFont.Default.With(size: 28, weight: FontWeight.Regular),
|
||||
},
|
||||
};
|
||||
|
||||
if (Result.IsHit())
|
||||
{
|
||||
AddInternal(ringExplosion = new RingExplosion(Result)
|
||||
{
|
||||
Colour = colours.ForHitResult(Result),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Plays the default animation for this judgement piece.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The base implementation only handles fade (for all result types) and misses.
|
||||
/// Individual rulesets are recommended to implement their appropriate hit animations.
|
||||
/// </remarks>
|
||||
public virtual void PlayAnimation()
|
||||
{
|
||||
switch (Result)
|
||||
{
|
||||
default:
|
||||
JudgementText
|
||||
.ScaleTo(Vector2.One)
|
||||
.ScaleTo(new Vector2(1.4f), 1800, Easing.OutQuint);
|
||||
break;
|
||||
|
||||
case HitResult.Miss:
|
||||
this.ScaleTo(1.6f);
|
||||
this.ScaleTo(1, 100, Easing.In);
|
||||
|
||||
this.MoveTo(Vector2.Zero);
|
||||
this.MoveToOffset(new Vector2(0, 100), 800, Easing.InQuint);
|
||||
|
||||
this.RotateTo(0);
|
||||
this.RotateTo(40, 800, Easing.InQuint);
|
||||
break;
|
||||
}
|
||||
|
||||
this.FadeOutFromOne(800);
|
||||
|
||||
ringExplosion?.PlayAnimation();
|
||||
}
|
||||
|
||||
public Drawable? GetAboveHitObjectsProxiedContent() => null;
|
||||
|
||||
private partial class RingExplosion : CompositeDrawable
|
||||
{
|
||||
private readonly float travel = 52;
|
||||
|
||||
public RingExplosion(HitResult result)
|
||||
{
|
||||
const float thickness = 4;
|
||||
|
||||
const float small_size = 9;
|
||||
const float large_size = 14;
|
||||
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
Blending = BlendingParameters.Additive;
|
||||
|
||||
int countSmall = 0;
|
||||
int countLarge = 0;
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case HitResult.Meh:
|
||||
countSmall = 3;
|
||||
travel *= 0.3f;
|
||||
break;
|
||||
|
||||
case HitResult.Ok:
|
||||
case HitResult.Good:
|
||||
countSmall = 4;
|
||||
travel *= 0.6f;
|
||||
break;
|
||||
|
||||
case HitResult.Great:
|
||||
case HitResult.Perfect:
|
||||
countSmall = 4;
|
||||
countLarge = 4;
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < countSmall; i++)
|
||||
AddInternal(new RingPiece(thickness) { Size = new Vector2(small_size) });
|
||||
|
||||
for (int i = 0; i < countLarge; i++)
|
||||
AddInternal(new RingPiece(thickness) { Size = new Vector2(large_size) });
|
||||
}
|
||||
|
||||
public void PlayAnimation()
|
||||
{
|
||||
foreach (var c in InternalChildren)
|
||||
{
|
||||
const float start_position_ratio = 0.3f;
|
||||
|
||||
float direction = RNG.NextSingle(0, 360);
|
||||
float distance = RNG.NextSingle(travel / 2, travel);
|
||||
|
||||
c.MoveTo(new Vector2(
|
||||
MathF.Cos(direction) * distance * start_position_ratio,
|
||||
MathF.Sin(direction) * distance * start_position_ratio
|
||||
));
|
||||
|
||||
c.MoveTo(new Vector2(
|
||||
MathF.Cos(direction) * distance,
|
||||
MathF.Sin(direction) * distance
|
||||
), 600, Easing.OutQuint);
|
||||
}
|
||||
|
||||
this.FadeOutFromOne(1000, Easing.OutQuint);
|
||||
}
|
||||
|
||||
public partial class RingPiece : CircularContainer
|
||||
{
|
||||
public RingPiece(float thickness = 9)
|
||||
{
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
Masking = true;
|
||||
BorderThickness = thickness;
|
||||
BorderColour = Color4.White;
|
||||
|
||||
Child = new Box
|
||||
{
|
||||
AlwaysPresent = true,
|
||||
Alpha = 0,
|
||||
RelativeSizeAxes = Axes.Both
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -35,8 +35,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
protected PatternGenerator(LegacyRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap)
|
||||
: base(hitObject, beatmap, previousPattern)
|
||||
{
|
||||
if (random == null) throw new ArgumentNullException(nameof(random));
|
||||
if (originalBeatmap == null) throw new ArgumentNullException(nameof(originalBeatmap));
|
||||
ArgumentNullException.ThrowIfNull(random);
|
||||
ArgumentNullException.ThrowIfNull(originalBeatmap);
|
||||
|
||||
Random = random;
|
||||
OriginalBeatmap = originalBeatmap;
|
||||
|
@ -33,9 +33,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
|
||||
|
||||
protected PatternGenerator(HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern)
|
||||
{
|
||||
if (hitObject == null) throw new ArgumentNullException(nameof(hitObject));
|
||||
if (beatmap == null) throw new ArgumentNullException(nameof(beatmap));
|
||||
if (previousPattern == null) throw new ArgumentNullException(nameof(previousPattern));
|
||||
ArgumentNullException.ThrowIfNull(hitObject);
|
||||
ArgumentNullException.ThrowIfNull(beatmap);
|
||||
ArgumentNullException.ThrowIfNull(previousPattern);
|
||||
|
||||
HitObject = hitObject;
|
||||
Beatmap = beatmap;
|
||||
|
@ -22,8 +22,7 @@ namespace osu.Game.Rulesets.Mania.MathUtils
|
||||
|
||||
public static void Sort(T[] keys, IComparer<T> comparer)
|
||||
{
|
||||
if (keys == null)
|
||||
throw new ArgumentNullException(nameof(keys));
|
||||
ArgumentNullException.ThrowIfNull(keys);
|
||||
|
||||
if (keys.Length == 0)
|
||||
return;
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
@ -18,20 +17,18 @@ using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
||||
{
|
||||
public partial class ArgonJudgementPiece : CompositeDrawable, IAnimatableJudgement
|
||||
public partial class ArgonJudgementPiece : JudgementPiece, IAnimatableJudgement
|
||||
{
|
||||
protected readonly HitResult Result;
|
||||
|
||||
protected SpriteText JudgementText { get; private set; } = null!;
|
||||
|
||||
private RingExplosion? ringExplosion;
|
||||
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; } = null!;
|
||||
|
||||
public ArgonJudgementPiece(HitResult result)
|
||||
: base(result)
|
||||
{
|
||||
Result = result;
|
||||
AutoSizeAxes = Axes.Both;
|
||||
|
||||
Origin = Anchor.Centre;
|
||||
Y = 160;
|
||||
}
|
||||
@ -39,22 +36,6 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
JudgementText = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Text = Result.GetDescription().ToUpperInvariant(),
|
||||
Colour = colours.ForHitResult(Result),
|
||||
Blending = BlendingParameters.Additive,
|
||||
Spacing = new Vector2(10, 0),
|
||||
Font = OsuFont.Default.With(size: 28, weight: FontWeight.Regular),
|
||||
},
|
||||
};
|
||||
|
||||
if (Result.IsHit())
|
||||
{
|
||||
AddInternal(ringExplosion = new RingExplosion(Result)
|
||||
@ -64,6 +45,16 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
||||
}
|
||||
}
|
||||
|
||||
protected override SpriteText CreateJudgementText() =>
|
||||
new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Blending = BlendingParameters.Additive,
|
||||
Spacing = new Vector2(10, 0),
|
||||
Font = OsuFont.Default.With(size: 28, weight: FontWeight.Regular),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Plays the default animation for this judgement piece.
|
||||
/// </summary>
|
||||
|
@ -27,6 +27,10 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
||||
switch (lookup)
|
||||
{
|
||||
case GameplaySkinComponentLookup<HitResult> resultComponent:
|
||||
// This should eventually be moved to a skin setting, when supported.
|
||||
if (Skin is ArgonProSkin && resultComponent.Component >= HitResult.Great)
|
||||
return Drawable.Empty();
|
||||
|
||||
return new ArgonJudgementPiece(resultComponent.Component);
|
||||
|
||||
case ManiaSkinComponentLookup maniaComponent:
|
||||
|
@ -29,8 +29,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
|
||||
public ManiaPlayfield(List<StageDefinition> stageDefinitions)
|
||||
{
|
||||
if (stageDefinitions == null)
|
||||
throw new ArgumentNullException(nameof(stageDefinitions));
|
||||
ArgumentNullException.ThrowIfNull(stageDefinitions);
|
||||
|
||||
if (stageDefinitions.Count <= 0)
|
||||
throw new ArgumentException("Can't have zero or fewer stages.");
|
||||
|
@ -160,9 +160,9 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
static bool assertSamples(HitObject hitObject) => hitObject.Samples.All(s => s.Name != HitSampleInfo.HIT_CLAP && s.Name != HitSampleInfo.HIT_WHISTLE);
|
||||
}
|
||||
|
||||
private Drawable testSimpleBig(int repeats = 0) => createSlider(2, repeats: repeats);
|
||||
private Drawable testSimpleBig(int repeats = 0) => createSlider(repeats: repeats);
|
||||
|
||||
private Drawable testSimpleBigLargeStackOffset(int repeats = 0) => createSlider(2, repeats: repeats, stackHeight: 10);
|
||||
private Drawable testSimpleBigLargeStackOffset(int repeats = 0) => createSlider(repeats: repeats, stackHeight: 10);
|
||||
|
||||
private Drawable testDistanceOverflow(int repeats = 0)
|
||||
{
|
||||
|
@ -84,8 +84,8 @@ namespace osu.Game.Rulesets.Osu.Replays
|
||||
{
|
||||
public int Compare(ReplayFrame? f1, ReplayFrame? f2)
|
||||
{
|
||||
if (f1 == null) throw new ArgumentNullException(nameof(f1));
|
||||
if (f2 == null) throw new ArgumentNullException(nameof(f2));
|
||||
ArgumentNullException.ThrowIfNull(f1);
|
||||
ArgumentNullException.ThrowIfNull(f2);
|
||||
|
||||
return f1.Time.CompareTo(f2.Time);
|
||||
}
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
@ -17,42 +16,24 @@ using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Skinning.Argon
|
||||
{
|
||||
public partial class ArgonJudgementPiece : CompositeDrawable, IAnimatableJudgement
|
||||
public partial class ArgonJudgementPiece : JudgementPiece, IAnimatableJudgement
|
||||
{
|
||||
protected readonly HitResult Result;
|
||||
|
||||
protected SpriteText JudgementText { get; private set; } = null!;
|
||||
|
||||
private RingExplosion? ringExplosion;
|
||||
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; } = null!;
|
||||
|
||||
public ArgonJudgementPiece(HitResult result)
|
||||
: base(result)
|
||||
{
|
||||
Result = result;
|
||||
AutoSizeAxes = Axes.Both;
|
||||
|
||||
Origin = Anchor.Centre;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
JudgementText = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Text = Result.GetDescription().ToUpperInvariant(),
|
||||
Colour = colours.ForHitResult(Result),
|
||||
Blending = BlendingParameters.Additive,
|
||||
Spacing = new Vector2(5, 0),
|
||||
Font = OsuFont.Default.With(size: 20, weight: FontWeight.Bold),
|
||||
},
|
||||
};
|
||||
|
||||
if (Result.IsHit())
|
||||
{
|
||||
AddInternal(ringExplosion = new RingExplosion(Result)
|
||||
@ -62,6 +43,16 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
|
||||
}
|
||||
}
|
||||
|
||||
protected override SpriteText CreateJudgementText() =>
|
||||
new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Blending = BlendingParameters.Additive,
|
||||
Spacing = new Vector2(5, 0),
|
||||
Font = OsuFont.Default.With(size: 20, weight: FontWeight.Bold),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Plays the default animation for this judgement piece.
|
||||
/// </summary>
|
||||
|
@ -19,6 +19,10 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
|
||||
switch (lookup)
|
||||
{
|
||||
case GameplaySkinComponentLookup<HitResult> resultComponent:
|
||||
// This should eventually be moved to a skin setting, when supported.
|
||||
if (Skin is ArgonProSkin && resultComponent.Component >= HitResult.Great)
|
||||
return Drawable.Empty();
|
||||
|
||||
return new ArgonJudgementPiece(resultComponent.Component);
|
||||
|
||||
case OsuSkinComponentLookup osuComponent:
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
@ -18,20 +17,16 @@ using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Skinning.Argon
|
||||
{
|
||||
public partial class ArgonJudgementPiece : CompositeDrawable, IAnimatableJudgement
|
||||
public partial class ArgonJudgementPiece : JudgementPiece, IAnimatableJudgement
|
||||
{
|
||||
protected readonly HitResult Result;
|
||||
|
||||
protected SpriteText JudgementText { get; private set; } = null!;
|
||||
|
||||
private RingExplosion? ringExplosion;
|
||||
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; } = null!;
|
||||
|
||||
public ArgonJudgementPiece(HitResult result)
|
||||
: base(result)
|
||||
{
|
||||
Result = result;
|
||||
RelativePositionAxes = Axes.Both;
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
}
|
||||
@ -39,21 +34,6 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Argon
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
JudgementText = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Text = Result.GetDescription().ToUpperInvariant(),
|
||||
Colour = colours.ForHitResult(Result),
|
||||
Blending = BlendingParameters.Additive,
|
||||
Spacing = new Vector2(10, 0),
|
||||
RelativePositionAxes = Axes.Both,
|
||||
Font = OsuFont.Default.With(size: 20, weight: FontWeight.Regular),
|
||||
},
|
||||
};
|
||||
|
||||
if (Result.IsHit())
|
||||
{
|
||||
AddInternal(ringExplosion = new RingExplosion(Result)
|
||||
@ -64,6 +44,17 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Argon
|
||||
}
|
||||
}
|
||||
|
||||
protected override SpriteText CreateJudgementText() =>
|
||||
new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Blending = BlendingParameters.Additive,
|
||||
Spacing = new Vector2(10, 0),
|
||||
RelativePositionAxes = Axes.Both,
|
||||
Font = OsuFont.Default.With(size: 20, weight: FontWeight.Regular),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Plays the default animation for this judgement piece.
|
||||
/// </summary>
|
||||
|
@ -19,6 +19,10 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Argon
|
||||
switch (component)
|
||||
{
|
||||
case GameplaySkinComponentLookup<HitResult> resultComponent:
|
||||
// This should eventually be moved to a skin setting, when supported.
|
||||
if (Skin is ArgonProSkin && resultComponent.Component >= HitResult.Great)
|
||||
return Drawable.Empty();
|
||||
|
||||
return new ArgonJudgementPiece(resultComponent.Component);
|
||||
|
||||
case TaikoSkinComponentLookup taikoComponent:
|
||||
|
@ -23,7 +23,7 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
{
|
||||
public partial class TestSceneZoomableScrollContainer : OsuManualInputManagerTestScene
|
||||
{
|
||||
private ZoomableScrollContainer scrollContainer;
|
||||
private TestZoomableScrollContainer scrollContainer;
|
||||
private Drawable innerBox;
|
||||
|
||||
[SetUpSteps]
|
||||
@ -47,7 +47,7 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = OsuColour.Gray(30)
|
||||
},
|
||||
scrollContainer = new ZoomableScrollContainer(1, 60, 1)
|
||||
scrollContainer = new TestZoomableScrollContainer(1, 60, 1)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
@ -93,6 +93,14 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
AddAssert("Inner container width matches scroll container", () => innerBox.DrawWidth == scrollContainer.DrawWidth);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestWidthUpdatesOnSecondZoomSetup()
|
||||
{
|
||||
AddAssert("Inner container width = 1x", () => innerBox.DrawWidth == scrollContainer.DrawWidth);
|
||||
AddStep("reload zoom", () => scrollContainer.SetupZoom(10, 10, 60));
|
||||
AddAssert("Inner container width = 10x", () => innerBox.DrawWidth == scrollContainer.DrawWidth * 10);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestZoom0()
|
||||
{
|
||||
@ -190,5 +198,15 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
|
||||
private Quad scrollQuad => scrollContainer.ScreenSpaceDrawQuad;
|
||||
private Quad boxQuad => innerBox.ScreenSpaceDrawQuad;
|
||||
|
||||
private partial class TestZoomableScrollContainer : ZoomableScrollContainer
|
||||
{
|
||||
public TestZoomableScrollContainer(int minimum, float maximum, float initial)
|
||||
: base(minimum, maximum, initial)
|
||||
{
|
||||
}
|
||||
|
||||
public new void SetupZoom(float initial, float minimum, float maximum) => base.SetupZoom(initial, minimum, maximum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -257,7 +257,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
prepareTestAPI(true);
|
||||
|
||||
createPlayerTest(false, createRuleset: () => new OsuRuleset
|
||||
createPlayerTest(createRuleset: () => new OsuRuleset
|
||||
{
|
||||
RulesetInfo =
|
||||
{
|
||||
|
280
osu.Game.Tests/Visual/Online/TestSceneSoloStatisticsWatcher.cs
Normal file
280
osu.Game.Tests/Visual/Online/TestSceneSoloStatisticsWatcher.cs
Normal file
@ -0,0 +1,280 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Models;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Solo;
|
||||
using osu.Game.Online.Spectator;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Online
|
||||
{
|
||||
[HeadlessTest]
|
||||
public partial class TestSceneSoloStatisticsWatcher : OsuTestScene
|
||||
{
|
||||
protected override bool UseOnlineAPI => false;
|
||||
|
||||
private SoloStatisticsWatcher watcher = null!;
|
||||
|
||||
[Resolved]
|
||||
private SpectatorClient spectatorClient { get; set; } = null!;
|
||||
|
||||
private DummyAPIAccess dummyAPI => (DummyAPIAccess)API;
|
||||
|
||||
private Action<GetUsersRequest>? handleGetUsersRequest;
|
||||
private Action<GetUserRequest>? handleGetUserRequest;
|
||||
|
||||
private readonly Dictionary<(int userId, string rulesetName), UserStatistics> serverSideStatistics = new Dictionary<(int userId, string rulesetName), UserStatistics>();
|
||||
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps()
|
||||
{
|
||||
AddStep("clear server-side stats", () => serverSideStatistics.Clear());
|
||||
AddStep("set up request handling", () =>
|
||||
{
|
||||
handleGetUserRequest = null;
|
||||
handleGetUsersRequest = null;
|
||||
|
||||
dummyAPI.HandleRequest = request =>
|
||||
{
|
||||
switch (request)
|
||||
{
|
||||
case GetUsersRequest getUsersRequest:
|
||||
if (handleGetUsersRequest != null)
|
||||
{
|
||||
handleGetUsersRequest?.Invoke(getUsersRequest);
|
||||
}
|
||||
else
|
||||
{
|
||||
int userId = getUsersRequest.UserIds.Single();
|
||||
var response = new GetUsersResponse
|
||||
{
|
||||
Users = new List<APIUser>
|
||||
{
|
||||
new APIUser
|
||||
{
|
||||
Id = userId,
|
||||
RulesetsStatistics = new Dictionary<string, UserStatistics>
|
||||
{
|
||||
["osu"] = tryGetStatistics(userId, "osu"),
|
||||
["taiko"] = tryGetStatistics(userId, "taiko"),
|
||||
["fruits"] = tryGetStatistics(userId, "fruits"),
|
||||
["mania"] = tryGetStatistics(userId, "mania"),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
getUsersRequest.TriggerSuccess(response);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
case GetUserRequest getUserRequest:
|
||||
if (handleGetUserRequest != null)
|
||||
{
|
||||
handleGetUserRequest.Invoke(getUserRequest);
|
||||
}
|
||||
else
|
||||
{
|
||||
int userId = int.Parse(getUserRequest.Lookup);
|
||||
string rulesetName = getUserRequest.Ruleset.ShortName;
|
||||
var response = new APIUser
|
||||
{
|
||||
Id = userId,
|
||||
Statistics = tryGetStatistics(userId, rulesetName)
|
||||
};
|
||||
getUserRequest.TriggerSuccess(response);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
AddStep("create watcher", () =>
|
||||
{
|
||||
Child = watcher = new SoloStatisticsWatcher();
|
||||
});
|
||||
}
|
||||
|
||||
private UserStatistics tryGetStatistics(int userId, string rulesetName)
|
||||
=> serverSideStatistics.TryGetValue((userId, rulesetName), out var stats) ? stats : new UserStatistics();
|
||||
|
||||
[Test]
|
||||
public void TestStatisticsUpdateFiredAfterRegistrationAddedAndScoreProcessed()
|
||||
{
|
||||
int userId = getUserId();
|
||||
long scoreId = getScoreId();
|
||||
setUpUser(userId);
|
||||
|
||||
var ruleset = new OsuRuleset().RulesetInfo;
|
||||
|
||||
SoloStatisticsUpdate? update = null;
|
||||
registerForUpdates(scoreId, ruleset, receivedUpdate => update = receivedUpdate);
|
||||
|
||||
feignScoreProcessing(userId, ruleset, 5_000_000);
|
||||
|
||||
AddStep("signal score processed", () => ((ISpectatorClient)spectatorClient).UserScoreProcessed(userId, scoreId));
|
||||
AddUntilStep("update received", () => update != null);
|
||||
AddAssert("values before are correct", () => update!.Before.TotalScore, () => Is.EqualTo(4_000_000));
|
||||
AddAssert("values after are correct", () => update!.After.TotalScore, () => Is.EqualTo(5_000_000));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStatisticsUpdateFiredAfterScoreProcessedAndRegistrationAdded()
|
||||
{
|
||||
int userId = getUserId();
|
||||
setUpUser(userId);
|
||||
|
||||
long scoreId = getScoreId();
|
||||
var ruleset = new OsuRuleset().RulesetInfo;
|
||||
|
||||
// note ordering - in this test processing completes *before* the registration is added.
|
||||
feignScoreProcessing(userId, ruleset, 5_000_000);
|
||||
|
||||
SoloStatisticsUpdate? update = null;
|
||||
registerForUpdates(scoreId, ruleset, receivedUpdate => update = receivedUpdate);
|
||||
|
||||
AddStep("signal score processed", () => ((ISpectatorClient)spectatorClient).UserScoreProcessed(userId, scoreId));
|
||||
AddUntilStep("update received", () => update != null);
|
||||
AddAssert("values before are correct", () => update!.Before.TotalScore, () => Is.EqualTo(4_000_000));
|
||||
AddAssert("values after are correct", () => update!.After.TotalScore, () => Is.EqualTo(5_000_000));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStatisticsUpdateNotFiredIfUserLoggedOut()
|
||||
{
|
||||
int userId = getUserId();
|
||||
setUpUser(userId);
|
||||
|
||||
long scoreId = getScoreId();
|
||||
var ruleset = new OsuRuleset().RulesetInfo;
|
||||
|
||||
SoloStatisticsUpdate? update = null;
|
||||
registerForUpdates(scoreId, ruleset, receivedUpdate => update = receivedUpdate);
|
||||
|
||||
feignScoreProcessing(userId, ruleset, 5_000_000);
|
||||
|
||||
AddStep("log out user", () => dummyAPI.Logout());
|
||||
|
||||
AddStep("signal score processed", () => ((ISpectatorClient)spectatorClient).UserScoreProcessed(userId, scoreId));
|
||||
AddWaitStep("wait a bit", 5);
|
||||
AddAssert("update not received", () => update == null);
|
||||
|
||||
AddStep("log in user", () => dummyAPI.Login("user", "password"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStatisticsUpdateNotFiredIfAnotherUserLoggedIn()
|
||||
{
|
||||
int userId = getUserId();
|
||||
setUpUser(userId);
|
||||
|
||||
long scoreId = getScoreId();
|
||||
var ruleset = new OsuRuleset().RulesetInfo;
|
||||
|
||||
SoloStatisticsUpdate? update = null;
|
||||
registerForUpdates(scoreId, ruleset, receivedUpdate => update = receivedUpdate);
|
||||
|
||||
feignScoreProcessing(userId, ruleset, 5_000_000);
|
||||
|
||||
AddStep("change user", () => dummyAPI.LocalUser.Value = new APIUser { Id = getUserId() });
|
||||
|
||||
AddStep("signal score processed", () => ((ISpectatorClient)spectatorClient).UserScoreProcessed(userId, scoreId));
|
||||
AddWaitStep("wait a bit", 5);
|
||||
AddAssert("update not received", () => update == null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStatisticsUpdateNotFiredIfScoreIdDoesNotMatch()
|
||||
{
|
||||
int userId = getUserId();
|
||||
setUpUser(userId);
|
||||
|
||||
long scoreId = getScoreId();
|
||||
var ruleset = new OsuRuleset().RulesetInfo;
|
||||
|
||||
SoloStatisticsUpdate? update = null;
|
||||
registerForUpdates(scoreId, ruleset, receivedUpdate => update = receivedUpdate);
|
||||
|
||||
feignScoreProcessing(userId, ruleset, 5_000_000);
|
||||
|
||||
AddStep("signal another score processed", () => ((ISpectatorClient)spectatorClient).UserScoreProcessed(userId, getScoreId()));
|
||||
AddWaitStep("wait a bit", 5);
|
||||
AddAssert("update not received", () => update == null);
|
||||
}
|
||||
|
||||
// the behaviour exercised in this test may not be final, it is mostly assumed for simplicity.
|
||||
// in the long run we may want each score's update to be entirely isolated from others, rather than have prior unobserved updates merge into the latest.
|
||||
[Test]
|
||||
public void TestIgnoredScoreUpdateIsMergedIntoNextOne()
|
||||
{
|
||||
int userId = getUserId();
|
||||
setUpUser(userId);
|
||||
|
||||
long firstScoreId = getScoreId();
|
||||
var ruleset = new OsuRuleset().RulesetInfo;
|
||||
|
||||
feignScoreProcessing(userId, ruleset, 5_000_000);
|
||||
|
||||
AddStep("signal score processed", () => ((ISpectatorClient)spectatorClient).UserScoreProcessed(userId, firstScoreId));
|
||||
|
||||
long secondScoreId = getScoreId();
|
||||
|
||||
feignScoreProcessing(userId, ruleset, 6_000_000);
|
||||
|
||||
SoloStatisticsUpdate? update = null;
|
||||
registerForUpdates(secondScoreId, ruleset, receivedUpdate => update = receivedUpdate);
|
||||
|
||||
AddStep("signal score processed", () => ((ISpectatorClient)spectatorClient).UserScoreProcessed(userId, secondScoreId));
|
||||
AddUntilStep("update received", () => update != null);
|
||||
AddAssert("values before are correct", () => update!.Before.TotalScore, () => Is.EqualTo(4_000_000));
|
||||
AddAssert("values after are correct", () => update!.After.TotalScore, () => Is.EqualTo(6_000_000));
|
||||
}
|
||||
|
||||
private int nextUserId = 2000;
|
||||
private long nextScoreId = 50000;
|
||||
|
||||
private int getUserId() => ++nextUserId;
|
||||
private long getScoreId() => ++nextScoreId;
|
||||
|
||||
private void setUpUser(int userId)
|
||||
{
|
||||
AddStep("fetch initial stats", () =>
|
||||
{
|
||||
serverSideStatistics[(userId, "osu")] = new UserStatistics { TotalScore = 4_000_000 };
|
||||
serverSideStatistics[(userId, "taiko")] = new UserStatistics { TotalScore = 3_000_000 };
|
||||
serverSideStatistics[(userId, "fruits")] = new UserStatistics { TotalScore = 2_000_000 };
|
||||
serverSideStatistics[(userId, "mania")] = new UserStatistics { TotalScore = 1_000_000 };
|
||||
|
||||
dummyAPI.LocalUser.Value = new APIUser { Id = userId };
|
||||
});
|
||||
}
|
||||
|
||||
private void registerForUpdates(long scoreId, RulesetInfo rulesetInfo, Action<SoloStatisticsUpdate> onUpdateReady) =>
|
||||
AddStep("register for updates", () => watcher.RegisterForStatisticsUpdateAfter(
|
||||
new ScoreInfo(Beatmap.Value.BeatmapInfo, new OsuRuleset().RulesetInfo, new RealmUser())
|
||||
{
|
||||
Ruleset = rulesetInfo,
|
||||
OnlineID = scoreId
|
||||
},
|
||||
onUpdateReady));
|
||||
|
||||
private void feignScoreProcessing(int userId, RulesetInfo rulesetInfo, long newTotalScore)
|
||||
=> AddStep("feign score processing", () => serverSideStatistics[(userId, rulesetInfo.ShortName)] = new UserStatistics { TotalScore = newTotalScore });
|
||||
}
|
||||
}
|
@ -4,8 +4,11 @@
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
@ -29,6 +32,15 @@ namespace osu.Game.Tests.Visual.Online
|
||||
AddStep("Show main page", () => wiki.Show());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCancellationDoesntShowError()
|
||||
{
|
||||
AddStep("Show main page", () => wiki.Show());
|
||||
AddStep("Show another page", () => wiki.ShowPage("Article_styling_criteria/Formatting"));
|
||||
|
||||
AddUntilStep("Current path is not error", () => wiki.CurrentPath != "error");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestArticlePage()
|
||||
{
|
||||
@ -56,7 +68,9 @@ namespace osu.Game.Tests.Visual.Online
|
||||
public void TestErrorPage()
|
||||
{
|
||||
setUpWikiResponse(responseArticlePage);
|
||||
AddStep("Show Error Page", () => wiki.ShowPage("Error"));
|
||||
AddStep("Show nonexistent page", () => wiki.ShowPage("This_page_will_error_out"));
|
||||
AddUntilStep("Wait for error page", () => wiki.CurrentPath == "error");
|
||||
AddUntilStep("Error message correct", () => wiki.ChildrenOfType<SpriteText>().Any(text => text.Text == "\"This_page_will_error_out\"."));
|
||||
}
|
||||
|
||||
private void setUpWikiResponse(APIWikiPage r, string redirectionPath = null)
|
||||
|
117
osu.Game.Tests/Visual/Ranking/TestSceneOverallRanking.cs
Normal file
117
osu.Game.Tests/Visual/Ranking/TestSceneOverallRanking.cs
Normal file
@ -0,0 +1,117 @@
|
||||
// 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.Graphics;
|
||||
using osu.Game.Online.Solo;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens.Ranking.Statistics.User;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Ranking
|
||||
{
|
||||
public partial class TestSceneOverallRanking : OsuTestScene
|
||||
{
|
||||
private OverallRanking overallRanking = null!;
|
||||
|
||||
[Test]
|
||||
public void TestUpdatePending()
|
||||
{
|
||||
createDisplay();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAllIncreased()
|
||||
{
|
||||
createDisplay();
|
||||
displayUpdate(
|
||||
new UserStatistics
|
||||
{
|
||||
GlobalRank = 12_345,
|
||||
Accuracy = 0.9899,
|
||||
MaxCombo = 2_322,
|
||||
RankedScore = 23_123_543_456,
|
||||
TotalScore = 123_123_543_456,
|
||||
PP = 5_072
|
||||
},
|
||||
new UserStatistics
|
||||
{
|
||||
GlobalRank = 1_234,
|
||||
Accuracy = 0.9907,
|
||||
MaxCombo = 2_352,
|
||||
RankedScore = 23_124_231_435,
|
||||
TotalScore = 123_124_231_435,
|
||||
PP = 5_434
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAllDecreased()
|
||||
{
|
||||
createDisplay();
|
||||
displayUpdate(
|
||||
new UserStatistics
|
||||
{
|
||||
GlobalRank = 1_234,
|
||||
Accuracy = 0.9907,
|
||||
MaxCombo = 2_352,
|
||||
RankedScore = 23_124_231_435,
|
||||
TotalScore = 123_124_231_435,
|
||||
PP = 5_434
|
||||
},
|
||||
new UserStatistics
|
||||
{
|
||||
GlobalRank = 12_345,
|
||||
Accuracy = 0.9899,
|
||||
MaxCombo = 2_322,
|
||||
RankedScore = 23_123_543_456,
|
||||
TotalScore = 123_123_543_456,
|
||||
PP = 5_072
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNoChanges()
|
||||
{
|
||||
var statistics = new UserStatistics
|
||||
{
|
||||
GlobalRank = 12_345,
|
||||
Accuracy = 0.9899,
|
||||
MaxCombo = 2_322,
|
||||
RankedScore = 23_123_543_456,
|
||||
TotalScore = 123_123_543_456,
|
||||
PP = 5_072
|
||||
};
|
||||
|
||||
createDisplay();
|
||||
displayUpdate(statistics, statistics);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNotRanked()
|
||||
{
|
||||
var statistics = new UserStatistics
|
||||
{
|
||||
GlobalRank = null,
|
||||
Accuracy = 0.9899,
|
||||
MaxCombo = 2_322,
|
||||
RankedScore = 23_123_543_456,
|
||||
TotalScore = 123_123_543_456,
|
||||
PP = null
|
||||
};
|
||||
|
||||
createDisplay();
|
||||
displayUpdate(statistics, statistics);
|
||||
}
|
||||
|
||||
private void createDisplay() => AddStep("create display", () => Child = overallRanking = new OverallRanking
|
||||
{
|
||||
Width = 400,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre
|
||||
});
|
||||
|
||||
private void displayUpdate(UserStatistics before, UserStatistics after) =>
|
||||
AddStep("display update", () => overallRanking.StatisticsUpdate.Value = new SoloStatisticsUpdate(new ScoreInfo(), before, after));
|
||||
}
|
||||
}
|
@ -32,7 +32,7 @@ namespace osu.Game.Tournament.Components
|
||||
|
||||
public TournamentBeatmapPanel(TournamentBeatmap beatmap, string mod = null)
|
||||
{
|
||||
if (beatmap == null) throw new ArgumentNullException(nameof(beatmap));
|
||||
ArgumentNullException.ThrowIfNull(beatmap);
|
||||
|
||||
Beatmap = beatmap;
|
||||
this.mod = mod;
|
||||
|
@ -211,8 +211,7 @@ namespace osu.Game.Beatmaps.ControlPoints
|
||||
public static T BinarySearch<T>(IReadOnlyList<T> list, double time)
|
||||
where T : class, IControlPoint
|
||||
{
|
||||
if (list == null)
|
||||
throw new ArgumentNullException(nameof(list));
|
||||
ArgumentNullException.ThrowIfNull(list);
|
||||
|
||||
if (list.Count == 0)
|
||||
return null;
|
||||
|
@ -15,8 +15,7 @@ namespace osu.Game.Beatmaps.Drawables
|
||||
|
||||
public BeatmapBackgroundSprite(IWorkingBeatmap working)
|
||||
{
|
||||
if (working == null)
|
||||
throw new ArgumentNullException(nameof(working));
|
||||
ArgumentNullException.ThrowIfNull(working);
|
||||
|
||||
this.working = working;
|
||||
}
|
||||
|
@ -18,8 +18,7 @@ namespace osu.Game.Beatmaps.Drawables
|
||||
|
||||
public OnlineBeatmapSetCover(IBeatmapSetOnlineInfo set, BeatmapSetCoverType type = BeatmapSetCoverType.Cover)
|
||||
{
|
||||
if (set == null)
|
||||
throw new ArgumentNullException(nameof(set));
|
||||
ArgumentNullException.ThrowIfNull(set);
|
||||
|
||||
this.set = set;
|
||||
this.type = type;
|
||||
|
@ -57,8 +57,7 @@ namespace osu.Game.Beatmaps.Formats
|
||||
public static Decoder<T> GetDecoder<T>(LineBufferedReader stream)
|
||||
where T : new()
|
||||
{
|
||||
if (stream == null)
|
||||
throw new ArgumentNullException(nameof(stream));
|
||||
ArgumentNullException.ThrowIfNull(stream);
|
||||
|
||||
if (!decoders.TryGetValue(typeof(T), out var typedDecoders))
|
||||
throw new IOException(@"Unknown decoder type");
|
||||
|
@ -36,8 +36,7 @@ namespace osu.Game.Graphics.Containers
|
||||
/// <param name="easing">The easing type of the initial transform.</param>
|
||||
public void StartTracking(OsuLogo logo, double duration = 0, Easing easing = Easing.None)
|
||||
{
|
||||
if (logo == null)
|
||||
throw new ArgumentNullException(nameof(logo));
|
||||
ArgumentNullException.ThrowIfNull(logo);
|
||||
|
||||
if (logo.IsTracking && Logo == null)
|
||||
throw new InvalidOperationException($"Cannot track an instance of {typeof(OsuLogo)} to multiple {typeof(LogoTrackingContainer)}s");
|
||||
|
@ -52,8 +52,8 @@ namespace osu.Game.Graphics.UserInterface
|
||||
|
||||
public readonly SpriteIcon Chevron;
|
||||
|
||||
//don't allow clicking between transitions and don't make the chevron clickable
|
||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Alpha == 1f && Text.ReceivePositionalInputAt(screenSpacePos);
|
||||
//don't allow clicking between transitions
|
||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Alpha == 1f && base.ReceivePositionalInputAt(screenSpacePos);
|
||||
|
||||
public override bool HandleNonPositionalInput => State == Visibility.Visible;
|
||||
public override bool HandlePositionalInput => State == Visibility.Visible;
|
||||
@ -95,7 +95,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
{
|
||||
Text.Font = Text.Font.With(size: 18);
|
||||
Text.Margin = new MarginPadding { Vertical = 8 };
|
||||
Padding = new MarginPadding { Right = padding + ChevronSize };
|
||||
Margin = new MarginPadding { Right = padding + ChevronSize };
|
||||
Add(Chevron = new SpriteIcon
|
||||
{
|
||||
Anchor = Anchor.CentreRight,
|
||||
|
@ -114,8 +114,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
get => current;
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
ArgumentNullException.ThrowIfNull(value);
|
||||
|
||||
current.UnbindBindings();
|
||||
current.BindTo(value);
|
||||
|
@ -23,8 +23,7 @@ namespace osu.Game.IO.FileAbstraction
|
||||
|
||||
public void CloseStream(Stream stream)
|
||||
{
|
||||
if (stream == null)
|
||||
throw new ArgumentNullException(nameof(stream));
|
||||
ArgumentNullException.ThrowIfNull(stream);
|
||||
|
||||
stream.Close();
|
||||
}
|
||||
|
@ -64,6 +64,16 @@ namespace osu.Game.Localisation
|
||||
/// </summary>
|
||||
public static LocalisableString RunSetupWizard => new TranslatableString(getKey(@"run_setup_wizard"), @"Run setup wizard");
|
||||
|
||||
/// <summary>
|
||||
/// "Learn more about lazer"
|
||||
/// </summary>
|
||||
public static LocalisableString LearnMoreAboutLazer => new TranslatableString(getKey(@"learn_more_about_lazer"), @"Learn more about lazer");
|
||||
|
||||
/// <summary>
|
||||
/// "Check out the feature comparison and FAQ"
|
||||
/// </summary>
|
||||
public static LocalisableString LearnMoreAboutLazerTooltip => new TranslatableString(getKey(@"check_out_the_feature_comparison"), @"Check out the feature comparison and FAQ");
|
||||
|
||||
/// <summary>
|
||||
/// "You are running the latest release ({0})"
|
||||
/// </summary>
|
||||
|
@ -9,7 +9,7 @@ namespace osu.Game.Online.API.Requests
|
||||
{
|
||||
public class GetUsersRequest : APIRequest<GetUsersResponse>
|
||||
{
|
||||
private readonly int[] userIds;
|
||||
public readonly int[] UserIds;
|
||||
|
||||
private const int max_ids_per_request = 50;
|
||||
|
||||
@ -18,9 +18,9 @@ namespace osu.Game.Online.API.Requests
|
||||
if (userIds.Length > max_ids_per_request)
|
||||
throw new ArgumentException($"{nameof(GetUsersRequest)} calls only support up to {max_ids_per_request} IDs at once");
|
||||
|
||||
this.userIds = userIds;
|
||||
UserIds = userIds;
|
||||
}
|
||||
|
||||
protected override string Target => "users/?ids[]=" + string.Join("&ids[]=", userIds);
|
||||
protected override string Target => "users/?ids[]=" + string.Join("&ids[]=", UserIds);
|
||||
}
|
||||
}
|
||||
|
@ -118,8 +118,7 @@ namespace osu.Game.Online.Chat
|
||||
/// <param name="name"></param>
|
||||
public void OpenChannel(string name)
|
||||
{
|
||||
if (name == null)
|
||||
throw new ArgumentNullException(nameof(name));
|
||||
ArgumentNullException.ThrowIfNull(name);
|
||||
|
||||
CurrentChannel.Value = AvailableChannels.FirstOrDefault(c => c.Name == name) ?? throw new ChannelNotFoundException(name);
|
||||
}
|
||||
@ -130,8 +129,7 @@ namespace osu.Game.Online.Chat
|
||||
/// <param name="user">The user the private channel is opened with.</param>
|
||||
public void OpenPrivateChannel(APIUser user)
|
||||
{
|
||||
if (user == null)
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
ArgumentNullException.ThrowIfNull(user);
|
||||
|
||||
if (user.Id == api.LocalUser.Value.Id)
|
||||
return;
|
||||
|
42
osu.Game/Online/Solo/SoloStatisticsUpdate.cs
Normal file
42
osu.Game/Online/Solo/SoloStatisticsUpdate.cs
Normal file
@ -0,0 +1,42 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Online.Solo
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains data about the change in a user's profile statistics after completing a score.
|
||||
/// </summary>
|
||||
public class SoloStatisticsUpdate
|
||||
{
|
||||
/// <summary>
|
||||
/// The score set by the user that triggered the update.
|
||||
/// </summary>
|
||||
public ScoreInfo Score { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The user's profile statistics prior to the score being set.
|
||||
/// </summary>
|
||||
public UserStatistics Before { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The user's profile statistics after the score was set.
|
||||
/// </summary>
|
||||
public UserStatistics After { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="SoloStatisticsUpdate"/>.
|
||||
/// </summary>
|
||||
/// <param name="score">The score set by the user that triggered the update.</param>
|
||||
/// <param name="before">The user's profile statistics prior to the score being set.</param>
|
||||
/// <param name="after">The user's profile statistics after the score was set.</param>
|
||||
public SoloStatisticsUpdate(ScoreInfo score, UserStatistics before, UserStatistics after)
|
||||
{
|
||||
Score = score;
|
||||
Before = before;
|
||||
After = after;
|
||||
}
|
||||
}
|
||||
}
|
144
osu.Game/Online/Solo/SoloStatisticsWatcher.cs
Normal file
144
osu.Game/Online/Solo/SoloStatisticsWatcher.cs
Normal file
@ -0,0 +1,144 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Extensions;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Spectator;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Online.Solo
|
||||
{
|
||||
/// <summary>
|
||||
/// A persistent component that binds to the spectator server and API in order to deliver updates about the logged in user's gameplay statistics.
|
||||
/// </summary>
|
||||
public partial class SoloStatisticsWatcher : Component
|
||||
{
|
||||
[Resolved]
|
||||
private SpectatorClient spectatorClient { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; } = null!;
|
||||
|
||||
private readonly Dictionary<long, StatisticsUpdateCallback> callbacks = new Dictionary<long, StatisticsUpdateCallback>();
|
||||
private long? lastProcessedScoreId;
|
||||
|
||||
private Dictionary<string, UserStatistics>? latestStatistics;
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
api.LocalUser.BindValueChanged(user => onUserChanged(user.NewValue), true);
|
||||
spectatorClient.OnUserScoreProcessed += userScoreProcessed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers for a user statistics update after the given <paramref name="score"/> has been processed server-side.
|
||||
/// </summary>
|
||||
/// <param name="score">The score to listen for the statistics update for.</param>
|
||||
/// <param name="onUpdateReady">The callback to be invoked once the statistics update has been prepared.</param>
|
||||
public void RegisterForStatisticsUpdateAfter(ScoreInfo score, Action<SoloStatisticsUpdate> onUpdateReady) => Schedule(() =>
|
||||
{
|
||||
if (!api.IsLoggedIn)
|
||||
return;
|
||||
|
||||
if (!score.Ruleset.IsLegacyRuleset())
|
||||
return;
|
||||
|
||||
var callback = new StatisticsUpdateCallback(score, onUpdateReady);
|
||||
|
||||
if (lastProcessedScoreId == score.OnlineID)
|
||||
{
|
||||
requestStatisticsUpdate(api.LocalUser.Value.Id, callback);
|
||||
return;
|
||||
}
|
||||
|
||||
callbacks.Add(score.OnlineID, callback);
|
||||
});
|
||||
|
||||
private void onUserChanged(APIUser? localUser) => Schedule(() =>
|
||||
{
|
||||
callbacks.Clear();
|
||||
lastProcessedScoreId = null;
|
||||
latestStatistics = null;
|
||||
|
||||
if (localUser == null || localUser.OnlineID <= 1)
|
||||
return;
|
||||
|
||||
var userRequest = new GetUsersRequest(new[] { localUser.OnlineID });
|
||||
userRequest.Success += response => Schedule(() =>
|
||||
{
|
||||
latestStatistics = new Dictionary<string, UserStatistics>();
|
||||
foreach (var rulesetStats in response.Users.Single().RulesetsStatistics)
|
||||
latestStatistics.Add(rulesetStats.Key, rulesetStats.Value);
|
||||
});
|
||||
api.Queue(userRequest);
|
||||
});
|
||||
|
||||
private void userScoreProcessed(int userId, long scoreId)
|
||||
{
|
||||
if (userId != api.LocalUser.Value?.OnlineID)
|
||||
return;
|
||||
|
||||
lastProcessedScoreId = scoreId;
|
||||
|
||||
if (!callbacks.TryGetValue(scoreId, out var callback))
|
||||
return;
|
||||
|
||||
requestStatisticsUpdate(userId, callback);
|
||||
callbacks.Remove(scoreId);
|
||||
}
|
||||
|
||||
private void requestStatisticsUpdate(int userId, StatisticsUpdateCallback callback)
|
||||
{
|
||||
var request = new GetUserRequest(userId, callback.Score.Ruleset);
|
||||
request.Success += user => Schedule(() => dispatchStatisticsUpdate(callback, user.Statistics));
|
||||
api.Queue(request);
|
||||
}
|
||||
|
||||
private void dispatchStatisticsUpdate(StatisticsUpdateCallback callback, UserStatistics updatedStatistics)
|
||||
{
|
||||
string rulesetName = callback.Score.Ruleset.ShortName;
|
||||
|
||||
if (latestStatistics == null)
|
||||
return;
|
||||
|
||||
latestStatistics.TryGetValue(rulesetName, out UserStatistics? latestRulesetStatistics);
|
||||
latestRulesetStatistics ??= new UserStatistics();
|
||||
|
||||
var update = new SoloStatisticsUpdate(callback.Score, latestRulesetStatistics, updatedStatistics);
|
||||
callback.OnUpdateReady.Invoke(update);
|
||||
|
||||
latestStatistics[rulesetName] = updatedStatistics;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
if (spectatorClient.IsNotNull())
|
||||
spectatorClient.OnUserScoreProcessed -= userScoreProcessed;
|
||||
|
||||
base.Dispose(isDisposing);
|
||||
}
|
||||
|
||||
private class StatisticsUpdateCallback
|
||||
{
|
||||
public ScoreInfo Score { get; }
|
||||
public Action<SoloStatisticsUpdate> OnUpdateReady { get; }
|
||||
|
||||
public StatisticsUpdateCallback(ScoreInfo score, Action<SoloStatisticsUpdate> onUpdateReady)
|
||||
{
|
||||
Score = score;
|
||||
OnUpdateReady = onUpdateReady;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -353,7 +353,10 @@ namespace osu.Game
|
||||
break;
|
||||
|
||||
case LinkAction.SearchBeatmapSet:
|
||||
SearchBeatmapSet(argString);
|
||||
if (link.Argument is RomanisableString romanisable)
|
||||
SearchBeatmapSet(romanisable.GetPreferred(Localisation.CurrentParameters.Value.PreferOriginalScript));
|
||||
else
|
||||
SearchBeatmapSet(argString);
|
||||
break;
|
||||
|
||||
case LinkAction.OpenEditorTimestamp:
|
||||
@ -1040,9 +1043,7 @@ namespace osu.Game
|
||||
|
||||
Logger.NewEntry += entry =>
|
||||
{
|
||||
if (entry.Level < LogLevel.Important || entry.Target > LoggingTarget.Database) return;
|
||||
|
||||
Debug.Assert(entry.Target != null);
|
||||
if (entry.Level < LogLevel.Important || entry.Target > LoggingTarget.Database || entry.Target == null) return;
|
||||
|
||||
const int short_term_display_limit = 3;
|
||||
|
||||
|
@ -46,6 +46,7 @@ using osu.Game.Online.API;
|
||||
using osu.Game.Online.Chat;
|
||||
using osu.Game.Online.Metadata;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Online.Solo;
|
||||
using osu.Game.Online.Spectator;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Settings;
|
||||
@ -193,6 +194,7 @@ namespace osu.Game
|
||||
protected MultiplayerClient MultiplayerClient { get; private set; }
|
||||
|
||||
private MetadataClient metadataClient;
|
||||
private SoloStatisticsWatcher soloStatisticsWatcher;
|
||||
|
||||
private RealmAccess realm;
|
||||
|
||||
@ -301,6 +303,7 @@ namespace osu.Game
|
||||
dependencies.CacheAs(spectatorClient = new OnlineSpectatorClient(endpoints));
|
||||
dependencies.CacheAs(MultiplayerClient = new OnlineMultiplayerClient(endpoints));
|
||||
dependencies.CacheAs(metadataClient = new OnlineMetadataClient(endpoints));
|
||||
dependencies.CacheAs(soloStatisticsWatcher = new SoloStatisticsWatcher());
|
||||
|
||||
AddInternal(new BeatmapOnlineChangeIngest(beatmapUpdater, realm, metadataClient));
|
||||
|
||||
@ -346,6 +349,7 @@ namespace osu.Game
|
||||
AddInternal(spectatorClient);
|
||||
AddInternal(MultiplayerClient);
|
||||
AddInternal(metadataClient);
|
||||
AddInternal(soloStatisticsWatcher);
|
||||
|
||||
AddInternal(rulesetConfigCache);
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
@ -12,17 +13,20 @@ using osu.Framework.Graphics.Colour;
|
||||
using osu.Game.Graphics.Cursor;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Drawables;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Chat;
|
||||
using osu.Game.Overlays.BeatmapSet.Buttons;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.BeatmapSet
|
||||
{
|
||||
@ -41,12 +45,10 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
|
||||
private readonly UpdateableOnlineBeatmapSetCover cover;
|
||||
private readonly Box coverGradient;
|
||||
private readonly OsuSpriteText title, artist;
|
||||
private readonly LinkFlowContainer title, artist;
|
||||
private readonly AuthorInfo author;
|
||||
|
||||
private readonly ExplicitContentBeatmapBadge explicitContent;
|
||||
private readonly SpotlightBeatmapBadge spotlight;
|
||||
private readonly FeaturedArtistBeatmapBadge featuredArtist;
|
||||
private ExternalLinkButton externalLink;
|
||||
|
||||
private readonly FillFlowContainer downloadButtonsContainer;
|
||||
private readonly BeatmapAvailability beatmapAvailability;
|
||||
@ -65,8 +67,6 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
|
||||
public BeatmapSetHeaderContent()
|
||||
{
|
||||
ExternalLinkButton externalLink;
|
||||
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
InternalChild = new Container
|
||||
@ -120,58 +120,19 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Child = Picker = new BeatmapPicker(),
|
||||
},
|
||||
new FillFlowContainer
|
||||
title = new MetadataFlowContainer(s =>
|
||||
{
|
||||
s.Font = OsuFont.GetFont(size: 30, weight: FontWeight.SemiBold, italics: true);
|
||||
})
|
||||
{
|
||||
Direction = FillDirection.Horizontal,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Margin = new MarginPadding { Top = 15 },
|
||||
Children = new Drawable[]
|
||||
{
|
||||
title = new OsuSpriteText
|
||||
{
|
||||
Font = OsuFont.GetFont(size: 30, weight: FontWeight.SemiBold, italics: true)
|
||||
},
|
||||
externalLink = new ExternalLinkButton
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
Margin = new MarginPadding { Left = 5, Bottom = 4 }, // To better lineup with the font
|
||||
},
|
||||
explicitContent = new ExplicitContentBeatmapBadge
|
||||
{
|
||||
Alpha = 0f,
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
Margin = new MarginPadding { Left = 10, Bottom = 4 },
|
||||
},
|
||||
spotlight = new SpotlightBeatmapBadge
|
||||
{
|
||||
Alpha = 0f,
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
Margin = new MarginPadding { Left = 10, Bottom = 4 },
|
||||
}
|
||||
}
|
||||
},
|
||||
new FillFlowContainer
|
||||
artist = new MetadataFlowContainer(s =>
|
||||
{
|
||||
s.Font = OsuFont.GetFont(size: 20, weight: FontWeight.Medium, italics: true);
|
||||
})
|
||||
{
|
||||
Direction = FillDirection.Horizontal,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Margin = new MarginPadding { Bottom = 20 },
|
||||
Children = new Drawable[]
|
||||
{
|
||||
artist = new OsuSpriteText
|
||||
{
|
||||
Font = OsuFont.GetFont(size: 20, weight: FontWeight.Medium, italics: true),
|
||||
},
|
||||
featuredArtist = new FeaturedArtistBeatmapBadge
|
||||
{
|
||||
Alpha = 0f,
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
Margin = new MarginPadding { Left = 10 }
|
||||
}
|
||||
}
|
||||
},
|
||||
new Container
|
||||
{
|
||||
@ -237,12 +198,17 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
Picker.Beatmap.ValueChanged += b =>
|
||||
{
|
||||
Details.BeatmapInfo = b.NewValue;
|
||||
externalLink.Link = $@"{api.WebsiteRootUrl}/beatmapsets/{BeatmapSet.Value?.OnlineID}#{b.NewValue?.Ruleset.ShortName}/{b.NewValue?.OnlineID}";
|
||||
updateExternalLink();
|
||||
|
||||
onlineStatusPill.Status = b.NewValue?.Status ?? BeatmapOnlineStatus.None;
|
||||
};
|
||||
}
|
||||
|
||||
private void updateExternalLink()
|
||||
{
|
||||
if (externalLink != null) externalLink.Link = $@"{api.WebsiteRootUrl}/beatmapsets/{BeatmapSet.Value?.OnlineID}#{Picker.Beatmap.Value?.Ruleset.ShortName}/{Picker.Beatmap.Value?.OnlineID}";
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OverlayColourProvider colourProvider)
|
||||
{
|
||||
@ -275,12 +241,38 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
|
||||
loading.Hide();
|
||||
|
||||
title.Text = new RomanisableString(setInfo.NewValue.TitleUnicode, setInfo.NewValue.Title);
|
||||
artist.Text = new RomanisableString(setInfo.NewValue.ArtistUnicode, setInfo.NewValue.Artist);
|
||||
var titleText = new RomanisableString(setInfo.NewValue.TitleUnicode, setInfo.NewValue.Title);
|
||||
var artistText = new RomanisableString(setInfo.NewValue.ArtistUnicode, setInfo.NewValue.Artist);
|
||||
|
||||
explicitContent.Alpha = setInfo.NewValue.HasExplicitContent ? 1 : 0;
|
||||
spotlight.Alpha = setInfo.NewValue.FeaturedInSpotlight ? 1 : 0;
|
||||
featuredArtist.Alpha = setInfo.NewValue.TrackId != null ? 1 : 0;
|
||||
title.Clear();
|
||||
artist.Clear();
|
||||
|
||||
title.AddLink(titleText, LinkAction.SearchBeatmapSet, titleText);
|
||||
|
||||
title.AddArbitraryDrawable(Empty().With(d => d.Width = 5));
|
||||
title.AddArbitraryDrawable(externalLink = new ExternalLinkButton());
|
||||
|
||||
if (setInfo.NewValue.HasExplicitContent)
|
||||
{
|
||||
title.AddArbitraryDrawable(Empty().With(d => d.Width = 10));
|
||||
title.AddArbitraryDrawable(new ExplicitContentBeatmapBadge());
|
||||
}
|
||||
|
||||
if (setInfo.NewValue.FeaturedInSpotlight)
|
||||
{
|
||||
title.AddArbitraryDrawable(Empty().With(d => d.Width = 10));
|
||||
title.AddArbitraryDrawable(new SpotlightBeatmapBadge());
|
||||
}
|
||||
|
||||
artist.AddLink(artistText, LinkAction.SearchBeatmapSet, artistText);
|
||||
|
||||
if (setInfo.NewValue.TrackId != null)
|
||||
{
|
||||
artist.AddArbitraryDrawable(Empty().With(d => d.Width = 10));
|
||||
artist.AddArbitraryDrawable(new FeaturedArtistBeatmapBadge());
|
||||
}
|
||||
|
||||
updateExternalLink();
|
||||
|
||||
onlineStatusPill.FadeIn(500, Easing.OutQuint);
|
||||
|
||||
@ -327,5 +319,32 @@ namespace osu.Game.Overlays.BeatmapSet
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public partial class MetadataFlowContainer : LinkFlowContainer
|
||||
{
|
||||
public MetadataFlowContainer(Action<SpriteText> defaultCreationParameters = null)
|
||||
: base(defaultCreationParameters)
|
||||
{
|
||||
TextAnchor = Anchor.CentreLeft;
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
}
|
||||
|
||||
protected override DrawableLinkCompiler CreateLinkCompiler(ITextPart textPart) => new MetadataLinkCompiler(textPart);
|
||||
|
||||
public partial class MetadataLinkCompiler : DrawableLinkCompiler
|
||||
{
|
||||
public MetadataLinkCompiler(ITextPart part)
|
||||
: base(part)
|
||||
{
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
IdleColour = Color4.White;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -95,8 +95,8 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||
new TableColumn(BeatmapsetsStrings.ShowScoreboardHeadersScore, Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize)),
|
||||
new TableColumn(BeatmapsetsStrings.ShowScoreboardHeadersAccuracy, Anchor.CentreLeft, new Dimension(GridSizeMode.Absolute, minSize: 60, maxSize: 70)),
|
||||
new TableColumn("", Anchor.CentreLeft, new Dimension(GridSizeMode.Absolute, 25)), // flag
|
||||
new TableColumn(BeatmapsetsStrings.ShowScoreboardHeadersPlayer, Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed, minSize: 125)),
|
||||
new TableColumn(BeatmapsetsStrings.ShowScoreboardHeadersCombo, Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed, minSize: 70, maxSize: 120))
|
||||
new TableColumn(BeatmapsetsStrings.ShowScoreboardHeadersPlayer, Anchor.CentreLeft, new Dimension(minSize: 125)),
|
||||
new TableColumn(BeatmapsetsStrings.ShowScoreboardHeadersCombo, Anchor.CentreLeft, new Dimension(minSize: 70, maxSize: 120))
|
||||
};
|
||||
|
||||
// All statistics across all scores, unordered.
|
||||
@ -116,7 +116,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||
|
||||
var displayName = ruleset.GetDisplayNameForHitResult(result);
|
||||
|
||||
columns.Add(new TableColumn(displayName, Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed, minSize: 35, maxSize: 60)));
|
||||
columns.Add(new TableColumn(displayName, Anchor.CentreLeft, new Dimension(minSize: 35, maxSize: 60)));
|
||||
statisticResultTypes.Add((result, displayName));
|
||||
}
|
||||
|
||||
|
@ -70,7 +70,7 @@ namespace osu.Game.Overlays
|
||||
/// <see cref="APIChangelogBuild.DisplayVersion"/> are specified, the header will instantly display them.</param>
|
||||
public void ShowBuild([NotNull] APIChangelogBuild build)
|
||||
{
|
||||
if (build == null) throw new ArgumentNullException(nameof(build));
|
||||
ArgumentNullException.ThrowIfNull(build);
|
||||
|
||||
Current.Value = build;
|
||||
Show();
|
||||
@ -78,8 +78,8 @@ namespace osu.Game.Overlays
|
||||
|
||||
public void ShowBuild([NotNull] string updateStream, [NotNull] string version)
|
||||
{
|
||||
if (updateStream == null) throw new ArgumentNullException(nameof(updateStream));
|
||||
if (version == null) throw new ArgumentNullException(nameof(version));
|
||||
ArgumentNullException.ThrowIfNull(updateStream);
|
||||
ArgumentNullException.ThrowIfNull(version);
|
||||
|
||||
performAfterFetch(() =>
|
||||
{
|
||||
|
@ -58,7 +58,7 @@ namespace osu.Game.Overlays
|
||||
/// <exception cref="InvalidOperationException">If <paramref name="configManager"/> is already being tracked from the same <paramref name="source"/>.</exception>
|
||||
public void BeginTracking(object source, ITrackableConfigManager configManager)
|
||||
{
|
||||
if (configManager == null) throw new ArgumentNullException(nameof(configManager));
|
||||
ArgumentNullException.ThrowIfNull(configManager);
|
||||
|
||||
if (trackedConfigManagers.ContainsKey((source, configManager)))
|
||||
throw new InvalidOperationException($"{nameof(configManager)} is already registered.");
|
||||
@ -82,7 +82,7 @@ namespace osu.Game.Overlays
|
||||
/// <exception cref="InvalidOperationException">If <paramref name="configManager"/> is not being tracked from the same <paramref name="source"/>.</exception>
|
||||
public void StopTracking(object source, ITrackableConfigManager configManager)
|
||||
{
|
||||
if (configManager == null) throw new ArgumentNullException(nameof(configManager));
|
||||
ArgumentNullException.ThrowIfNull(configManager);
|
||||
|
||||
if (!trackedConfigManagers.TryGetValue((source, configManager), out var existing))
|
||||
return;
|
||||
|
@ -1,12 +1,11 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Localisation;
|
||||
using osu.Game.Overlays.Settings.Sections.General;
|
||||
|
||||
@ -15,7 +14,10 @@ namespace osu.Game.Overlays.Settings.Sections
|
||||
public partial class GeneralSection : SettingsSection
|
||||
{
|
||||
[Resolved(CanBeNull = true)]
|
||||
private FirstRunSetupOverlay firstRunSetupOverlay { get; set; }
|
||||
private FirstRunSetupOverlay? firstRunSetupOverlay { get; set; }
|
||||
|
||||
[Resolved(CanBeNull = true)]
|
||||
private OsuGame? game { get; set; }
|
||||
|
||||
public override LocalisableString Header => GeneralSettingsStrings.GeneralSectionHeader;
|
||||
|
||||
@ -24,15 +26,24 @@ namespace osu.Game.Overlays.Settings.Sections
|
||||
Icon = FontAwesome.Solid.Cog
|
||||
};
|
||||
|
||||
public GeneralSection()
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new SettingsButton
|
||||
{
|
||||
Text = GeneralSettingsStrings.RunSetupWizard,
|
||||
TooltipText = FirstRunSetupOverlayStrings.FirstRunSetupDescription,
|
||||
Action = () => firstRunSetupOverlay?.Show(),
|
||||
},
|
||||
new SettingsButton
|
||||
{
|
||||
Text = GeneralSettingsStrings.LearnMoreAboutLazer,
|
||||
TooltipText = GeneralSettingsStrings.LearnMoreAboutLazerTooltip,
|
||||
BackgroundColour = colours.YellowDark,
|
||||
Action = () => game?.ShowWiki(@"Help_centre/Upgrading_to_lazer")
|
||||
},
|
||||
new LanguageSettings(),
|
||||
new UpdateSettings(),
|
||||
};
|
||||
|
@ -28,8 +28,7 @@ namespace osu.Game.Overlays.Volume
|
||||
get => current;
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
ArgumentNullException.ThrowIfNull(value);
|
||||
|
||||
current.UnbindBindings();
|
||||
current.BindTo(value);
|
||||
|
@ -21,6 +21,8 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
private const string index_path = @"main_page";
|
||||
|
||||
public string CurrentPath => path.Value;
|
||||
|
||||
private readonly Bindable<string> path = new Bindable<string>(index_path);
|
||||
|
||||
private readonly Bindable<APIWikiPage> wikiData = new Bindable<APIWikiPage>();
|
||||
@ -105,6 +107,9 @@ namespace osu.Game.Overlays
|
||||
if (e.NewValue == wikiData.Value?.Path)
|
||||
return;
|
||||
|
||||
if (e.NewValue == "error")
|
||||
return;
|
||||
|
||||
cancellationToken?.Cancel();
|
||||
request?.Cancel();
|
||||
|
||||
@ -118,7 +123,11 @@ namespace osu.Game.Overlays
|
||||
Loading.Show();
|
||||
|
||||
request.Success += response => Schedule(() => onSuccess(response));
|
||||
request.Failure += _ => Schedule(onFail);
|
||||
request.Failure += ex =>
|
||||
{
|
||||
if (ex is not OperationCanceledException)
|
||||
Schedule(onFail, request.Path);
|
||||
};
|
||||
|
||||
api.PerformAsync(request);
|
||||
}
|
||||
@ -146,10 +155,11 @@ namespace osu.Game.Overlays
|
||||
}
|
||||
}
|
||||
|
||||
private void onFail()
|
||||
private void onFail(string originalPath)
|
||||
{
|
||||
path.Value = "error";
|
||||
LoadDisplay(articlePage = new WikiArticlePage($@"{api.WebsiteRootUrl}/wiki/",
|
||||
$"Something went wrong when trying to fetch page \"{path.Value}\".\n\n[Return to the main page](Main_Page)."));
|
||||
$"Something went wrong when trying to fetch page \"{originalPath}\".\n\n[Return to the main page](Main_Page)."));
|
||||
}
|
||||
|
||||
private void showParentPage()
|
||||
|
@ -1,12 +1,7 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
@ -15,40 +10,25 @@ using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Judgements
|
||||
{
|
||||
public partial class DefaultJudgementPiece : CompositeDrawable, IAnimatableJudgement
|
||||
public partial class DefaultJudgementPiece : JudgementPiece, IAnimatableJudgement
|
||||
{
|
||||
protected readonly HitResult Result;
|
||||
|
||||
protected SpriteText JudgementText { get; private set; }
|
||||
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; }
|
||||
|
||||
public DefaultJudgementPiece(HitResult result)
|
||||
{
|
||||
Result = result;
|
||||
Origin = Anchor.Centre;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
: base(result)
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
JudgementText = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Text = Result.GetDescription().ToUpperInvariant(),
|
||||
Colour = colours.ForHitResult(Result),
|
||||
Font = OsuFont.Numeric.With(size: 20),
|
||||
Scale = new Vector2(0.85f, 1),
|
||||
}
|
||||
};
|
||||
Origin = Anchor.Centre;
|
||||
}
|
||||
|
||||
protected override SpriteText CreateJudgementText() =>
|
||||
new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Font = OsuFont.Numeric.With(size: 20),
|
||||
Scale = new Vector2(0.85f, 1),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Plays the default animation for this judgement piece.
|
||||
/// </summary>
|
||||
@ -75,6 +55,6 @@ namespace osu.Game.Rulesets.Judgements
|
||||
this.FadeOutFromOne(800);
|
||||
}
|
||||
|
||||
public Drawable GetAboveHitObjectsProxiedContent() => null;
|
||||
public Drawable? GetAboveHitObjectsProxiedContent() => null;
|
||||
}
|
||||
}
|
||||
|
38
osu.Game/Rulesets/Judgements/JudgementPiece.cs
Normal file
38
osu.Game/Rulesets/Judgements/JudgementPiece.cs
Normal file
@ -0,0 +1,38 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Judgements
|
||||
{
|
||||
public abstract partial class JudgementPiece : CompositeDrawable
|
||||
{
|
||||
protected readonly HitResult Result;
|
||||
|
||||
protected SpriteText JudgementText { get; private set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; } = null!;
|
||||
|
||||
protected JudgementPiece(HitResult result)
|
||||
{
|
||||
Result = result;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
AddInternal(JudgementText = CreateJudgementText());
|
||||
|
||||
JudgementText.Colour = colours.ForHitResult(Result);
|
||||
JudgementText.Text = Result.GetDescription().ToUpperInvariant();
|
||||
}
|
||||
|
||||
protected abstract SpriteText CreateJudgementText();
|
||||
}
|
||||
}
|
@ -126,8 +126,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
get => this;
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
ArgumentNullException.ThrowIfNull(value);
|
||||
|
||||
if (currentBound != null) UnbindFrom(currentBound);
|
||||
BindTo(currentBound = value);
|
||||
|
@ -208,8 +208,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
/// </summary>
|
||||
public void Apply([NotNull] HitObject hitObject)
|
||||
{
|
||||
if (hitObject == null)
|
||||
throw new ArgumentNullException($"Cannot apply a null {nameof(HitObject)}.");
|
||||
ArgumentNullException.ThrowIfNull(hitObject);
|
||||
|
||||
Apply(new SyntheticHitObjectEntry(hitObject));
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.UI
|
||||
{
|
||||
public override void Add(T judgement)
|
||||
{
|
||||
if (judgement == null) throw new ArgumentNullException(nameof(judgement));
|
||||
ArgumentNullException.ThrowIfNull(judgement);
|
||||
|
||||
// remove any existing judgements for the judged object.
|
||||
// this can be the case when rewinding.
|
||||
|
@ -71,8 +71,8 @@ namespace osu.Game.Scoring
|
||||
|
||||
// These properties are known to be non-null, but these final checks ensure a null hasn't come from somewhere (or the refetch has failed).
|
||||
// Under no circumstance do we want these to be written to realm as null.
|
||||
if (model.BeatmapInfo == null) throw new ArgumentNullException(nameof(model.BeatmapInfo));
|
||||
if (model.Ruleset == null) throw new ArgumentNullException(nameof(model.Ruleset));
|
||||
ArgumentNullException.ThrowIfNull(model.BeatmapInfo);
|
||||
ArgumentNullException.ThrowIfNull(model.Ruleset);
|
||||
|
||||
PopulateMaximumStatistics(model);
|
||||
|
||||
|
@ -99,9 +99,11 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
|
||||
minZoom = minimum;
|
||||
maxZoom = maximum;
|
||||
CurrentZoom = zoomTarget = initial;
|
||||
isZoomSetUp = true;
|
||||
|
||||
CurrentZoom = zoomTarget = initial;
|
||||
zoomedContentWidthCache.Invalidate();
|
||||
|
||||
isZoomSetUp = true;
|
||||
zoomedContent.Show();
|
||||
}
|
||||
|
||||
|
@ -147,7 +147,7 @@ namespace osu.Game.Screens.Menu
|
||||
|
||||
private void addAmplitudesFromSource(IHasAmplitudes source)
|
||||
{
|
||||
if (source == null) throw new ArgumentNullException(nameof(source));
|
||||
ArgumentNullException.ThrowIfNull(source);
|
||||
|
||||
var amplitudes = source.CurrentAmplitudes.FrequencyAmplitudes.Span;
|
||||
|
||||
|
@ -33,8 +33,7 @@ namespace osu.Game.Screens.Play.HUD
|
||||
get => current.Current;
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
ArgumentNullException.ThrowIfNull(value);
|
||||
|
||||
current.Current = value;
|
||||
}
|
||||
|
@ -30,8 +30,7 @@ namespace osu.Game.Screens.Play.HUD
|
||||
get => current.Current;
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
ArgumentNullException.ThrowIfNull(value);
|
||||
|
||||
current.Current = value;
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
public override void Add(KeyCounter key)
|
||||
{
|
||||
if (key == null) throw new ArgumentNullException(nameof(key));
|
||||
ArgumentNullException.ThrowIfNull(key);
|
||||
|
||||
base.Add(key);
|
||||
key.IsCounting = IsCounting;
|
||||
|
@ -96,11 +96,11 @@ namespace osu.Game.Screens.Ranking
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
statisticsPanel = new StatisticsPanel
|
||||
statisticsPanel = CreateStatisticsPanel().With(panel =>
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Score = { BindTarget = SelectedScore }
|
||||
},
|
||||
panel.RelativeSizeAxes = Axes.Both;
|
||||
panel.Score.BindTarget = SelectedScore;
|
||||
}),
|
||||
ScorePanelList = new ScorePanelList
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
@ -231,6 +231,11 @@ namespace osu.Game.Screens.Ranking
|
||||
/// <returns>An <see cref="APIRequest"/> responsible for the fetch operation. This will be queued and performed automatically.</returns>
|
||||
protected virtual APIRequest FetchNextPage(int direction, Action<IEnumerable<ScoreInfo>> scoresCallback) => null;
|
||||
|
||||
/// <summary>
|
||||
/// Creates the <see cref="StatisticsPanel"/> to be used to display extended information about scores.
|
||||
/// </summary>
|
||||
protected virtual StatisticsPanel CreateStatisticsPanel() => new StatisticsPanel();
|
||||
|
||||
private void fetchScoresCallback(IEnumerable<ScoreInfo> scores) => Schedule(() =>
|
||||
{
|
||||
foreach (var s in scores)
|
||||
|
@ -7,11 +7,14 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Online.Solo;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens.Ranking.Statistics;
|
||||
|
||||
namespace osu.Game.Screens.Ranking
|
||||
{
|
||||
@ -22,11 +25,28 @@ namespace osu.Game.Screens.Ranking
|
||||
[Resolved]
|
||||
private RulesetStore rulesets { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private SoloStatisticsWatcher soloStatisticsWatcher { get; set; }
|
||||
|
||||
private readonly Bindable<SoloStatisticsUpdate> statisticsUpdate = new Bindable<SoloStatisticsUpdate>();
|
||||
|
||||
public SoloResultsScreen(ScoreInfo score, bool allowRetry)
|
||||
: base(score, allowRetry)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
soloStatisticsWatcher.RegisterForStatisticsUpdateAfter(Score, update => statisticsUpdate.Value = update);
|
||||
}
|
||||
|
||||
protected override StatisticsPanel CreateStatisticsPanel() => new SoloStatisticsPanel(Score)
|
||||
{
|
||||
StatisticsUpdate = { BindTarget = statisticsUpdate }
|
||||
};
|
||||
|
||||
protected override APIRequest FetchScores(Action<IEnumerable<ScoreInfo>> scoresCallback)
|
||||
{
|
||||
if (Score.BeatmapInfo.OnlineID <= 0 || Score.BeatmapInfo.Status <= BeatmapOnlineStatus.Pending)
|
||||
|
54
osu.Game/Screens/Ranking/Statistics/SoloStatisticsPanel.cs
Normal file
54
osu.Game/Screens/Ranking/Statistics/SoloStatisticsPanel.cs
Normal file
@ -0,0 +1,54 @@
|
||||
// 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.Game.Beatmaps;
|
||||
using osu.Game.Online.Solo;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens.Ranking.Statistics.User;
|
||||
|
||||
namespace osu.Game.Screens.Ranking.Statistics
|
||||
{
|
||||
public partial class SoloStatisticsPanel : StatisticsPanel
|
||||
{
|
||||
private readonly ScoreInfo achievedScore;
|
||||
|
||||
public SoloStatisticsPanel(ScoreInfo achievedScore)
|
||||
{
|
||||
this.achievedScore = achievedScore;
|
||||
}
|
||||
|
||||
public Bindable<SoloStatisticsUpdate?> StatisticsUpdate { get; } = new Bindable<SoloStatisticsUpdate?>();
|
||||
|
||||
protected override ICollection<StatisticRow> CreateStatisticRows(ScoreInfo newScore, IBeatmap playableBeatmap)
|
||||
{
|
||||
var rows = base.CreateStatisticRows(newScore, playableBeatmap);
|
||||
|
||||
if (newScore.UserID > 1
|
||||
&& newScore.UserID == achievedScore.UserID
|
||||
&& newScore.OnlineID > 0
|
||||
&& newScore.OnlineID == achievedScore.OnlineID)
|
||||
{
|
||||
rows = rows.Append(new StatisticRow
|
||||
{
|
||||
Columns = new[]
|
||||
{
|
||||
new StatisticItem("Overall Ranking", () => new OverallRanking
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Width = 0.5f,
|
||||
StatisticsUpdate = { BindTarget = StatisticsUpdate }
|
||||
})
|
||||
}
|
||||
}).ToArray();
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
}
|
||||
}
|
@ -100,7 +100,7 @@ namespace osu.Game.Screens.Ranking.Statistics
|
||||
bool hitEventsAvailable = newScore.HitEvents.Count != 0;
|
||||
Container<Drawable> container;
|
||||
|
||||
var statisticRows = newScore.Ruleset.CreateInstance().CreateStatisticsForScore(newScore, task.GetResultSafely());
|
||||
var statisticRows = CreateStatisticRows(newScore, task.GetResultSafely());
|
||||
|
||||
if (!hitEventsAvailable && statisticRows.SelectMany(r => r.Columns).All(c => c.RequiresHitEvents))
|
||||
{
|
||||
@ -218,6 +218,14 @@ namespace osu.Game.Screens.Ranking.Statistics
|
||||
}), localCancellationSource.Token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the <see cref="StatisticRow"/>s to be displayed in this panel for a given <paramref name="newScore"/>.
|
||||
/// </summary>
|
||||
/// <param name="newScore">The score to create the rows for.</param>
|
||||
/// <param name="playableBeatmap">The beatmap on which the score was set.</param>
|
||||
protected virtual ICollection<StatisticRow> CreateStatisticRows(ScoreInfo newScore, IBeatmap playableBeatmap)
|
||||
=> newScore.Ruleset.CreateInstance().CreateStatisticsForScore(newScore, playableBeatmap);
|
||||
|
||||
protected override bool OnClick(ClickEvent e)
|
||||
{
|
||||
ToggleVisibility();
|
||||
|
@ -0,0 +1,35 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
using osu.Game.Utils;
|
||||
|
||||
namespace osu.Game.Screens.Ranking.Statistics.User
|
||||
{
|
||||
public partial class AccuracyChangeRow : RankingChangeRow<double>
|
||||
{
|
||||
public AccuracyChangeRow()
|
||||
: base(stats => stats.Accuracy)
|
||||
{
|
||||
}
|
||||
|
||||
protected override LocalisableString Label => UsersStrings.ShowStatsHitAccuracy;
|
||||
|
||||
protected override LocalisableString FormatCurrentValue(double current) => current.FormatAccuracy();
|
||||
|
||||
protected override int CalculateDifference(double previous, double current, out LocalisableString formattedDifference)
|
||||
{
|
||||
double difference = current - previous;
|
||||
|
||||
if (difference < 0)
|
||||
formattedDifference = difference.FormatAccuracy();
|
||||
else if (difference > 0)
|
||||
formattedDifference = LocalisableString.Interpolate($@"+{difference.FormatAccuracy()}");
|
||||
else
|
||||
formattedDifference = string.Empty;
|
||||
|
||||
return current.CompareTo(previous);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
// 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.Diagnostics;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
using osu.Game.Utils;
|
||||
|
||||
namespace osu.Game.Screens.Ranking.Statistics.User
|
||||
{
|
||||
public partial class GlobalRankChangeRow : RankingChangeRow<int?>
|
||||
{
|
||||
public GlobalRankChangeRow()
|
||||
: base(stats => stats.GlobalRank)
|
||||
{
|
||||
}
|
||||
|
||||
protected override LocalisableString Label => UsersStrings.ShowRankGlobalSimple;
|
||||
|
||||
protected override LocalisableString FormatCurrentValue(int? current)
|
||||
=> current == null ? string.Empty : current.Value.FormatRank();
|
||||
|
||||
protected override int CalculateDifference(int? previous, int? current, out LocalisableString formattedDifference)
|
||||
{
|
||||
if (previous == null && current == null)
|
||||
{
|
||||
formattedDifference = string.Empty;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (previous == null && current != null)
|
||||
{
|
||||
formattedDifference = LocalisableString.Interpolate($"+{current.Value.FormatRank()}");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (previous != null && current == null)
|
||||
{
|
||||
formattedDifference = LocalisableString.Interpolate($"-{previous.Value.FormatRank()}");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Debug.Assert(previous != null && current != null);
|
||||
|
||||
// note that ranks work backwards, i.e. lower rank is _better_.
|
||||
int difference = previous.Value - current.Value;
|
||||
|
||||
if (difference < 0)
|
||||
formattedDifference = difference.FormatRank();
|
||||
else if (difference > 0)
|
||||
formattedDifference = LocalisableString.Interpolate($"+{difference.FormatRank()}");
|
||||
else
|
||||
formattedDifference = string.Empty;
|
||||
|
||||
return difference;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
|
||||
namespace osu.Game.Screens.Ranking.Statistics.User
|
||||
{
|
||||
public partial class MaximumComboChangeRow : RankingChangeRow<int>
|
||||
{
|
||||
public MaximumComboChangeRow()
|
||||
: base(stats => stats.MaxCombo)
|
||||
{
|
||||
}
|
||||
|
||||
protected override LocalisableString Label => UsersStrings.ShowStatsMaximumCombo;
|
||||
|
||||
protected override LocalisableString FormatCurrentValue(int current) => LocalisableString.Interpolate($@"{current:N0}x");
|
||||
|
||||
protected override int CalculateDifference(int previous, int current, out LocalisableString formattedDifference)
|
||||
{
|
||||
int difference = current - previous;
|
||||
|
||||
if (difference < 0)
|
||||
formattedDifference = LocalisableString.Interpolate($@"{difference:N0}x");
|
||||
else if (difference > 0)
|
||||
formattedDifference = LocalisableString.Interpolate($@"+{difference:N0}x");
|
||||
else
|
||||
formattedDifference = string.Empty;
|
||||
|
||||
return current.CompareTo(previous);
|
||||
}
|
||||
}
|
||||
}
|
78
osu.Game/Screens/Ranking/Statistics/User/OverallRanking.cs
Normal file
78
osu.Game/Screens/Ranking/Statistics/User/OverallRanking.cs
Normal file
@ -0,0 +1,78 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.Solo;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Screens.Ranking.Statistics.User
|
||||
{
|
||||
public partial class OverallRanking : CompositeDrawable
|
||||
{
|
||||
private const float transition_duration = 300;
|
||||
|
||||
public Bindable<SoloStatisticsUpdate?> StatisticsUpdate { get; } = new Bindable<SoloStatisticsUpdate?>();
|
||||
|
||||
private LoadingLayer loadingLayer = null!;
|
||||
private FillFlowContainer content = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
AutoSizeAxes = Axes.Y;
|
||||
AutoSizeEasing = Easing.OutQuint;
|
||||
AutoSizeDuration = transition_duration;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
loadingLayer = new LoadingLayer(withBox: false)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
content = new FillFlowContainer
|
||||
{
|
||||
AlwaysPresent = true,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(10),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new GlobalRankChangeRow { StatisticsUpdate = { BindTarget = StatisticsUpdate } },
|
||||
new AccuracyChangeRow { StatisticsUpdate = { BindTarget = StatisticsUpdate } },
|
||||
new MaximumComboChangeRow { StatisticsUpdate = { BindTarget = StatisticsUpdate } },
|
||||
new RankedScoreChangeRow { StatisticsUpdate = { BindTarget = StatisticsUpdate } },
|
||||
new TotalScoreChangeRow { StatisticsUpdate = { BindTarget = StatisticsUpdate } },
|
||||
new PerformancePointsChangeRow { StatisticsUpdate = { BindTarget = StatisticsUpdate } }
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
StatisticsUpdate.BindValueChanged(onUpdateReceived, true);
|
||||
FinishTransforms(true);
|
||||
}
|
||||
|
||||
private void onUpdateReceived(ValueChangedEvent<SoloStatisticsUpdate?> update)
|
||||
{
|
||||
if (update.NewValue == null)
|
||||
{
|
||||
loadingLayer.Show();
|
||||
content.FadeOut(transition_duration, Easing.OutQuint);
|
||||
}
|
||||
else
|
||||
{
|
||||
loadingLayer.Hide();
|
||||
content.FadeIn(transition_duration, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
// 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.Diagnostics;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
|
||||
namespace osu.Game.Screens.Ranking.Statistics.User
|
||||
{
|
||||
public partial class PerformancePointsChangeRow : RankingChangeRow<decimal?>
|
||||
{
|
||||
public PerformancePointsChangeRow()
|
||||
: base(stats => stats.PP)
|
||||
{
|
||||
}
|
||||
|
||||
protected override LocalisableString Label => RankingsStrings.StatPerformance;
|
||||
|
||||
protected override LocalisableString FormatCurrentValue(decimal? current)
|
||||
=> current == null ? string.Empty : LocalisableString.Interpolate($@"{current:N0}pp");
|
||||
|
||||
protected override int CalculateDifference(decimal? previous, decimal? current, out LocalisableString formattedDifference)
|
||||
{
|
||||
if (previous == null && current == null)
|
||||
{
|
||||
formattedDifference = string.Empty;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (previous == null && current != null)
|
||||
{
|
||||
formattedDifference = LocalisableString.Interpolate($"+{current.Value:N0}pp");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (previous != null && current == null)
|
||||
{
|
||||
formattedDifference = LocalisableString.Interpolate($"-{previous.Value:N0}pp");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Debug.Assert(previous != null && current != null);
|
||||
|
||||
decimal difference = current.Value - previous.Value;
|
||||
|
||||
if (difference < 0)
|
||||
formattedDifference = LocalisableString.Interpolate($@"{difference:N0}pp");
|
||||
else if (difference > 0)
|
||||
formattedDifference = LocalisableString.Interpolate($@"+{difference:N0}pp");
|
||||
else
|
||||
formattedDifference = string.Empty;
|
||||
|
||||
return current.Value.CompareTo(previous.Value);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Extensions.LocalisationExtensions;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
|
||||
namespace osu.Game.Screens.Ranking.Statistics.User
|
||||
{
|
||||
public partial class RankedScoreChangeRow : RankingChangeRow<long>
|
||||
{
|
||||
public RankedScoreChangeRow()
|
||||
: base(stats => stats.RankedScore)
|
||||
{
|
||||
}
|
||||
|
||||
protected override LocalisableString Label => UsersStrings.ShowStatsRankedScore;
|
||||
|
||||
protected override LocalisableString FormatCurrentValue(long current) => current.ToLocalisableString(@"N0");
|
||||
|
||||
protected override int CalculateDifference(long previous, long current, out LocalisableString formattedDifference)
|
||||
{
|
||||
long difference = current - previous;
|
||||
|
||||
if (difference < 0)
|
||||
formattedDifference = difference.ToLocalisableString(@"N0");
|
||||
else if (difference > 0)
|
||||
formattedDifference = LocalisableString.Interpolate($@"+{difference:N0}");
|
||||
else
|
||||
formattedDifference = string.Empty;
|
||||
|
||||
return current.CompareTo(previous);
|
||||
}
|
||||
}
|
||||
}
|
144
osu.Game/Screens/Ranking/Statistics/User/RankingChangeRow.cs
Normal file
144
osu.Game/Screens/Ranking/Statistics/User/RankingChangeRow.cs
Normal file
@ -0,0 +1,144 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Online.Solo;
|
||||
using osu.Game.Users;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Screens.Ranking.Statistics.User
|
||||
{
|
||||
public abstract partial class RankingChangeRow<T> : CompositeDrawable
|
||||
{
|
||||
public Bindable<SoloStatisticsUpdate?> StatisticsUpdate { get; } = new Bindable<SoloStatisticsUpdate?>();
|
||||
|
||||
private readonly Func<UserStatistics, T> accessor;
|
||||
|
||||
private OsuSpriteText currentValueText = null!;
|
||||
private SpriteIcon changeIcon = null!;
|
||||
private OsuSpriteText changeText = null!;
|
||||
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; } = null!;
|
||||
|
||||
protected RankingChangeRow(
|
||||
Func<UserStatistics, T> accessor)
|
||||
{
|
||||
this.accessor = accessor;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = Label,
|
||||
Font = OsuFont.Default.With(size: 18)
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
Direction = FillDirection.Vertical,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
Direction = FillDirection.Horizontal,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Spacing = new Vector2(5),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
changeIcon = new SpriteIcon
|
||||
{
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
Size = new Vector2(18)
|
||||
},
|
||||
currentValueText = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
Font = OsuFont.Default.With(size: 18, weight: FontWeight.Bold)
|
||||
},
|
||||
}
|
||||
},
|
||||
changeText = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
Font = OsuFont.Default.With(weight: FontWeight.Bold)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
StatisticsUpdate.BindValueChanged(onStatisticsUpdate, true);
|
||||
}
|
||||
|
||||
private void onStatisticsUpdate(ValueChangedEvent<SoloStatisticsUpdate?> statisticsUpdate)
|
||||
{
|
||||
var update = statisticsUpdate.NewValue;
|
||||
|
||||
if (update == null)
|
||||
return;
|
||||
|
||||
T previousValue = accessor.Invoke(update.Before);
|
||||
T currentValue = accessor.Invoke(update.After);
|
||||
int comparisonResult = CalculateDifference(previousValue, currentValue, out var formattedDifference);
|
||||
|
||||
Colour4 comparisonColour;
|
||||
IconUsage icon;
|
||||
|
||||
if (comparisonResult < 0)
|
||||
{
|
||||
comparisonColour = colours.Red1;
|
||||
icon = FontAwesome.Solid.ArrowDown;
|
||||
}
|
||||
else if (comparisonResult > 0)
|
||||
{
|
||||
comparisonColour = colours.Lime1;
|
||||
icon = FontAwesome.Solid.ArrowUp;
|
||||
}
|
||||
else
|
||||
{
|
||||
comparisonColour = colours.Orange1;
|
||||
icon = FontAwesome.Solid.Minus;
|
||||
}
|
||||
|
||||
currentValueText.Text = FormatCurrentValue(currentValue);
|
||||
|
||||
changeIcon.Icon = icon;
|
||||
changeIcon.Colour = comparisonColour;
|
||||
|
||||
changeText.Text = formattedDifference;
|
||||
changeText.Colour = comparisonColour;
|
||||
}
|
||||
|
||||
protected abstract LocalisableString Label { get; }
|
||||
|
||||
protected abstract LocalisableString FormatCurrentValue(T current);
|
||||
protected abstract int CalculateDifference(T previous, T current, out LocalisableString formattedDifference);
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Extensions.LocalisationExtensions;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
|
||||
namespace osu.Game.Screens.Ranking.Statistics.User
|
||||
{
|
||||
public partial class TotalScoreChangeRow : RankingChangeRow<long>
|
||||
{
|
||||
public TotalScoreChangeRow()
|
||||
: base(stats => stats.TotalScore)
|
||||
{
|
||||
}
|
||||
|
||||
protected override LocalisableString Label => UsersStrings.ShowStatsTotalScore;
|
||||
|
||||
protected override LocalisableString FormatCurrentValue(long current) => current.ToLocalisableString(@"N0");
|
||||
|
||||
protected override int CalculateDifference(long previous, long current, out LocalisableString formattedDifference)
|
||||
{
|
||||
long difference = current - previous;
|
||||
|
||||
if (difference < 0)
|
||||
formattedDifference = difference.ToLocalisableString(@"N0");
|
||||
else if (difference > 0)
|
||||
formattedDifference = LocalisableString.Interpolate($@"+{difference:N0}");
|
||||
else
|
||||
formattedDifference = string.Empty;
|
||||
|
||||
return current.CompareTo(previous);
|
||||
}
|
||||
}
|
||||
}
|
@ -27,8 +27,7 @@ namespace osu.Game.Users.Drawables
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(TextureStore ts)
|
||||
{
|
||||
if (ts == null)
|
||||
throw new ArgumentNullException(nameof(ts));
|
||||
ArgumentNullException.ThrowIfNull(ts);
|
||||
|
||||
string textureName = countryCode == CountryCode.Unknown ? "__" : countryCode.ToString();
|
||||
Texture = ts.Get($@"Flags/{textureName}") ?? ts.Get(@"Flags/__");
|
||||
|
@ -36,8 +36,7 @@ namespace osu.Game.Users
|
||||
protected UserPanel(APIUser user)
|
||||
: base(HoverSampleSet.Button)
|
||||
{
|
||||
if (user == null)
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
ArgumentNullException.ThrowIfNull(user);
|
||||
|
||||
User = user;
|
||||
}
|
||||
|
@ -35,7 +35,7 @@
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Realm" Version="10.18.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2022.1219.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2022.1226.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.1221.0" />
|
||||
<PackageReference Include="Sentry" Version="3.23.1" />
|
||||
<PackageReference Include="SharpCompress" Version="0.32.2" />
|
||||
|
@ -16,7 +16,7 @@
|
||||
<RuntimeIdentifier>iossimulator-x64</RuntimeIdentifier>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2022.1219.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2022.1226.0" />
|
||||
|
||||
<!-- Required since Veldrid references a library that depends on Microsoft.DotNet.PlatformAbstractions (2.0.3), which doesn't play nice with Realm. -->
|
||||
<PackageReference Include="System.IO.FileSystem.Primitives" Version="4.3.0" />
|
||||
|
@ -146,7 +146,7 @@
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=PropertyCanBeMadeInitOnly_002EGlobal/@EntryIndexedValue">HINT</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=PropertyCanBeMadeInitOnly_002ELocal/@EntryIndexedValue">HINT</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=PublicConstructorInAbstractClass/@EntryIndexedValue">WARNING</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=RedundantArgumentDefaultValue/@EntryIndexedValue">WARNING</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=RedundantArgumentDefaultValue/@EntryIndexedValue">HINT</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=RedundantArrayCreationExpression/@EntryIndexedValue">WARNING</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=RedundantAttributeParentheses/@EntryIndexedValue">WARNING</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=RedundantAttributeUsageProperty/@EntryIndexedValue">WARNING</s:String>
|
||||
|
Loading…
x
Reference in New Issue
Block a user