mirror of
https://github.com/ppy/osu.git
synced 2026-05-25 01:00:00 +08:00
Split quickplay beatmap & "random" panel into separate classes (V2) (#35701)
* Load all beatmaps in bulk for SubScreenBeatmapSelect * Fix tests no longer working due to drawable changes * Remove test that no longer makes sense * Split matchmaking panel into subclasses for each panel type * Adjust tests to match new structure * Add `ConfigureAwait` * Display loading spinner while beatmaps are being fetched * Fix test failure * Load playlist items directly in `LoadComplete` * Convert `MatchmakingSelectPanel` card content classes into nested classes * Wait for panels to be loaded before operating on them * Add ConfigureAwait() --------- Co-authored-by: Dan Balasescu <smoogipoo@smgi.me>
This commit is contained in:
committed by
GitHub
Unverified
parent
45e8df7af2
commit
8b778e8106
@@ -2,17 +2,20 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Screens.OnlinePlay.Matchmaking.Match.BeatmapSelect;
|
||||
using osu.Game.Tests.Visual.OnlinePlay;
|
||||
using osuTK;
|
||||
@@ -21,7 +24,7 @@ namespace osu.Game.Tests.Visual.Matchmaking
|
||||
{
|
||||
public partial class TestSceneBeatmapSelectGrid : OnlinePlayTestScene
|
||||
{
|
||||
private MultiplayerPlaylistItem[] items = null!;
|
||||
private MatchmakingPlaylistItem[] items = null!;
|
||||
|
||||
private BeatmapSelectGrid grid = null!;
|
||||
|
||||
@@ -36,24 +39,44 @@ namespace osu.Game.Tests.Visual.Matchmaking
|
||||
.Take(50)
|
||||
.ToArray();
|
||||
|
||||
IEnumerable<MatchmakingPlaylistItem> playlistItems;
|
||||
|
||||
if (beatmaps.Length > 0)
|
||||
{
|
||||
items = Enumerable.Range(1, 50).Select(i => new MultiplayerPlaylistItem
|
||||
playlistItems = Enumerable.Range(1, 50).Select(i =>
|
||||
{
|
||||
ID = i,
|
||||
BeatmapID = beatmaps[i % beatmaps.Length].OnlineID,
|
||||
StarRating = i / 10.0,
|
||||
}).ToArray();
|
||||
var beatmap = beatmaps[i % beatmaps.Length];
|
||||
|
||||
return new MatchmakingPlaylistItem(
|
||||
new MultiplayerPlaylistItem
|
||||
{
|
||||
ID = i,
|
||||
BeatmapID = beatmap.OnlineID,
|
||||
StarRating = i / 10.0,
|
||||
},
|
||||
CreateAPIBeatmap(beatmap),
|
||||
Array.Empty<Mod>()
|
||||
);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
items = Enumerable.Range(1, 50).Select(i => new MultiplayerPlaylistItem
|
||||
{
|
||||
ID = i,
|
||||
BeatmapID = i,
|
||||
StarRating = i / 10.0,
|
||||
}).ToArray();
|
||||
playlistItems = Enumerable.Range(1, 50).Select(i => new MatchmakingPlaylistItem(
|
||||
new MultiplayerPlaylistItem
|
||||
{
|
||||
ID = i,
|
||||
BeatmapID = i,
|
||||
StarRating = i / 10.0,
|
||||
},
|
||||
CreateAPIBeatmap(),
|
||||
Array.Empty<Mod>()
|
||||
));
|
||||
}
|
||||
|
||||
foreach (var item in playlistItems)
|
||||
item.Beatmap.StarRating = item.PlaylistItem.StarRating;
|
||||
|
||||
items = playlistItems.ToArray();
|
||||
}
|
||||
|
||||
public override void SetUpSteps()
|
||||
@@ -70,8 +93,7 @@ namespace osu.Game.Tests.Visual.Matchmaking
|
||||
|
||||
AddStep("add items", () =>
|
||||
{
|
||||
foreach (var item in items)
|
||||
grid.AddItem(item);
|
||||
grid.AddItems(items);
|
||||
});
|
||||
|
||||
AddWaitStep("wait for panels", 3);
|
||||
@@ -85,17 +107,17 @@ namespace osu.Game.Tests.Visual.Matchmaking
|
||||
// test scene is weird.
|
||||
});
|
||||
|
||||
AddStep("add selection 1", () => grid.ChildrenOfType<BeatmapSelectPanel>().First().AddUser(new APIUser
|
||||
AddStep("add selection 1", () => grid.ChildrenOfType<MatchmakingSelectPanel>().First().AddUser(new APIUser
|
||||
{
|
||||
Id = DummyAPIAccess.DUMMY_USER_ID,
|
||||
Username = "Maarvin",
|
||||
}));
|
||||
AddStep("add selection 2", () => grid.ChildrenOfType<BeatmapSelectPanel>().Skip(5).First().AddUser(new APIUser
|
||||
AddStep("add selection 2", () => grid.ChildrenOfType<MatchmakingSelectPanel>().Skip(5).First().AddUser(new APIUser
|
||||
{
|
||||
Id = 2,
|
||||
Username = "peppy",
|
||||
}));
|
||||
AddStep("add selection 3", () => grid.ChildrenOfType<BeatmapSelectPanel>().Skip(10).First().AddUser(new APIUser
|
||||
AddStep("add selection 3", () => grid.ChildrenOfType<MatchmakingSelectPanel>().Skip(10).First().AddUser(new APIUser
|
||||
{
|
||||
Id = 1040328,
|
||||
Username = "smoogipoo",
|
||||
@@ -180,7 +202,7 @@ namespace osu.Game.Tests.Visual.Matchmaking
|
||||
|
||||
AddStep("display roll order", () =>
|
||||
{
|
||||
var panels = grid.ChildrenOfType<BeatmapSelectPanel>().ToArray();
|
||||
var panels = grid.ChildrenOfType<MatchmakingSelectPanel>().ToArray();
|
||||
|
||||
for (int i = 0; i < panels.Length; i++)
|
||||
{
|
||||
@@ -211,7 +233,7 @@ namespace osu.Game.Tests.Visual.Matchmaking
|
||||
|
||||
AddWaitStep("wait for animation", 5);
|
||||
|
||||
AddStep("reveal beatmap", () => grid.RevealRandomItem(new MultiplayerPlaylistItem()));
|
||||
AddStep("reveal beatmap", () => grid.RevealRandomItem(items[RNG.Next(items.Length)].PlaylistItem));
|
||||
}
|
||||
|
||||
private (long[] candidateItems, long finalItem) pickRandomItems(int count)
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Graphics.Cursor;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Overlays;
|
||||
@@ -44,14 +42,14 @@ namespace osu.Game.Tests.Visual.Matchmaking
|
||||
[Test]
|
||||
public void TestBeatmapPanel()
|
||||
{
|
||||
BeatmapSelectPanel? panel = null;
|
||||
MatchmakingSelectPanel? panel = null;
|
||||
|
||||
AddStep("add panel", () =>
|
||||
{
|
||||
Child = new OsuContextMenuContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = panel = new BeatmapSelectPanel(new MultiplayerPlaylistItem())
|
||||
Child = panel = new MatchmakingSelectPanelBeatmap(new MatchmakingPlaylistItem(new MultiplayerPlaylistItem(), CreateAPIBeatmap(), []))
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
@@ -81,53 +79,17 @@ namespace osu.Game.Tests.Visual.Matchmaking
|
||||
AddToggleStep("allow selection", value => panel!.AllowSelection = value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestFailedBeatmapLookup()
|
||||
{
|
||||
AddStep("setup request handle", () =>
|
||||
{
|
||||
var api = (DummyAPIAccess)API;
|
||||
var handler = api.HandleRequest;
|
||||
api.HandleRequest = req =>
|
||||
{
|
||||
switch (req)
|
||||
{
|
||||
case GetBeatmapRequest:
|
||||
case GetBeatmapsRequest:
|
||||
req.TriggerFailure(new InvalidOperationException());
|
||||
return false;
|
||||
|
||||
default:
|
||||
return handler?.Invoke(req) ?? false;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
AddStep("add panel", () =>
|
||||
{
|
||||
Child = new OsuContextMenuContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = new BeatmapSelectPanel(new MultiplayerPlaylistItem())
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestRandomPanel()
|
||||
{
|
||||
BeatmapSelectPanel? panel = null;
|
||||
MatchmakingSelectPanelRandom? panel = null;
|
||||
|
||||
AddStep("add panel", () =>
|
||||
{
|
||||
Child = new OsuContextMenuContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = panel = new BeatmapSelectPanel(new MultiplayerPlaylistItem { ID = -1 })
|
||||
Child = panel = new MatchmakingSelectPanelRandom(new MultiplayerPlaylistItem { ID = -1 })
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
@@ -137,7 +99,7 @@ namespace osu.Game.Tests.Visual.Matchmaking
|
||||
|
||||
AddToggleStep("allow selection", value => panel!.AllowSelection = value);
|
||||
|
||||
AddStep("reveal beatmap", () => panel!.DisplayItem(new MultiplayerPlaylistItem()));
|
||||
AddStep("reveal beatmap", () => panel!.RevealBeatmap(CreateAPIBeatmap(), []));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -145,15 +107,12 @@ namespace osu.Game.Tests.Visual.Matchmaking
|
||||
{
|
||||
AddStep("add panel", () =>
|
||||
{
|
||||
BeatmapSelectPanel? panel;
|
||||
MatchmakingSelectPanel? panel;
|
||||
|
||||
Child = new OsuContextMenuContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = panel = new BeatmapSelectPanel(new MultiplayerPlaylistItem
|
||||
{
|
||||
RequiredMods = [new APIMod(new OsuModHardRock()), new APIMod(new OsuModDoubleTime())]
|
||||
})
|
||||
Child = panel = new MatchmakingSelectPanelBeatmap(new MatchmakingPlaylistItem(new MultiplayerPlaylistItem(), CreateAPIBeatmap(), [new OsuModHardRock(), new OsuModDoubleTime()]))
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
|
||||
-119
@@ -1,119 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.BeatmapSelect
|
||||
{
|
||||
public partial class BeatmapCardMatchmaking : OsuClickableContainer
|
||||
{
|
||||
public const float WIDTH = 345;
|
||||
public const float HEIGHT = 80;
|
||||
|
||||
[Resolved]
|
||||
private BeatmapLookupCache beatmapLookupCache { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private RulesetStore rulesetStore { get; set; } = null!;
|
||||
|
||||
private readonly List<APIUser> users = new List<APIUser>();
|
||||
|
||||
private Container contentContainer = null!;
|
||||
private Drawable flashLayer = null!;
|
||||
private BeatmapCardMatchmakingContent? content;
|
||||
|
||||
public BeatmapCardMatchmaking()
|
||||
{
|
||||
Width = WIDTH;
|
||||
Height = HEIGHT;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Children = new[]
|
||||
{
|
||||
contentContainer = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
},
|
||||
flashLayer = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void AddUser(APIUser user)
|
||||
{
|
||||
users.Add(user);
|
||||
content?.SelectionOverlay.AddUser(user);
|
||||
}
|
||||
|
||||
public void RemoveUser(APIUser user)
|
||||
{
|
||||
users.Remove(user);
|
||||
content?.SelectionOverlay.RemoveUser(user.Id);
|
||||
}
|
||||
|
||||
public void DisplayItem(MultiplayerPlaylistItem item)
|
||||
{
|
||||
Ruleset? ruleset = rulesetStore.GetRuleset(item.RulesetID)?.CreateInstance();
|
||||
|
||||
if (ruleset == null)
|
||||
return;
|
||||
|
||||
Mod[] mods = item.RequiredMods.Select(m => m.ToMod(ruleset)).ToArray();
|
||||
|
||||
Task.Run(loadBeatmap);
|
||||
|
||||
async Task loadBeatmap()
|
||||
{
|
||||
APIBeatmap? beatmap = await beatmapLookupCache.GetBeatmapAsync(item.BeatmapID).ConfigureAwait(false);
|
||||
|
||||
beatmap ??= new APIBeatmap
|
||||
{
|
||||
BeatmapSet = new APIBeatmapSet
|
||||
{
|
||||
Title = "unknown beatmap",
|
||||
TitleUnicode = "unknown beatmap",
|
||||
Artist = "unknown artist",
|
||||
ArtistUnicode = "unknown artist",
|
||||
}
|
||||
};
|
||||
|
||||
beatmap.StarRating = item.StarRating;
|
||||
|
||||
loadContent(new BeatmapCardMatchmakingBeatmapContent(beatmap, mods));
|
||||
}
|
||||
}
|
||||
|
||||
public void DisplayRandom() => loadContent(new BeatmapCardMatchmakingRandomContent());
|
||||
|
||||
private void loadContent(BeatmapCardMatchmakingContent newContent) => Schedule(() =>
|
||||
{
|
||||
bool flashNewContent = content != null;
|
||||
|
||||
contentContainer.Child = content = newContent;
|
||||
|
||||
foreach (var user in users)
|
||||
newContent.SelectionOverlay.AddUser(user);
|
||||
|
||||
if (flashNewContent)
|
||||
flashLayer.FadeOutFromOne(1000, Easing.In);
|
||||
});
|
||||
}
|
||||
}
|
||||
-378
@@ -1,378 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Extensions.LocalisationExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Drawables;
|
||||
using osu.Game.Beatmaps.Drawables.Cards;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Localisation;
|
||||
using osu.Game.Online;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.BeatmapSet;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.BeatmapSelect
|
||||
{
|
||||
public partial class BeatmapCardMatchmakingBeatmapContent : BeatmapCardMatchmakingContent, IHasContextMenu
|
||||
{
|
||||
public override AvatarOverlay SelectionOverlay => selectionOverlay;
|
||||
|
||||
[Resolved]
|
||||
private OverlayColourProvider colourProvider { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private BeatmapSetOverlay? beatmapSetOverlay { get; set; }
|
||||
|
||||
private readonly IBindable<DownloadState> downloadState = new Bindable<DownloadState>();
|
||||
private readonly IBindableNumber<double> downloadProgress = new BindableDouble();
|
||||
private readonly Bindable<BeatmapSetFavouriteState> favouriteState = new Bindable<BeatmapSetFavouriteState>();
|
||||
private readonly APIBeatmapSet beatmapSet;
|
||||
private readonly APIBeatmap beatmap;
|
||||
private readonly Mod[] mods;
|
||||
|
||||
private BeatmapCardThumbnail thumbnail = null!;
|
||||
private CollapsibleButtonContainer buttonContainer = null!;
|
||||
private FillFlowContainer idleBottomContent = null!;
|
||||
private BeatmapCardDownloadProgressBar downloadProgressBar = null!;
|
||||
private AvatarOverlay selectionOverlay = null!;
|
||||
|
||||
public BeatmapCardMatchmakingBeatmapContent(APIBeatmap beatmap, Mod[] mods)
|
||||
{
|
||||
this.beatmap = beatmap;
|
||||
this.mods = mods;
|
||||
|
||||
beatmapSet = beatmap.BeatmapSet!;
|
||||
favouriteState.Value = new BeatmapSetFavouriteState(beatmapSet.HasFavourited, beatmapSet.FavouriteCount);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
FillFlowContainer leftIconArea;
|
||||
FillFlowContainer titleBadgeArea;
|
||||
GridContainer artistContainer;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new BeatmapDownloadTracker(beatmap.BeatmapSet!)
|
||||
{
|
||||
State = { BindTarget = downloadState },
|
||||
Progress = { BindTarget = downloadProgress },
|
||||
},
|
||||
thumbnail = new BeatmapCardThumbnail(beatmapSet, beatmapSet, keepLoaded: true)
|
||||
{
|
||||
Name = @"Left (icon) area",
|
||||
Size = new Vector2(BeatmapCardMatchmaking.HEIGHT),
|
||||
Padding = new MarginPadding { Right = BeatmapCard.CORNER_RADIUS },
|
||||
Child = leftIconArea = new FillFlowContainer
|
||||
{
|
||||
Margin = new MarginPadding(4),
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(1)
|
||||
}
|
||||
},
|
||||
buttonContainer = new CollapsibleButtonContainer(beatmapSet, allowNavigationToBeatmap: false, keepBackgroundLoaded: true)
|
||||
{
|
||||
X = BeatmapCardMatchmaking.HEIGHT - BeatmapCard.CORNER_RADIUS,
|
||||
Width = BeatmapCard.WIDTH - BeatmapCardMatchmaking.HEIGHT + BeatmapCard.CORNER_RADIUS,
|
||||
FavouriteState = { BindTarget = favouriteState },
|
||||
ButtonsCollapsedWidth = 0,
|
||||
ButtonsExpandedWidth = 24,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new GridContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
ColumnDimensions = new[]
|
||||
{
|
||||
new Dimension(),
|
||||
new Dimension(GridSizeMode.AutoSize),
|
||||
},
|
||||
RowDimensions = new[]
|
||||
{
|
||||
new Dimension(GridSizeMode.AutoSize)
|
||||
},
|
||||
Content = new[]
|
||||
{
|
||||
new Drawable[]
|
||||
{
|
||||
new TruncatingSpriteText
|
||||
{
|
||||
Text = new RomanisableString(beatmapSet.TitleUnicode, beatmapSet.Title),
|
||||
Font = OsuFont.Default.With(size: 18f, weight: FontWeight.SemiBold),
|
||||
RelativeSizeAxes = Axes.X,
|
||||
},
|
||||
titleBadgeArea = new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.BottomRight,
|
||||
Origin = Anchor.BottomRight,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
artistContainer = new GridContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
ColumnDimensions = new[]
|
||||
{
|
||||
new Dimension(),
|
||||
new Dimension(GridSizeMode.AutoSize)
|
||||
},
|
||||
RowDimensions = new[]
|
||||
{
|
||||
new Dimension(GridSizeMode.AutoSize)
|
||||
},
|
||||
Content = new[]
|
||||
{
|
||||
new[]
|
||||
{
|
||||
new TruncatingSpriteText
|
||||
{
|
||||
Text = BeatmapsetsStrings.ShowDetailsByArtist(new RomanisableString(beatmapSet.ArtistUnicode, beatmapSet.Artist)),
|
||||
Font = OsuFont.Default.With(size: 14f, weight: FontWeight.SemiBold),
|
||||
RelativeSizeAxes = Axes.X,
|
||||
},
|
||||
Empty()
|
||||
},
|
||||
}
|
||||
},
|
||||
new LinkFlowContainer(s =>
|
||||
{
|
||||
s.Shadow = false;
|
||||
s.Font = OsuFont.GetFont(size: 11f, weight: FontWeight.SemiBold);
|
||||
}).With(d =>
|
||||
{
|
||||
d.AutoSizeAxes = Axes.Both;
|
||||
d.Margin = new MarginPadding { Top = 1 };
|
||||
d.AddText("mapped by ", t => t.Colour = colourProvider.Content2);
|
||||
d.AddUserLink(beatmapSet.Author);
|
||||
}),
|
||||
}
|
||||
},
|
||||
new Container
|
||||
{
|
||||
Name = @"Bottom content",
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
idleBottomContent = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(0, 2),
|
||||
AlwaysPresent = true,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new GridContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
ColumnDimensions = new[]
|
||||
{
|
||||
new Dimension(),
|
||||
new Dimension(GridSizeMode.AutoSize)
|
||||
},
|
||||
RowDimensions = new[]
|
||||
{
|
||||
new Dimension(GridSizeMode.AutoSize)
|
||||
},
|
||||
Content = new[]
|
||||
{
|
||||
new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
Masking = true,
|
||||
CornerRadius = BeatmapCard.CORNER_RADIUS,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Colour = colours.ForStarDifficulty(beatmap.StarRating).Darken(0.8f),
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
Padding = new MarginPadding(4),
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(6, 0),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new StarRatingDisplay(new StarDifficulty(beatmap.StarRating, 0), StarRatingDisplaySize.Small, animated: true)
|
||||
{
|
||||
Origin = Anchor.CentreLeft,
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Scale = new Vector2(0.9f),
|
||||
},
|
||||
new TruncatingSpriteText
|
||||
{
|
||||
Text = beatmap.DifficultyName,
|
||||
Font = OsuFont.Style.Caption1.With(weight: FontWeight.Bold),
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
new ModFlowDisplay
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Scale = new Vector2(0.5f),
|
||||
Margin = new MarginPadding { Left = 5 },
|
||||
Current = { Value = mods }
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
downloadProgressBar = new BeatmapCardDownloadProgressBar
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 5,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
State = { BindTarget = downloadState },
|
||||
Progress = { BindTarget = downloadProgress }
|
||||
}
|
||||
}
|
||||
},
|
||||
selectionOverlay = new AvatarOverlay
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (beatmapSet.HasVideo)
|
||||
leftIconArea.Add(new VideoIconPill { IconSize = new Vector2(16) });
|
||||
|
||||
if (beatmapSet.HasStoryboard)
|
||||
leftIconArea.Add(new StoryboardIconPill { IconSize = new Vector2(16) });
|
||||
|
||||
if (beatmapSet.FeaturedInSpotlight)
|
||||
{
|
||||
titleBadgeArea.Add(new SpotlightBeatmapBadge
|
||||
{
|
||||
Anchor = Anchor.BottomRight,
|
||||
Origin = Anchor.BottomRight,
|
||||
Margin = new MarginPadding { Left = 4 }
|
||||
});
|
||||
}
|
||||
|
||||
if (beatmapSet.HasExplicitContent)
|
||||
{
|
||||
titleBadgeArea.Add(new ExplicitContentBeatmapBadge
|
||||
{
|
||||
Anchor = Anchor.BottomRight,
|
||||
Origin = Anchor.BottomRight,
|
||||
Margin = new MarginPadding { Left = 4 }
|
||||
});
|
||||
}
|
||||
|
||||
if (beatmapSet.TrackId != null)
|
||||
{
|
||||
artistContainer.Content[0][1] = new FeaturedArtistBeatmapBadge
|
||||
{
|
||||
Anchor = Anchor.BottomRight,
|
||||
Origin = Anchor.BottomRight,
|
||||
Margin = new MarginPadding { Left = 4 }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
downloadState.BindValueChanged(_ => updateState(), true);
|
||||
|
||||
FinishTransforms(true);
|
||||
}
|
||||
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
updateState();
|
||||
return base.OnHover(e);
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(HoverLostEvent e)
|
||||
{
|
||||
updateState();
|
||||
base.OnHoverLost(e);
|
||||
}
|
||||
|
||||
private void updateState()
|
||||
{
|
||||
bool showDetails = IsHovered;
|
||||
|
||||
buttonContainer.ShowDetails.Value = showDetails;
|
||||
thumbnail.Dimmed.Value = showDetails;
|
||||
|
||||
bool showProgress = downloadState.Value == DownloadState.Downloading || downloadState.Value == DownloadState.Importing;
|
||||
|
||||
idleBottomContent.FadeTo(showProgress ? 0 : 1, 340, Easing.OutQuint);
|
||||
downloadProgressBar.FadeTo(showProgress ? 1 : 0, 340, Easing.OutQuint);
|
||||
}
|
||||
|
||||
public MenuItem[] ContextMenuItems
|
||||
{
|
||||
get
|
||||
{
|
||||
List<MenuItem> items = new List<MenuItem>
|
||||
{
|
||||
new OsuMenuItem(ContextMenuStrings.ViewBeatmap, MenuItemType.Highlighted, () => beatmapSetOverlay?.FetchAndShowBeatmap(beatmap.OnlineID))
|
||||
};
|
||||
|
||||
foreach (var button in buttonContainer.Buttons)
|
||||
{
|
||||
if (button.Enabled.Value)
|
||||
items.Add(new OsuMenuItem(button.TooltipText.ToSentence(), MenuItemType.Standard, () => button.TriggerClick()));
|
||||
}
|
||||
|
||||
return items.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-153
@@ -1,153 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.BeatmapSelect
|
||||
{
|
||||
public abstract partial class BeatmapCardMatchmakingContent : CompositeDrawable
|
||||
{
|
||||
public abstract AvatarOverlay SelectionOverlay { get; }
|
||||
|
||||
protected BeatmapCardMatchmakingContent()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
public partial class AvatarOverlay : CompositeDrawable
|
||||
{
|
||||
private readonly Container<SelectionAvatar> avatars;
|
||||
|
||||
private Sample? userAddedSample;
|
||||
private double? lastSamplePlayback;
|
||||
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; } = null!;
|
||||
|
||||
public AvatarOverlay()
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
|
||||
InternalChild = avatars = new Container<SelectionAvatar>
|
||||
{
|
||||
AutoSizeAxes = Axes.X,
|
||||
Height = SelectionAvatar.AVATAR_SIZE,
|
||||
};
|
||||
|
||||
Padding = new MarginPadding { Vertical = 5 };
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(AudioManager audio)
|
||||
{
|
||||
userAddedSample = audio.Samples.Get(@"Multiplayer/player-ready");
|
||||
}
|
||||
|
||||
public bool AddUser(APIUser user)
|
||||
{
|
||||
if (avatars.Any(a => a.User.Id == user.Id))
|
||||
return false;
|
||||
|
||||
var avatar = new SelectionAvatar(user, user.Equals(api.LocalUser.Value));
|
||||
|
||||
avatars.Add(avatar);
|
||||
|
||||
if (lastSamplePlayback == null || Time.Current - lastSamplePlayback > OsuGameBase.SAMPLE_DEBOUNCE_TIME)
|
||||
{
|
||||
userAddedSample?.Play();
|
||||
lastSamplePlayback = Time.Current;
|
||||
}
|
||||
|
||||
updateAvatarLayout();
|
||||
|
||||
avatar.FinishTransforms();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool RemoveUser(int id)
|
||||
{
|
||||
if (avatars.SingleOrDefault(a => a.User.Id == id) is not SelectionAvatar avatar)
|
||||
return false;
|
||||
|
||||
avatar.PopOutAndExpire();
|
||||
avatars.ChangeChildDepth(avatar, float.MaxValue);
|
||||
|
||||
updateAvatarLayout();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void updateAvatarLayout()
|
||||
{
|
||||
const double stagger = 30;
|
||||
const float spacing = 4;
|
||||
|
||||
double delay = 0;
|
||||
float x = 0;
|
||||
|
||||
for (int i = avatars.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var avatar = avatars[i];
|
||||
|
||||
if (avatar.Expired)
|
||||
continue;
|
||||
|
||||
avatar.Delay(delay).MoveToX(x, 500, Easing.OutElasticQuarter);
|
||||
|
||||
x -= avatar.LayoutSize.X + spacing;
|
||||
|
||||
delay += stagger;
|
||||
}
|
||||
}
|
||||
|
||||
public partial class SelectionAvatar : CompositeDrawable
|
||||
{
|
||||
public const float AVATAR_SIZE = 30;
|
||||
|
||||
public APIUser User { get; }
|
||||
|
||||
public bool Expired { get; private set; }
|
||||
|
||||
private readonly MatchmakingAvatar avatar;
|
||||
|
||||
public SelectionAvatar(APIUser user, bool isOwnUser)
|
||||
{
|
||||
User = user;
|
||||
Size = new Vector2(AVATAR_SIZE);
|
||||
|
||||
InternalChild = avatar = new MatchmakingAvatar(user, isOwnUser)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
avatar.ScaleTo(0)
|
||||
.ScaleTo(1, 500, Easing.OutElasticHalf)
|
||||
.FadeIn(200);
|
||||
}
|
||||
|
||||
public void PopOutAndExpire()
|
||||
{
|
||||
avatar.ScaleTo(0, 400, Easing.OutExpo);
|
||||
|
||||
this.FadeOut(100).Expire();
|
||||
Expired = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-77
@@ -1,77 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Overlays;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.BeatmapSelect
|
||||
{
|
||||
public partial class BeatmapCardMatchmakingRandomContent : BeatmapCardMatchmakingContent
|
||||
{
|
||||
public override AvatarOverlay SelectionOverlay => selectionOverlay;
|
||||
|
||||
[Resolved]
|
||||
private OverlayColourProvider colourProvider { get; set; } = null!;
|
||||
|
||||
private AvatarOverlay selectionOverlay = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = colourProvider.Background2,
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding
|
||||
{
|
||||
Horizontal = 10,
|
||||
Vertical = 4
|
||||
},
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Vertical,
|
||||
Children =
|
||||
[
|
||||
new SpriteIcon
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Size = new Vector2(32),
|
||||
Icon = FontAwesome.Solid.Random,
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Text = "Random",
|
||||
}
|
||||
]
|
||||
},
|
||||
selectionOverlay = new AvatarOverlay
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Toolkit.HighPerformance;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
@@ -33,10 +34,12 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.BeatmapSelect
|
||||
|
||||
public event Action<MultiplayerPlaylistItem>? ItemSelected;
|
||||
|
||||
private readonly Dictionary<long, BeatmapSelectPanel> panelLookup = new Dictionary<long, BeatmapSelectPanel>();
|
||||
private readonly Dictionary<long, MatchmakingSelectPanel> panelLookup = new Dictionary<long, MatchmakingSelectPanel>();
|
||||
private readonly Dictionary<long, MatchmakingPlaylistItem> playlistItems = new Dictionary<long, MatchmakingPlaylistItem>();
|
||||
private MatchmakingSelectPanelRandom randomPanel = null!;
|
||||
|
||||
private readonly PanelGridContainer panelGridContainer;
|
||||
private readonly Container<BeatmapSelectPanel> rollContainer;
|
||||
private readonly Container<MatchmakingSelectPanel> rollContainer;
|
||||
private readonly OsuScrollContainer scroll;
|
||||
|
||||
private bool allowSelection = true;
|
||||
@@ -64,15 +67,12 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.BeatmapSelect
|
||||
Spacing = new Vector2(panel_spacing)
|
||||
},
|
||||
},
|
||||
rollContainer = new Container<BeatmapSelectPanel>
|
||||
rollContainer = new Container<MatchmakingSelectPanel>
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
},
|
||||
};
|
||||
|
||||
// Special item denoting a random selection.
|
||||
AddItem(new MultiplayerPlaylistItem { ID = -1 });
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@@ -86,9 +86,33 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.BeatmapSelect
|
||||
swooshSample = audio.Samples.Get(@"SongSelect/options-pop-out");
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
public void AddItems(IEnumerable<MatchmakingPlaylistItem> items)
|
||||
{
|
||||
base.LoadComplete();
|
||||
foreach (var item in items)
|
||||
{
|
||||
playlistItems[item.ID] = item;
|
||||
|
||||
var panel = panelLookup[item.ID] = new MatchmakingSelectPanelBeatmap(item)
|
||||
{
|
||||
AllowSelection = allowSelection,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Action = i => ItemSelected?.Invoke(i),
|
||||
};
|
||||
|
||||
panelGridContainer.Add(panel);
|
||||
panelGridContainer.SetLayoutPosition(panel, (float)panel.Item.StarRating);
|
||||
}
|
||||
|
||||
panelLookup[-1] = randomPanel = new MatchmakingSelectPanelRandom(new MultiplayerPlaylistItem { ID = -1 })
|
||||
{
|
||||
AllowSelection = allowSelection,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Action = i => ItemSelected?.Invoke(i),
|
||||
};
|
||||
panelGridContainer.Add(randomPanel);
|
||||
panelGridContainer.SetLayoutPosition(randomPanel, float.MinValue);
|
||||
|
||||
const double enter_duration = 500;
|
||||
|
||||
@@ -104,24 +128,12 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.BeatmapSelect
|
||||
|
||||
panel.FadeInAndEnterFromBelow(duration: enter_duration, delay: delay);
|
||||
}
|
||||
|
||||
panelsLoaded.SetResult();
|
||||
});
|
||||
}
|
||||
|
||||
public void AddItem(MultiplayerPlaylistItem item)
|
||||
{
|
||||
var panel = panelLookup[item.ID] = new BeatmapSelectPanel(item)
|
||||
{
|
||||
AllowSelection = allowSelection,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Action = i => ItemSelected?.Invoke(i),
|
||||
};
|
||||
|
||||
panelGridContainer.Add(panel);
|
||||
panelGridContainer.SetLayoutPosition(panel, (float)item.StarRating);
|
||||
}
|
||||
|
||||
public void SetUserSelection(APIUser user, long itemId, bool selected)
|
||||
public void SetUserSelection(APIUser user, long itemId, bool selected) => whenPanelsLoaded(() =>
|
||||
{
|
||||
if (!panelLookup.TryGetValue(itemId, out var panel))
|
||||
return;
|
||||
@@ -130,18 +142,19 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.BeatmapSelect
|
||||
panel.AddUser(user);
|
||||
else
|
||||
panel.RemoveUser(user);
|
||||
}
|
||||
});
|
||||
|
||||
public void RevealRandomItem(MultiplayerPlaylistItem item)
|
||||
public void RevealRandomItem(MultiplayerPlaylistItem item) => whenPanelsLoaded(() =>
|
||||
{
|
||||
if (!panelLookup.TryGetValue(-1, out var panel))
|
||||
return;
|
||||
playlistItems.TryGetValue(item.ID, out var playlistItem);
|
||||
|
||||
Debug.Assert(playlistItem != null);
|
||||
|
||||
panel.DisplayItem(item);
|
||||
randomRevealSample?.Play();
|
||||
}
|
||||
randomPanel.RevealBeatmap(playlistItem.Beatmap, playlistItem.Mods);
|
||||
});
|
||||
|
||||
public void RollAndDisplayFinalBeatmap(long[] candidateItemIds, long finalItemId)
|
||||
public void RollAndDisplayFinalBeatmap(long[] candidateItemIds, long finalItemId) => whenPanelsLoaded(() =>
|
||||
{
|
||||
Debug.Assert(candidateItemIds.Length >= 1);
|
||||
Debug.Assert(candidateItemIds.Contains(finalItemId));
|
||||
@@ -168,7 +181,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.BeatmapSelect
|
||||
.Delay(roll_duration + present_beatmap_delay)
|
||||
.Schedule(() => PresentRolledBeatmap(finalItemId));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
internal void TransferCandidatePanelsToRollContainer(long[] candidateItemIds, double duration = hide_duration)
|
||||
{
|
||||
@@ -177,7 +190,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.BeatmapSelect
|
||||
|
||||
var rng = new Random();
|
||||
|
||||
var remainingPanels = new List<BeatmapSelectPanel>();
|
||||
var remainingPanels = new List<MatchmakingSelectPanel>();
|
||||
|
||||
foreach (var panel in panelGridContainer.Children.ToArray())
|
||||
{
|
||||
@@ -217,7 +230,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.BeatmapSelect
|
||||
{
|
||||
var panel = rollContainer.Children[i];
|
||||
|
||||
var position = positions[i] * (BeatmapSelectPanel.SIZE + new Vector2(panel_spacing));
|
||||
var position = positions[i] * (MatchmakingSelectPanel.SIZE + new Vector2(panel_spacing));
|
||||
|
||||
panel.MoveTo(position, duration + stagger * i, new SplitEasingFunction(Easing.InCubic, Easing.OutExpo, 0.3f));
|
||||
|
||||
@@ -286,7 +299,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.BeatmapSelect
|
||||
while ((numSteps - 1) % rollContainer.Children.Count != finalItemIndex)
|
||||
numSteps++;
|
||||
|
||||
BeatmapSelectPanel? lastPanel = null;
|
||||
MatchmakingSelectPanel? lastPanel = null;
|
||||
|
||||
for (int i = 0; i < numSteps; i++)
|
||||
{
|
||||
@@ -347,7 +360,15 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.BeatmapSelect
|
||||
PresentRolledBeatmap(finalItem);
|
||||
}
|
||||
|
||||
private partial class PanelGridContainer : FillFlowContainer<BeatmapSelectPanel>
|
||||
private readonly TaskCompletionSource panelsLoaded = new TaskCompletionSource();
|
||||
|
||||
private void whenPanelsLoaded(Action action) => Task.Run(async () =>
|
||||
{
|
||||
await panelsLoaded.Task.ConfigureAwait(false);
|
||||
Schedule(action);
|
||||
});
|
||||
|
||||
private partial class PanelGridContainer : FillFlowContainer<MatchmakingSelectPanel>
|
||||
{
|
||||
public bool LayoutDisabled;
|
||||
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.BeatmapSelect
|
||||
{
|
||||
public record MatchmakingPlaylistItem(MultiplayerPlaylistItem PlaylistItem, APIBeatmap Beatmap, Mod[] Mods)
|
||||
{
|
||||
public long ID => PlaylistItem.ID;
|
||||
}
|
||||
}
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.BeatmapSelect
|
||||
{
|
||||
public partial class MatchmakingSelectPanel
|
||||
{
|
||||
public abstract partial class CardContent : CompositeDrawable
|
||||
{
|
||||
public abstract AvatarOverlay SelectionOverlay { get; }
|
||||
|
||||
protected CardContent()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
public partial class AvatarOverlay : CompositeDrawable
|
||||
{
|
||||
private readonly Container<SelectionAvatar> avatars;
|
||||
|
||||
private Sample? userAddedSample;
|
||||
private double? lastSamplePlayback;
|
||||
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; } = null!;
|
||||
|
||||
public AvatarOverlay()
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
|
||||
InternalChild = avatars = new Container<SelectionAvatar>
|
||||
{
|
||||
AutoSizeAxes = Axes.X,
|
||||
Height = SelectionAvatar.AVATAR_SIZE,
|
||||
};
|
||||
|
||||
Padding = new MarginPadding { Vertical = 5 };
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(AudioManager audio)
|
||||
{
|
||||
userAddedSample = audio.Samples.Get(@"Multiplayer/player-ready");
|
||||
}
|
||||
|
||||
public bool AddUser(APIUser user)
|
||||
{
|
||||
if (avatars.Any(a => a.User.Id == user.Id))
|
||||
return false;
|
||||
|
||||
var avatar = new SelectionAvatar(user, user.Equals(api.LocalUser.Value));
|
||||
|
||||
avatars.Add(avatar);
|
||||
|
||||
if (lastSamplePlayback == null || Time.Current - lastSamplePlayback > OsuGameBase.SAMPLE_DEBOUNCE_TIME)
|
||||
{
|
||||
userAddedSample?.Play();
|
||||
lastSamplePlayback = Time.Current;
|
||||
}
|
||||
|
||||
updateAvatarLayout();
|
||||
|
||||
avatar.FinishTransforms();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool RemoveUser(int id)
|
||||
{
|
||||
if (avatars.SingleOrDefault(a => a.User.Id == id) is not SelectionAvatar avatar)
|
||||
return false;
|
||||
|
||||
avatar.PopOutAndExpire();
|
||||
avatars.ChangeChildDepth(avatar, float.MaxValue);
|
||||
|
||||
updateAvatarLayout();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void updateAvatarLayout()
|
||||
{
|
||||
const double stagger = 30;
|
||||
const float spacing = 4;
|
||||
|
||||
double delay = 0;
|
||||
float x = 0;
|
||||
|
||||
for (int i = avatars.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var avatar = avatars[i];
|
||||
|
||||
if (avatar.Expired)
|
||||
continue;
|
||||
|
||||
avatar.Delay(delay).MoveToX(x, 500, Easing.OutElasticQuarter);
|
||||
|
||||
x -= avatar.LayoutSize.X + spacing;
|
||||
|
||||
delay += stagger;
|
||||
}
|
||||
}
|
||||
|
||||
public partial class SelectionAvatar : CompositeDrawable
|
||||
{
|
||||
public const float AVATAR_SIZE = 30;
|
||||
|
||||
public APIUser User { get; }
|
||||
|
||||
public bool Expired { get; private set; }
|
||||
|
||||
private readonly MatchmakingAvatar avatar;
|
||||
|
||||
public SelectionAvatar(APIUser user, bool isOwnUser)
|
||||
{
|
||||
User = user;
|
||||
Size = new Vector2(AVATAR_SIZE);
|
||||
|
||||
InternalChild = avatar = new MatchmakingAvatar(user, isOwnUser)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
avatar.ScaleTo(0)
|
||||
.ScaleTo(1, 500, Easing.OutElasticHalf)
|
||||
.FadeIn(200);
|
||||
}
|
||||
|
||||
public void PopOutAndExpire()
|
||||
{
|
||||
avatar.ScaleTo(0, 400, Easing.OutExpo);
|
||||
|
||||
this.FadeOut(100).Expire();
|
||||
Expired = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+381
@@ -0,0 +1,381 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Extensions.LocalisationExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Drawables;
|
||||
using osu.Game.Beatmaps.Drawables.Cards;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Localisation;
|
||||
using osu.Game.Online;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.BeatmapSet;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.BeatmapSelect
|
||||
{
|
||||
public partial class MatchmakingSelectPanel
|
||||
{
|
||||
public partial class CardContentBeatmap : CardContent, IHasContextMenu
|
||||
{
|
||||
public override AvatarOverlay SelectionOverlay => selectionOverlay;
|
||||
|
||||
[Resolved]
|
||||
private OverlayColourProvider colourProvider { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private BeatmapSetOverlay? beatmapSetOverlay { get; set; }
|
||||
|
||||
private readonly IBindable<DownloadState> downloadState = new Bindable<DownloadState>();
|
||||
private readonly IBindableNumber<double> downloadProgress = new BindableDouble();
|
||||
private readonly Bindable<BeatmapSetFavouriteState> favouriteState = new Bindable<BeatmapSetFavouriteState>();
|
||||
private readonly APIBeatmapSet beatmapSet;
|
||||
private readonly APIBeatmap beatmap;
|
||||
private readonly Mod[] mods;
|
||||
|
||||
private BeatmapCardThumbnail thumbnail = null!;
|
||||
private CollapsibleButtonContainer buttonContainer = null!;
|
||||
private FillFlowContainer idleBottomContent = null!;
|
||||
private BeatmapCardDownloadProgressBar downloadProgressBar = null!;
|
||||
private AvatarOverlay selectionOverlay = null!;
|
||||
|
||||
public CardContentBeatmap(APIBeatmap beatmap, Mod[] mods)
|
||||
{
|
||||
this.beatmap = beatmap;
|
||||
this.mods = mods;
|
||||
|
||||
beatmapSet = beatmap.BeatmapSet!;
|
||||
favouriteState.Value = new BeatmapSetFavouriteState(beatmapSet.HasFavourited, beatmapSet.FavouriteCount);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
FillFlowContainer leftIconArea;
|
||||
FillFlowContainer titleBadgeArea;
|
||||
GridContainer artistContainer;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new BeatmapDownloadTracker(beatmap.BeatmapSet!)
|
||||
{
|
||||
State = { BindTarget = downloadState },
|
||||
Progress = { BindTarget = downloadProgress },
|
||||
},
|
||||
thumbnail = new BeatmapCardThumbnail(beatmapSet, beatmapSet, keepLoaded: true)
|
||||
{
|
||||
Name = @"Left (icon) area",
|
||||
Size = new Vector2(MatchmakingSelectPanel.HEIGHT),
|
||||
Padding = new MarginPadding { Right = BeatmapCard.CORNER_RADIUS },
|
||||
Child = leftIconArea = new FillFlowContainer
|
||||
{
|
||||
Margin = new MarginPadding(4),
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(1)
|
||||
}
|
||||
},
|
||||
buttonContainer = new CollapsibleButtonContainer(beatmapSet, allowNavigationToBeatmap: false, keepBackgroundLoaded: true)
|
||||
{
|
||||
X = MatchmakingSelectPanel.HEIGHT - BeatmapCard.CORNER_RADIUS,
|
||||
Width = BeatmapCard.WIDTH - MatchmakingSelectPanel.HEIGHT + BeatmapCard.CORNER_RADIUS,
|
||||
FavouriteState = { BindTarget = favouriteState },
|
||||
ButtonsCollapsedWidth = 0,
|
||||
ButtonsExpandedWidth = 24,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new GridContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
ColumnDimensions = new[]
|
||||
{
|
||||
new Dimension(),
|
||||
new Dimension(GridSizeMode.AutoSize),
|
||||
},
|
||||
RowDimensions = new[]
|
||||
{
|
||||
new Dimension(GridSizeMode.AutoSize)
|
||||
},
|
||||
Content = new[]
|
||||
{
|
||||
new Drawable[]
|
||||
{
|
||||
new TruncatingSpriteText
|
||||
{
|
||||
Text = new RomanisableString(beatmapSet.TitleUnicode, beatmapSet.Title),
|
||||
Font = OsuFont.Default.With(size: 18f, weight: FontWeight.SemiBold),
|
||||
RelativeSizeAxes = Axes.X,
|
||||
},
|
||||
titleBadgeArea = new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.BottomRight,
|
||||
Origin = Anchor.BottomRight,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
artistContainer = new GridContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
ColumnDimensions = new[]
|
||||
{
|
||||
new Dimension(),
|
||||
new Dimension(GridSizeMode.AutoSize)
|
||||
},
|
||||
RowDimensions = new[]
|
||||
{
|
||||
new Dimension(GridSizeMode.AutoSize)
|
||||
},
|
||||
Content = new[]
|
||||
{
|
||||
new[]
|
||||
{
|
||||
new TruncatingSpriteText
|
||||
{
|
||||
Text = BeatmapsetsStrings.ShowDetailsByArtist(new RomanisableString(beatmapSet.ArtistUnicode, beatmapSet.Artist)),
|
||||
Font = OsuFont.Default.With(size: 14f, weight: FontWeight.SemiBold),
|
||||
RelativeSizeAxes = Axes.X,
|
||||
},
|
||||
Empty()
|
||||
},
|
||||
}
|
||||
},
|
||||
new LinkFlowContainer(s =>
|
||||
{
|
||||
s.Shadow = false;
|
||||
s.Font = OsuFont.GetFont(size: 11f, weight: FontWeight.SemiBold);
|
||||
}).With(d =>
|
||||
{
|
||||
d.AutoSizeAxes = Axes.Both;
|
||||
d.Margin = new MarginPadding { Top = 1 };
|
||||
d.AddText("mapped by ", t => t.Colour = colourProvider.Content2);
|
||||
d.AddUserLink(beatmapSet.Author);
|
||||
}),
|
||||
}
|
||||
},
|
||||
new Container
|
||||
{
|
||||
Name = @"Bottom content",
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
idleBottomContent = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(0, 2),
|
||||
AlwaysPresent = true,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new GridContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
ColumnDimensions = new[]
|
||||
{
|
||||
new Dimension(),
|
||||
new Dimension(GridSizeMode.AutoSize)
|
||||
},
|
||||
RowDimensions = new[]
|
||||
{
|
||||
new Dimension(GridSizeMode.AutoSize)
|
||||
},
|
||||
Content = new[]
|
||||
{
|
||||
new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
Masking = true,
|
||||
CornerRadius = BeatmapCard.CORNER_RADIUS,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Colour = colours.ForStarDifficulty(beatmap.StarRating).Darken(0.8f),
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
Padding = new MarginPadding(4),
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(6, 0),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new StarRatingDisplay(new StarDifficulty(beatmap.StarRating, 0), StarRatingDisplaySize.Small, animated: true)
|
||||
{
|
||||
Origin = Anchor.CentreLeft,
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Scale = new Vector2(0.9f),
|
||||
},
|
||||
new TruncatingSpriteText
|
||||
{
|
||||
Text = beatmap.DifficultyName,
|
||||
Font = OsuFont.Style.Caption1.With(weight: FontWeight.Bold),
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
new ModFlowDisplay
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Scale = new Vector2(0.5f),
|
||||
Margin = new MarginPadding { Left = 5 },
|
||||
Current = { Value = mods }
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
downloadProgressBar = new BeatmapCardDownloadProgressBar
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 5,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
State = { BindTarget = downloadState },
|
||||
Progress = { BindTarget = downloadProgress }
|
||||
}
|
||||
}
|
||||
},
|
||||
selectionOverlay = new AvatarOverlay
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (beatmapSet.HasVideo)
|
||||
leftIconArea.Add(new VideoIconPill { IconSize = new Vector2(16) });
|
||||
|
||||
if (beatmapSet.HasStoryboard)
|
||||
leftIconArea.Add(new StoryboardIconPill { IconSize = new Vector2(16) });
|
||||
|
||||
if (beatmapSet.FeaturedInSpotlight)
|
||||
{
|
||||
titleBadgeArea.Add(new SpotlightBeatmapBadge
|
||||
{
|
||||
Anchor = Anchor.BottomRight,
|
||||
Origin = Anchor.BottomRight,
|
||||
Margin = new MarginPadding { Left = 4 }
|
||||
});
|
||||
}
|
||||
|
||||
if (beatmapSet.HasExplicitContent)
|
||||
{
|
||||
titleBadgeArea.Add(new ExplicitContentBeatmapBadge
|
||||
{
|
||||
Anchor = Anchor.BottomRight,
|
||||
Origin = Anchor.BottomRight,
|
||||
Margin = new MarginPadding { Left = 4 }
|
||||
});
|
||||
}
|
||||
|
||||
if (beatmapSet.TrackId != null)
|
||||
{
|
||||
artistContainer.Content[0][1] = new FeaturedArtistBeatmapBadge
|
||||
{
|
||||
Anchor = Anchor.BottomRight,
|
||||
Origin = Anchor.BottomRight,
|
||||
Margin = new MarginPadding { Left = 4 }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
downloadState.BindValueChanged(_ => updateState(), true);
|
||||
|
||||
FinishTransforms(true);
|
||||
}
|
||||
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
updateState();
|
||||
return base.OnHover(e);
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(HoverLostEvent e)
|
||||
{
|
||||
updateState();
|
||||
base.OnHoverLost(e);
|
||||
}
|
||||
|
||||
private void updateState()
|
||||
{
|
||||
bool showDetails = IsHovered;
|
||||
|
||||
buttonContainer.ShowDetails.Value = showDetails;
|
||||
thumbnail.Dimmed.Value = showDetails;
|
||||
|
||||
bool showProgress = downloadState.Value == DownloadState.Downloading || downloadState.Value == DownloadState.Importing;
|
||||
|
||||
idleBottomContent.FadeTo(showProgress ? 0 : 1, 340, Easing.OutQuint);
|
||||
downloadProgressBar.FadeTo(showProgress ? 1 : 0, 340, Easing.OutQuint);
|
||||
}
|
||||
|
||||
public MenuItem[] ContextMenuItems
|
||||
{
|
||||
get
|
||||
{
|
||||
List<MenuItem> items = new List<MenuItem>
|
||||
{
|
||||
new OsuMenuItem(ContextMenuStrings.ViewBeatmap, MenuItemType.Highlighted, () => beatmapSetOverlay?.FetchAndShowBeatmap(beatmap.OnlineID))
|
||||
};
|
||||
|
||||
foreach (var button in buttonContainer.Buttons)
|
||||
{
|
||||
if (button.Enabled.Value)
|
||||
items.Add(new OsuMenuItem(button.TooltipText.ToSentence(), MenuItemType.Standard, () => button.TriggerClick()));
|
||||
}
|
||||
|
||||
return items.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Overlays;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.BeatmapSelect
|
||||
{
|
||||
public partial class MatchmakingSelectPanel
|
||||
{
|
||||
public partial class CardContentRandom : CardContent
|
||||
{
|
||||
public override AvatarOverlay SelectionOverlay => selectionOverlay;
|
||||
|
||||
[Resolved]
|
||||
private OverlayColourProvider colourProvider { get; set; } = null!;
|
||||
|
||||
private AvatarOverlay selectionOverlay = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = colourProvider.Background2,
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding
|
||||
{
|
||||
Horizontal = 10,
|
||||
Vertical = 4
|
||||
},
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Vertical,
|
||||
Children =
|
||||
[
|
||||
new SpriteIcon
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Size = new Vector2(32),
|
||||
Icon = FontAwesome.Solid.Random,
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Text = "Random",
|
||||
}
|
||||
]
|
||||
},
|
||||
selectionOverlay = new AvatarOverlay
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+61
-73
@@ -10,6 +10,7 @@ using osu.Framework.Graphics.Effects;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Beatmaps.Drawables.Cards;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Overlays;
|
||||
@@ -19,9 +20,12 @@ using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.BeatmapSelect
|
||||
{
|
||||
public partial class BeatmapSelectPanel : Container
|
||||
public abstract partial class MatchmakingSelectPanel : Container
|
||||
{
|
||||
public static readonly Vector2 SIZE = new Vector2(BeatmapCard.WIDTH, BeatmapCardNormal.HEIGHT);
|
||||
public const float WIDTH = 345;
|
||||
public const float HEIGHT = 80;
|
||||
|
||||
public static readonly Vector2 SIZE = new Vector2(WIDTH, HEIGHT);
|
||||
|
||||
public bool AllowSelection { get; set; }
|
||||
|
||||
@@ -29,14 +33,15 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.BeatmapSelect
|
||||
|
||||
public Action<MultiplayerPlaylistItem>? Action { private get; init; }
|
||||
|
||||
protected override Container<Drawable> Content { get; } = new Container { RelativeSizeAxes = Axes.Both };
|
||||
|
||||
private const float border_width = 3;
|
||||
|
||||
private Container scaleContainer = null!;
|
||||
private Drawable lighting = null!;
|
||||
private Container border = null!;
|
||||
private BeatmapCardMatchmaking card = null!;
|
||||
|
||||
public BeatmapSelectPanel(MultiplayerPlaylistItem item)
|
||||
protected MatchmakingSelectPanel(MultiplayerPlaylistItem item)
|
||||
{
|
||||
Item = item;
|
||||
Size = SIZE;
|
||||
@@ -45,88 +50,70 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.BeatmapSelect
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OverlayColourProvider colourProvider)
|
||||
{
|
||||
InternalChild = scaleContainer = new Container
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Children = new[]
|
||||
scaleContainer = new Container
|
||||
{
|
||||
new Container
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Children = new[]
|
||||
{
|
||||
Masking = true,
|
||||
CornerRadius = BeatmapCard.CORNER_RADIUS,
|
||||
CornerExponent = 10,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new[]
|
||||
new Container
|
||||
{
|
||||
card = new BeatmapCardMatchmaking
|
||||
Masking = true,
|
||||
CornerRadius = BeatmapCard.CORNER_RADIUS,
|
||||
CornerExponent = 10,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new[]
|
||||
{
|
||||
Action = () =>
|
||||
Content,
|
||||
lighting = new Box
|
||||
{
|
||||
if (AllowSelection)
|
||||
Action?.Invoke(Item);
|
||||
Blending = BlendingParameters.Additive,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0,
|
||||
},
|
||||
},
|
||||
lighting = new Box
|
||||
{
|
||||
Blending = BlendingParameters.Additive,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0,
|
||||
},
|
||||
}
|
||||
},
|
||||
border = new Container
|
||||
{
|
||||
Alpha = 0,
|
||||
Masking = true,
|
||||
CornerRadius = BeatmapCard.CORNER_RADIUS,
|
||||
CornerExponent = 10,
|
||||
Blending = BlendingParameters.Additive,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
BorderThickness = border_width,
|
||||
BorderColour = colourProvider.Light1,
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Radius = 40,
|
||||
Roundness = 300,
|
||||
Colour = colourProvider.Light3.Opacity(0.1f),
|
||||
}
|
||||
},
|
||||
Children = new Drawable[]
|
||||
border = new Container
|
||||
{
|
||||
new Box
|
||||
Alpha = 0,
|
||||
Masking = true,
|
||||
CornerRadius = BeatmapCard.CORNER_RADIUS,
|
||||
CornerExponent = 10,
|
||||
Blending = BlendingParameters.Additive,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
BorderThickness = border_width,
|
||||
BorderColour = colourProvider.Light1,
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
AlwaysPresent = true,
|
||||
Alpha = 0,
|
||||
Colour = Color4.Black,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Type = EdgeEffectType.Glow,
|
||||
Radius = 40,
|
||||
Roundness = 300,
|
||||
Colour = colourProvider.Light3.Opacity(0.1f),
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
AlwaysPresent = true,
|
||||
Alpha = 0,
|
||||
Colour = Color4.Black,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
new HoverClickSounds(),
|
||||
};
|
||||
|
||||
if (Item.ID == -1)
|
||||
card.DisplayRandom();
|
||||
else
|
||||
card.DisplayItem(Item);
|
||||
}
|
||||
|
||||
public void AddUser(APIUser user)
|
||||
{
|
||||
card.AddUser(user);
|
||||
}
|
||||
// TODO: making these abstract for now but avatar overlay should really be owned by the top level class
|
||||
public abstract void AddUser(APIUser user);
|
||||
|
||||
public void RemoveUser(APIUser user)
|
||||
{
|
||||
card.RemoveUser(user);
|
||||
}
|
||||
|
||||
public void DisplayItem(MultiplayerPlaylistItem item)
|
||||
{
|
||||
card.DisplayItem(item);
|
||||
}
|
||||
public abstract void RemoveUser(APIUser user);
|
||||
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
@@ -171,10 +158,11 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.BeatmapSelect
|
||||
lighting.FadeTo(0.5f, 50)
|
||||
.Then()
|
||||
.FadeTo(0.1f, 400);
|
||||
|
||||
Action?.Invoke(Item);
|
||||
}
|
||||
|
||||
// pass through to let the beatmap card handle actual click.
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ShowChosenBorder()
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.BeatmapSelect
|
||||
{
|
||||
public partial class MatchmakingSelectPanelBeatmap : MatchmakingSelectPanel
|
||||
{
|
||||
private readonly APIBeatmap beatmap;
|
||||
private readonly Mod[] mods;
|
||||
|
||||
public MatchmakingSelectPanelBeatmap(MatchmakingPlaylistItem item)
|
||||
: base(item.PlaylistItem)
|
||||
{
|
||||
beatmap = item.Beatmap;
|
||||
mods = item.Mods;
|
||||
}
|
||||
|
||||
private CardContent content = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Add(content = new CardContentBeatmap(beatmap, mods));
|
||||
}
|
||||
|
||||
public override void AddUser(APIUser user)
|
||||
{
|
||||
content.SelectionOverlay.AddUser(user);
|
||||
}
|
||||
|
||||
public override void RemoveUser(APIUser user)
|
||||
{
|
||||
content.SelectionOverlay.RemoveUser(user.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.BeatmapSelect
|
||||
{
|
||||
public partial class MatchmakingSelectPanelRandom : MatchmakingSelectPanel
|
||||
{
|
||||
public MatchmakingSelectPanelRandom(MultiplayerPlaylistItem item)
|
||||
: base(item)
|
||||
{
|
||||
}
|
||||
|
||||
private CardContent content = null!;
|
||||
private readonly List<APIUser> users = new List<APIUser>();
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Add(content = new CardContentRandom());
|
||||
}
|
||||
|
||||
public void RevealBeatmap(APIBeatmap beatmap, Mod[] mods)
|
||||
{
|
||||
content.Expire();
|
||||
|
||||
var flashLayer = new Box { RelativeSizeAxes = Axes.Both };
|
||||
|
||||
AddRange(new Drawable[]
|
||||
{
|
||||
content = new CardContentBeatmap(beatmap, mods),
|
||||
flashLayer,
|
||||
});
|
||||
|
||||
foreach (var user in users)
|
||||
content.SelectionOverlay.AddUser(user);
|
||||
|
||||
flashLayer.FadeOutFromOne(1000, Easing.In);
|
||||
}
|
||||
|
||||
public override void AddUser(APIUser user)
|
||||
{
|
||||
users.Add(user);
|
||||
content.SelectionOverlay.AddUser(user);
|
||||
}
|
||||
|
||||
public override void RemoveUser(APIUser user)
|
||||
{
|
||||
users.Remove(user);
|
||||
content.SelectionOverlay.RemoveUser(user.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
+67
-14
@@ -1,14 +1,23 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Online.Multiplayer.MatchTypes.Matchmaking;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.BeatmapSelect
|
||||
{
|
||||
@@ -18,10 +27,17 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.BeatmapSelect
|
||||
public override Drawable PlayersDisplayArea { get; }
|
||||
|
||||
private readonly BeatmapSelectGrid beatmapSelectGrid;
|
||||
private readonly LoadingSpinner loadingSpinner;
|
||||
|
||||
[Resolved]
|
||||
private MultiplayerClient client { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private RulesetStore rulesetStore { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private BeatmapLookupCache beatmapLookupCache { get; set; } = null!;
|
||||
|
||||
public SubScreenBeatmapSelect()
|
||||
{
|
||||
InternalChildren = new Drawable[]
|
||||
@@ -30,9 +46,19 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.BeatmapSelect
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding { Horizontal = 200 },
|
||||
Child = beatmapSelectGrid = new BeatmapSelectGrid
|
||||
Children = new Drawable[]
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
beatmapSelectGrid = new BeatmapSelectGrid
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
loadingSpinner = new LoadingSpinner
|
||||
{
|
||||
Size = new Vector2(64),
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
State = { Value = Visibility.Visible }
|
||||
}
|
||||
},
|
||||
},
|
||||
new Container
|
||||
@@ -50,25 +76,53 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.BeatmapSelect
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
client.ItemAdded += onItemAdded;
|
||||
|
||||
foreach (var item in client.Room!.Playlist)
|
||||
onItemAdded(item);
|
||||
|
||||
beatmapSelectGrid.ItemSelected += item => client.MatchmakingToggleSelection(item.ID);
|
||||
|
||||
client.MatchmakingItemSelected += onItemSelected;
|
||||
client.MatchmakingItemDeselected += onItemDeselected;
|
||||
client.SettingsChanged += onSettingsChanged;
|
||||
|
||||
Debug.Assert(client.Room != null);
|
||||
|
||||
loadItems(client.Room.Playlist.ToArray()).FireAndForget();
|
||||
}
|
||||
|
||||
private void onItemAdded(MultiplayerPlaylistItem item) => Scheduler.Add(() =>
|
||||
private async Task loadItems(MultiplayerPlaylistItem[] items)
|
||||
{
|
||||
if (item.Expired)
|
||||
return;
|
||||
var beatmaps = await beatmapLookupCache.GetBeatmapsAsync(items.Select(it => it.BeatmapID).ToArray()).ConfigureAwait(false);
|
||||
var matchmakingItems = new List<MatchmakingPlaylistItem>();
|
||||
|
||||
beatmapSelectGrid.AddItem(item);
|
||||
});
|
||||
foreach (var entry in items.Zip(beatmaps))
|
||||
{
|
||||
var (item, beatmap) = entry;
|
||||
|
||||
beatmap ??= new APIBeatmap
|
||||
{
|
||||
BeatmapSet = new APIBeatmapSet
|
||||
{
|
||||
Title = "unknown beatmap",
|
||||
TitleUnicode = "unknown beatmap",
|
||||
Artist = "unknown artist",
|
||||
ArtistUnicode = "unknown artist",
|
||||
}
|
||||
};
|
||||
|
||||
beatmap.StarRating = item.StarRating;
|
||||
|
||||
Ruleset? ruleset = rulesetStore.GetRuleset(item.RulesetID)?.CreateInstance();
|
||||
|
||||
Debug.Assert(ruleset != null);
|
||||
|
||||
Mod[] mods = item.RequiredMods.Select(m => m.ToMod(ruleset)).ToArray();
|
||||
|
||||
matchmakingItems.Add(new MatchmakingPlaylistItem(item, beatmap, mods));
|
||||
}
|
||||
|
||||
Scheduler.Add(() =>
|
||||
{
|
||||
loadingSpinner.Hide();
|
||||
beatmapSelectGrid.AddItems(matchmakingItems);
|
||||
});
|
||||
}
|
||||
|
||||
private void onItemSelected(int userId, long itemId)
|
||||
{
|
||||
@@ -104,7 +158,6 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.BeatmapSelect
|
||||
|
||||
if (client.IsNotNull())
|
||||
{
|
||||
client.ItemAdded -= onItemAdded;
|
||||
client.MatchmakingItemSelected -= onItemSelected;
|
||||
client.MatchmakingItemDeselected -= onItemDeselected;
|
||||
client.SettingsChanged -= onSettingsChanged;
|
||||
|
||||
Reference in New Issue
Block a user