diff --git a/osu.Desktop.VisualTests/Tests/TestCaseDrawableRoom.cs b/osu.Desktop.VisualTests/Tests/TestCaseDrawableRoom.cs new file mode 100644 index 0000000000..de58323abe --- /dev/null +++ b/osu.Desktop.VisualTests/Tests/TestCaseDrawableRoom.cs @@ -0,0 +1,74 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics; +using osu.Framework.Testing; +using osu.Game.Screens.Multiplayer; +using osu.Game.Online.Multiplayer; +using osu.Game.Users; +using osu.Game.Database; + +namespace osu.Desktop.VisualTests.Tests +{ + internal class TestCaseDrawableRoom : TestCase + { + public override string Description => @"Select your favourite room"; + + public override void Reset() + { + base.Reset(); + + DrawableRoom first; + DrawableRoom second; + Add(new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Y, + Width = 500f, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + first = new DrawableRoom(new Room()), + second = new DrawableRoom(new Room()), + } + }); + + first.Room.Name.Value = @"Great Room Right Here"; + first.Room.Host.Value = new User { Username = @"Naeferith", Id = 9492835, Country = new Country { FlagName = @"FR" }}; + first.Room.Status.Value = new RoomStatusOpen(); + first.Room.Beatmap.Value = new BeatmapMetadata { Title = @"Seiryu", Artist = @"Critical Crystal" }; + + second.Room.Name.Value = @"Relax It's The Weekend"; + second.Room.Host.Value = new User { Username = @"peppy", Id = 2, Country = new Country { FlagName = @"AU" }}; + second.Room.Status.Value = new RoomStatusPlaying(); + second.Room.Beatmap.Value = new BeatmapMetadata { Title = @"ZAQ", Artist = @"Serendipity" }; + + AddStep(@"change state", () => + { + first.Room.Status.Value = new RoomStatusPlaying(); + }); + + AddStep(@"change name", () => + { + first.Room.Name.Value = @"I Changed Name"; + }); + + AddStep(@"change host", () => + { + first.Room.Host.Value = new User { Username = @"DrabWeb", Id = 6946022, Country = new Country { FlagName = @"CA" } }; + }); + + AddStep(@"change beatmap", () => + { + first.Room.Beatmap.Value = null; + }); + + AddStep(@"change state", () => + { + first.Room.Status.Value = new RoomStatusOpen(); + }); + } + } +} diff --git a/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj index 68dc8bb7b8..7b7997063b 100644 --- a/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj +++ b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj @@ -220,6 +220,7 @@ + diff --git a/osu.Game.Rulesets.Osu/Objects/Spinner.cs b/osu.Game.Rulesets.Osu/Objects/Spinner.cs index eff60ba935..6ba499739a 100644 --- a/osu.Game.Rulesets.Osu/Objects/Spinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Spinner.cs @@ -24,6 +24,9 @@ namespace osu.Game.Rulesets.Osu.Objects base.ApplyDefaults(controlPointInfo, difficulty); SpinsRequired = (int)(Duration / 1000 * BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 3, 5, 7.5)); + + // spinning doesn't match 1:1 with stable, so let's fudge them easier for the time being. + SpinsRequired = (int)(SpinsRequired * 0.6); } } } diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs index 5cca57be8a..9a19819af8 100644 --- a/osu.Game/Graphics/Backgrounds/Triangles.cs +++ b/osu.Game/Graphics/Backgrounds/Triangles.cs @@ -1,20 +1,29 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.Linq; -using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using osu.Framework.MathUtils; using OpenTK; using OpenTK.Graphics; using System; +using osu.Framework.Graphics.OpenGL; +using osu.Framework.Graphics.Shaders; +using osu.Framework.Graphics.Textures; +using OpenTK.Graphics.ES30; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Allocation; +using System.Collections.Generic; +using osu.Framework.Graphics.Batches; +using osu.Framework.Lists; namespace osu.Game.Graphics.Backgrounds { - public class Triangles : Container + public class Triangles : Drawable { + private const float triangle_size = 100; + public override bool HandleInput => false; public Color4 ColourLight = Color4.White; @@ -49,6 +58,28 @@ namespace osu.Game.Graphics.Backgrounds /// public float Velocity = 1; + private readonly SortedList parts = new SortedList(Comparer.Default); + + private Shader shader; + private readonly Texture texture; + + public Triangles() + { + texture = Texture.WhitePixel; + } + + [BackgroundDependencyLoader] + private void load(ShaderManager shaders) + { + shader = shaders?.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE_ROUNDED); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + addTriangles(true); + } + public float TriangleScale { get { return triangleScale; } @@ -57,42 +88,71 @@ namespace osu.Game.Graphics.Backgrounds float change = value / triangleScale; triangleScale = value; - if (change != 1) - Children.ForEach(t => t.Scale *= change); + for (int i = 0; i < parts.Count; i++) + { + TriangleParticle newParticle = parts[i]; + newParticle.Scale *= change; + parts[i] = newParticle; + } } } - protected override void LoadComplete() - { - base.LoadComplete(); - - addTriangles(true); - } - - private int aimTriangleCount => (int)(DrawWidth * DrawHeight * 0.002f / (triangleScale * triangleScale) * SpawnRatio); - protected override void Update() { base.Update(); - float adjustedAlpha = HideAlphaDiscrepancies ? - // Cubically scale alpha to make it drop off more sharply. - (float)Math.Pow(DrawInfo.Colour.AverageColour.Linear.A, 3) : - 1; + Invalidate(Invalidation.DrawNode, shallPropagate: false); - foreach (var t in Children) + for (int i = 0; i < parts.Count; i++) { - t.Alpha = adjustedAlpha; - t.Position -= new Vector2(0, (float)(t.Scale.X * (50 / DrawHeight) * (Time.Elapsed / 950)) / triangleScale * Velocity); - if (ExpireOffScreenTriangles && t.DrawPosition.Y + t.DrawSize.Y * t.Scale.Y < 0) - t.Expire(); + TriangleParticle newParticle = parts[i]; + + float adjustedAlpha = HideAlphaDiscrepancies ? + // Cubically scale alpha to make it drop off more sharply. + (float)Math.Pow(DrawInfo.Colour.AverageColour.Linear.A, 3) : + 1; + + + newParticle.Position += new Vector2(0, -(parts[i].Scale * (50 / DrawHeight)) / triangleScale * Velocity) * ((float)Time.Elapsed / 950); + newParticle.Colour.A = adjustedAlpha; + + parts[i] = newParticle; + + if (!CreateNewTriangles) + continue; + + float bottomPos = parts[i].Position.Y + triangle_size * parts[i].Scale * 0.866f / DrawHeight; + + if (bottomPos < 0) + parts.RemoveAt(i); } - if (CreateNewTriangles) - addTriangles(false); + addTriangles(false); } - protected virtual Triangle CreateTriangle() + private void addTriangles(bool randomY) + { + int aimTriangleCount = (int)(DrawWidth * DrawHeight * 0.002f / (triangleScale * triangleScale) * SpawnRatio); + + for (int i = 0; i < aimTriangleCount - parts.Count; i++) + parts.Add(createTriangle(randomY)); + } + + private TriangleParticle createTriangle(bool randomY) + { + TriangleParticle particle = CreateTriangle(); + + particle.Position = new Vector2(RNG.NextSingle(), randomY ? RNG.NextSingle() : 1); + particle.Colour = CreateTriangleShade(); + + return particle; + } + + /// + /// Creates a triangle particle with a random scale. + /// + /// The triangle particle. + protected virtual TriangleParticle CreateTriangle() { const float std_dev = 0.16f; const float mean = 0.5f; @@ -102,32 +162,100 @@ namespace osu.Game.Graphics.Backgrounds float randStdNormal = (float)(Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2)); //random normal(0,1) var scale = Math.Max(triangleScale * (mean + std_dev * randStdNormal), 0.1f); //random normal(mean,stdDev^2) - const float size = 100; - - return new EquilateralTriangle - { - Origin = Anchor.TopCentre, - RelativePositionAxes = Axes.Both, - Size = new Vector2(size), - Scale = new Vector2(scale), - EdgeSmoothness = new Vector2(1), - Colour = GetTriangleShade(), - Depth = scale, - }; + return new TriangleParticle { Scale = scale }; } - protected virtual Color4 GetTriangleShade() => Interpolation.ValueAt(RNG.NextSingle(), ColourDark, ColourLight, 0, 1); + /// + /// Creates a shade of colour for the triangles. + /// + /// The colour. + protected virtual Color4 CreateTriangleShade() => Interpolation.ValueAt(RNG.NextSingle(), ColourDark, ColourLight, 0, 1); - private void addTriangles(bool randomY) + protected override DrawNode CreateDrawNode() => new TrianglesDrawNode(); + + private readonly TrianglesDrawNodeSharedData sharedData = new TrianglesDrawNodeSharedData(); + protected override void ApplyDrawNode(DrawNode node) { - int addCount = aimTriangleCount - Children.Count(); - for (int i = 0; i < addCount; i++) + base.ApplyDrawNode(node); + + var trianglesNode = (TrianglesDrawNode)node; + + trianglesNode.Shader = shader; + trianglesNode.Texture = texture; + trianglesNode.Size = DrawSize; + trianglesNode.Shared = sharedData; + + trianglesNode.Parts.Clear(); + trianglesNode.Parts.AddRange(parts); + } + + private class TrianglesDrawNodeSharedData + { + public readonly LinearBatch VertexBatch = new LinearBatch(100 * 3, 10, PrimitiveType.Triangles); + } + + private class TrianglesDrawNode : DrawNode + { + public Shader Shader; + public Texture Texture; + + public TrianglesDrawNodeSharedData Shared; + + public readonly List Parts = new List(); + public Vector2 Size; + + public override void Draw(Action vertexAction) { - var sprite = CreateTriangle(); - float triangleHeight = sprite.DrawHeight / DrawHeight; - sprite.Position = new Vector2(RNG.NextSingle(), randomY ? RNG.NextSingle() * (1 + triangleHeight) - triangleHeight : 1); - Add(sprite); + base.Draw(vertexAction); + + Shader.Bind(); + Texture.TextureGL.Bind(); + + foreach (TriangleParticle particle in Parts) + { + var offset = new Vector2(particle.Scale * 0.5f, particle.Scale * 0.866f); + + var triangle = new Triangle( + particle.Position * Size * DrawInfo.Matrix, + (particle.Position * Size + offset * triangle_size) * DrawInfo.Matrix, + (particle.Position * Size + new Vector2(-offset.X, offset.Y) * triangle_size) * DrawInfo.Matrix + ); + + ColourInfo colourInfo = DrawInfo.Colour; + colourInfo.ApplyChild(particle.Colour); + + Texture.DrawTriangle(triangle, colourInfo, null, Shared.VertexBatch.Add); + } + + Shader.Unbind(); } } + + protected struct TriangleParticle : IComparable + { + /// + /// The position of the top vertex of the triangle. + /// + public Vector2 Position; + + /// + /// The colour of the triangle. + /// + public Color4 Colour; + + /// + /// The scale of the triangle. + /// + public float Scale; + + /// + /// Compares two s. This is a reverse comparer because when the + /// triangles are added to the particles list, they should be drawn from largest to smallest + /// such that the smaller triangles appear on top. + /// + /// + /// + public int CompareTo(TriangleParticle other) => other.Scale.CompareTo(Scale); + } } } diff --git a/osu.Game/Graphics/UserInterface/OsuDropdown.cs b/osu.Game/Graphics/UserInterface/OsuDropdown.cs index 9c1799c04c..14483f3bfb 100644 --- a/osu.Game/Graphics/UserInterface/OsuDropdown.cs +++ b/osu.Game/Graphics/UserInterface/OsuDropdown.cs @@ -42,7 +42,7 @@ namespace osu.Game.Graphics.UserInterface protected override DropdownMenuItem CreateMenuItem(string text, T value) => new OsuDropdownMenuItem(text, value) { AccentColour = AccentColour }; - private class OsuDropdownMenuItem : DropdownMenuItem + public class OsuDropdownMenuItem : DropdownMenuItem { public OsuDropdownMenuItem(string text, T current) : base(text, current) { @@ -60,7 +60,7 @@ namespace osu.Game.Graphics.UserInterface AutoSizeAxes = Axes.Y, Children = new Drawable[] { - chevron = new TextAwesome + Chevron = new TextAwesome { AlwaysPresent = true, Icon = FontAwesome.fa_chevron_right, @@ -84,12 +84,12 @@ namespace osu.Game.Graphics.UserInterface private Color4? accentColour; - private readonly TextAwesome chevron; + protected readonly TextAwesome Chevron; protected override void FormatForeground(bool hover = false) { base.FormatForeground(hover); - chevron.Alpha = hover ? 1 : 0; + Chevron.Alpha = hover ? 1 : 0; } public Color4 AccentColour @@ -115,11 +115,11 @@ namespace osu.Game.Graphics.UserInterface public class OsuDropdownHeader : DropdownHeader { - private readonly SpriteText label; + protected readonly SpriteText Text; protected override string Label { - get { return label.Text; } - set { label.Text = value; } + get { return Text.Text; } + set { Text.Text = value; } } protected readonly TextAwesome Icon; @@ -146,7 +146,7 @@ namespace osu.Game.Graphics.UserInterface Foreground.Children = new Drawable[] { - label = new OsuSpriteText + Text = new OsuSpriteText { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, diff --git a/osu.Game/Online/Multiplayer/Room.cs b/osu.Game/Online/Multiplayer/Room.cs new file mode 100644 index 0000000000..c82025f902 --- /dev/null +++ b/osu.Game/Online/Multiplayer/Room.cs @@ -0,0 +1,17 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Configuration; +using osu.Game.Database; +using osu.Game.Users; + +namespace osu.Game.Online.Multiplayer +{ + public class Room + { + public Bindable Name = new Bindable(); + public Bindable Host = new Bindable(); + public Bindable Status = new Bindable(); + public Bindable Beatmap = new Bindable(); + } +} diff --git a/osu.Game/Online/Multiplayer/RoomStatus.cs b/osu.Game/Online/Multiplayer/RoomStatus.cs new file mode 100644 index 0000000000..4f943596a7 --- /dev/null +++ b/osu.Game/Online/Multiplayer/RoomStatus.cs @@ -0,0 +1,26 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK.Graphics; +using osu.Game.Graphics; + +namespace osu.Game.Online.Multiplayer +{ + public abstract class RoomStatus + { + public abstract string Message { get; } + public abstract Color4 GetAppropriateColour(OsuColour colours); + } + + public class RoomStatusOpen : RoomStatus + { + public override string Message => @"Welcoming Players"; + public override Color4 GetAppropriateColour(OsuColour colours) => colours.GreenLight; + } + + public class RoomStatusPlaying : RoomStatus + { + public override string Message => @"Now Playing"; + public override Color4 GetAppropriateColour(OsuColour colours) => colours.Purple; + } +} diff --git a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs index d94388ed87..561f81d6c3 100644 --- a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs @@ -13,15 +13,23 @@ using osu.Game.Online.API; using OpenTK; using osu.Framework.Input; using osu.Game.Users; +using System.ComponentModel; +using osu.Game.Graphics; +using OpenTK.Graphics; +using osu.Framework.Extensions.Color4Extensions; + +using Container = osu.Framework.Graphics.Containers.Container; namespace osu.Game.Overlays.Settings.Sections.General { - public class LoginSettings : SettingsSubsection, IOnlineComponent + public class LoginSettings : FillFlowContainer, IOnlineComponent { private bool bounding = true; private LoginForm form; + private OsuColour colours; - protected override string Header => "Account"; + private UserPanel panel; + private UserDropdown dropdown; public override RectangleF BoundingBox => bounding ? base.BoundingBox : RectangleF.Empty; @@ -35,9 +43,18 @@ namespace osu.Game.Overlays.Settings.Sections.General } } - [BackgroundDependencyLoader(permitNulls: true)] - private void load(APIAccess api) + public LoginSettings() { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Direction = FillDirection.Vertical; + Spacing = new Vector2(0f, 5f); + } + + [BackgroundDependencyLoader(permitNulls: true)] + private void load(OsuColour colours, APIAccess api) + { + this.colours = colours; api?.Register(this); } @@ -50,6 +67,12 @@ namespace osu.Game.Overlays.Settings.Sections.General case APIState.Offline: Children = new Drawable[] { + new OsuSpriteText + { + Text = "ACCOUNT", + Margin = new MarginPadding { Bottom = 5 }, + Font = @"Exo2.0-Black", + }, form = new LoginForm() }; break; @@ -67,24 +90,73 @@ namespace osu.Game.Overlays.Settings.Sections.General { new OsuSpriteText { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, Text = "Connecting...", + Margin = new MarginPadding { Top = 10, Bottom = 10 }, }, }; break; case APIState.Online: Children = new Drawable[] { - new UserPanel(api.LocalUser.Value) + new FillFlowContainer { RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Left = 20, Right = 20 }, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0f, 10f), + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new[] + { + new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = "Signed in", + TextSize = 18, + Font = @"Exo2.0-Bold", + Margin = new MarginPadding { Top = 5, Bottom = 5 }, + }, + }, + }, + panel = new UserPanel(api.LocalUser.Value) { RelativeSizeAxes = Axes.X }, + dropdown = new UserDropdown { RelativeSizeAxes = Axes.X }, + }, }, - new OsuButton + }; + + panel.Status.BindTo(api.LocalUser.Value.Status); + + dropdown.Current.ValueChanged += newValue => + { + switch (newValue) { - RelativeSizeAxes = Axes.X, - Text = "Sign out", - Action = api.Logout + case UserAction.Online: + api.LocalUser.Value.Status.Value = new UserStatusOnline(); + dropdown.StatusColour = colours.Green; + break; + case UserAction.DoNotDisturb: + api.LocalUser.Value.Status.Value = new UserStatusDoNotDisturb(); + dropdown.StatusColour = colours.Red; + break; + case UserAction.AppearOffline: + api.LocalUser.Value.Status.Value = new UserStatusOffline(); + dropdown.StatusColour = colours.Gray7; + break; + case UserAction.SignOut: + api.Logout(); + break; } }; + dropdown.Current.TriggerChange(); + break; } @@ -171,5 +243,119 @@ namespace osu.Game.Overlays.Settings.Sections.General return base.OnFocus(state); } } + + private class UserDropdown : OsuEnumDropdown + { + protected override DropdownHeader CreateHeader() => new UserDropdownHeader { AccentColour = AccentColour }; + protected override Menu CreateMenu() => new UserDropdownMenu(); + protected override DropdownMenuItem CreateMenuItem(string text, UserAction value) => new UserDropdownMenuItem(text, value) { AccentColour = AccentColour }; + + public Color4 StatusColour + { + set + { + var h = Header as UserDropdownHeader; + if (h == null) return; + h.StatusColour = value; + } + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + AccentColour = colours.Gray5; + } + + private class UserDropdownHeader : OsuDropdownHeader + { + public const float LABEL_LEFT_MARGIN = 20; + + private readonly TextAwesome statusIcon; + public Color4 StatusColour + { + set + { + statusIcon.FadeColour(value, 500, EasingTypes.OutQuint); + } + } + + public UserDropdownHeader() + { + Foreground.Padding = new MarginPadding { Left = 10, Right = 10 }; + Margin = new MarginPadding { Bottom = 5 }; + Masking = true; + CornerRadius = 5; + EdgeEffect = new EdgeEffect + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(0.25f), + Radius = 4, + }; + + Icon.TextSize = 14; + Icon.Margin = new MarginPadding(0); + + Foreground.Add(statusIcon = new TextAwesome + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Icon = FontAwesome.fa_circle_o, + TextSize = 14, + }); + + Text.Margin = new MarginPadding { Left = LABEL_LEFT_MARGIN }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + BackgroundColour = colours.Gray3; + } + } + + private class UserDropdownMenu : OsuMenu + { + public UserDropdownMenu() + { + Margin = new MarginPadding { Bottom = 5 }; + CornerRadius = 5; + ItemsContainer.Padding = new MarginPadding(0); + Masking = true; + EdgeEffect = new EdgeEffect + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(0.25f), + Radius = 4, + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Background.Colour = colours.Gray3; + } + } + + private class UserDropdownMenuItem : OsuDropdownMenuItem + { + public UserDropdownMenuItem(string text, UserAction current) : base(text, current) + { + Foreground.Padding = new MarginPadding { Top = 5, Bottom = 5, Left = UserDropdownHeader.LABEL_LEFT_MARGIN, Right = 5 }; + Chevron.Margin = new MarginPadding { Left = 2, Right = 3 }; + CornerRadius = 5; + } + } + } + + private enum UserAction + { + Online, + [Description(@"Do not disturb")] + DoNotDisturb, + [Description(@"Appear offline")] + AppearOffline, + [Description(@"Sign out")] + SignOut, + } } } diff --git a/osu.Game/Screens/Menu/MenuSideFlashes.cs b/osu.Game/Screens/Menu/MenuSideFlashes.cs index 0cf1fa54fa..77239726e8 100644 --- a/osu.Game/Screens/Menu/MenuSideFlashes.cs +++ b/osu.Game/Screens/Menu/MenuSideFlashes.cs @@ -36,6 +36,8 @@ namespace osu.Game.Screens.Menu public MenuSideFlashes() { + EarlyActivationMilliseconds = box_fade_in_time; + RelativeSizeAxes = Axes.Both; Anchor = Anchor.Centre; Origin = Anchor.Centre; diff --git a/osu.Game/Screens/Multiplayer/DrawableRoom.cs b/osu.Game/Screens/Multiplayer/DrawableRoom.cs new file mode 100644 index 0000000000..7365963085 --- /dev/null +++ b/osu.Game/Screens/Multiplayer/DrawableRoom.cs @@ -0,0 +1,259 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Localisation; +using osu.Game.Database; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Online.Multiplayer; +using osu.Game.Users; + +namespace osu.Game.Screens.Multiplayer +{ + public class DrawableRoom : ClickableContainer + { + private const float content_padding = 5; + private const float height = 90; + + private readonly Box sideStrip; + private readonly UpdateableAvatar avatar; + private readonly OsuSpriteText name; + private readonly Container flagContainer; + private readonly OsuSpriteText host; + private readonly OsuSpriteText rankBounds; + private readonly OsuSpriteText status; + private readonly FillFlowContainer beatmapInfoFlow; + private readonly OsuSpriteText beatmapTitle; + private readonly OsuSpriteText beatmapDash; + private readonly OsuSpriteText beatmapArtist; + + private OsuColour colours; + private LocalisationEngine localisation; + + public readonly Room Room; + + public DrawableRoom(Room room) + { + Room = room; + + RelativeSizeAxes = Axes.X; + Height = height; + CornerRadius = 5; + Masking = true; + EdgeEffect = new EdgeEffect + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(40), + Radius = 5, + }; + + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.Gray(34), + }, + sideStrip = new Box + { + RelativeSizeAxes = Axes.Y, + Width = content_padding, + }, + avatar = new UpdateableAvatar + { + Size = new Vector2(Height - content_padding* 2), + Masking = true, + CornerRadius = 5f, + Margin = new MarginPadding { Left = content_padding * 2, Top = content_padding }, + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Top = content_padding, Bottom = content_padding, Left = Height + content_padding * 2, Right = content_padding }, + Children = new Drawable[] + { + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(5f), + Children = new Drawable[] + { + name = new OsuSpriteText + { + TextSize = 18, + }, + new Container + { + RelativeSizeAxes = Axes.X, + Height = 20f, + Children = new Drawable[] + { + new FillFlowContainer + { + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(5f, 0f), + Children = new Drawable[] + { + flagContainer = new Container + { + Width = 30f, + RelativeSizeAxes = Axes.Y, + }, + new Container + { + Width = 40f, + RelativeSizeAxes = Axes.Y, + }, + new OsuSpriteText + { + Text = "hosted by", + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + TextSize = 14, + }, + host = new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + TextSize = 14, + Font = @"Exo2.0-BoldItalic", + }, + }, + }, + rankBounds = new OsuSpriteText + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Text = "#0 - #0", + TextSize = 14, + Margin = new MarginPadding { Right = 10 }, + }, + }, + }, + }, + }, + new FillFlowContainer + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Margin = new MarginPadding { Bottom = content_padding }, + Children = new Drawable[] + { + status = new OsuSpriteText + { + TextSize = 14, + Font = @"Exo2.0-Bold", + }, + beatmapInfoFlow = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Horizontal, + Children = new[] + { + beatmapTitle = new OsuSpriteText + { + TextSize = 14, + Font = @"Exo2.0-BoldItalic", + }, + beatmapDash = new OsuSpriteText + { + TextSize = 14, + Font = @"Exo2.0-RegularItalic", + }, + beatmapArtist = new OsuSpriteText + { + TextSize = 14, + Font = @"Exo2.0-RegularItalic", + }, + }, + }, + }, + }, + }, + }, + }; + + Room.Name.ValueChanged += displayName; + Room.Host.ValueChanged += displayUser; + Room.Status.ValueChanged += displayStatus; + Room.Beatmap.ValueChanged += displayBeatmap; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours, LocalisationEngine localisation) + { + this.localisation = localisation; + this.colours = colours; + + beatmapInfoFlow.Colour = rankBounds.Colour = colours.Gray9; + host.Colour = colours.Blue; + + displayStatus(Room.Status.Value); + } + + private void displayName(string value) + { + name.Text = value; + } + + private void displayUser(User value) + { + avatar.User = value; + host.Text = value.Username; + flagContainer.Children = new[] { new DrawableFlag(value.Country?.FlagName ?? @"__") { RelativeSizeAxes = Axes.Both } }; + } + + private void displayStatus(RoomStatus value) + { + if (value == null) return; + status.Text = value.Message; + + foreach (Drawable d in new Drawable[] { sideStrip, status }) + d.FadeColour(value.GetAppropriateColour(colours), 100); + } + + private void displayBeatmap(BeatmapMetadata value) + { + if (value != null) + { + beatmapTitle.Current = localisation.GetUnicodePreference(value.TitleUnicode, value.Title); + beatmapDash.Text = @" - "; + beatmapArtist.Current = localisation.GetUnicodePreference(value.ArtistUnicode, value.Artist); + } + else + { + beatmapTitle.Current = null; + beatmapArtist.Current = null; + + beatmapTitle.Text = @"Changing map"; + beatmapDash.Text = string.Empty; + beatmapArtist.Text = string.Empty; + } + } + + protected override void Dispose(bool isDisposing) + { + Room.Name.ValueChanged -= displayName; + Room.Host.ValueChanged -= displayUser; + Room.Status.ValueChanged -= displayStatus; + Room.Beatmap.ValueChanged -= displayBeatmap; + + base.Dispose(isDisposing); + } + } +} diff --git a/osu.Game/Users/User.cs b/osu.Game/Users/User.cs index 1361eefcff..93933c8fe9 100644 --- a/osu.Game/Users/User.cs +++ b/osu.Game/Users/User.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using Newtonsoft.Json; +using osu.Framework.Configuration; namespace osu.Game.Users { @@ -19,6 +20,8 @@ namespace osu.Game.Users [JsonProperty(@"country")] public Country Country; + public Bindable Status = new Bindable(); + //public Team Team; [JsonProperty(@"profile_colour")] diff --git a/osu.Game/Users/UserPanel.cs b/osu.Game/Users/UserPanel.cs index c78a69dac8..bdfe6d1c8e 100644 --- a/osu.Game/Users/UserPanel.cs +++ b/osu.Game/Users/UserPanel.cs @@ -158,14 +158,19 @@ namespace osu.Game.Users }, }, }; - - Status.ValueChanged += displayStatus; } [BackgroundDependencyLoader] private void load(OsuColour colours) { this.colours = colours; + Status.ValueChanged += displayStatus; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + Status.TriggerChange(); } private void displayStatus(UserStatus status) diff --git a/osu.Game/Users/UserStatus.cs b/osu.Game/Users/UserStatus.cs index dcb5ccbd8f..461008db0f 100644 --- a/osu.Game/Users/UserStatus.cs +++ b/osu.Game/Users/UserStatus.cs @@ -58,4 +58,10 @@ namespace osu.Game.Users public override string Message => @"Modding a map"; public override Color4 GetAppropriateColour(OsuColour colours) => colours.PurpleDark; } + + public class UserStatusDoNotDisturb : UserStatus + { + public override string Message => @"Do not disturb"; + public override Color4 GetAppropriateColour(OsuColour colours) => colours.RedDark; + } } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 98fb4b9707..25b692151f 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -432,6 +432,9 @@ + + +