diff --git a/osu.Game.Tournament.Tests/TestCaseLadderManager.cs b/osu.Game.Tournament.Tests/TestCaseLadderManager.cs index b53660c703..ac56803c90 100644 --- a/osu.Game.Tournament.Tests/TestCaseLadderManager.cs +++ b/osu.Game.Tournament.Tests/TestCaseLadderManager.cs @@ -21,7 +21,7 @@ namespace osu.Game.Tournament.Tests private readonly LadderManager manager; [Cached] - private Bindable conditions = new Bindable(new TournamentConditions { BestOf = 9 }); + private Bindable conditions = new Bindable(new TournamentConditions()); public TestCaseLadderManager() { diff --git a/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs b/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs index f1116b2db7..dc67807c64 100644 --- a/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs +++ b/osu.Game.Tournament.Tests/TestCaseMatchPairings.cs @@ -24,7 +24,7 @@ namespace osu.Game.Tournament.Tests }; [Cached] - private Bindable conditions = new Bindable(new TournamentConditions { BestOf = 9 }); + private Bindable conditions = new Bindable(new TournamentConditions()); public TestCaseMatchPairings() { diff --git a/osu.Game.Tournament/Components/DrawableTournamentTeam.cs b/osu.Game.Tournament/Components/DrawableTournamentTeam.cs index ec60d24c2c..016db57773 100644 --- a/osu.Game.Tournament/Components/DrawableTournamentTeam.cs +++ b/osu.Game.Tournament/Components/DrawableTournamentTeam.cs @@ -29,7 +29,7 @@ namespace osu.Game.Tournament.Components AcronymText = new OsuSpriteText { - Text = team?.Acronym.ToUpperInvariant() ?? string.Empty, + Text = team?.Acronym?.ToUpperInvariant() ?? string.Empty, Font = @"Exo2.0-Regular" }; } diff --git a/osu.Game.Tournament/Components/TournamentTeam.cs b/osu.Game.Tournament/Components/TournamentTeam.cs index 14e874e9ef..cb6fc9fb92 100644 --- a/osu.Game.Tournament/Components/TournamentTeam.cs +++ b/osu.Game.Tournament/Components/TournamentTeam.cs @@ -20,7 +20,7 @@ namespace osu.Game.Tournament.Components /// public string FlagName { - get { return flagName ?? Acronym.Substring(0, 2); } + get { return flagName ?? Acronym?.Substring(0, 2); } set { flagName = value; } } @@ -31,10 +31,12 @@ namespace osu.Game.Tournament.Components /// public string Acronym { - get { return acronym ?? FullName.Substring(0, 3); } + get { return acronym ?? FullName?.Substring(0, 3); } set { acronym = value; } } public List Players { get; set; } + + public override string ToString() => FullName ?? Acronym; } } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs index 131959892c..f6771d70b4 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchPairing.cs @@ -5,9 +5,11 @@ using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; using osu.Framework.Input.EventArgs; using osu.Framework.Input.States; using OpenTK; +using OpenTK.Graphics; using OpenTK.Input; using SixLabors.Primitives; @@ -18,6 +20,11 @@ namespace osu.Game.Tournament.Screens.Ladder.Components public readonly MatchPairing Pairing; private readonly FillFlowContainer flow; private readonly Bindable conditions = new Bindable(); + private readonly Drawable selectionBox; + private Bindable globalSelection; + + [Resolved(CanBeNull = true)] + private LadderEditorInfo editorInfo { get; set; } = null; public DrawableMatchPairing(MatchPairing pairing) { @@ -29,8 +36,20 @@ namespace osu.Game.Tournament.Screens.Ladder.Components Margin = new MarginPadding(5); - InternalChildren = new Drawable[] + InternalChildren = new[] { + selectionBox = new Container + { + CornerRadius = 5, + Masking = true, + Scale = new Vector2(1.05f), + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Alpha = 0, + Colour = Color4.YellowGreen, + Child = new Box { RelativeSizeAxes = Axes.Both } + }, flow = new FillFlowContainer { AutoSizeAxes = Axes.Both, @@ -38,13 +57,11 @@ namespace osu.Game.Tournament.Screens.Ladder.Components Spacing = new Vector2(2) } }; - pairing.Team1.BindValueChanged(_ => updateTeams()); pairing.Team2.BindValueChanged(_ => updateTeams()); - pairing.Team1Score.BindValueChanged(_ => updateWinConditions()); pairing.Team2Score.BindValueChanged(_ => updateWinConditions()); - + pairing.BestOf.BindValueChanged(_ => updateWinConditions()); pairing.Completed.BindValueChanged(_ => updateProgression()); pairing.Progression.BindValueChanged(_ => updateProgression()); @@ -60,6 +77,27 @@ namespace osu.Game.Tournament.Screens.Ladder.Components this.conditions.BindTo(conditions); } + private bool selected; + + public bool Selected + { + get => selected; + + set + { + if (value == selected) return; + selected = value; + + if (selected) + { + selectionBox.Show(); + editorInfo.Selected.Value = Pairing; + } + else + selectionBox.Hide(); + } + } + private void updateProgression() { var progression = Pairing.Progression?.Value; @@ -76,13 +114,22 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { if (conditions.Value == null) return; - Pairing.Completed.Value = Pairing.Team1Score.Value + Pairing.Team2Score.Value >= conditions.Value.BestOf; + Pairing.Completed.Value = Pairing.Team1Score.Value + Pairing.Team2Score.Value >= Pairing.BestOf.Value; } protected override void LoadComplete() { base.LoadComplete(); updateTeams(); + + if (editorInfo != null) + { + globalSelection = editorInfo.Selected.GetBoundCopy(); + globalSelection.BindValueChanged(s => + { + if (s != Pairing) Selected = false; + }); + } } private void updateTeams() @@ -109,10 +156,34 @@ namespace osu.Game.Tournament.Screens.Ladder.Components protected override bool OnDragStart(InputState state) => true; + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + { + if (Selected && editorInfo.EditingEnabled && args.Key == Key.Delete) + { + Remove(); + return true; + } + + return base.OnKeyDown(state, args); + } + + protected override bool OnClick(InputState state) + { + if (!editorInfo.EditingEnabled) + return false; + + Selected = true; + return true; + } + protected override bool OnDrag(InputState state) { if (base.OnDrag(state)) return true; + if (!editorInfo.EditingEnabled) + return false; + + Selected = true; this.MoveToOffset(state.Mouse.Delta); var pos = Position; @@ -122,10 +193,9 @@ namespace osu.Game.Tournament.Screens.Ladder.Components public void Remove() { - if (Pairing.ProgressionSource.Value != null) - Pairing.ProgressionSource.Value.Progression.Value = null; - + Selected = false; Pairing.Progression.Value = null; + Expire(); } } diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs index e74545f073..a517c5f475 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableMatchTeam.cs @@ -25,7 +25,6 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { public class DrawableMatchTeam : DrawableTournamentTeam, IHasContextMenu { - private readonly Bindable team; private readonly MatchPairing pairing; private OsuSpriteText scoreText; private Box background; @@ -39,10 +38,12 @@ namespace osu.Game.Tournament.Screens.Ladder.Components private readonly Func isWinner; private LadderManager manager; + [Resolved(CanBeNull = true)] + private LadderEditorInfo editorInfo { get; set; } = null; + public DrawableMatchTeam(Bindable team, MatchPairing pairing) : base(team) { - this.team = team.GetBoundCopy(); this.pairing = pairing; Size = new Vector2(150, 40); @@ -127,7 +128,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) { - if (Team == null) return false; + if (Team == null || editorInfo.EditingEnabled) return false; if (args.Button == MouseButton.Left) { @@ -158,12 +159,20 @@ namespace osu.Game.Tournament.Screens.Ladder.Components scoreText.Font = AcronymText.Font = winner ? "Exo2.0-Bold" : "Exo2.0-Regular"; } - public MenuItem[] ContextMenuItems => new MenuItem[] + public MenuItem[] ContextMenuItems { - new OsuMenuItem("Populate team", MenuItemType.Standard, () => team.Value = manager.Teams.Random()), - new OsuMenuItem("Join with", MenuItemType.Standard, () => manager.RequestJoin(pairing)), - new OsuMenuItem("Remove", MenuItemType.Destructive, () => manager.Remove(pairing)), - }; + get + { + if (!editorInfo.EditingEnabled) + return new MenuItem[0]; + + return new MenuItem[] + { + new OsuMenuItem("Join with", MenuItemType.Standard, () => manager.RequestJoin(pairing)), + new OsuMenuItem("Remove", MenuItemType.Destructive, () => manager.Remove(pairing)), + }; + } + } } internal static class Extensions diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderSettings.cs b/osu.Game.Tournament/Screens/Ladder/Components/LadderSettings.cs new file mode 100644 index 0000000000..9ef2ccfa0b --- /dev/null +++ b/osu.Game.Tournament/Screens/Ladder/Components/LadderSettings.cs @@ -0,0 +1,147 @@ +// 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 System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Overlays.Settings; +using osu.Game.Screens.Play.PlayerSettings; +using osu.Game.Tournament.Components; + +namespace osu.Game.Tournament.Screens.Ladder.Components +{ + public class LadderEditorSettings : PlayerSettingsGroup + { + private const int padding = 10; + + protected override string Title => @"ladder"; + + private PlayerSliderBar sliderBestOf; + + private SettingsDropdown dropdownTeam1; + private SettingsDropdown dropdownTeam2; + + [Resolved] + private LadderEditorInfo editorInfo { get; set; } = null; + + [BackgroundDependencyLoader] + private void load() + { + var teamEntries = editorInfo.Teams.Select(t => new KeyValuePair(t.ToString(), t)).Prepend(new KeyValuePair("Empty", new TournamentTeam())); + + Children = new Drawable[] + { + new PlayerCheckbox + { + Bindable = editorInfo.EditingEnabled, + LabelText = "Enable editing" + }, + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Horizontal = padding }, + Children = new Drawable[] + { + new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Text = "Team1", + }, + }, + }, + dropdownTeam1 = new SettingsDropdown + { + Items = teamEntries, + Bindable = new Bindable + { + Value = teamEntries.First().Value, + Default = teamEntries.First().Value + } + }, + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Horizontal = padding }, + Children = new Drawable[] + { + new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Text = "Team2", + }, + }, + }, + dropdownTeam2 = new SettingsDropdown + { + Items = teamEntries, + Bindable = new Bindable + { + Value = teamEntries.First().Value, + Default = teamEntries.First().Value + } + }, + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Horizontal = padding }, + Children = new Drawable[] + { + new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Text = "Best of", + }, + }, + }, + sliderBestOf = new PlayerSliderBar + { + Bindable = new BindableDouble + { + Default = 5, + Value = 5, + MinValue = 1, + MaxValue = 20, + Precision = 1, + }, + } + }; + + editorInfo.Selected.ValueChanged += selection => + { + dropdownTeam1.Bindable.Value = dropdownTeam1.Items.FirstOrDefault(i => i.Value.Acronym == selection?.Team1.Value?.Acronym).Value; + dropdownTeam2.Bindable.Value = dropdownTeam1.Items.FirstOrDefault(i => i.Value.Acronym == selection?.Team2.Value?.Acronym).Value; + sliderBestOf.Bindable.Value = selection?.BestOf ?? sliderBestOf.Bindable.Default; + }; + + dropdownTeam1.Bindable.ValueChanged += val => + { + if (editorInfo.Selected.Value != null) editorInfo.Selected.Value.Team1.Value = val.Acronym == null ? null : val; + }; + + dropdownTeam2.Bindable.ValueChanged += val => + { + if (editorInfo.Selected.Value != null) editorInfo.Selected.Value.Team2.Value = val.Acronym == null ? null : val; + }; + + sliderBestOf.Bindable.ValueChanged += val => + { + if (editorInfo.Selected.Value != null) editorInfo.Selected.Value.BestOf.Value = (int)val; + }; + + editorInfo.EditingEnabled.ValueChanged += enabled => + { + if (!enabled) editorInfo.Selected.Value = null; + }; + } + } +} diff --git a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs index 994486ffdd..772a196f37 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/MatchPairing.cs @@ -25,38 +25,16 @@ namespace osu.Game.Tournament.Screens.Ladder.Components public readonly Bindable Completed = new Bindable(); - [JsonIgnore] - public readonly Bindable Progression = new Bindable(); + public readonly BindableInt BestOf = new BindableInt(5); [JsonIgnore] - public readonly Bindable ProgressionSource = new Bindable(); + public readonly Bindable Progression = new Bindable(); [JsonProperty] public Point Position; - private MatchPairing lastProgression; // todo: fix if we ever get LastValue inside Bindable<>. - public MatchPairing() { - Progression.ValueChanged += progression => - { - if (lastProgression != null) - // clear the source from the previous progression. - lastProgression.ProgressionSource.Value = null; - - if (progression != null) - // set the source on the new progression. - progression.ProgressionSource.Value = this; - - lastProgression = progression; - }; - - ProgressionSource.ValueChanged += source => - { - if (source != null) - // ennsure no two-way progressions. - Progression.Value = null; - }; } public MatchPairing(TournamentTeam team1 = null, TournamentTeam team2 = null) diff --git a/osu.Game.Tournament/Screens/Ladder/Components/TournamentConditions.cs b/osu.Game.Tournament/Screens/Ladder/Components/TournamentConditions.cs index fd0e8cea87..045149945c 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/TournamentConditions.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/TournamentConditions.cs @@ -5,9 +5,5 @@ namespace osu.Game.Tournament.Screens.Ladder.Components /// public class TournamentConditions { - /// - /// How many matches before a winner is decided. - /// - public int BestOf; } } diff --git a/osu.Game.Tournament/Screens/Ladder/LadderManager.cs b/osu.Game.Tournament/Screens/Ladder/LadderManager.cs index e57f9b739b..6c14a4225f 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderManager.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderManager.cs @@ -1,5 +1,8 @@ +using System; using System.Collections.Generic; using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; @@ -13,15 +16,25 @@ using SixLabors.Primitives; namespace osu.Game.Tournament.Screens.Ladder { + public class LadderEditorInfo + { + public readonly BindableBool EditingEnabled = new BindableBool(); + public List Teams = new List(); + public readonly Bindable Selected = new Bindable(); + } + public class LadderManager : CompositeDrawable, IHasContextMenu { public readonly List Teams; private readonly Container pairingsContainer; private readonly Container paths; + [Cached] + private readonly LadderEditorInfo editorInfo = new LadderEditorInfo(); + public LadderManager(LadderInfo info, List teams) { - Teams = teams; + editorInfo.Teams = Teams = teams; RelativeSizeAxes = Axes.Both; @@ -32,11 +45,25 @@ namespace osu.Game.Tournament.Screens.Ladder { paths = new Container { RelativeSizeAxes = Axes.Both }, pairingsContainer = new Container { RelativeSizeAxes = Axes.Both }, + new LadderEditorSettings + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Margin = new MarginPadding(5) + } } }; foreach (var pair in info.Progressions) - info.Pairings.Single(p => p.ID == pair.Item1).Progression.Value = info.Pairings.Single(p => p.ID == pair.Item2); + { + var src = info.Pairings.FirstOrDefault(p => p.ID == pair.Item1); + var dest = info.Pairings.FirstOrDefault(p => p.ID == pair.Item2); + + if (src == null) throw new InvalidOperationException(); + + if (dest != null) + src.Progression.Value = dest; + } foreach (var pairing in info.Pairings) addPairing(pairing); @@ -58,14 +85,23 @@ namespace osu.Game.Tournament.Screens.Ladder private void addPairing(MatchPairing pairing) => pairingsContainer.Add(new DrawableMatchPairing(pairing)); - public MenuItem[] ContextMenuItems => new MenuItem[] + public MenuItem[] ContextMenuItems { - new OsuMenuItem("Create new match", MenuItemType.Highlighted, () => + get { - var pos = ToLocalSpace(GetContainingInputManager().CurrentState.Mouse.Position); - addPairing(new MatchPairing { Position = new Point((int)pos.X, (int)pos.Y) }); - }), - }; + if (!editorInfo.EditingEnabled) + return new MenuItem[0]; + + return new MenuItem[] + { + new OsuMenuItem("Create new match", MenuItemType.Highlighted, () => + { + var pos = ToLocalSpace(GetContainingInputManager().CurrentState.Mouse.Position); + addPairing(new MatchPairing { Position = new Point((int)pos.X, (int)pos.Y) }); + }), + }; + } + } protected override void Update() { @@ -79,7 +115,15 @@ namespace osu.Game.Tournament.Screens.Ladder pairing.Pairing.ID = id++; if (pairing.Pairing.Progression.Value != null) - paths.Add(new ProgressionPath(pairing, pairingsContainer.Single(p => p.Pairing == pairing.Pairing.Progression.Value))); + { + var dest = pairingsContainer.FirstOrDefault(p => p.Pairing == pairing.Pairing.Progression.Value); + + if (dest == null) + // clean up outdated progressions. + pairing.Pairing.Progression.Value = null; + else + paths.Add(new ProgressionPath(pairing, dest)); + } } } diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index 0f8d3aa2ac..88c3971ab2 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -63,8 +63,10 @@ namespace osu.Game.Overlays.Settings set { + controlWithCurrent?.Current.UnbindBindings(); bindable = value; controlWithCurrent?.Current.BindTo(bindable); + if (ShowsDefaultIndicator) { restoreDefaultButton.Bindable = bindable.GetBoundCopy();