1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-30 15:05:05 +08:00
Files
osu-lazer/osu.Game.Tests/Visual/RankedPlay/TestScenePlayerCardHand.cs
T
maarvin 7b0e5ecc13 Code quality improvements for child/draw order handling in HandOfCards (#37423)
Attempt at adressing the points made in
https://github.com/ppy/osu/pull/37419#issuecomment-4279123323

- `CardContainer` is now being sorted immediately instead of only doing
it once per frame. Given that its only ever gonna have a handful of
children there wasn't really a need to optimize that that in the first
place.
- `HandOfCards.Cards` now exposes `cardLookup.Values` as an
`IEnumerable` instead of exposing the card container's children
directly.
- `HandOfCard` now exposes `GetCardsInDisplayOrder` which returns a copy
of all cards in display order. Since it's making a copy I made sure this
isn't called on any hot code paths.
- `HandOfCard.Clear` previously didn't clear the `cardLookup`
dictionary. Didn't cause any issues since we're not re-adding cards to
the hand anywhere but not good regardless.
Switched to looping over all cards and calling `RemoveCard` to make sure
changes to the removal logic can't get overlooked there again.

---------

Co-authored-by: Bartłomiej Dach <dach.bartlomiej@gmail.com>
2026-04-21 22:54:36 +09:00

216 lines
9.1 KiB
C#

// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Linq;
using Humanizer;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Testing;
using osu.Game.Online.Multiplayer.MatchTypes.RankedPlay;
using osu.Game.Overlays;
using osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay;
using osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Hand;
using osuTK.Input;
namespace osu.Game.Tests.Visual.RankedPlay
{
public partial class TestScenePlayerCardHand : OsuManualInputManagerTestScene
{
[Cached]
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple);
private PlayerHandOfCards handOfCards = null!;
[BackgroundDependencyLoader]
private void load()
{
Child = handOfCards = new PlayerHandOfCards
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.Both,
Height = 0.5f,
};
}
[SetUpSteps]
public void SetupSteps()
{
AddStep("reset card hand", () => Child = handOfCards = new PlayerHandOfCards
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.Both,
Height = 0.5f,
});
}
[Test]
public void TestSingleSelectionMode()
{
AddStep("add cards", () =>
{
for (int i = 0; i < 5; i++)
handOfCards.AddCard(new RankedPlayCardWithPlaylistItem(new RankedPlayCardItem()));
});
AddStep("single selection mode", () => handOfCards.SelectionMode = HandSelectionMode.Single);
AddStep("click first card", () => handOfCards.GetCardsInDisplayOrder()[0].TriggerClick());
AddAssert("first card selected", () => handOfCards.Selection.SequenceEqual([handOfCards.GetCardsInDisplayOrder()[0].Item]));
AddStep("click second card", () => handOfCards.GetCardsInDisplayOrder()[1].TriggerClick());
AddAssert("second card selected", () => handOfCards.Selection.SequenceEqual([handOfCards.GetCardsInDisplayOrder()[1].Item]));
AddStep("click second card again", () => handOfCards.GetCardsInDisplayOrder()[1].TriggerClick());
AddAssert("second card selected", () => handOfCards.Selection.SequenceEqual([handOfCards.GetCardsInDisplayOrder()[1].Item]));
}
[Test]
public void TestMultiSelectionMode()
{
AddStep("add cards", () =>
{
for (int i = 0; i < 5; i++)
handOfCards.AddCard(new RankedPlayCardWithPlaylistItem(new RankedPlayCardItem()));
});
AddStep("multi selection mode", () => handOfCards.SelectionMode = HandSelectionMode.Multiple);
AddStep("click first card", () => handOfCards.GetCardsInDisplayOrder().First().TriggerClick());
AddAssert("first card selected", () => handOfCards.Selection.SequenceEqual([handOfCards.GetCardsInDisplayOrder().First().Item]));
AddStep("click second card", () => handOfCards.GetCardsInDisplayOrder()[1].TriggerClick());
AddAssert("both cards selected", () => handOfCards.Selection.SequenceEqual([handOfCards.GetCardsInDisplayOrder()[0].Item, handOfCards.GetCardsInDisplayOrder()[1].Item]));
AddStep("click second card again", () => handOfCards.GetCardsInDisplayOrder()[1].TriggerClick());
AddAssert("first card selected", () => handOfCards.Selection.SequenceEqual([handOfCards.GetCardsInDisplayOrder()[0].Item]));
}
[Test]
public void TestCardCount()
{
for (int i = 1; i <= 8; i++)
{
int numCards = i;
AddStep($"{i} {"cards".Pluralize(i == 1)}", () =>
{
Child = handOfCards = new PlayerHandOfCards
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.Both,
Height = 0.5f,
};
for (int j = 0; j < numCards; j++)
handOfCards.AddCard(new RankedPlayCardWithPlaylistItem(new RankedPlayCardItem()));
});
}
}
[Test]
public void TestKeyboardSelectionSingleSelection()
{
bool playActionTriggered = false;
AddStep("add cards", () =>
{
playActionTriggered = false;
handOfCards.PlayCardAction = () => playActionTriggered = true;
handOfCards.Clear();
for (int i = 0; i < 5; i++)
handOfCards.AddCard(new RankedPlayCardWithPlaylistItem(new RankedPlayCardItem()));
});
AddStep("single selection mode", () => handOfCards.SelectionMode = HandSelectionMode.Single);
for (int i = 0; i < 5; i++)
{
int i1 = i;
Key key = Key.Number1 + i;
AddStep($"key {i + 1}", () => InputManager.Key(key));
AddAssert("first card selected", () => handOfCards.Selection.SequenceEqual([handOfCards.GetCardsInDisplayOrder()[i1].Item]));
}
AddStep("right arrow", () => InputManager.Key(Key.Right));
AddAssert("first card selected", () => handOfCards.Selection.SequenceEqual([handOfCards.GetCardsInDisplayOrder()[0].Item]));
AddStep("right arrow", () => InputManager.Key(Key.Right));
AddAssert("second card selected", () => handOfCards.Selection.SequenceEqual([handOfCards.GetCardsInDisplayOrder()[1].Item]));
AddStep("left arrow", () => InputManager.Key(Key.Left));
AddAssert("first card selected", () => handOfCards.Selection.SequenceEqual([handOfCards.GetCardsInDisplayOrder()[0].Item]));
AddStep("left arrow", () => InputManager.Key(Key.Left));
AddAssert("last card selected", () => handOfCards.Selection.SequenceEqual([handOfCards.GetCardsInDisplayOrder()[^1].Item]));
AddStep("space", () => InputManager.Key(Key.Space));
AddAssert("play action triggered", () => playActionTriggered);
}
[Test]
public void TestKeyboardSelectionMultiSelection()
{
AddStep("add cards", () =>
{
for (int i = 0; i < 5; i++)
handOfCards.AddCard(new RankedPlayCardWithPlaylistItem(new RankedPlayCardItem()));
});
AddStep("multi selection mode", () => handOfCards.SelectionMode = HandSelectionMode.Multiple);
for (int i = 0; i < 5; i++)
{
int i1 = i;
Key key = Key.Number1 + i;
AddStep($"key {i + 1}", () => InputManager.Key(key));
AddAssert("card hovered", () => handOfCards.GetCardsInDisplayOrder()[i1].CardHovered);
AddAssert("card not selected", () => !handOfCards.Selection.Contains(handOfCards.GetCardsInDisplayOrder()[i1].Card.Item));
AddStep("space", () => InputManager.Key(Key.Space));
AddAssert("card selected", () => handOfCards.Selection.Contains(handOfCards.GetCardsInDisplayOrder()[i1].Card.Item));
}
}
[Test]
public void TestContract()
{
AddStep("add cards", () =>
{
for (int i = 0; i < 5; i++)
handOfCards.AddCard(new RankedPlayCardWithPlaylistItem(new RankedPlayCardItem()));
});
AddWaitStep("wait", 5);
AddStep("contract", () => handOfCards.Contract());
AddWaitStep("wait", 5);
AddAssert(
"all cards outside bounds", () =>
handOfCards
.ChildrenOfType<HandOfCards.HandCard>()
.All(card => !card.ScreenSpaceDrawQuad.AABBFloat.IntersectsWith(handOfCards.ScreenSpaceDrawQuad.AABBFloat))
);
}
[Test]
public void TestRemoveCardsWhileDragging()
{
AddStep("add cards", () =>
{
for (int i = 0; i < 5; i++)
handOfCards.AddCard(new RankedPlayCardWithPlaylistItem(new RankedPlayCardItem()));
});
AddStep("hover card", () => InputManager.MoveMouseTo(handOfCards.GetCardsInDisplayOrder()[0]));
AddStep("start drag", () => InputManager.PressButton(MouseButton.Left));
AddStep("move card", () => InputManager.MoveMouseTo(handOfCards.GetCardsInDisplayOrder()[3]));
AddStep("remove cards", () =>
{
foreach (var card in handOfCards.Cards.ToArray())
handOfCards.RemoveCard(card.Item);
});
AddStep("release mouse", () => InputManager.ReleaseButton(MouseButton.Left));
}
}
}