mirror of
https://github.com/ppy/osu.git
synced 2026-05-22 07:49:51 +08:00
c4a49f647f
Adds the ability to drag and reorder cards. Card order is preserved
between rounds and is synchronized between both players (each player can
see the other player drag around and reorder their cards).
To make this possible I had to rewrite the card layout algorithm to be
stateless (e0a46fefaf), there wasn't
really a way around that since I needed a way to calculate a layout
position based on a card's index. Should hopefully be a lot easier to
read now though.
Some noteworthy stuff:
- I didn't really know what the best place to store the card order is,
so I put it on `RankedPlayCardWithPlaylistItem` since that one will stay
the same instance per round.
- To prevent the opponent's cards from dragged into the middle of the
playfield, only the x axis of the drag gets synchronized for the
`OpponentHandOfCards` with a fixed y value.
- I adjusted the replay recorder/player parameters a little. With the
drag events happening every frame the replay recorder record new frames
every 25ms and end up dropping half the replay frames per flush
interval, so I increased the sample interval to 50ms so the buffer size
matches the sample rate exactly (50ms -> 20 samples per flush every
1000ms).
I also increased the buffer size in the replay player a bit so slight
fluctuations in latency won't make it start to drop frames.
https://github.com/user-attachments/assets/b810cb85-db02-4edf-a63e-bfc96cf59665
https://github.com/user-attachments/assets/4d2f884d-fcce-4948-9659-fbb314634cb8
---------
Co-authored-by: Dean Herbert <pe@ppy.sh>
197 lines
8.0 KiB
C#
197 lines
8.0 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.Cards.First().TriggerClick());
|
|
AddAssert("first card selected", () => handOfCards.Selection.SequenceEqual([handOfCards.Cards.First().Item]));
|
|
|
|
AddStep("click second card", () => handOfCards.Cards.ElementAt(1).TriggerClick());
|
|
AddAssert("second card selected", () => handOfCards.Selection.SequenceEqual([handOfCards.Cards.ElementAt(1).Item]));
|
|
|
|
AddStep("click second card again", () => handOfCards.Cards.ElementAt(1).TriggerClick());
|
|
AddAssert("second card selected", () => handOfCards.Selection.SequenceEqual([handOfCards.Cards.ElementAt(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.Cards.First().TriggerClick());
|
|
AddAssert("first card selected", () => handOfCards.Selection.SequenceEqual([handOfCards.Cards.First().Item]));
|
|
|
|
AddStep("click second card", () => handOfCards.Cards.ElementAt(1).TriggerClick());
|
|
AddAssert("both cards selected", () => handOfCards.Selection.SequenceEqual([handOfCards.Cards.ElementAt(0).Item, handOfCards.Cards.ElementAt(1).Item]));
|
|
|
|
AddStep("click second card again", () => handOfCards.Cards.ElementAt(1).TriggerClick());
|
|
AddAssert("first card selected", () => handOfCards.Selection.SequenceEqual([handOfCards.Cards.ElementAt(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.Cards.ElementAt(i1).Item]));
|
|
}
|
|
|
|
AddStep("right arrow", () => InputManager.Key(Key.Right));
|
|
AddAssert("first card selected", () => handOfCards.Selection.SequenceEqual([handOfCards.Cards.ElementAt(0).Item]));
|
|
|
|
AddStep("right arrow", () => InputManager.Key(Key.Right));
|
|
AddAssert("second card selected", () => handOfCards.Selection.SequenceEqual([handOfCards.Cards.ElementAt(1).Item]));
|
|
|
|
AddStep("left arrow", () => InputManager.Key(Key.Left));
|
|
AddAssert("first card selected", () => handOfCards.Selection.SequenceEqual([handOfCards.Cards.ElementAt(0).Item]));
|
|
|
|
AddStep("left arrow", () => InputManager.Key(Key.Left));
|
|
AddAssert("last card selected", () => handOfCards.Selection.SequenceEqual([handOfCards.Cards.ElementAt(^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.Cards.ElementAt(i1).CardHovered);
|
|
|
|
AddAssert("card not selected", () => !handOfCards.Selection.Contains(handOfCards.Cards.ElementAt(i1).Card.Item));
|
|
AddStep("space", () => InputManager.Key(Key.Space));
|
|
AddAssert("card selected", () => handOfCards.Selection.Contains(handOfCards.Cards.ElementAt(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))
|
|
);
|
|
}
|
|
}
|
|
}
|