Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ppy
GitHub Repository: ppy/osu
Path: blob/master/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreenStack.cs
2264 views
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using System;
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Screens;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Screens.OnlinePlay.Lounge;

namespace osu.Game.Screens.OnlinePlay
{
    public partial class OnlinePlaySubScreenStack : OsuScreenStack
    {
        private OsuScreenDependencies dependencies = null!;

        // Note - these bindables must be stored to fields of this component to be correctly unbound on disposal.
        private Bindable<WorkingBeatmap> beatmap = null!;
        private Bindable<RulesetInfo> ruleset = null!;
        private Bindable<IReadOnlyList<Mod>> mods = null!;

        protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
        {
            // Bindables are leased by the OnlinePlayScreen, but pulled locally in order to not rely on screen load timings.
            // They will all be initially enabled while there is no screen in this stack.
            dependencies = new OsuScreenDependencies(true, base.CreateChildDependencies(parent))
            {
                Beatmap = { Disabled = false },
                Ruleset = { Disabled = false },
                Mods = { Disabled = false }
            };

            beatmap = dependencies.Beatmap;
            ruleset = dependencies.Ruleset;
            mods = dependencies.Mods;

            return dependencies;
        }

        protected override void ScreenChanged(IScreen prev, IScreen? next)
        {
            base.ScreenChanged(prev, next);

            if (next is not OsuScreen osuNext)
                throw new InvalidOperationException("There must always be an online play subscreen.");

            // See: OnlinePlayScreen.DisallowExternalBeatmapRulesetChanges.
            //
            // Bindable leases are held by the OnlinePlayScreen and NOT by the subscreens,
            // because PlayerLoader needs to resolve LeasedBindables to function correctly.
            //
            // An unfortunate consequence of this is we need to manually control bindable
            // enablement depending on what effect the subscreens want.
            //
            // This is a two-part process...

            // First, emulate the behaviour of DisallowExternalBeatmapRulesetChanges to disable toolbar buttons.
            beatmap.Disabled = osuNext.DisallowExternalBeatmapRulesetChanges;
            ruleset.Disabled = osuNext.DisallowExternalBeatmapRulesetChanges;
            mods.Disabled = osuNext.DisallowExternalBeatmapRulesetChanges;

            // Second, when an OsuScreen is exited with DisallowExternalBeatmapRulesetChanges=true, leased bindables
            // are normally returned which reverts the mod and ruleset bindables to their original states.
            //
            // The exact behaiour of the revert is awkward to emulate, but we particularly care about resetting mods
            // when returning to the lounge so that they don't stick around if the user then goes to create a new room.
            if (next is LoungeSubScreen)
                mods.Value = [];
        }
    }
}