mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 15:33:05 +08:00
Merge pull request #11641 from smoogipoo/freemods
Add support for optional per-user mods in multiplayer (aka freemod)
This commit is contained in:
commit
9258836f10
@ -8,6 +8,8 @@ using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer.Participants;
|
||||
using osu.Game.Users;
|
||||
using osuTK;
|
||||
@ -123,5 +125,32 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUserWithMods()
|
||||
{
|
||||
AddStep("add user", () =>
|
||||
{
|
||||
Client.AddUser(new User
|
||||
{
|
||||
Id = 0,
|
||||
Username = "User 0",
|
||||
CurrentModeRank = RNG.Next(1, 100000),
|
||||
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
|
||||
});
|
||||
|
||||
Client.ChangeUserMods(0, new Mod[]
|
||||
{
|
||||
new OsuModHardRock(),
|
||||
new OsuModDifficultyAdjust { ApproachRate = { Value = 1 } }
|
||||
});
|
||||
});
|
||||
|
||||
for (var i = MultiplayerUserState.Idle; i < MultiplayerUserState.Results; i++)
|
||||
{
|
||||
var state = i;
|
||||
AddStep($"set state: {state}", () => Client.ChangeUserState(0, state));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,11 +19,11 @@ using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Screens.OnlinePlay.Components;
|
||||
using osu.Game.Screens.Select;
|
||||
using osu.Game.Screens.OnlinePlay.Playlists;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public class TestSceneMatchSongSelect : RoomTestScene
|
||||
public class TestScenePlaylistsSongSelect : RoomTestScene
|
||||
{
|
||||
[Resolved]
|
||||
private BeatmapManager beatmapManager { get; set; }
|
||||
@ -32,7 +32,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
private RulesetStore rulesets;
|
||||
|
||||
private TestMatchSongSelect songSelect;
|
||||
private TestPlaylistsSongSelect songSelect;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(GameHost host, AudioManager audio)
|
||||
@ -89,7 +89,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
Beatmap.SetDefault();
|
||||
});
|
||||
|
||||
AddStep("create song select", () => LoadScreen(songSelect = new TestMatchSongSelect()));
|
||||
AddStep("create song select", () => LoadScreen(songSelect = new TestPlaylistsSongSelect()));
|
||||
AddUntilStep("wait for present", () => songSelect.IsCurrentScreen());
|
||||
}
|
||||
|
||||
@ -176,7 +176,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddAssert("item has rate 1.5", () => Precision.AlmostEquals(1.5, ((OsuModDoubleTime)Room.Playlist.First().RequiredMods[0]).SpeedChange.Value));
|
||||
}
|
||||
|
||||
private class TestMatchSongSelect : MatchSongSelect
|
||||
private class TestPlaylistsSongSelect : PlaylistsSongSelect
|
||||
{
|
||||
public new MatchBeatmapDetailArea BeatmapDetails => (MatchBeatmapDetailArea)base.BeatmapDetails;
|
||||
}
|
@ -1,7 +1,9 @@
|
||||
// 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.Threading.Tasks;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.Rooms;
|
||||
|
||||
namespace osu.Game.Online.Multiplayer
|
||||
@ -55,6 +57,13 @@ namespace osu.Game.Online.Multiplayer
|
||||
/// <param name="beatmapAvailability">The new beatmap availability state of the user.</param>
|
||||
Task UserBeatmapAvailabilityChanged(int userId, BeatmapAvailability beatmapAvailability);
|
||||
|
||||
/// <summary>
|
||||
/// Signals that a user in this room changed their local mods.
|
||||
/// </summary>
|
||||
/// <param name="userId">The ID of the user whose mods have changed.</param>
|
||||
/// <param name="mods">The user's new local mods.</param>
|
||||
Task UserModsChanged(int userId, IEnumerable<APIMod> mods);
|
||||
|
||||
/// <summary>
|
||||
/// Signals that a match is to be started. This will *only* be sent to clients which are to begin loading at this point.
|
||||
/// </summary>
|
||||
|
@ -1,7 +1,9 @@
|
||||
// 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.Threading.Tasks;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.Rooms;
|
||||
|
||||
namespace osu.Game.Online.Multiplayer
|
||||
@ -47,6 +49,12 @@ namespace osu.Game.Online.Multiplayer
|
||||
/// <param name="newBeatmapAvailability">The proposed new beatmap availability state.</param>
|
||||
Task ChangeBeatmapAvailability(BeatmapAvailability newBeatmapAvailability);
|
||||
|
||||
/// <summary>
|
||||
/// Change the local user's mods in the currently joined room.
|
||||
/// </summary>
|
||||
/// <param name="newMods">The proposed new mods, excluding any required by the room itself.</param>
|
||||
Task ChangeUserMods(IEnumerable<APIMod> newMods);
|
||||
|
||||
/// <summary>
|
||||
/// As the host of a room, start the match.
|
||||
/// </summary>
|
||||
|
@ -4,6 +4,7 @@
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@ -91,6 +92,7 @@ namespace osu.Game.Online.Multiplayer
|
||||
connection.On(nameof(IMultiplayerClient.LoadRequested), ((IMultiplayerClient)this).LoadRequested);
|
||||
connection.On(nameof(IMultiplayerClient.MatchStarted), ((IMultiplayerClient)this).MatchStarted);
|
||||
connection.On(nameof(IMultiplayerClient.ResultsReady), ((IMultiplayerClient)this).ResultsReady);
|
||||
connection.On<int, IEnumerable<APIMod>>(nameof(IMultiplayerClient.UserModsChanged), ((IMultiplayerClient)this).UserModsChanged);
|
||||
|
||||
connection.Closed += async ex =>
|
||||
{
|
||||
@ -189,6 +191,14 @@ namespace osu.Game.Online.Multiplayer
|
||||
return connection.InvokeAsync(nameof(IMultiplayerServer.ChangeBeatmapAvailability), newBeatmapAvailability);
|
||||
}
|
||||
|
||||
public override Task ChangeUserMods(IEnumerable<APIMod> newMods)
|
||||
{
|
||||
if (!isConnected.Value)
|
||||
return Task.CompletedTask;
|
||||
|
||||
return connection.InvokeAsync(nameof(IMultiplayerServer.ChangeUserMods), newMods);
|
||||
}
|
||||
|
||||
public override Task StartMatch()
|
||||
{
|
||||
if (!isConnected.Value)
|
||||
|
@ -30,15 +30,24 @@ namespace osu.Game.Online.Multiplayer
|
||||
|
||||
[NotNull]
|
||||
[Key(4)]
|
||||
public IEnumerable<APIMod> Mods { get; set; } = Enumerable.Empty<APIMod>();
|
||||
public IEnumerable<APIMod> RequiredMods { get; set; } = Enumerable.Empty<APIMod>();
|
||||
|
||||
[NotNull]
|
||||
[Key(5)]
|
||||
public IEnumerable<APIMod> AllowedMods { get; set; } = Enumerable.Empty<APIMod>();
|
||||
|
||||
public bool Equals(MultiplayerRoomSettings other)
|
||||
=> BeatmapID == other.BeatmapID
|
||||
&& BeatmapChecksum == other.BeatmapChecksum
|
||||
&& Mods.SequenceEqual(other.Mods)
|
||||
&& RequiredMods.SequenceEqual(other.RequiredMods)
|
||||
&& AllowedMods.SequenceEqual(other.AllowedMods)
|
||||
&& RulesetID == other.RulesetID
|
||||
&& Name.Equals(other.Name, StringComparison.Ordinal);
|
||||
|
||||
public override string ToString() => $"Name:{Name} Beatmap:{BeatmapID} ({BeatmapChecksum}) Mods:{string.Join(',', Mods)} Ruleset:{RulesetID}";
|
||||
public override string ToString() => $"Name:{Name}"
|
||||
+ $" Beatmap:{BeatmapID} ({BeatmapChecksum})"
|
||||
+ $" RequiredMods:{string.Join(',', RequiredMods)}"
|
||||
+ $" AllowedMods:{string.Join(',', AllowedMods)}"
|
||||
+ $" Ruleset:{RulesetID}";
|
||||
}
|
||||
}
|
||||
|
@ -4,8 +4,12 @@
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using MessagePack;
|
||||
using Newtonsoft.Json;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Users;
|
||||
|
||||
@ -27,6 +31,13 @@ namespace osu.Game.Online.Multiplayer
|
||||
[Key(2)]
|
||||
public BeatmapAvailability BeatmapAvailability { get; set; } = BeatmapAvailability.LocallyAvailable();
|
||||
|
||||
/// <summary>
|
||||
/// Any mods applicable only to the local user.
|
||||
/// </summary>
|
||||
[Key(3)]
|
||||
[NotNull]
|
||||
public IEnumerable<APIMod> Mods { get; set; } = Enumerable.Empty<APIMod>();
|
||||
|
||||
[IgnoreMember]
|
||||
public User? User { get; set; }
|
||||
|
||||
|
@ -21,6 +21,7 @@ using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Online.Rooms.RoomStatuses;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Users;
|
||||
using osu.Game.Utils;
|
||||
|
||||
@ -191,7 +192,8 @@ namespace osu.Game.Online.Multiplayer
|
||||
BeatmapID = item.GetOr(existingPlaylistItem).BeatmapID,
|
||||
BeatmapChecksum = item.GetOr(existingPlaylistItem).Beatmap.Value.MD5Hash,
|
||||
RulesetID = item.GetOr(existingPlaylistItem).RulesetID,
|
||||
Mods = item.HasValue ? item.Value.AsNonNull().RequiredMods.Select(m => new APIMod(m)).ToList() : Room.Settings.Mods
|
||||
RequiredMods = item.HasValue ? item.Value.AsNonNull().RequiredMods.Select(m => new APIMod(m)).ToList() : Room.Settings.RequiredMods,
|
||||
AllowedMods = item.HasValue ? item.Value.AsNonNull().AllowedMods.Select(m => new APIMod(m)).ToList() : Room.Settings.AllowedMods
|
||||
});
|
||||
}
|
||||
|
||||
@ -229,6 +231,14 @@ namespace osu.Game.Online.Multiplayer
|
||||
|
||||
public abstract Task ChangeBeatmapAvailability(BeatmapAvailability newBeatmapAvailability);
|
||||
|
||||
/// <summary>
|
||||
/// Change the local user's mods in the currently joined room.
|
||||
/// </summary>
|
||||
/// <param name="newMods">The proposed new mods, excluding any required by the room itself.</param>
|
||||
public Task ChangeUserMods(IEnumerable<Mod> newMods) => ChangeUserMods(newMods.Select(m => new APIMod(m)).ToList());
|
||||
|
||||
public abstract Task ChangeUserMods(IEnumerable<APIMod> newMods);
|
||||
|
||||
public abstract Task StartMatch();
|
||||
|
||||
Task IMultiplayerClient.RoomStateChanged(MultiplayerRoomState state)
|
||||
@ -377,6 +387,27 @@ namespace osu.Game.Online.Multiplayer
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task UserModsChanged(int userId, IEnumerable<APIMod> mods)
|
||||
{
|
||||
if (Room == null)
|
||||
return Task.CompletedTask;
|
||||
|
||||
Scheduler.Add(() =>
|
||||
{
|
||||
var user = Room?.Users.SingleOrDefault(u => u.UserID == userId);
|
||||
|
||||
// errors here are not critical - user mods are mostly for display.
|
||||
if (user == null)
|
||||
return;
|
||||
|
||||
user.Mods = mods;
|
||||
|
||||
RoomUpdated?.Invoke();
|
||||
}, false);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
Task IMultiplayerClient.LoadRequested()
|
||||
{
|
||||
if (Room == null)
|
||||
@ -500,7 +531,8 @@ namespace osu.Game.Online.Multiplayer
|
||||
beatmap.MD5Hash = settings.BeatmapChecksum;
|
||||
|
||||
var ruleset = rulesets.GetRuleset(settings.RulesetID).CreateInstance();
|
||||
var mods = settings.Mods.Select(m => m.ToMod(ruleset));
|
||||
var mods = settings.RequiredMods.Select(m => m.ToMod(ruleset));
|
||||
var allowedMods = settings.AllowedMods.Select(m => m.ToMod(ruleset));
|
||||
|
||||
PlaylistItem playlistItem = new PlaylistItem
|
||||
{
|
||||
@ -510,6 +542,7 @@ namespace osu.Game.Online.Multiplayer
|
||||
};
|
||||
|
||||
playlistItem.RequiredMods.AddRange(mods);
|
||||
playlistItem.AllowedMods.AddRange(allowedMods);
|
||||
|
||||
apiRoom.Playlist.Clear(); // Clearing should be unnecessary, but here for sanity.
|
||||
apiRoom.Playlist.Add(playlistItem);
|
||||
|
63
osu.Game/Screens/OnlinePlay/FooterButtonFreeMods.cs
Normal file
63
osu.Game/Screens/OnlinePlay/FooterButtonFreeMods.cs
Normal file
@ -0,0 +1,63 @@
|
||||
// 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.Graphics;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using osu.Game.Screens.Select;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay
|
||||
{
|
||||
public class FooterButtonFreeMods : FooterButton, IHasCurrentValue<IReadOnlyList<Mod>>
|
||||
{
|
||||
public Bindable<IReadOnlyList<Mod>> Current
|
||||
{
|
||||
get => modDisplay.Current;
|
||||
set => modDisplay.Current = value;
|
||||
}
|
||||
|
||||
private readonly ModDisplay modDisplay;
|
||||
|
||||
public FooterButtonFreeMods()
|
||||
{
|
||||
ButtonContentContainer.Add(modDisplay = new ModDisplay
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
DisplayUnrankedText = false,
|
||||
Scale = new Vector2(0.8f),
|
||||
ExpansionMode = ExpansionMode.AlwaysContracted,
|
||||
});
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
SelectedColour = colours.Yellow;
|
||||
DeselectedColour = SelectedColour.Opacity(0.5f);
|
||||
Text = @"freemods";
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
Current.BindValueChanged(_ => updateModDisplay(), true);
|
||||
}
|
||||
|
||||
private void updateModDisplay()
|
||||
{
|
||||
if (Current.Value?.Count > 0)
|
||||
modDisplay.FadeIn();
|
||||
else
|
||||
modDisplay.FadeOut();
|
||||
}
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
@ -29,6 +30,11 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
||||
[Resolved(typeof(Room), nameof(Room.Playlist))]
|
||||
protected BindableList<PlaylistItem> Playlist { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Any mods applied by/to the local user.
|
||||
/// </summary>
|
||||
protected readonly Bindable<IReadOnlyList<Mod>> UserMods = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>());
|
||||
|
||||
[Resolved]
|
||||
private MusicController music { get; set; }
|
||||
|
||||
@ -68,6 +74,8 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
||||
|
||||
managerUpdated = beatmapManager.ItemUpdated.GetBoundCopy();
|
||||
managerUpdated.BindValueChanged(beatmapUpdated);
|
||||
|
||||
UserMods.BindValueChanged(_ => updateMods());
|
||||
}
|
||||
|
||||
public override void OnEntering(IScreen last)
|
||||
@ -86,6 +94,7 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
||||
{
|
||||
base.OnResuming(last);
|
||||
beginHandlingTrack();
|
||||
updateMods();
|
||||
}
|
||||
|
||||
public override bool OnExiting(IScreen next)
|
||||
@ -108,12 +117,17 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
||||
{
|
||||
updateWorkingBeatmap();
|
||||
|
||||
var item = SelectedItem.Value;
|
||||
if (SelectedItem.Value == null)
|
||||
return;
|
||||
|
||||
Mods.Value = item?.RequiredMods?.ToArray() ?? Array.Empty<Mod>();
|
||||
// Remove any user mods that are no longer allowed.
|
||||
UserMods.Value = UserMods.Value
|
||||
.Where(m => SelectedItem.Value.AllowedMods.Any(a => m.GetType() == a.GetType()))
|
||||
.ToList();
|
||||
|
||||
if (item?.Ruleset != null)
|
||||
Ruleset.Value = item.Ruleset.Value;
|
||||
updateMods();
|
||||
|
||||
Ruleset.Value = SelectedItem.Value.Ruleset.Value;
|
||||
}
|
||||
|
||||
private void beatmapUpdated(ValueChangedEvent<WeakReference<BeatmapSetInfo>> weakSet) => Schedule(updateWorkingBeatmap);
|
||||
@ -128,6 +142,14 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
||||
Beatmap.Value = beatmapManager.GetWorkingBeatmap(localBeatmap);
|
||||
}
|
||||
|
||||
private void updateMods()
|
||||
{
|
||||
if (SelectedItem.Value == null)
|
||||
return;
|
||||
|
||||
Mods.Value = UserMods.Value.Concat(SelectedItem.Value.RequiredMods).ToList();
|
||||
}
|
||||
|
||||
private void beginHandlingTrack()
|
||||
{
|
||||
Beatmap.BindValueChanged(applyLoopingToTrack, true);
|
||||
|
@ -1,70 +1,32 @@
|
||||
// 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 Humanizer;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Overlays.Mods;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Screens.Select;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
{
|
||||
public class MultiplayerMatchSongSelect : SongSelect, IOnlinePlaySubScreen
|
||||
public class MultiplayerMatchSongSelect : OnlinePlaySongSelect
|
||||
{
|
||||
public string ShortTitle => "song selection";
|
||||
|
||||
public override string Title => ShortTitle.Humanize();
|
||||
|
||||
[Resolved(typeof(Room), nameof(Room.Playlist))]
|
||||
private BindableList<PlaylistItem> playlist { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private StatefulMultiplayerClient client { get; set; }
|
||||
|
||||
private LoadingLayer loadingLayer;
|
||||
|
||||
private WorkingBeatmap initialBeatmap;
|
||||
private RulesetInfo initialRuleset;
|
||||
private IReadOnlyList<Mod> initialMods;
|
||||
|
||||
private bool itemSelected;
|
||||
|
||||
public MultiplayerMatchSongSelect()
|
||||
{
|
||||
Padding = new MarginPadding { Horizontal = HORIZONTAL_OVERFLOW_PADDING };
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
AddInternal(loadingLayer = new LoadingLayer(true));
|
||||
initialBeatmap = Beatmap.Value;
|
||||
initialRuleset = Ruleset.Value;
|
||||
initialMods = Mods.Value.ToList();
|
||||
}
|
||||
|
||||
protected override bool OnStart()
|
||||
protected override void SelectItem(PlaylistItem item)
|
||||
{
|
||||
itemSelected = true;
|
||||
var item = new PlaylistItem();
|
||||
|
||||
item.Beatmap.Value = Beatmap.Value.BeatmapInfo;
|
||||
item.Ruleset.Value = Ruleset.Value;
|
||||
|
||||
item.RequiredMods.Clear();
|
||||
item.RequiredMods.AddRange(Mods.Value.Select(m => m.CreateCopy()));
|
||||
|
||||
// If the client is already in a room, update via the client.
|
||||
// Otherwise, update the playlist directly in preparation for it to be submitted to the API on match creation.
|
||||
if (client.Room != null)
|
||||
@ -89,30 +51,14 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
}
|
||||
else
|
||||
{
|
||||
playlist.Clear();
|
||||
playlist.Add(item);
|
||||
Playlist.Clear();
|
||||
Playlist.Add(item);
|
||||
this.Exit();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool OnExiting(IScreen next)
|
||||
{
|
||||
if (!itemSelected)
|
||||
{
|
||||
Beatmap.Value = initialBeatmap;
|
||||
Ruleset.Value = initialRuleset;
|
||||
Mods.Value = initialMods;
|
||||
}
|
||||
|
||||
return base.OnExiting(next);
|
||||
}
|
||||
|
||||
protected override BeatmapDetailArea CreateBeatmapDetailArea() => new PlayBeatmapDetailArea();
|
||||
|
||||
protected override ModSelectOverlay CreateModSelectOverlay() => new SoloModSelectOverlay { IsValidMod = isValidMod };
|
||||
|
||||
private bool isValidMod(Mod mod) => !(mod is ModAutoplay) && (mod as MultiMod)?.Mods.Any(mm => mm is ModAutoplay) != true;
|
||||
protected override bool IsValidFreeMod(Mod mod) => base.IsValidFreeMod(mod) && !(mod is ModTimeRamp) && !(mod is ModRateAdjust) && !mod.RequiresConfiguration;
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
@ -13,12 +14,16 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Overlays.Mods;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Screens.OnlinePlay.Components;
|
||||
using osu.Game.Screens.OnlinePlay.Match;
|
||||
using osu.Game.Screens.OnlinePlay.Match.Components;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer.Match;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer.Participants;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using osu.Game.Users;
|
||||
using osuTK;
|
||||
using ParticipantsList = osu.Game.Screens.OnlinePlay.Multiplayer.Participants.ParticipantsList;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
@ -36,7 +41,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
[Resolved]
|
||||
private OngoingOperationTracker ongoingOperationTracker { get; set; }
|
||||
|
||||
private ModSelectOverlay userModsSelectOverlay;
|
||||
private MultiplayerMatchSettingsOverlay settingsOverlay;
|
||||
private Drawable userModsSection;
|
||||
|
||||
private IBindable<bool> isConnected;
|
||||
|
||||
@ -88,13 +95,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
OpenSettings = () => settingsOverlay.Show()
|
||||
}
|
||||
},
|
||||
new Drawable[]
|
||||
{
|
||||
new GridContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Content = new[]
|
||||
{
|
||||
new Drawable[]
|
||||
{
|
||||
new Container
|
||||
@ -102,6 +102,20 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding { Horizontal = 5, Vertical = 10 },
|
||||
Child = new GridContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
ColumnDimensions = new[]
|
||||
{
|
||||
new Dimension(GridSizeMode.Relative, size: 0.5f, maxSize: 400),
|
||||
new Dimension(),
|
||||
new Dimension(GridSizeMode.Relative, size: 0.5f, maxSize: 600),
|
||||
},
|
||||
Content = new[]
|
||||
{
|
||||
new Drawable[]
|
||||
{
|
||||
// Main left column
|
||||
new GridContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
RowDimensions = new[]
|
||||
@ -119,20 +133,63 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
// Spacer
|
||||
null,
|
||||
// Main right column
|
||||
new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Children = new[]
|
||||
{
|
||||
new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Padding = new MarginPadding { Horizontal = 5 },
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new OverlinedHeader("Beatmap"),
|
||||
new BeatmapSelectionControl { RelativeSizeAxes = Axes.X }
|
||||
}
|
||||
},
|
||||
userModsSection = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Margin = new MarginPadding { Top = 10 },
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new OverlinedHeader("Extra mods"),
|
||||
new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(10, 0),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new PurpleTriangleButton
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Width = 90,
|
||||
Text = "Select",
|
||||
Action = () => userModsSelectOverlay.Show()
|
||||
},
|
||||
new ModDisplay
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
DisplayUnrankedText = false,
|
||||
Current = UserMods,
|
||||
Scale = new Vector2(0.8f),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -173,6 +230,18 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
new Dimension(GridSizeMode.AutoSize),
|
||||
}
|
||||
},
|
||||
new Container
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Height = 0.5f,
|
||||
Child = userModsSelectOverlay = new UserModSelectOverlay
|
||||
{
|
||||
SelectedMods = { BindTarget = UserMods },
|
||||
IsValidMod = _ => false
|
||||
}
|
||||
},
|
||||
settingsOverlay = new MultiplayerMatchSettingsOverlay
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
@ -199,6 +268,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
base.LoadComplete();
|
||||
|
||||
Playlist.BindCollectionChanged(onPlaylistChanged, true);
|
||||
UserMods.BindValueChanged(onUserModsChanged);
|
||||
|
||||
client.LoadRequested += onLoadRequested;
|
||||
|
||||
@ -218,10 +288,39 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
return true;
|
||||
}
|
||||
|
||||
if (userModsSelectOverlay.State.Value == Visibility.Visible)
|
||||
{
|
||||
userModsSelectOverlay.Hide();
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnBackButton();
|
||||
}
|
||||
|
||||
private void onPlaylistChanged(object sender, NotifyCollectionChangedEventArgs e) => SelectedItem.Value = Playlist.FirstOrDefault();
|
||||
private void onPlaylistChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
SelectedItem.Value = Playlist.FirstOrDefault();
|
||||
|
||||
if (SelectedItem.Value?.AllowedMods.Any() != true)
|
||||
{
|
||||
userModsSection.Hide();
|
||||
userModsSelectOverlay.Hide();
|
||||
userModsSelectOverlay.IsValidMod = _ => false;
|
||||
}
|
||||
else
|
||||
{
|
||||
userModsSection.Show();
|
||||
userModsSelectOverlay.IsValidMod = m => SelectedItem.Value.AllowedMods.Any(a => a.GetType() == m.GetType());
|
||||
}
|
||||
}
|
||||
|
||||
private void onUserModsChanged(ValueChangedEvent<IReadOnlyList<Mod>> mods)
|
||||
{
|
||||
if (client.Room == null)
|
||||
return;
|
||||
|
||||
client.ChangeUserMods(mods.NewValue);
|
||||
}
|
||||
|
||||
private void onReadyClick()
|
||||
{
|
||||
@ -274,5 +373,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
if (client != null)
|
||||
client.LoadRequested -= onLoadRequested;
|
||||
}
|
||||
|
||||
private class UserModSelectOverlay : ModSelectOverlay
|
||||
{
|
||||
public UserModSelectOverlay()
|
||||
{
|
||||
CustomiseButton.Alpha = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
// 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.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
@ -15,6 +16,8 @@ using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using osu.Game.Users;
|
||||
using osu.Game.Users.Drawables;
|
||||
using osuTK;
|
||||
@ -29,6 +32,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private RulesetStore rulesets { get; set; }
|
||||
|
||||
private ModDisplay userModsDisplay;
|
||||
private StateDisplay userStateDisplay;
|
||||
private SpriteIcon crown;
|
||||
|
||||
@ -121,6 +128,19 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants
|
||||
}
|
||||
}
|
||||
},
|
||||
new Container
|
||||
{
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Margin = new MarginPadding { Right = 70 },
|
||||
Child = userModsDisplay = new ModDisplay
|
||||
{
|
||||
Scale = new Vector2(0.5f),
|
||||
ExpansionMode = ExpansionMode.AlwaysContracted,
|
||||
DisplayUnrankedText = false,
|
||||
}
|
||||
},
|
||||
userStateDisplay = new StateDisplay
|
||||
{
|
||||
Anchor = Anchor.CentreRight,
|
||||
@ -142,7 +162,10 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants
|
||||
|
||||
const double fade_time = 50;
|
||||
|
||||
var ruleset = rulesets.GetRuleset(Room.Settings.RulesetID).CreateInstance();
|
||||
|
||||
userStateDisplay.Status = User.State;
|
||||
userModsDisplay.Current.Value = User.Mods.Select(m => m.ToMod(ruleset)).ToList();
|
||||
|
||||
if (Room.Host?.Equals(User) == true)
|
||||
crown.FadeIn(fade_time);
|
||||
|
154
osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs
Normal file
154
osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs
Normal file
@ -0,0 +1,154 @@
|
||||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Humanizer;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Overlays.Mods;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Screens.Select;
|
||||
using osu.Game.Utils;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay
|
||||
{
|
||||
public abstract class OnlinePlaySongSelect : SongSelect, IOnlinePlaySubScreen
|
||||
{
|
||||
public string ShortTitle => "song selection";
|
||||
|
||||
public override string Title => ShortTitle.Humanize();
|
||||
|
||||
public override bool AllowEditing => false;
|
||||
|
||||
[Resolved(typeof(Room), nameof(Room.Playlist))]
|
||||
protected BindableList<PlaylistItem> Playlist { get; private set; }
|
||||
|
||||
private readonly Bindable<IReadOnlyList<Mod>> freeMods = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>());
|
||||
private readonly FreeModSelectOverlay freeModSelectOverlay;
|
||||
|
||||
private WorkingBeatmap initialBeatmap;
|
||||
private RulesetInfo initialRuleset;
|
||||
private IReadOnlyList<Mod> initialMods;
|
||||
private bool itemSelected;
|
||||
|
||||
protected OnlinePlaySongSelect()
|
||||
{
|
||||
Padding = new MarginPadding { Horizontal = HORIZONTAL_OVERFLOW_PADDING };
|
||||
|
||||
freeModSelectOverlay = new FreeModSelectOverlay
|
||||
{
|
||||
SelectedMods = { BindTarget = freeMods },
|
||||
IsValidMod = IsValidFreeMod,
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
initialBeatmap = Beatmap.Value;
|
||||
initialRuleset = Ruleset.Value;
|
||||
initialMods = Mods.Value.ToList();
|
||||
|
||||
FooterPanels.Add(freeModSelectOverlay);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
// At this point, Mods contains both the required and allowed mods. For selection purposes, it should only contain the required mods.
|
||||
// Similarly, freeMods is currently empty but should only contain the allowed mods.
|
||||
Mods.Value = Playlist.FirstOrDefault()?.RequiredMods.Select(m => m.CreateCopy()).ToArray() ?? Array.Empty<Mod>();
|
||||
freeMods.Value = Playlist.FirstOrDefault()?.AllowedMods.Select(m => m.CreateCopy()).ToArray() ?? Array.Empty<Mod>();
|
||||
|
||||
Ruleset.BindValueChanged(onRulesetChanged);
|
||||
}
|
||||
|
||||
private void onRulesetChanged(ValueChangedEvent<RulesetInfo> ruleset)
|
||||
{
|
||||
freeMods.Value = Array.Empty<Mod>();
|
||||
}
|
||||
|
||||
protected sealed override bool OnStart()
|
||||
{
|
||||
itemSelected = true;
|
||||
|
||||
var item = new PlaylistItem();
|
||||
|
||||
item.Beatmap.Value = Beatmap.Value.BeatmapInfo;
|
||||
item.Ruleset.Value = Ruleset.Value;
|
||||
|
||||
item.RequiredMods.Clear();
|
||||
item.RequiredMods.AddRange(Mods.Value.Select(m => m.CreateCopy()));
|
||||
|
||||
item.AllowedMods.Clear();
|
||||
item.AllowedMods.AddRange(freeMods.Value.Select(m => m.CreateCopy()));
|
||||
|
||||
SelectItem(item);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the user has requested a selection of a beatmap.
|
||||
/// </summary>
|
||||
/// <param name="item">The resultant <see cref="PlaylistItem"/>. This item has not yet been added to the <see cref="Room"/>'s.</param>
|
||||
protected abstract void SelectItem(PlaylistItem item);
|
||||
|
||||
public override bool OnBackButton()
|
||||
{
|
||||
if (freeModSelectOverlay.State.Value == Visibility.Visible)
|
||||
{
|
||||
freeModSelectOverlay.Hide();
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnBackButton();
|
||||
}
|
||||
|
||||
public override bool OnExiting(IScreen next)
|
||||
{
|
||||
if (!itemSelected)
|
||||
{
|
||||
Beatmap.Value = initialBeatmap;
|
||||
Ruleset.Value = initialRuleset;
|
||||
Mods.Value = initialMods;
|
||||
}
|
||||
|
||||
return base.OnExiting(next);
|
||||
}
|
||||
|
||||
protected override ModSelectOverlay CreateModSelectOverlay() => new SoloModSelectOverlay
|
||||
{
|
||||
IsValidMod = IsValidMod
|
||||
};
|
||||
|
||||
protected override IEnumerable<(FooterButton, OverlayContainer)> CreateFooterButtons()
|
||||
{
|
||||
var buttons = base.CreateFooterButtons().ToList();
|
||||
buttons.Insert(buttons.FindIndex(b => b.Item1 is FooterButtonMods) + 1, (new FooterButtonFreeMods { Current = freeMods }, freeModSelectOverlay));
|
||||
return buttons;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether a given <see cref="Mod"/> is valid for global selection.
|
||||
/// </summary>
|
||||
/// <param name="mod">The <see cref="Mod"/> to check.</param>
|
||||
/// <returns>Whether <paramref name="mod"/> is a valid mod for online play.</returns>
|
||||
protected virtual bool IsValidMod(Mod mod) => mod.HasImplementation && !ModUtils.FlattenMod(mod).Any(m => m is ModAutoplay);
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether a given <see cref="Mod"/> is valid for per-player free-mod selection.
|
||||
/// </summary>
|
||||
/// <param name="mod">The <see cref="Mod"/> to check.</param>
|
||||
/// <returns>Whether <paramref name="mod"/> is a selectable free-mod.</returns>
|
||||
protected virtual bool IsValidFreeMod(Mod mod) => IsValidMod(mod);
|
||||
}
|
||||
}
|
@ -13,7 +13,6 @@ using osu.Game.Online.Rooms;
|
||||
using osu.Game.Screens.OnlinePlay.Components;
|
||||
using osu.Game.Screens.OnlinePlay.Match;
|
||||
using osu.Game.Screens.OnlinePlay.Match.Components;
|
||||
using osu.Game.Screens.Select;
|
||||
using osu.Game.Users;
|
||||
using Footer = osu.Game.Screens.OnlinePlay.Match.Components.Footer;
|
||||
|
||||
@ -188,7 +187,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
settingsOverlay = new PlaylistsMatchSettingsOverlay
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
EditPlaylist = () => this.Push(new MatchSongSelect()),
|
||||
EditPlaylist = () => this.Push(new PlaylistsSongSelect()),
|
||||
State = { Value = roomId.Value == null ? Visibility.Visible : Visibility.Hidden }
|
||||
}
|
||||
};
|
||||
|
@ -1,48 +1,27 @@
|
||||
// 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 Humanizer;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Overlays.Mods;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Screens.OnlinePlay;
|
||||
using osu.Game.Screens.OnlinePlay.Components;
|
||||
using osu.Game.Screens.Select;
|
||||
|
||||
namespace osu.Game.Screens.Select
|
||||
namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
{
|
||||
public class MatchSongSelect : SongSelect, IOnlinePlaySubScreen
|
||||
public class PlaylistsSongSelect : OnlinePlaySongSelect
|
||||
{
|
||||
public Action<PlaylistItem> Selected;
|
||||
|
||||
public string ShortTitle => "song selection";
|
||||
public override string Title => ShortTitle.Humanize();
|
||||
|
||||
public override bool AllowEditing => false;
|
||||
|
||||
[Resolved(typeof(Room), nameof(Room.Playlist))]
|
||||
protected BindableList<PlaylistItem> Playlist { get; private set; }
|
||||
|
||||
[Resolved]
|
||||
private BeatmapManager beatmaps { get; set; }
|
||||
|
||||
public MatchSongSelect()
|
||||
{
|
||||
Padding = new MarginPadding { Horizontal = HORIZONTAL_OVERFLOW_PADDING };
|
||||
}
|
||||
|
||||
protected override BeatmapDetailArea CreateBeatmapDetailArea() => new MatchBeatmapDetailArea
|
||||
{
|
||||
CreateNewItem = createNewItem
|
||||
};
|
||||
|
||||
protected override bool OnStart()
|
||||
protected override void SelectItem(PlaylistItem item)
|
||||
{
|
||||
switch (Playlist.Count)
|
||||
{
|
||||
@ -56,8 +35,6 @@ namespace osu.Game.Screens.Select
|
||||
}
|
||||
|
||||
this.Exit();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void createNewItem()
|
||||
@ -80,9 +57,5 @@ namespace osu.Game.Screens.Select
|
||||
item.RequiredMods.Clear();
|
||||
item.RequiredMods.AddRange(Mods.Value.Select(m => m.CreateCopy()));
|
||||
}
|
||||
|
||||
protected override ModSelectOverlay CreateModSelectOverlay() => new SoloModSelectOverlay { IsValidMod = isValidMod };
|
||||
|
||||
private bool isValidMod(Mod mod) => !(mod is ModAutoplay) && (mod as MultiMod)?.Mods.Any(mm => mm is ModAutoplay) != true;
|
||||
}
|
||||
}
|
@ -28,19 +28,16 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
private readonly List<OverlayContainer> overlays = new List<OverlayContainer>();
|
||||
|
||||
/// <param name="button">THe button to be added.</param>
|
||||
/// <param name="button">The button to be added.</param>
|
||||
/// <param name="overlay">The <see cref="OverlayContainer"/> to be toggled by this button.</param>
|
||||
public void AddButton(FooterButton button, OverlayContainer overlay)
|
||||
{
|
||||
if (overlay != null)
|
||||
{
|
||||
overlays.Add(overlay);
|
||||
button.Action = () => showOverlay(overlay);
|
||||
|
||||
AddButton(button);
|
||||
}
|
||||
|
||||
/// <param name="button">Button to be added.</param>
|
||||
public void AddButton(FooterButton button)
|
||||
{
|
||||
button.Hovered = updateModeLight;
|
||||
button.HoverLost = updateModeLight;
|
||||
|
||||
|
@ -263,9 +263,8 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
if (Footer != null)
|
||||
{
|
||||
Footer.AddButton(new FooterButtonMods { Current = Mods }, ModSelect);
|
||||
Footer.AddButton(new FooterButtonRandom { Action = triggerRandom });
|
||||
Footer.AddButton(new FooterButtonOptions(), BeatmapOptions);
|
||||
foreach (var (button, overlay) in CreateFooterButtons())
|
||||
Footer.AddButton(button, overlay);
|
||||
|
||||
BeatmapOptions.AddButton(@"Manage", @"collections", FontAwesome.Solid.Book, colours.Green, () => manageCollectionsDialog?.Show());
|
||||
BeatmapOptions.AddButton(@"Delete", @"all difficulties", FontAwesome.Solid.Trash, colours.Pink, () => delete(Beatmap.Value.BeatmapSetInfo));
|
||||
@ -301,6 +300,17 @@ namespace osu.Game.Screens.Select
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the buttons to be displayed in the footer.
|
||||
/// </summary>
|
||||
/// <returns>A set of <see cref="FooterButton"/> and an optional <see cref="OverlayContainer"/> which the button opens when pressed.</returns>
|
||||
protected virtual IEnumerable<(FooterButton, OverlayContainer)> CreateFooterButtons() => new (FooterButton, OverlayContainer)[]
|
||||
{
|
||||
(new FooterButtonMods { Current = Mods }, ModSelect),
|
||||
(new FooterButtonRandom { Action = triggerRandom }, null),
|
||||
(new FooterButtonOptions(), BeatmapOptions)
|
||||
};
|
||||
|
||||
protected virtual ModSelectOverlay CreateModSelectOverlay() => new SoloModSelectOverlay();
|
||||
|
||||
protected virtual void ApplyFilterToCarousel(FilterCriteria criteria)
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#nullable enable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
@ -11,6 +12,7 @@ using osu.Framework.Bindables;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
@ -122,6 +124,21 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public void ChangeUserMods(int userId, IEnumerable<Mod> newMods)
|
||||
=> ChangeUserMods(userId, newMods.Select(m => new APIMod(m)).ToList());
|
||||
|
||||
public void ChangeUserMods(int userId, IEnumerable<APIMod> newMods)
|
||||
{
|
||||
Debug.Assert(Room != null);
|
||||
((IMultiplayerClient)this).UserModsChanged(userId, newMods.ToList());
|
||||
}
|
||||
|
||||
public override Task ChangeUserMods(IEnumerable<APIMod> newMods)
|
||||
{
|
||||
ChangeUserMods(api.LocalUser.Value.Id, newMods);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public override Task StartMatch()
|
||||
{
|
||||
Debug.Assert(Room != null);
|
||||
|
Loading…
Reference in New Issue
Block a user