// Copyright (c) ppy Pty Ltd . 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 osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Screens; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Online.Rooms; using osu.Game.Overlays; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Play; namespace osu.Game.Screens.OnlinePlay.Match { [Cached(typeof(IPreviewTrackOwner))] public abstract class RoomSubScreen : OnlinePlaySubScreen, IPreviewTrackOwner { [Cached(typeof(IBindable))] protected readonly Bindable SelectedItem = new Bindable(); public override bool DisallowExternalBeatmapRulesetChanges => true; private Sample sampleStart; /// /// Any mods applied by/to the local user. /// protected readonly Bindable> UserMods = new Bindable>(Array.Empty()); [Resolved] private MusicController music { get; set; } [Resolved] private BeatmapManager beatmapManager { get; set; } [Resolved(canBeNull: true)] protected OnlinePlayScreen ParentScreen { get; private set; } private IBindable> managerUpdated; [Cached] protected OnlinePlayBeatmapAvailablilityTracker BeatmapAvailablilityTracker { get; } protected IBindable BeatmapAvailability => BeatmapAvailablilityTracker.Availability; protected RoomSubScreen() { AddInternal(BeatmapAvailablilityTracker = new OnlinePlayBeatmapAvailablilityTracker { SelectedItem = { BindTarget = SelectedItem } }); } protected override void ClearInternal(bool disposeChildren = true) => throw new InvalidOperationException($"{nameof(RoomSubScreen)}'s children should not be cleared as it will remove required components"); [BackgroundDependencyLoader] private void load(AudioManager audio) { sampleStart = audio.Samples.Get(@"SongSelect/confirm-selection"); } protected override void LoadComplete() { base.LoadComplete(); SelectedItem.BindValueChanged(_ => Scheduler.AddOnce(selectedItemChanged)); managerUpdated = beatmapManager.ItemUpdated.GetBoundCopy(); managerUpdated.BindValueChanged(beatmapUpdated); UserMods.BindValueChanged(_ => Scheduler.AddOnce(UpdateMods)); } public override void OnEntering(IScreen last) { base.OnEntering(last); beginHandlingTrack(); } public override void OnSuspending(IScreen next) { endHandlingTrack(); base.OnSuspending(next); } public override void OnResuming(IScreen last) { base.OnResuming(last); beginHandlingTrack(); Scheduler.AddOnce(UpdateMods); } public override bool OnExiting(IScreen next) { RoomManager?.PartRoom(); Mods.Value = Array.Empty(); endHandlingTrack(); return base.OnExiting(next); } protected void StartPlay(Func player) { sampleStart?.Play(); ParentScreen?.Push(new PlayerLoader(player)); } private void selectedItemChanged() { updateWorkingBeatmap(); if (SelectedItem.Value == null) return; // 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(); UpdateMods(); Ruleset.Value = SelectedItem.Value.Ruleset.Value; } private void beatmapUpdated(ValueChangedEvent> weakSet) => Schedule(updateWorkingBeatmap); private void updateWorkingBeatmap() { var beatmap = SelectedItem.Value?.Beatmap.Value; // Retrieve the corresponding local beatmap, since we can't directly use the playlist's beatmap info var localBeatmap = beatmap == null ? null : beatmapManager.QueryBeatmap(b => b.OnlineBeatmapID == beatmap.OnlineBeatmapID); Beatmap.Value = beatmapManager.GetWorkingBeatmap(localBeatmap); } protected virtual void UpdateMods() { if (SelectedItem.Value == null) return; Mods.Value = UserMods.Value.Concat(SelectedItem.Value.RequiredMods).ToList(); } private void beginHandlingTrack() { Beatmap.BindValueChanged(applyLoopingToTrack, true); } private void endHandlingTrack() { Beatmap.ValueChanged -= applyLoopingToTrack; cancelTrackLooping(); } private void applyLoopingToTrack(ValueChangedEvent _ = null) { if (!this.IsCurrentScreen()) return; var track = Beatmap.Value?.Track; if (track != null) { track.RestartPoint = Beatmap.Value.Metadata.PreviewTime; track.Looping = true; music?.EnsurePlayingSomething(); } } private void cancelTrackLooping() { var track = Beatmap?.Value?.Track; if (track != null) { track.Looping = false; track.RestartPoint = 0; } } } }