diff --git a/osu.Desktop.VisualTests/Tests/TestCaseDrawableRoom.cs b/osu.Desktop.VisualTests/Tests/TestCaseDrawableRoom.cs index ddda4119bf..4f4ef9bbb5 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseDrawableRoom.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseDrawableRoom.cs @@ -8,6 +8,7 @@ using osu.Game.Screens.Multiplayer; using osu.Game.Online.Multiplayer; using osu.Game.Users; using osu.Game.Database; +using osu.Framework.Allocation; namespace osu.Desktop.VisualTests.Tests { @@ -15,74 +16,117 @@ namespace osu.Desktop.VisualTests.Tests { public override string Description => @"Select your favourite room"; + private RulesetDatabase rulesets; + protected override void LoadComplete() { base.LoadComplete(); DrawableRoom first; - DrawableRoom second; Add(new FillFlowContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, AutoSizeAxes = Axes.Y, - Width = 500f, + Width = 580f, Direction = FillDirection.Vertical, Children = new Drawable[] { - first = new DrawableRoom(new Room()), - second = new DrawableRoom(new Room()), + first = new DrawableRoom(new Room + { + Name = { Value = @"Great Room Right Here" }, + Host = { Value = new User { Username = @"Naeferith", Id = 9492835, Country = new Country { FlagName = @"FR" } } }, + Status = { Value = new RoomStatusOpen() }, + Type = { Value = new GameTypeTeamVersus() }, + Beatmap = + { + Value = new BeatmapInfo + { + StarDifficulty = 4.65, + Ruleset = rulesets.GetRuleset(3), + Metadata = new BeatmapMetadata + { + Title = @"Critical Crystal", + Artist = @"Seiryu", + }, + BeatmapSet = new BeatmapSetInfo + { + OnlineInfo = new BeatmapSetOnlineInfo + { + Covers = new BeatmapSetOnlineCovers + { + Cover = @"https://assets.ppy.sh//beatmaps/376340/covers/cover.jpg?1456478455", + }, + }, + }, + }, + }, + Participants = + { + Value = new[] + { + new User { GlobalRank = 1355 }, + new User { GlobalRank = 8756 }, + }, + }, + }), + new DrawableRoom(new Room + { + Name = { Value = @"Relax It's The Weekend" }, + Host = { Value = new User { Username = @"peppy", Id = 2, Country = new Country { FlagName = @"AU" } } }, + Status = { Value = new RoomStatusPlaying() }, + Type = { Value = new GameTypeTagTeam() }, + Beatmap = + { + Value = new BeatmapInfo + { + StarDifficulty = 1.96, + Ruleset = rulesets.GetRuleset(0), + Metadata = new BeatmapMetadata + { + Title = @"Serendipity", + Artist = @"ZAQ", + }, + BeatmapSet = new BeatmapSetInfo + { + OnlineInfo = new BeatmapSetOnlineInfo + { + Covers = new BeatmapSetOnlineCovers + { + Cover = @"https://assets.ppy.sh//beatmaps/526839/covers/cover.jpg?1493815706", + }, + }, + }, + }, + }, + Participants = + { + Value = new[] + { + new User { GlobalRank = 578975 }, + new User { GlobalRank = 24554 }, + }, + }, + }), } }); - 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 BeatmapInfo + AddStep(@"change title", () => 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 status", () => first.Room.Status.Value = new RoomStatusPlaying()); + AddStep(@"change type", () => first.Room.Type.Value = new GameTypeVersus()); + AddStep(@"change beatmap", () => first.Room.Beatmap.Value = null); + AddStep(@"change participants", () => first.Room.Participants.Value = new[] { - Metadata = 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 BeatmapInfo - { - Metadata = new BeatmapMetadata - { - Title = @"Serendipity", - Artist = @"ZAQ", - }, - }; - - AddStep(@"change state", () => - { - first.Room.Status.Value = new RoomStatusPlaying(); + new User { GlobalRank = 1254 }, + new User { GlobalRank = 123189 }, }); + } - 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(); - }); + [BackgroundDependencyLoader] + private void load(RulesetDatabase rulesets) + { + this.rulesets = rulesets; } } } diff --git a/osu.Desktop.VisualTests/Tests/TestCaseMedalOverlay.cs b/osu.Desktop.VisualTests/Tests/TestCaseMedalOverlay.cs new file mode 100644 index 0000000000..1533f2141e --- /dev/null +++ b/osu.Desktop.VisualTests/Tests/TestCaseMedalOverlay.cs @@ -0,0 +1,27 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Testing; +using osu.Game.Overlays; +using osu.Game.Users; + +namespace osu.Desktop.VisualTests.Tests +{ + internal class TestCaseMedalOverlay : TestCase + { + public override string Description => @"medal get!"; + + public TestCaseMedalOverlay() + { + AddStep(@"display", () => + { + LoadComponentAsync(new MedalOverlay(new Medal + { + Name = @"Animations", + InternalName = @"all-intro-doubletime", + Description = @"More complex than you think.", + }), Add); + }); + } + } +} diff --git a/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj index c11d79192e..b65bab0d17 100644 --- a/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj +++ b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj @@ -231,6 +231,7 @@ + diff --git a/osu.Game/Beatmaps/Drawables/BeatmapSetCover.cs b/osu.Game/Beatmaps/Drawables/BeatmapSetCover.cs new file mode 100644 index 0000000000..4e41424ae8 --- /dev/null +++ b/osu.Game/Beatmaps/Drawables/BeatmapSetCover.cs @@ -0,0 +1,28 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Game.Database; + +namespace osu.Game.Beatmaps.Drawables +{ + public class BeatmapSetCover : Sprite + { + private readonly BeatmapSetInfo set; + public BeatmapSetCover(BeatmapSetInfo set) + { + this.set = set; + } + + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + string resource = set.OnlineInfo.Covers.Cover; + + if (resource != null) + Texture = textures.Get(resource); + } + } +} diff --git a/osu.Game/Online/Multiplayer/GameType.cs b/osu.Game/Online/Multiplayer/GameType.cs index 4d1a6c4839..22e2ffac31 100644 --- a/osu.Game/Online/Multiplayer/GameType.cs +++ b/osu.Game/Online/Multiplayer/GameType.cs @@ -52,10 +52,32 @@ namespace osu.Game.Online.Multiplayer public override string Name => "Tag Team"; public override Drawable GetIcon(OsuColour colours, float size) { - return new VersusRow(colours.Blue, colours.Blue, size * 0.6f) + return new FillFlowContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(2f), + Children = new[] + { + new TextAwesome + { + Icon = FontAwesome.fa_refresh, + TextSize = size * 0.75f, + Colour = colours.Blue, + Shadow = false, + UseFullGlyphHeight = false, + }, + new TextAwesome + { + Icon = FontAwesome.fa_refresh, + TextSize = size * 0.75f, + Colour = colours.Pink, + Shadow = false, + UseFullGlyphHeight = false, + }, + }, }; } } diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/Direct/DirectPanel.cs index 02b6e32192..4fc9a922a8 100644 --- a/osu.Game/Overlays/Direct/DirectPanel.cs +++ b/osu.Game/Overlays/Direct/DirectPanel.cs @@ -3,11 +3,9 @@ using System.Collections.Generic; using OpenTK; -using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Textures; using osu.Game.Beatmaps.Drawables; using osu.Game.Database; using osu.Game.Graphics; @@ -34,7 +32,7 @@ namespace osu.Game.Overlays.Direct return icons; } - protected Drawable CreateBackground() => new DelayedLoadWrapper(new BeatmapSetBackgroundSprite(SetInfo) + protected Drawable CreateBackground() => new DelayedLoadWrapper(new BeatmapSetCover(SetInfo) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -89,23 +87,5 @@ namespace osu.Game.Overlays.Direct Value = value; } } - - private class BeatmapSetBackgroundSprite : Sprite - { - private readonly BeatmapSetInfo set; - public BeatmapSetBackgroundSprite(BeatmapSetInfo set) - { - this.set = set; - } - - [BackgroundDependencyLoader] - private void load(TextureStore textures) - { - string resource = set.OnlineInfo.Covers.Card; - - if (resource != null) - Texture = textures.Get(resource); - } - } } } diff --git a/osu.Game/Overlays/MedalOverlay.cs b/osu.Game/Overlays/MedalOverlay.cs new file mode 100644 index 0000000000..5f85474ede --- /dev/null +++ b/osu.Game/Overlays/MedalOverlay.cs @@ -0,0 +1,304 @@ +// 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.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Sprites; +using osu.Game.Users; +using osu.Game.Graphics; +using osu.Game.Graphics.Backgrounds; +using osu.Game.Overlays.MedalSplash; +using osu.Framework.Allocation; +using osu.Framework.Audio.Sample; +using osu.Framework.Audio; +using osu.Framework.Graphics.Textures; +using osu.Framework.Input; +using OpenTK.Input; +using System.Linq; +using osu.Framework.Graphics.Shapes; +using System; +using osu.Framework.MathUtils; + +namespace osu.Game.Overlays +{ + public class MedalOverlay : FocusedOverlayContainer + { + public const float DISC_SIZE = 400; + + private const float border_width = 5; + + private readonly Medal medal; + private readonly Box background; + private readonly Container backgroundStrip, particleContainer; + private readonly BackgroundStrip leftStrip, rightStrip; + private readonly CircularContainer disc; + private readonly Sprite innerSpin, outerSpin; + private DrawableMedal drawableMedal; + + private SampleChannel getSample; + + public MedalOverlay(Medal medal) + { + this.medal = medal; + RelativeSizeAxes = Axes.Both; + + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black.Opacity(60), + }, + outerSpin = new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(DISC_SIZE + 500), + Alpha = 0f, + }, + backgroundStrip = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.X, + Height = border_width, + Alpha = 0f, + Children = new[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.CentreRight, + Width = 0.5f, + Padding = new MarginPadding { Right = DISC_SIZE / 2 }, + Children = new[] + { + leftStrip = new BackgroundStrip(0f, 1f) + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + }, + }, + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.CentreLeft, + Width = 0.5f, + Padding = new MarginPadding { Left = DISC_SIZE / 2 }, + Children = new[] + { + rightStrip = new BackgroundStrip(1f, 0f), + }, + }, + }, + }, + particleContainer = new Container + { + RelativeSizeAxes = Axes.Both, + Alpha = 0f, + }, + disc = new CircularContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Alpha = 0f, + Masking = true, + AlwaysPresent = true, + BorderColour = Color4.White, + BorderThickness = border_width, + Size = new Vector2(DISC_SIZE), + Scale = new Vector2(0.8f), + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.FromHex(@"05262f"), + }, + new Triangles + { + RelativeSizeAxes = Axes.Both, + TriangleScale = 2, + ColourDark = OsuColour.FromHex(@"04222b"), + ColourLight = OsuColour.FromHex(@"052933"), + }, + innerSpin = new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(1.05f), + Alpha = 0.25f, + }, + }, + }, + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours, TextureStore textures, AudioManager audio) + { + getSample = audio.Sample.Get(@"MedalSplash/medal-get"); + innerSpin.Texture = outerSpin.Texture = textures.Get(@"MedalSplash/disc-spin"); + + disc.EdgeEffect = leftStrip.EdgeEffect = rightStrip.EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Colour = colours.Blue.Opacity(0.5f), + Radius = 50, + }; + + disc.Add(drawableMedal = new DrawableMedal(medal) + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.X, + }); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + Show(); + } + + protected override void Update() + { + base.Update(); + + particleContainer.Add(new MedalParticle(RNG.Next(0, 359))); + } + + protected override bool OnClick(InputState state) + { + dismiss(); + return true; + } + + protected override void OnFocusLost(InputState state) + { + if (state.Keyboard.Keys.Contains(Key.Escape)) dismiss(); + } + + private const double initial_duration = 400; + private const double step_duration = 900; + + protected override void PopIn() + { + base.PopIn(); + + FadeIn(200); + background.FlashColour(Color4.White.Opacity(0.25f), 400); + + getSample.Play(); + + using (innerSpin.BeginLoopedSequence()) + innerSpin.RotateTo(360, 20000); + + using (outerSpin.BeginLoopedSequence()) + outerSpin.RotateTo(360, 40000); + + using (BeginDelayedSequence(200, true)) + { + disc.FadeIn(initial_duration); + particleContainer.FadeIn(initial_duration); + outerSpin.FadeTo(0.1f, initial_duration * 2); + disc.ScaleTo(1f, initial_duration * 2, EasingTypes.OutElastic); + + using (BeginDelayedSequence(initial_duration + 200, true)) + { + backgroundStrip.FadeIn(step_duration); + leftStrip.ResizeWidthTo(1f, step_duration, EasingTypes.OutQuint); + rightStrip.ResizeWidthTo(1f, step_duration, EasingTypes.OutQuint); + Schedule(() => { if (drawableMedal.State != DisplayState.Full) drawableMedal.State = DisplayState.Icon; }); + + using (BeginDelayedSequence(step_duration, true)) + { + Schedule(() => { if (drawableMedal.State != DisplayState.Full) drawableMedal.State = DisplayState.MedalUnlocked; }); + + using (BeginDelayedSequence(step_duration, true)) + Schedule(() => { if (drawableMedal.State != DisplayState.Full) drawableMedal.State = DisplayState.Full; }); + } + } + } + } + + protected override void PopOut() + { + base.PopOut(); + FadeOut(200); + } + + private void dismiss() + { + if (drawableMedal.State != DisplayState.Full) + { + // if we haven't yet, play out the animation fully + drawableMedal.State = DisplayState.Full; + Flush(true); + return; + } + + Hide(); + Expire(); + } + + private class BackgroundStrip : Container + { + public BackgroundStrip(float start, float end) + { + RelativeSizeAxes = Axes.Both; + Width = 0f; + ColourInfo = ColourInfo.GradientHorizontal(Color4.White.Opacity(start), Color4.White.Opacity(end)); + Masking = true; + + Children = new[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.White, + } + }; + } + } + + private class MedalParticle : CircularContainer + { + private readonly float direction; + + private Vector2 positionForOffset(float offset) => new Vector2((float)(offset * Math.Sin(direction)), (float)(offset * Math.Cos(direction))); + + public MedalParticle(float direction) + { + this.direction = direction; + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + Position = positionForOffset(DISC_SIZE / 2); + Masking = true; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Colour = colours.Blue.Opacity(0.5f), + Radius = 5, + }; + + MoveTo(positionForOffset(DISC_SIZE / 2 + 200), 500); + FadeOut(500); + Expire(); + } + } + } +} diff --git a/osu.Game/Overlays/MedalSplash/DrawableMedal.cs b/osu.Game/Overlays/MedalSplash/DrawableMedal.cs new file mode 100644 index 0000000000..7d7ffbd12a --- /dev/null +++ b/osu.Game/Overlays/MedalSplash/DrawableMedal.cs @@ -0,0 +1,183 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework; +using OpenTK; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Users; + +namespace osu.Game.Overlays.MedalSplash +{ + public class DrawableMedal : Container, IStateful + { + private const float scale_when_unlocked = 0.76f; + private const float scale_when_full = 0.6f; + + private readonly Medal medal; + private readonly Container medalContainer; + private readonly Sprite medalSprite, medalGlow; + private readonly OsuSpriteText unlocked, name; + private readonly TextFlowContainer description; + private readonly FillFlowContainer infoFlow; + private DisplayState state; + public DrawableMedal(Medal medal) + { + this.medal = medal; + Position = new Vector2(0f, MedalOverlay.DISC_SIZE / 2); + + Children = new Drawable[] + { + medalContainer = new Container + { + Anchor = Anchor.TopCentre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Both, + Alpha = 0f, + Children = new Drawable[] + { + medalSprite = new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scale = new Vector2(0.81f), + }, + medalGlow = new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + }, + }, + unlocked = new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = "Medal Unlocked".ToUpper(), + TextSize = 24, + Font = @"Exo2.0-Light", + Alpha = 0f, + Scale = new Vector2(1f / scale_when_unlocked), + }, + infoFlow = new FillFlowContainer + { + Anchor = Anchor.TopCentre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Width = 0.6f, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0f, 5f), + Children = new Drawable[] + { + name = new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = medal.Name, + TextSize = 20, + Font = @"Exo2.0-Bold", + Alpha = 0f, + Scale = new Vector2(1f / scale_when_full), + }, + description = new TextFlowContainer + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Alpha = 0f, + Scale = new Vector2(1f / scale_when_full), + }, + }, + }, + }; + + description.AddText(medal.Description, s => + { + s.Anchor = Anchor.TopCentre; + s.Origin = Anchor.TopCentre; + s.TextSize = 16; + }); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours, TextureStore textures) + { + medalSprite.Texture = textures.Get(medal.ImageUrl); + medalGlow.Texture = textures.Get(@"MedalSplash/medal-glow"); + description.Colour = colours.BlueLight; + + unlocked.Position = new Vector2(0f, medalContainer.Size.Y / 2 + 10); + infoFlow.Position = new Vector2(0f, unlocked.Position.Y + 90); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + updateState(); + } + + public DisplayState State + { + get { return state; } + set + { + if (state == value) return; + + state = value; + updateState(); + } + } + + private void updateState() + { + if (!IsLoaded) return; + + const double duration = 900; + + switch (state) + { + case DisplayState.None: + medalContainer.ScaleTo(0); + break; + case DisplayState.Icon: + medalContainer.ScaleTo(1, duration, EasingTypes.OutElastic); + medalContainer.FadeIn(duration); + break; + case DisplayState.MedalUnlocked: + medalContainer.ScaleTo(1); + medalContainer.Show(); + + ScaleTo(scale_when_unlocked, duration, EasingTypes.OutExpo); + MoveToY(MedalOverlay.DISC_SIZE / 2 - 30, duration, EasingTypes.OutExpo); + unlocked.FadeInFromZero(duration); + break; + case DisplayState.Full: + medalContainer.ScaleTo(1); + medalContainer.Show(); + + ScaleTo(scale_when_full, duration, EasingTypes.OutExpo); + MoveToY(MedalOverlay.DISC_SIZE / 2 - 60, duration, EasingTypes.OutExpo); + name.FadeInFromZero(duration + 100); + description.FadeInFromZero(duration * 2); + break; + } + + + } + } + + public enum DisplayState + { + None, + Icon, + MedalUnlocked, + Full, + } +} diff --git a/osu.Game/Screens/Multiplayer/DrawableRoom.cs b/osu.Game/Screens/Multiplayer/DrawableRoom.cs index 620797aff6..d8963be116 100644 --- a/osu.Game/Screens/Multiplayer/DrawableRoom.cs +++ b/osu.Game/Screens/Multiplayer/DrawableRoom.cs @@ -4,11 +4,13 @@ using OpenTK; using OpenTK.Graphics; using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Localisation; +using osu.Game.Beatmaps.Drawables; using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Graphics.Containers; @@ -20,20 +22,25 @@ namespace osu.Game.Screens.Multiplayer { public class DrawableRoom : OsuClickableContainer { - private const float content_padding = 5; - private const float height = 90; + private const float transition_duration = 100; + private const float content_padding = 10; + private const float height = 100; + private const float side_strip_width = 5; + private const float cover_width = 145; 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 Container coverContainer; + private readonly OsuSpriteText name, status, beatmapTitle, beatmapDash, beatmapArtist; private readonly FillFlowContainer beatmapInfoFlow; - private readonly OsuSpriteText beatmapTitle; - private readonly OsuSpriteText beatmapDash; - private readonly OsuSpriteText beatmapArtist; + private readonly ParticipantInfo participantInfo; + private readonly ModeTypeInfo modeTypeInfo; + + private readonly Bindable nameBind = new Bindable(); + private readonly Bindable hostBind = new Bindable(); + private readonly Bindable statusBind = new Bindable(); + private readonly Bindable typeBind = new Bindable(); + private readonly Bindable beatmapBind = new Bindable(); + private readonly Bindable participantsBind = new Bindable(); private OsuColour colours; private LocalisationEngine localisation; @@ -60,24 +67,41 @@ namespace osu.Game.Screens.Multiplayer new Box { RelativeSizeAxes = Axes.Both, - Colour = OsuColour.Gray(34), + Colour = OsuColour.FromHex(@"212121"), }, sideStrip = new Box { RelativeSizeAxes = Axes.Y, - Width = content_padding, + Width = side_strip_width, }, - avatar = new UpdateableAvatar + new Container { - Size = new Vector2(Height - content_padding * 2), + Width = cover_width, + RelativeSizeAxes = Axes.Y, Masking = true, - CornerRadius = 5f, - Margin = new MarginPadding { Left = content_padding * 2, Top = content_padding }, + Margin = new MarginPadding { Left = side_strip_width }, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + }, + coverContainer = new Container + { + RelativeSizeAxes = Axes.Both, + }, + }, }, new Container { RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Top = content_padding, Bottom = content_padding, Left = Height + content_padding * 2, Right = content_padding }, + Padding = new MarginPadding + { + Vertical = content_padding, + Left = side_strip_width + cover_width + content_padding, + Right = content_padding, + }, Children = new Drawable[] { new FillFlowContainer @@ -92,56 +116,7 @@ namespace osu.Game.Screens.Multiplayer { 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 }, - }, - }, - }, + participantInfo = new ParticipantInfo(), }, }, new FillFlowContainer @@ -151,7 +126,6 @@ namespace osu.Game.Screens.Multiplayer RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Direction = FillDirection.Vertical, - Margin = new MarginPadding { Bottom = content_padding }, Children = new Drawable[] { status = new OsuSpriteText @@ -174,7 +148,7 @@ namespace osu.Game.Screens.Multiplayer beatmapDash = new OsuSpriteText { TextSize = 14, - Font = @"Exo2.0-RegularItalic", + Font = @"Exo2.0-BoldItalic", }, beatmapArtist = new OsuSpriteText { @@ -185,14 +159,26 @@ namespace osu.Game.Screens.Multiplayer }, }, }, + modeTypeInfo = new ModeTypeInfo + { + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + }, }, }, }; - Room.Name.ValueChanged += displayName; - Room.Host.ValueChanged += displayUser; - Room.Status.ValueChanged += displayStatus; - Room.Beatmap.ValueChanged += displayBeatmap; + nameBind.ValueChanged += displayName; + hostBind.ValueChanged += displayUser; + typeBind.ValueChanged += displayGameType; + participantsBind.ValueChanged += displayParticipants; + + nameBind.BindTo(Room.Name); + hostBind.BindTo(Room.Host); + statusBind.BindTo(Room.Status); + typeBind.BindTo(Room.Type); + beatmapBind.BindTo(Room.Beatmap); + participantsBind.BindTo(Room.Participants); } [BackgroundDependencyLoader] @@ -201,10 +187,14 @@ namespace osu.Game.Screens.Multiplayer this.localisation = localisation; this.colours = colours; - beatmapInfoFlow.Colour = rankBounds.Colour = colours.Gray9; - host.Colour = colours.Blue; + beatmapInfoFlow.Colour = colours.Gray9; - displayStatus(Room.Status.Value); + //binded here instead of ctor because dependencies are needed + statusBind.ValueChanged += displayStatus; + beatmapBind.ValueChanged += displayBeatmap; + + statusBind.TriggerChange(); + beatmapBind.TriggerChange(); } private void displayName(string value) @@ -214,9 +204,7 @@ namespace osu.Game.Screens.Multiplayer private void displayUser(User value) { - avatar.User = value; - host.Text = value.Username; - flagContainer.Children = new[] { new DrawableFlag(value.Country?.FlagName ?? @"__") { RelativeSizeAxes = Axes.Both } }; + participantInfo.Host = value; } private void displayStatus(RoomStatus value) @@ -228,33 +216,48 @@ namespace osu.Game.Screens.Multiplayer d.FadeColour(value.GetAppropriateColour(colours), 100); } + private void displayGameType(GameType value) + { + modeTypeInfo.Type = value; + } + private void displayBeatmap(BeatmapInfo value) { + modeTypeInfo.Beatmap = value; + if (value != null) { + coverContainer.FadeIn(transition_duration); + coverContainer.Children = new[] + { + new AsyncLoadWrapper(new BeatmapSetCover(value.BeatmapSet) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + FillMode = FillMode.Fill, + OnLoadComplete = d => d.FadeInFromZero(400, EasingTypes.Out), + }) { RelativeSizeAxes = Axes.Both }, + }; + beatmapTitle.Current = localisation.GetUnicodePreference(value.Metadata.TitleUnicode, value.Metadata.Title); beatmapDash.Text = @" - "; beatmapArtist.Current = localisation.GetUnicodePreference(value.Metadata.ArtistUnicode, value.Metadata.Artist); } else { + coverContainer.FadeOut(transition_duration); + beatmapTitle.Current = null; beatmapArtist.Current = null; - beatmapTitle.Text = @"Changing map"; - beatmapDash.Text = string.Empty; - beatmapArtist.Text = string.Empty; + beatmapTitle.Text = "Changing map"; + beatmapDash.Text = beatmapArtist.Text = string.Empty; } } - protected override void Dispose(bool isDisposing) + private void displayParticipants(User[] value) { - Room.Name.ValueChanged -= displayName; - Room.Host.ValueChanged -= displayUser; - Room.Status.ValueChanged -= displayStatus; - Room.Beatmap.ValueChanged -= displayBeatmap; - - base.Dispose(isDisposing); + participantInfo.Participants = value; } } } diff --git a/osu.Game/Screens/Multiplayer/ModeTypeInfo.cs b/osu.Game/Screens/Multiplayer/ModeTypeInfo.cs new file mode 100644 index 0000000000..fff40aeed5 --- /dev/null +++ b/osu.Game/Screens/Multiplayer/ModeTypeInfo.cs @@ -0,0 +1,82 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps.Drawables; +using osu.Game.Database; +using osu.Game.Online.Multiplayer; + +namespace osu.Game.Screens.Multiplayer +{ + public class ModeTypeInfo : Container + { + private const float height = 30; + private const float transition_duration = 100; + + private readonly Container rulesetContainer, gameTypeContainer; + + public BeatmapInfo Beatmap + { + set + { + if (value != null) + { + rulesetContainer.FadeIn(transition_duration); + rulesetContainer.Children = new[] + { + new DifficultyIcon(value) + { + Size = new Vector2(height), + }, + }; + } + else + { + rulesetContainer.FadeOut(transition_duration); + } + } + } + + public GameType Type + { + set + { + gameTypeContainer.Children = new[] + { + new DrawableGameType(value) + { + Size = new Vector2(height), + }, + }; + } + } + + public ModeTypeInfo() + { + AutoSizeAxes = Axes.Both; + + Children = new[] + { + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(5f, 0f), + Children = new[] + { + rulesetContainer = new Container + { + AutoSizeAxes = Axes.Both, + }, + gameTypeContainer = new Container + { + AutoSizeAxes = Axes.Both, + }, + }, + }, + }; + } + } +} diff --git a/osu.Game/Screens/Multiplayer/ParticipantInfo.cs b/osu.Game/Screens/Multiplayer/ParticipantInfo.cs new file mode 100644 index 0000000000..639f29567f --- /dev/null +++ b/osu.Game/Screens/Multiplayer/ParticipantInfo.cs @@ -0,0 +1,145 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.Linq; +using OpenTK; +using osu.Framework.Allocation; +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.Users; + +namespace osu.Game.Screens.Multiplayer +{ + public class ParticipantInfo : Container + { + private readonly Container flagContainer; + private readonly OsuSpriteText host; + private readonly FillFlowContainer levelRangeContainer; + private readonly OsuSpriteText levelRangeLower; + private readonly OsuSpriteText levelRangeHigher; + + public User Host + { + set + { + host.Text = value.Username; + flagContainer.Children = new[] { new DrawableFlag(value.Country?.FlagName ?? @"__") { RelativeSizeAxes = Axes.Both } }; + } + } + + public IEnumerable Participants + { + set + { + var ranks = value.Select(u => u.GlobalRank); + levelRangeLower.Text = ranks.Min().ToString(); + levelRangeHigher.Text = ranks.Max().ToString(); + } + } + + public ParticipantInfo(string rankPrefix = null) + { + RelativeSizeAxes = Axes.X; + Height = 15f; + + 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 = 22f, + RelativeSizeAxes = Axes.Y, + }, + new Container //todo: team banners + { + Width = 38f, + RelativeSizeAxes = Axes.Y, + CornerRadius = 2f, + Masking = true, + Children = new[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.FromHex(@"ad387e"), + }, + }, + }, + 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", + }, + }, + }, + levelRangeContainer = new FillFlowContainer + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Children = new[] + { + new OsuSpriteText + { + Text = rankPrefix, + TextSize = 14, + }, + new OsuSpriteText + { + Text = "#", + TextSize = 14, + }, + levelRangeLower = new OsuSpriteText + { + TextSize = 14, + Font = @"Exo2.0-Bold", + }, + new OsuSpriteText + { + Text = " - ", + TextSize = 14, + }, + new OsuSpriteText + { + Text = "#", + TextSize = 14, + }, + levelRangeHigher = new OsuSpriteText + { + TextSize = 14, + Font = @"Exo2.0-Bold", + }, + }, + }, + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + levelRangeContainer.Colour = colours.Gray9; + host.Colour = colours.Blue; + } + } +} diff --git a/osu.Game/Screens/Multiplayer/RoomInspector.cs b/osu.Game/Screens/Multiplayer/RoomInspector.cs index 73783bab12..9d63fdb948 100644 --- a/osu.Game/Screens/Multiplayer/RoomInspector.cs +++ b/osu.Game/Screens/Multiplayer/RoomInspector.cs @@ -12,8 +12,6 @@ using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Textures; using osu.Framework.Localisation; using osu.Game.Beatmaps.Drawables; using osu.Game.Database; @@ -28,12 +26,13 @@ namespace osu.Game.Screens.Multiplayer { private readonly MarginPadding contentPadding = new MarginPadding { Horizontal = 20, Vertical = 10 }; private const float transition_duration = 100; - private const float ruleset_height = 30; private readonly Box statusStrip; - private readonly Container coverContainer, rulesetContainer, gameTypeContainer, flagContainer; - private readonly FillFlowContainer topFlow, levelRangeContainer, participantsFlow; - private readonly OsuSpriteText participants, participantsSlash, maxParticipants, name, status, beatmapTitle, beatmapDash, beatmapArtist, beatmapAuthor, host, levelRangeLower, levelRangeHigher; + private readonly Container coverContainer; + private readonly FillFlowContainer topFlow, participantsFlow; + private readonly ModeTypeInfo modeTypeInfo; + private readonly OsuSpriteText participants, participantsSlash, maxParticipants, name, status, beatmapTitle, beatmapDash, beatmapArtist, beatmapAuthor; + private readonly ParticipantInfo participantInfo; private readonly ScrollContainer participantsScroll; private readonly Bindable nameBind = new Bindable(); @@ -190,20 +189,13 @@ namespace osu.Game.Screens.Multiplayer new FillFlowContainer { AutoSizeAxes = Axes.X, - Height = ruleset_height, + Height = 30, Direction = FillDirection.Horizontal, LayoutDuration = transition_duration, Spacing = new Vector2(5f, 0f), Children = new Drawable[] { - rulesetContainer = new Container - { - AutoSizeAxes = Axes.Both, - }, - gameTypeContainer = new Container - { - AutoSizeAxes = Axes.Both, - }, + modeTypeInfo = new ModeTypeInfo(), new Container { AutoSizeAxes = Axes.X, @@ -252,91 +244,7 @@ namespace osu.Game.Screens.Multiplayer Padding = contentPadding, Children = new Drawable[] { - new FillFlowContainer - { - AutoSizeAxes = Axes.X, - Height = 15f, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(5f, 0f), - Children = new Drawable[] - { - flagContainer = new Container - { - Width = 22f, - RelativeSizeAxes = Axes.Y, - }, - new Container //todo: team banners - { - Width = 38f, - RelativeSizeAxes = Axes.Y, - CornerRadius = 2f, - Masking = true, - Children = new[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = OsuColour.FromHex(@"ad387e"), - }, - }, - }, - 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", - }, - }, - }, - levelRangeContainer = new FillFlowContainer - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Children = new[] - { - new OsuSpriteText - { - Text = "Rank Range ", - TextSize = 14, - }, - new OsuSpriteText - { - Text = "#", - TextSize = 14, - }, - levelRangeLower = new OsuSpriteText - { - TextSize = 14, - Font = @"Exo2.0-Bold", - }, - new OsuSpriteText - { - Text = " - ", - TextSize = 14, - }, - new OsuSpriteText - { - Text = "#", - TextSize = 14, - }, - levelRangeHigher = new OsuSpriteText - { - Text = "6251", - TextSize = 14, - Font = @"Exo2.0-Bold", - }, - }, - }, + participantInfo = new ParticipantInfo(@"Rank Range "), }, }, }, @@ -362,6 +270,7 @@ namespace osu.Game.Screens.Multiplayer nameBind.ValueChanged += displayName; hostBind.ValueChanged += displayUser; + typeBind.ValueChanged += displayGameType; maxParticipantsBind.ValueChanged += displayMaxParticipants; participantsBind.ValueChanged += displayParticipants; } @@ -372,16 +281,13 @@ namespace osu.Game.Screens.Multiplayer this.localisation = localisation; this.colours = colours; - beatmapAuthor.Colour = levelRangeContainer.Colour = colours.Gray9; - host.Colour = colours.Blue; + beatmapAuthor.Colour = colours.Gray9; //binded here instead of ctor because dependencies are needed statusBind.ValueChanged += displayStatus; - typeBind.ValueChanged += displayGameType; beatmapBind.ValueChanged += displayBeatmap; statusBind.TriggerChange(); - typeBind.TriggerChange(); beatmapBind.TriggerChange(); } @@ -399,14 +305,7 @@ namespace osu.Game.Screens.Multiplayer private void displayUser(User value) { - host.Text = value.Username; - flagContainer.Children = new[] - { - new DrawableFlag(value.Country?.FlagName ?? @"__") - { - RelativeSizeAxes = Axes.Both, - }, - }; + participantInfo.Host = value; } private void displayStatus(RoomStatus value) @@ -419,39 +318,26 @@ namespace osu.Game.Screens.Multiplayer private void displayGameType(GameType value) { - gameTypeContainer.Children = new[] - { - new DrawableGameType(value) - { - Size = new Vector2(ruleset_height), - }, - }; + modeTypeInfo.Type = value; } private void displayBeatmap(BeatmapInfo value) { + modeTypeInfo.Beatmap = value; + if (value != null) { coverContainer.FadeIn(transition_duration); coverContainer.Children = new[] { - new AsyncLoadWrapper(new CoverSprite(value.BeatmapSet) + new AsyncLoadWrapper(new BeatmapSetCover(value.BeatmapSet) { RelativeSizeAxes = Axes.Both, Anchor = Anchor.Centre, Origin = Anchor.Centre, FillMode = FillMode.Fill, OnLoadComplete = d => d.FadeInFromZero(400, EasingTypes.Out), - }) { RelativeSizeAxes = Axes.Both } - }; - - rulesetContainer.FadeIn(transition_duration); - rulesetContainer.Children = new[] - { - new DifficultyIcon(value) - { - Size = new Vector2(ruleset_height), - } + }) { RelativeSizeAxes = Axes.Both }, }; beatmapTitle.Current = localisation.GetUnicodePreference(value.Metadata.TitleUnicode, value.Metadata.Title); @@ -462,7 +348,6 @@ namespace osu.Game.Screens.Multiplayer else { coverContainer.FadeOut(transition_duration); - rulesetContainer.FadeOut(transition_duration); beatmapTitle.Current = null; beatmapArtist.Current = null; @@ -490,11 +375,7 @@ namespace osu.Game.Screens.Multiplayer private void displayParticipants(User[] value) { participants.Text = value.Length.ToString(); - - var ranks = value.Select(u => u.GlobalRank); - levelRangeLower.Text = ranks.Min().ToString(); - levelRangeHigher.Text = ranks.Max().ToString(); - + participantInfo.Participants = value; participantsFlow.ChildrenEnumerable = value.Select(u => new UserTile(u)); } @@ -526,22 +407,5 @@ namespace osu.Game.Screens.Multiplayer }; } } - - private class CoverSprite : Sprite - { - private readonly BeatmapSetInfo set; - - public CoverSprite(BeatmapSetInfo set) - { - this.set = set; - } - - [BackgroundDependencyLoader] - private void load(TextureStore textures) - { - if (set.OnlineInfo?.Covers?.Cover != null) - Texture = textures.Get(set.OnlineInfo.Covers.Cover); - } - } } } diff --git a/osu.Game/Screens/Play/SkipButton.cs b/osu.Game/Screens/Play/SkipButton.cs index a3ee2aeb72..b0e3de0ea4 100644 --- a/osu.Game/Screens/Play/SkipButton.cs +++ b/osu.Game/Screens/Play/SkipButton.cs @@ -277,9 +277,19 @@ namespace osu.Game.Screens.Play protected override bool OnClick(InputState state) { + if (!Enabled) + return false; + box.FlashColour(Color4.White, 500, EasingTypes.OutQuint); aspect.ScaleTo(1.2f, 2000, EasingTypes.OutQuint); - return base.OnClick(state); + + bool result = base.OnClick(state); + + // for now, let's disable the skip button after the first press. + // this will likely need to be contextual in the future (bound from external components). + Enabled.Value = false; + + return result; } } } diff --git a/osu.Game/Users/Medal.cs b/osu.Game/Users/Medal.cs new file mode 100644 index 0000000000..aa7382b457 --- /dev/null +++ b/osu.Game/Users/Medal.cs @@ -0,0 +1,13 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Users +{ + public class Medal + { + public string Name { get; set; } + public string InternalName { get; set; } + public string ImageUrl => $@"https://s.ppy.sh/images/medals-client/{InternalName}@2x.png"; + public string Description { get; set; } + } +} diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 959642ce8b..cf72d0914f 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -489,9 +489,15 @@ + + + + + +