Skip to content

Add argon style judgement counter #32297

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file not shown.
2 changes: 2 additions & 0 deletions osu.Game.Tests/Skins/SkinDeserialisationTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ public class SkinDeserialisationTest
"Archives/modified-argon-20250116.osk",
// Covers player team flag
"Archives/modified-argon-20250214.osk",
// Covers argon judgement counter
"Archives/modified-argon-20250308.osk",
};

/// <summary>
Expand Down
193 changes: 193 additions & 0 deletions osu.Game.Tests/Visual/Gameplay/TestSceneArgonJudgementCounter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using System;
using System.Diagnostics;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Testing;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Play.HUD.JudgementCounter;
using osu.Game.Skinning.Components;

namespace osu.Game.Tests.Visual.Gameplay
{
public partial class TestSceneArgonJudgementCounter : OsuTestScene
{
private ScoreProcessor scoreProcessor = null!;
private JudgementCountController judgementCountController = null!;
private TestArgonJudgementCounterDisplay counterDisplay = null!;

private DependencyProvidingContainer content = null!;

protected override Container<Drawable> Content => content;

private readonly Bindable<JudgementResult> lastJudgementResult = new Bindable<JudgementResult>();

private int iteration;

[SetUpSteps]
public void SetUpSteps() => AddStep("Create components", () =>
{
var ruleset = CreateRuleset();

Debug.Assert(ruleset != null);

scoreProcessor = new ScoreProcessor(ruleset);
base.Content.Child = new DependencyProvidingContainer
{
RelativeSizeAxes = Axes.Both,
CachedDependencies = new (Type, object)[] { (typeof(ScoreProcessor), scoreProcessor), (typeof(Ruleset), ruleset) },
Children = new Drawable[]
{
judgementCountController = new JudgementCountController(),
content = new DependencyProvidingContainer
{
RelativeSizeAxes = Axes.Both,
CachedDependencies = new (Type, object)[] { (typeof(JudgementCountController), judgementCountController) },
}
},
};
});

protected override Ruleset CreateRuleset() => new OsuRuleset();

private void applyOneJudgement(HitResult result)
{
lastJudgementResult.Value = new OsuJudgementResult(new HitObject
{
StartTime = iteration * 10000
}, new OsuJudgement())
{
Type = result,
};
scoreProcessor.ApplyResult(lastJudgementResult.Value);

iteration++;
}

[Test]
public void TestAddJudgementsToCounters()
{
AddStep("create counter", () => Child = counterDisplay = new TestArgonJudgementCounterDisplay());

AddRepeatStep("Add judgement", () => applyOneJudgement(HitResult.Great), 2);
AddRepeatStep("Add judgement", () => applyOneJudgement(HitResult.Miss), 2);
AddRepeatStep("Add judgement", () => applyOneJudgement(HitResult.Meh), 2);
}

[Test]
public void TestAddWhilstHidden()
{
AddStep("create counter", () => Child = counterDisplay = new TestArgonJudgementCounterDisplay());

AddRepeatStep("Add judgement", () => applyOneJudgement(HitResult.LargeTickHit), 2);
AddAssert("Check value added whilst hidden", () => hiddenCount() == 2);
AddStep("Show all judgements", () => counterDisplay.Mode.Value = ArgonJudgementCounterDisplay.DisplayMode.All);
}

[Test]
public void TestChangeFlowDirection()
{
AddStep("create counter", () => Child = counterDisplay = new TestArgonJudgementCounterDisplay());

AddStep("Set direction vertical", () => counterDisplay.FlowDirection.Value = Direction.Vertical);
AddStep("Set direction horizontal", () => counterDisplay.FlowDirection.Value = Direction.Horizontal);
}

[Test]
public void TestToggleJudgementNames()
{
AddStep("create counter", () => Child = counterDisplay = new TestArgonJudgementCounterDisplay());

AddStep("Show label", () => counterDisplay.ShowLabel.Value = true);
AddWaitStep("wait some", 2);
AddAssert("Assert hidden", () => counterDisplay.CounterFlow.Children.First().Alpha == 1);
AddStep("Hide label", () => counterDisplay.ShowLabel.Value = false);
AddWaitStep("wait some", 2);
AddAssert("Assert shown", () => counterDisplay.CounterFlow.Children.First().Alpha == 1);
}

[Test]
public void TestHideMaxValue()
{
AddStep("create counter", () => Child = counterDisplay = new TestArgonJudgementCounterDisplay());

AddStep("Hide max judgement", () => counterDisplay.ShowMaxJudgement.Value = false);
AddWaitStep("wait some", 2);
AddAssert("Check max hidden", () => counterDisplay.CounterFlow.ChildrenOfType<ArgonJudgementCounter>().First().Alpha == 0);
AddStep("Show max judgement", () => counterDisplay.ShowMaxJudgement.Value = true);
}

[Test]
public void TestMaxValueStartsHidden()
{
AddStep("create counter", () => Child = counterDisplay = new TestArgonJudgementCounterDisplay
{
ShowMaxJudgement = { Value = false }
});
AddAssert("Check max hidden", () => counterDisplay.CounterFlow.ChildrenOfType<ArgonJudgementCounter>().First().Alpha == 0);
}

[Test]
public void TestMaxValueHiddenOnModeChange()
{
AddStep("create counter", () => Child = counterDisplay = new TestArgonJudgementCounterDisplay());

AddStep("Set max judgement to hide itself", () => counterDisplay.ShowMaxJudgement.Value = false);
AddStep("Show all judgements", () => counterDisplay.Mode.Value = ArgonJudgementCounterDisplay.DisplayMode.All);
AddWaitStep("wait some", 2);
AddAssert("Assert max judgement hidden", () => counterDisplay.CounterFlow.ChildrenOfType<ArgonJudgementCounter>().First().Alpha == 0);
}

[Test]
public void TestNoDuplicates()
{
AddStep("create counter", () => Child = counterDisplay = new TestArgonJudgementCounterDisplay());
AddStep("Show all judgements", () => counterDisplay.Mode.Value = ArgonJudgementCounterDisplay.DisplayMode.All);
AddAssert("Check no duplicates",
() => counterDisplay.CounterFlow.ChildrenOfType<ArgonJudgementCounter>().Count(),
() => Is.EqualTo(counterDisplay.CounterFlow.ChildrenOfType<ArgonJudgementCounter>().Select(c => c.JudgementName).Distinct().Count()));
}

[Test]
public void TestCycleDisplayModes()
{
AddStep("create counter", () => Child = counterDisplay = new TestArgonJudgementCounterDisplay());

AddStep("Show basic judgements", () => counterDisplay.Mode.Value = ArgonJudgementCounterDisplay.DisplayMode.Simple);
AddWaitStep("wait some", 2);
AddAssert("Check only basic", () => counterDisplay.CounterFlow.ChildrenOfType<ArgonJudgementCounter>().Last().Alpha == 0);
AddStep("Show normal judgements", () => counterDisplay.Mode.Value = ArgonJudgementCounterDisplay.DisplayMode.Normal);
AddStep("Show all judgements", () => counterDisplay.Mode.Value = ArgonJudgementCounterDisplay.DisplayMode.All);
AddWaitStep("wait some", 2);
AddAssert("Check all visible", () => counterDisplay.CounterFlow.ChildrenOfType<ArgonJudgementCounter>().Last().Alpha == 1);
}

private int hiddenCount()
{
var num = counterDisplay.CounterFlow.Children.First(child => child.JudgementCounter.Types.Contains(HitResult.LargeTickHit));
return num.JudgementCounter.ResultCount.Value;
}

private partial class TestArgonJudgementCounterDisplay : ArgonJudgementCounterDisplay
{
public new FillFlowContainer<ArgonJudgementCounter> CounterFlow => base.CounterFlow;

public TestArgonJudgementCounterDisplay()
{
Margin = new MarginPadding { Top = 100 };
Anchor = Anchor.TopCentre;
Origin = Anchor.TopCentre;
}
}
}
}
12 changes: 10 additions & 2 deletions osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osuTK;
using osuTK.Graphics;

namespace osu.Game.Screens.Play.HUD
{
Expand All @@ -26,6 +27,8 @@ public partial class ArgonCounterTextComponent : CompositeDrawable, IHasText

public IBindable<float> WireframeOpacity { get; } = new BindableFloat();
public Bindable<bool> ShowLabel { get; } = new BindableBool();
public Bindable<Color4> LabelColour { get; } = new Bindable<Color4>(Color4.White);
public Bindable<Color4> TextColour { get; } = new Bindable<Color4>(Color4.White);

public Container NumberContainer { get; private set; }

Expand Down Expand Up @@ -58,7 +61,6 @@ public ArgonCounterTextComponent(Anchor anchor, LocalisableString? label = null)
labelText = new OsuSpriteText
{
Alpha = 0,
BypassAutoSizeAxes = Axes.X,
Text = label.GetValueOrDefault(),
Font = OsuFont.Torus.With(size: 12, weight: FontWeight.Bold),
Margin = new MarginPadding { Left = 2.5f },
Expand Down Expand Up @@ -110,7 +112,7 @@ private string wireframesLookup(char c)
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
labelText.Colour = colours.Blue0;
LabelColour.Value = colours.Blue0;
}

protected override void LoadComplete()
Expand All @@ -122,6 +124,12 @@ protected override void LoadComplete()
labelText.Alpha = s.NewValue ? 1 : 0;
NumberContainer.Y = s.NewValue ? 12 : 0;
}, true);
LabelColour.BindValueChanged(c => labelText.Colour = c.NewValue, true);
TextColour.BindValueChanged(c =>
{
textPart.Colour = c.NewValue;
wireframesPart.Colour = c.NewValue;
}, true);
}

private partial class ArgonCounterSpriteText : OsuSpriteText
Expand Down
70 changes: 70 additions & 0 deletions osu.Game/Skinning/Components/ArgonJudgementCounter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using System;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.LocalisationExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Play.HUD;
using osu.Game.Screens.Play.HUD.JudgementCounter;
using osuTK.Graphics;

namespace osu.Game.Skinning.Components
{
public sealed partial class ArgonJudgementCounter : VisibilityContainer
{
public ArgonCounterTextComponent TextComponent;
private OsuColour colours = null!;
public readonly JudgementCount JudgementCounter;
public BindableInt DisplayedValue = new BindableInt();
public string JudgementName;

public ArgonJudgementCounter(JudgementCount judgementCounter)
{
JudgementCounter = judgementCounter;
JudgementName = judgementCounter.DisplayName.ToUpper().ToString();

AutoSizeAxes = Axes.Both;
AddInternal(TextComponent = new ArgonCounterTextComponent(Anchor.TopRight, judgementCounter.DisplayName.ToUpper()));
}

[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
this.colours = colours;
}

private void updateWireframe()
{
int wireframeLenght = Math.Max(2, TextComponent.Text.ToString().Length);
TextComponent.WireframeTemplate = new string('#', wireframeLenght);
}

protected override void LoadComplete()
{
base.LoadComplete();
DisplayedValue.BindValueChanged(v =>
{
TextComponent.Text = v.NewValue.ToString();
updateWireframe();
}, true);

var result = JudgementCounter.Types.First();
TextComponent.LabelColour.Value = getJudgementColor(result);
TextComponent.ShowLabel.BindValueChanged(v => TextComponent.TextColour.Value = !v.NewValue ? getJudgementColor(result) : Color4.White);
}

private Color4 getJudgementColor(HitResult result)
{
return result.IsBasic() ? colours.ForHitResult(result) : !result.IsBonus() ? colours.PurpleLight : colours.PurpleLighter;
}

protected override void PopIn() => this.FadeIn(JudgementCounterDisplay.TRANSFORM_DURATION, Easing.OutQuint);
protected override void PopOut() => this.FadeOut(100);
}
}
Loading