diff --git a/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs b/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs index d5835896a4..a1ab2c7a48 100644 --- a/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs +++ b/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs @@ -23,6 +23,9 @@ namespace osu.Game.Tournament.Tests public TestCaseMatchPairings() { + FillFlowContainer level1; + FillFlowContainer level2; + var pairing1 = new MatchPairing( new TournamentTeam { FlagName = "AU", FullName = "Australia", }, new TournamentTeam { FlagName = "JP", FullName = "Japan", Acronym = "JPN" }) @@ -41,21 +44,53 @@ namespace osu.Game.Tournament.Tests Child = new FillFlowContainer { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, Children = new Drawable[] { - new DrawableMatchPairing(pairing1), - new DrawableMatchPairing(pairing2), - new DrawableMatchPairing(new MatchPairing()) + level1 = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Children = new[] + { + new DrawableMatchPairing(pairing1), + new DrawableMatchPairing(pairing2), + new DrawableMatchPairing(new MatchPairing()), + } + }, + level2 = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Margin = new MarginPadding(20), + Children = new[] + { + new DrawableMatchPairing(new MatchPairing()), + new DrawableMatchPairing(new MatchPairing()) + } + } } }; + level1.Children[0].Progression = level2.Children[0]; + level1.Children[1].Progression = level2.Children[0]; + AddStep("mark complete", () => pairing1.Completed.Value = true); AddRepeatStep("change scores", () => pairing1.Team2Score.Value++, 5); AddStep("mark complete", () => pairing1.Completed.Value = true); + AddStep("add new team", () => pairing2.Team2.Value = new TournamentTeam { FlagName = "PT", FullName = "Portugal" }); + AddStep("Add progression", () => level1.Children[2].Progression = level2.Children[1]); - AddStep("add new team", () => pairing2.Team2.Value = - new TournamentTeam { FlagName = "PT", FullName = "Portugal" } - ); + AddStep("start match", () => pairing2.ResetScores()); + + AddRepeatStep("change scores", () => pairing2.Team1Score.Value++, 5); + AddStep("mark complete", () => pairing2.Completed.Value = true); + + AddStep("start submatch", () => level2.Children[0].Pairing.ResetScores()); + + AddRepeatStep("change scores", () => level2.Children[0].Pairing.Team1Score.Value++, 5); + AddStep("mark complete", () => level2.Children[0].Pairing.Completed.Value = true); } } } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs index dd98e92ca3..461199327e 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs @@ -1,47 +1,148 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Lines; +using osu.Framework.MathUtils; using OpenTK; namespace osu.Game.Tournament.Screens.Ladder.Components { public class DrawableMatchPairing : CompositeDrawable { - private readonly MatchPairing pairing; + public readonly MatchPairing Pairing; private readonly FillFlowContainer flow; + private DrawableMatchPairing progression; + + private readonly Path path; + + public DrawableMatchPairing Progression + { + get => progression; + set + { + if (progression == value) return; + progression = value; + + if (LoadState == LoadState.Loaded) + updateProgression(); + + path.FadeInFromZero(200); + } + } + + private Vector2 progressionStart; + private Vector2 progressionEnd; + + private const float line_width = 2; + + private void updateProgression() + { + if (progression == null) + { + path.Positions = new List(); + return; + } + + Vector2 getCenteredVector(Vector2 top, Vector2 bottom) => new Vector2(top.X, top.Y + (bottom.Y - top.Y) / 2); + + const float padding = 5; + + var start = getCenteredVector(ScreenSpaceDrawQuad.TopRight, ScreenSpaceDrawQuad.BottomRight); + var end = getCenteredVector(progression.ScreenSpaceDrawQuad.TopLeft, progression.ScreenSpaceDrawQuad.BottomLeft); + + bool progressionAbove = progression.ScreenSpaceDrawQuad.TopLeft.Y < ScreenSpaceDrawQuad.TopLeft.Y; + + if (!Precision.AlmostEquals(progressionStart, start) || !Precision.AlmostEquals(progressionEnd, end)) + { + progressionStart = start; + progressionEnd = end; + + path.Origin = progressionAbove ? Anchor.BottomLeft : Anchor.TopLeft; + path.Y = progressionAbove ? line_width : -line_width; + + Vector2 startPosition = path.ToLocalSpace(start) + new Vector2(padding, 0); + Vector2 endPosition = path.ToLocalSpace(end) + new Vector2(-padding, 0); + Vector2 intermediate1 = startPosition + new Vector2(padding, 0); + Vector2 intermediate2 = new Vector2(intermediate1.X, endPosition.Y); + + path.Positions = new List + { + startPosition, + intermediate1, + intermediate2, + endPosition + }; + } + + var destinationForWinner = progressionAbove ? progression.Pairing.Team2 : progression.Pairing.Team1; + + destinationForWinner.Value = Pairing.Winner; + } + public DrawableMatchPairing(MatchPairing pairing) { - this.pairing = pairing; + Pairing = pairing; AutoSizeAxes = Axes.Both; Margin = new MarginPadding(5); - InternalChild = flow = new FillFlowContainer + InternalChildren = new Drawable[] { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Spacing = new Vector2(2) + flow = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(2) + }, + path = new Path + { + Alpha = 0, + BypassAutoSizeAxes = Axes.Both, + Anchor = Anchor.CentreRight, + PathWidth = line_width, + }, }; pairing.Team1.BindValueChanged(_ => updateTeams()); pairing.Team2.BindValueChanged(_ => updateTeams()); + pairing.Completed.BindValueChanged(_ => updateProgression()); + updateTeams(); } + protected override void LoadComplete() + { + base.LoadComplete(); + updateTeams(); + } + + protected override void UpdateAfterAutoSize() + { + // required because the lines rely on flow being completed by other elements. + base.UpdateAfterAutoSize(); + updateProgression(); + } + private void updateTeams() { + if (LoadState != LoadState.Loaded) + return; + // todo: teams may need to be bindable for transitions at a later point. flow.Children = new[] { - new DrawableMatchTeam(pairing.Team1, pairing), - new DrawableMatchTeam(pairing.Team2, pairing) + new DrawableMatchTeam(Pairing.Team1, Pairing), + new DrawableMatchTeam(Pairing.Team2, Pairing) }; + + updateProgression(); } } } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs index 26beb5c598..b19b551a04 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs @@ -27,5 +27,13 @@ namespace osu.Game.Tournament.Screens.Ladder.Components Team1Score.ValueChanged += _ => Completed.Value = false; Team2Score.ValueChanged += _ => Completed.Value = false; } + + public TournamentTeam Winner => !Completed.Value ? null : (Team1Score.Value > Team2Score.Value ? Team1.Value : Team2.Value); + + public void ResetScores() + { + Team1Score.Value = 0; + Team2Score.Value = 0; + } } }