Merge pull request #3229 from DrabWeb/multiplayer-room-settings

Multiplayer room settings
This commit is contained in:
Dan Balasescu 2018-11-26 17:07:32 +09:00 committed by GitHub
commit 033026c138
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 637 additions and 1 deletions

View File

@ -0,0 +1,119 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Testing.Input;
using osu.Game.Online.Multiplayer;
using osu.Game.Screens.Multi.Screens.Match.Settings;
using osuTK.Input;
namespace osu.Game.Tests.Visual
{
[TestFixture]
public class TestCaseRoomSettings : ManualInputManagerTestCase
{
private readonly Room room;
private readonly TestRoomSettingsOverlay overlay;
public TestCaseRoomSettings()
{
room = new Room
{
Name = { Value = "One Testing Room" },
Availability = { Value = RoomAvailability.Public },
Type = { Value = new GameTypeTeamVersus() },
MaxParticipants = { Value = 10 },
};
Add(overlay = new TestRoomSettingsOverlay(room)
{
RelativeSizeAxes = Axes.Both,
Height = 0.75f,
});
AddStep(@"show", overlay.Show);
assertAll();
AddStep(@"set name", () => overlay.CurrentName = @"Two Testing Room");
AddStep(@"set max", () => overlay.CurrentMaxParticipants = null);
AddStep(@"set availability", () => overlay.CurrentAvailability = RoomAvailability.InviteOnly);
AddStep(@"set type", () => overlay.CurrentType = new GameTypeTagTeam());
apply();
assertAll();
AddStep(@"show", overlay.Show);
AddStep(@"set room name", () => room.Name.Value = @"Room Changed Name!");
AddStep(@"set room availability", () => room.Availability.Value = RoomAvailability.Public);
AddStep(@"set room type", () => room.Type.Value = new GameTypeTag());
AddStep(@"set room max", () => room.MaxParticipants.Value = 100);
assertAll();
AddStep(@"set name", () => overlay.CurrentName = @"Unsaved Testing Room");
AddStep(@"set max", () => overlay.CurrentMaxParticipants = 20);
AddStep(@"set availability", () => overlay.CurrentAvailability = RoomAvailability.FriendsOnly);
AddStep(@"set type", () => overlay.CurrentType = new GameTypeVersus());
AddStep(@"hide", overlay.Hide);
AddWaitStep(5);
AddStep(@"show", overlay.Show);
assertAll();
AddStep(@"hide", overlay.Hide);
}
private void apply()
{
AddStep(@"apply", () =>
{
overlay.ClickApplyButton(InputManager);
});
}
private void assertAll()
{
AddAssert(@"name == room name", () => overlay.CurrentName == room.Name.Value);
AddAssert(@"max == room max", () => overlay.CurrentMaxParticipants == room.MaxParticipants.Value);
AddAssert(@"availability == room availability", () => overlay.CurrentAvailability == room.Availability.Value);
AddAssert(@"type == room type", () => Equals(overlay.CurrentType, room.Type.Value));
}
private class TestRoomSettingsOverlay : RoomSettingsOverlay
{
public string CurrentName
{
get => NameField.Text;
set => NameField.Text = value;
}
public int? CurrentMaxParticipants
{
get
{
if (int.TryParse(MaxParticipantsField.Text, out int max))
return max;
return null;
}
set => MaxParticipantsField.Text = value?.ToString();
}
public RoomAvailability CurrentAvailability
{
get => AvailabilityPicker.Current.Value;
set => AvailabilityPicker.Current.Value = value;
}
public GameType CurrentType
{
get => TypePicker.Current.Value;
set => TypePicker.Current.Value = value;
}
public TestRoomSettingsOverlay(Room room) : base(room)
{
}
public void ClickApplyButton(ManualInputManager inputManager)
{
inputManager.MoveMouseTo(ApplyButton);
inputManager.Click(MouseButton.Left);
}
}
}
}

View File

@ -14,6 +14,9 @@ namespace osu.Game.Online.Multiplayer
{
public abstract string Name { get; }
public abstract Drawable GetIcon(OsuColour colours, float size);
public override int GetHashCode() => GetType().GetHashCode();
public override bool Equals(object obj) => GetType() == obj?.GetType();
}
public class GameTypeTag : GameType

View File

@ -7,6 +7,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Online.Multiplayer;
using osu.Game.Screens.Multi.Screens.Match.Settings;
using osu.Game.Screens.Select;
using osu.Game.Users;
@ -34,11 +35,15 @@ namespace osu.Game.Screens.Multi.Screens.Match
{
this.room = room;
Header header;
RoomSettingsOverlay settings;
Info info;
Children = new Drawable[]
{
header = new Header(),
header = new Header
{
Depth = -1,
},
info = new Info
{
Margin = new MarginPadding { Top = Header.HEIGHT },
@ -48,6 +53,16 @@ namespace osu.Game.Screens.Multi.Screens.Match
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = Header.HEIGHT + Info.HEIGHT },
},
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = Header.HEIGHT },
Child = settings = new RoomSettingsOverlay(room)
{
RelativeSizeAxes = Axes.Both,
Height = 0.9f,
},
},
};
header.OnRequestSelectBeatmap = () => Push(new MatchSongSelect());
@ -59,6 +74,20 @@ namespace osu.Game.Screens.Multi.Screens.Match
info.Beatmap = b;
}, true);
header.Tabs.Current.ValueChanged += t =>
{
if (t == MatchHeaderPage.Settings)
settings.Show();
else
settings.Hide();
};
settings.StateChanged += s =>
{
if (s == Visibility.Hidden)
header.Tabs.Current.Value = MatchHeaderPage.Room;
};
nameBind.BindTo(room.Name);
nameBind.BindValueChanged(n => info.Name = n, true);

View File

@ -0,0 +1,110 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Events;
using osu.Game.Graphics;
using osu.Game.Online.Multiplayer;
using osu.Game.Screens.Multi.Components;
using osuTK;
namespace osu.Game.Screens.Multi.Screens.Match.Settings
{
public class GameTypePicker : TabControl<GameType>
{
private const float height = 40;
private const float selection_width = 3;
protected override TabItem<GameType> CreateTabItem(GameType value) => new GameTypePickerItem(value);
protected override Dropdown<GameType> CreateDropdown() => null;
public GameTypePicker()
{
Height = height + selection_width * 2;
TabContainer.Spacing = new Vector2(10 - selection_width * 2);
AddItem(new GameTypeTag());
AddItem(new GameTypeVersus());
AddItem(new GameTypeTagTeam());
AddItem(new GameTypeTeamVersus());
}
private class GameTypePickerItem : TabItem<GameType>
{
private const float transition_duration = 200;
private readonly CircularContainer hover, selection;
public GameTypePickerItem(GameType value) : base(value)
{
AutoSizeAxes = Axes.Both;
Children = new Drawable[]
{
selection = new CircularContainer
{
RelativeSizeAxes = Axes.Both,
Masking = true,
Alpha = 0,
Child = new Box
{
RelativeSizeAxes = Axes.Both,
},
},
new DrawableGameType(Value)
{
Size = new Vector2(height),
Margin = new MarginPadding(selection_width),
},
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding(selection_width),
Child = hover = new CircularContainer
{
RelativeSizeAxes = Axes.Both,
Masking = true,
Alpha = 0,
Child = new Box
{
RelativeSizeAxes = Axes.Both,
},
},
},
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
selection.Colour = colours.Yellow;
}
protected override bool OnHover(HoverEvent e)
{
hover.FadeTo(0.05f, transition_duration, Easing.OutQuint);
return base.OnHover(e);
}
protected override void OnHoverLost(HoverLostEvent e)
{
hover.FadeOut(transition_duration, Easing.OutQuint);
base.OnHoverLost(e);
}
protected override void OnActivated()
{
selection.FadeIn(transition_duration, Easing.OutQuint);
}
protected override void OnDeactivated()
{
selection.FadeOut(transition_duration, Easing.OutQuint);
}
}
}
}

View File

@ -0,0 +1,105 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Events;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.Multiplayer;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Screens.Multi.Screens.Match.Settings
{
public class RoomAvailabilityPicker : TabControl<RoomAvailability>
{
protected override TabItem<RoomAvailability> CreateTabItem(RoomAvailability value) => new RoomAvailabilityPickerItem(value);
protected override Dropdown<RoomAvailability> CreateDropdown() => null;
public RoomAvailabilityPicker()
{
RelativeSizeAxes = Axes.X;
Height = 35;
TabContainer.Spacing = new Vector2(10);
AddItem(RoomAvailability.Public);
AddItem(RoomAvailability.FriendsOnly);
AddItem(RoomAvailability.InviteOnly);
}
private class RoomAvailabilityPickerItem : TabItem<RoomAvailability>
{
private const float transition_duration = 200;
private readonly Box hover, selection;
public RoomAvailabilityPickerItem(RoomAvailability value) : base(value)
{
RelativeSizeAxes = Axes.Y;
Width = 120;
Masking = true;
CornerRadius = 5;
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.FromHex(@"3d3943"),
},
selection = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
},
hover = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.White,
Alpha = 0,
},
new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Font = @"Exo2.0-Bold",
Text = value.GetDescription(),
},
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
selection.Colour = colours.GreenLight;
}
protected override bool OnHover(HoverEvent e)
{
hover.FadeTo(0.05f, transition_duration, Easing.OutQuint);
return base.OnHover(e);
}
protected override void OnHoverLost(HoverLostEvent e)
{
hover.FadeOut(transition_duration, Easing.OutQuint);
base.OnHoverLost(e);
}
protected override void OnActivated()
{
selection.FadeIn(transition_duration, Easing.OutQuint);
}
protected override void OnDeactivated()
{
selection.FadeOut(transition_duration, Easing.OutQuint);
}
}
}
}

View File

@ -0,0 +1,270 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.Multiplayer;
using osu.Game.Overlays.SearchableList;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Screens.Multi.Screens.Match.Settings
{
public class RoomSettingsOverlay : FocusedOverlayContainer
{
private const float transition_duration = 350;
private const float field_padding = 45;
private readonly Bindable<string> nameBind = new Bindable<string>();
private readonly Bindable<RoomAvailability> availabilityBind = new Bindable<RoomAvailability>();
private readonly Bindable<GameType> typeBind = new Bindable<GameType>();
private readonly Bindable<int?> maxParticipantsBind = new Bindable<int?>();
private readonly Container content;
private readonly OsuSpriteText typeLabel;
protected readonly OsuTextBox NameField, MaxParticipantsField;
protected readonly RoomAvailabilityPicker AvailabilityPicker;
protected readonly GameTypePicker TypePicker;
protected readonly TriangleButton ApplyButton;
public RoomSettingsOverlay(Room room)
{
Masking = true;
Child = content = new Container
{
RelativeSizeAxes = Axes.Both,
RelativePositionAxes = Axes.Y,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.FromHex(@"28242d"),
},
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = 35, Bottom = 75, Horizontal = SearchableListOverlay.WIDTH_PADDING },
Children = new[]
{
new SectionContainer
{
Padding = new MarginPadding { Right = field_padding / 2 },
Children = new[]
{
new Section("ROOM NAME")
{
Child = NameField = new SettingsTextBox
{
RelativeSizeAxes = Axes.X,
TabbableContentContainer = this,
OnCommit = (sender, text) => apply(),
},
},
new Section("ROOM VISIBILITY")
{
Child = AvailabilityPicker = new RoomAvailabilityPicker(),
},
new Section("GAME TYPE")
{
Child = new FillFlowContainer
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Direction = FillDirection.Vertical,
Spacing = new Vector2(7),
Children = new Drawable[]
{
TypePicker = new GameTypePicker
{
RelativeSizeAxes = Axes.X,
},
typeLabel = new OsuSpriteText
{
TextSize = 14,
},
},
},
},
},
},
new SectionContainer
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Padding = new MarginPadding { Left = field_padding / 2 },
Children = new[]
{
new Section("MAX PARTICIPANTS")
{
Child = MaxParticipantsField = new SettingsNumberTextBox
{
RelativeSizeAxes = Axes.X,
TabbableContentContainer = this,
OnCommit = (sender, text) => apply(),
},
},
new Section("PASSWORD (OPTIONAL)")
{
Child = new SettingsPasswordTextBox
{
RelativeSizeAxes = Axes.X,
TabbableContentContainer = this,
OnCommit = (sender, text) => apply(),
},
},
},
},
},
},
ApplyButton = new ApplySettingsButton
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
Size = new Vector2(230, 35),
Margin = new MarginPadding { Bottom = 20 },
Action = apply,
},
},
};
TypePicker.Current.ValueChanged += t => typeLabel.Text = t.Name;
nameBind.ValueChanged += n => NameField.Text = n;
availabilityBind.ValueChanged += a => AvailabilityPicker.Current.Value = a;
typeBind.ValueChanged += t => TypePicker.Current.Value = t;
maxParticipantsBind.ValueChanged += m => MaxParticipantsField.Text = m?.ToString();
nameBind.BindTo(room.Name);
availabilityBind.BindTo(room.Availability);
typeBind.BindTo(room.Type);
maxParticipantsBind.BindTo(room.MaxParticipants);
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
typeLabel.Colour = colours.Yellow;
}
protected override void PopIn()
{
// reapply the rooms values if the overlay was completely closed
if (content.Y == -1)
{
nameBind.TriggerChange();
availabilityBind.TriggerChange();
typeBind.TriggerChange();
maxParticipantsBind.TriggerChange();
}
content.MoveToY(0, transition_duration, Easing.OutQuint);
}
protected override void PopOut()
{
content.MoveToY(-1, transition_duration, Easing.InSine);
}
private void apply()
{
nameBind.Value = NameField.Text;
availabilityBind.Value = AvailabilityPicker.Current.Value;
typeBind.Value = TypePicker.Current.Value;
if (int.TryParse(MaxParticipantsField.Text, out int max))
maxParticipantsBind.Value = max;
else
maxParticipantsBind.Value = null;
Hide();
}
private class SettingsTextBox : OsuTextBox
{
protected override Color4 BackgroundUnfocused => Color4.Black;
protected override Color4 BackgroundFocused => Color4.Black;
}
private class SettingsNumberTextBox : SettingsTextBox
{
protected override bool CanAddCharacter(char character) => char.IsNumber(character);
}
private class SettingsPasswordTextBox : OsuPasswordTextBox
{
protected override Color4 BackgroundUnfocused => Color4.Black;
protected override Color4 BackgroundFocused => Color4.Black;
}
private class SectionContainer : FillFlowContainer<Section>
{
public SectionContainer()
{
RelativeSizeAxes = Axes.Both;
Width = 0.5f;
Direction = FillDirection.Vertical;
Spacing = new Vector2(field_padding);
}
}
private class Section : Container
{
private readonly Container content;
protected override Container<Drawable> Content => content;
public Section(string title)
{
AutoSizeAxes = Axes.Y;
RelativeSizeAxes = Axes.X;
InternalChild = new FillFlowContainer
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Direction = FillDirection.Vertical,
Spacing = new Vector2(5),
Children = new Drawable[]
{
new OsuSpriteText
{
TextSize = 12,
Font = @"Exo2.0-Bold",
Text = title.ToUpper(),
},
content = new Container
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
},
},
};
}
}
private class ApplySettingsButton : TriangleButton
{
public ApplySettingsButton()
{
Text = "Apply";
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
BackgroundColour = colours.Yellow;
Triangles.ColourLight = colours.YellowLight;
Triangles.ColourDark = colours.YellowDark;
}
}
}
}