diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs index a7ebbfa8b3..285baad872 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs @@ -64,6 +64,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components pairing.Grouping.BindValueChanged(_ => updateWinConditions()); pairing.Completed.BindValueChanged(_ => updateProgression()); pairing.Progression.BindValueChanged(_ => updateProgression()); + pairing.LosersProgression.BindValueChanged(_ => updateProgression()); updateTeams(); } @@ -102,12 +103,21 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { var progression = Pairing.Progression?.Value; - if (progression == null) return; + if (progression != null) + { + bool progressionAbove = progression.ID < Pairing.ID; - bool progressionAbove = progression.ID < Pairing.ID; + var destinationForWinner = progressionAbove || progression.Team1.Value != null && progression.Team1.Value != Pairing.Winner ? progression.Team2 : progression.Team1; + destinationForWinner.Value = Pairing.Winner; + } - var destinationForWinner = progressionAbove ? progression.Team2 : progression.Team1; - destinationForWinner.Value = Pairing.Winner; + if ((progression = Pairing.LosersProgression?.Value) != null) + { + bool progressionAbove = progression.ID < Pairing.ID; + + var destinationForLoser = progressionAbove || progression.Team1.Value != null && progression.Team1.Value != Pairing.Winner ? progression.Team2 : progression.Team1; + destinationForLoser.Value = Pairing.Loser; + } } private void updateWinConditions() @@ -197,6 +207,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { Selected = false; Pairing.Progression.Value = null; + Pairing.LosersProgression.Value = null; Expire(); } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs index a517c5f475..c66d3fff55 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs @@ -141,6 +141,10 @@ namespace osu.Game.Tournament.Screens.Ladder.Components } else { + if (pairing.Progression.Value.Completed) + // don't allow changing scores if the match has a progression. can cause large data loss + return false; + if (score.Value > 0) score.Value--; else @@ -168,7 +172,8 @@ namespace osu.Game.Tournament.Screens.Ladder.Components return new MenuItem[] { - new OsuMenuItem("Join with", MenuItemType.Standard, () => manager.RequestJoin(pairing)), + new OsuMenuItem("Join with", MenuItemType.Standard, () => manager.RequestJoin(pairing, false)), + new OsuMenuItem("Join with (loser)", MenuItemType.Standard, () => manager.RequestJoin(pairing, true)), new OsuMenuItem("Remove", MenuItemType.Destructive, () => manager.Remove(pairing)), }; } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderInfo.cs b/osu.Game.Tournament/Screens/Ladder/Components/LadderInfo.cs index e1da676f22..ba64b974d9 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/LadderInfo.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/LadderInfo.cs @@ -8,7 +8,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components public class LadderInfo { public List Pairings = new List(); - public List<(int, int)> Progressions = new List<(int, int)>(); + public List Progressions = new List(); public List Groupings = new List(); } } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderSettings.cs b/osu.Game.Tournament/Screens/Ladder/Components/LadderSettings.cs index 379bb36c09..d8e37a5c80 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/LadderSettings.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/LadderSettings.cs @@ -23,6 +23,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components private OsuTextBox textboxTeam1; private OsuTextBox textboxTeam2; private SettingsDropdown groupingDropdown; + private PlayerCheckbox losersCheckbox; [Resolved] private LadderEditorInfo editorInfo { get; set; } = null; @@ -32,7 +33,8 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { var teamEntries = editorInfo.Teams; - var groupingOptions = editorInfo.Groupings.Select(g => new KeyValuePair(g.Name, g)).Prepend(new KeyValuePair("None", new TournamentGrouping())); + var groupingOptions = editorInfo.Groupings.Select(g => new KeyValuePair(g.Name, g)) + .Prepend(new KeyValuePair("None", new TournamentGrouping())); Children = new Drawable[] { @@ -78,6 +80,11 @@ namespace osu.Game.Tournament.Screens.Ladder.Components Bindable = new Bindable(), Items = groupingOptions }, + losersCheckbox = new PlayerCheckbox + { + LabelText = "Losers Bracket", + Bindable = new Bindable() + } // new Container // { // RelativeSizeAxes = Axes.X, @@ -111,6 +118,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components textboxTeam1.Text = selection?.Team1.Value?.Acronym; textboxTeam2.Text = selection?.Team2.Value?.Acronym; groupingDropdown.Bindable.Value = selection?.Grouping.Value ?? groupingOptions.First().Value; + losersCheckbox.Current.Value = selection?.Losers.Value ?? false; }; textboxTeam1.OnCommit = (val, newText) => @@ -131,6 +139,12 @@ namespace osu.Game.Tournament.Screens.Ladder.Components editorInfo.Selected.Value.Grouping.Value = grouping; }; + losersCheckbox.Current.ValueChanged += losers => + { + if (editorInfo.Selected.Value != null) + editorInfo.Selected.Value.Losers.Value = losers; + }; + // sliderBestOf.Bindable.ValueChanged += val => // { // if (editorInfo.Selected.Value != null) editorInfo.Selected.Value.BestOf.Value = (int)val; diff --git a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs index a97354a1c4..49a477701f 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs @@ -25,12 +25,17 @@ namespace osu.Game.Tournament.Screens.Ladder.Components public readonly Bindable Completed = new Bindable(); + public readonly Bindable Losers = new Bindable(); + [JsonIgnore] public readonly Bindable Grouping = new Bindable(); [JsonIgnore] public readonly Bindable Progression = new Bindable(); + [JsonIgnore] + public readonly Bindable LosersProgression = new Bindable(); + [JsonProperty] public Point Position; @@ -47,6 +52,9 @@ namespace osu.Game.Tournament.Screens.Ladder.Components [JsonIgnore] public TournamentTeam Winner => !Completed.Value ? null : Team1Score.Value > Team2Score.Value ? Team1.Value : Team2.Value; + [JsonIgnore] + public TournamentTeam Loser => !Completed.Value ? null : Team1Score.Value > Team2Score.Value ? Team2.Value : Team1.Value; + /// /// Remove scores from the match, in case of a false click or false start. /// diff --git a/osu.Game.Tournament/Screens/Ladder/Components/TournamentProgression.cs b/osu.Game.Tournament/Screens/Ladder/Components/TournamentProgression.cs new file mode 100644 index 0000000000..9f2d2c4045 --- /dev/null +++ b/osu.Game.Tournament/Screens/Ladder/Components/TournamentProgression.cs @@ -0,0 +1,20 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Tournament.Screens.Ladder.Components +{ + public class TournamentProgression + { + public int Item1; + public int Item2; + + public bool Losers; + + public TournamentProgression(int item1, int item2, bool losers = false) + { + Item1 = item1; + Item2 = item2; + Losers = losers; + } + } +} diff --git a/osu.Game.Tournament/Screens/Ladder/DrawableTournamentGrouping.cs b/osu.Game.Tournament/Screens/Ladder/DrawableTournamentGrouping.cs index 28e1183b27..df7f621c24 100644 --- a/osu.Game.Tournament/Screens/Ladder/DrawableTournamentGrouping.cs +++ b/osu.Game.Tournament/Screens/Ladder/DrawableTournamentGrouping.cs @@ -10,7 +10,7 @@ namespace osu.Game.Tournament.Screens.Ladder { public class DrawableTournamentGrouping : CompositeDrawable { - public DrawableTournamentGrouping(TournamentGrouping grouping) + public DrawableTournamentGrouping(TournamentGrouping grouping, bool losers = false) { AutoSizeAxes = Axes.Both; InternalChild = new FillFlowContainer @@ -27,7 +27,7 @@ namespace osu.Game.Tournament.Screens.Ladder }, new OsuSpriteText { - Text = grouping.Name.ToUpper(), + Text = ((losers ? "Losers " : "") + grouping.Name).ToUpper(), Font = "Exo2.0-Bold", Origin = Anchor.TopCentre, Anchor = Anchor.TopCentre diff --git a/osu.Game.Tournament/Screens/Ladder/LadderManager.cs b/osu.Game.Tournament/Screens/Ladder/LadderManager.cs index 95b2a0a8d1..d5ff651ef3 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderManager.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderManager.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Runtime.InteropServices; using osu.Framework.Allocation; using osu.Framework.Caching; using osu.Framework.Configuration; @@ -54,7 +53,7 @@ namespace osu.Game.Tournament.Screens.Ladder Children = new Drawable[] { paths = new Container { RelativeSizeAxes = Axes.Both }, - headings = new Container() { RelativeSizeAxes = Axes.Both }, + headings = new Container { RelativeSizeAxes = Axes.Both }, pairingsContainer = new Container { RelativeSizeAxes = Axes.Both }, } }, @@ -75,7 +74,12 @@ namespace osu.Game.Tournament.Screens.Ladder if (src == null) throw new InvalidOperationException(); if (dest != null) - src.Progression.Value = dest; + { + if (pair.Losers) + src.LosersProgression.Value = dest; + else + src.Progression.Value = dest; + } } foreach (var pairing in info.Pairings) @@ -99,10 +103,9 @@ namespace osu.Game.Tournament.Screens.Ladder return new LadderInfo { Pairings = pairings, - Progressions = pairings - .Where(p => p.Progression.Value != null) - .Select(p => (p.ID, p.Progression.Value.ID)) - .ToList(), + Progressions = pairings.Where(p => p.Progression.Value != null).Select(p => new TournamentProgression(p.ID, p.Progression.Value.ID)).Concat( + pairings.Where(p => p.LosersProgression.Value != null).Select(p => new TournamentProgression(p.ID, p.LosersProgression.Value.ID, true))) + .ToList(), Groupings = editorInfo.Groupings }; } @@ -120,7 +123,7 @@ namespace osu.Game.Tournament.Screens.Ladder { new OsuMenuItem("Create new match", MenuItemType.Highlighted, () => { - var pos = ToLocalSpace(GetContainingInputManager().CurrentState.Mouse.Position); + var pos = pairingsContainer.ToLocalSpace(GetContainingInputManager().CurrentState.Mouse.Position); addPairing(new MatchPairing { Position = new Point((int)pos.X, (int)pos.Y) }); }), }; @@ -161,7 +164,7 @@ namespace osu.Game.Tournament.Screens.Ladder foreach (var group in editorInfo.Groupings) { - var topPairing = pairingsContainer.Where(p => p.Pairing.Grouping.Value == group).OrderBy(p => p.Y).FirstOrDefault(); + var topPairing = pairingsContainer.Where(p => !p.Pairing.Losers && p.Pairing.Grouping.Value == group).OrderBy(p => p.Y).FirstOrDefault(); if (topPairing == null) continue; @@ -173,25 +176,44 @@ namespace osu.Game.Tournament.Screens.Ladder }); } + foreach (var group in editorInfo.Groupings) + { + var topPairing = pairingsContainer.Where(p => p.Pairing.Losers && p.Pairing.Grouping.Value == group).OrderBy(p => p.Y).FirstOrDefault(); + + if (topPairing == null) continue; + + headings.Add(new DrawableTournamentGrouping(group, true) + { + Position = headings.ToLocalSpace((topPairing.ScreenSpaceDrawQuad.TopLeft + topPairing.ScreenSpaceDrawQuad.TopRight) / 2), + Margin = new MarginPadding { Bottom = 10 }, + Origin = Anchor.BottomCentre, + }); + } + layout.Validate(); } - public void RequestJoin(MatchPairing pairing) => AddInternal(new JoinRequestHandler(pairingsContainer, pairing)); + public void RequestJoin(MatchPairing pairing, bool losers) => AddInternal(new JoinRequestHandler(pairingsContainer, pairing, losers)); private class JoinRequestHandler : CompositeDrawable { private readonly Container pairingsContainer; public readonly MatchPairing Source; + private readonly bool losers; private ProgressionPath path; - public JoinRequestHandler(Container pairingsContainer, MatchPairing source) + public JoinRequestHandler(Container pairingsContainer, MatchPairing source, bool losers) { this.pairingsContainer = pairingsContainer; RelativeSizeAxes = Axes.Both; Source = source; - Source.Progression.Value = null; + this.losers = losers; + if (losers) + Source.LosersProgression.Value = null; + else + Source.Progression.Value = null; } private DrawableMatchPairing findTarget(InputState state) => pairingsContainer.FirstOrDefault(d => d.ReceiveMouseInputAt(state.Mouse.NativeState.Position)); @@ -221,7 +243,13 @@ namespace osu.Game.Tournament.Screens.Ladder if (found != null) { if (found.Pairing != Source) - Source.Progression.Value = found.Pairing; + { + if (losers) + Source.LosersProgression.Value = found.Pairing; + else + Source.Progression.Value = found.Pairing; + } + Expire(); return true; }