Ejemplo n.º 1
0
 public void BindProcessor(ScoreProcessor processor)
 {
     //bind processor bindables to combocounter, score display etc.   
     processor.TotalScore.ValueChanged += delegate { ScoreCounter?.Set((ulong)processor.TotalScore.Value); };
     processor.Accuracy.ValueChanged += delegate { AccuracyCounter?.Set((float)processor.Accuracy.Value); };
     processor.Combo.ValueChanged += delegate { ComboCounter?.Set((ulong)processor.Combo.Value); };
     processor.Health.ValueChanged += delegate { HealthDisplay?.Set(processor.Health.Value); };
 }
Ejemplo n.º 2
0
 public void BindProcessor(ScoreProcessor processor)
 {
     info.AccuracyDisplay.Current.BindTo(processor.Accuracy);
     info.GradeDisplay.Current.BindTo(processor.Rank);
 }
Ejemplo n.º 3
0
 public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor)
 {
     Applied = true;
 }
Ejemplo n.º 4
0
 public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor)
 {
     scoreProcessor.Health.ValueChanged += health => { blinds.AnimateClosedness((float)health.NewValue); };
 }
Ejemplo n.º 5
0
 private void load(OsuColour colours, ScoreProcessor scoreProcessor)
 {
     Colour = colours.BlueLighter;
     Current.BindTo(scoreProcessor.Combo);
 }
Ejemplo n.º 6
0
        private void load(AudioManager audio, BeatmapDatabase beatmaps, OsuGameBase game, OsuConfigManager config)
        {
            dimLevel = config.GetBindable <int>(OsuConfig.DimLevel);
            try
            {
                if (Beatmap == null)
                {
                    Beatmap = beatmaps.GetWorkingBeatmap(BeatmapInfo, withStoryboard: true);
                }

                if ((Beatmap?.Beatmap?.HitObjects.Count ?? 0) == 0)
                {
                    throw new Exception("No valid objects were found!");
                }
            }
            catch (Exception e)
            {
                Logger.Log($"Could not load this beatmap sucessfully ({e})!", LoggingTarget.Runtime, LogLevel.Error);

                //couldn't load, hard abort!
                Exit();
                return;
            }

            Track track = Beatmap.Track;

            if (track != null)
            {
                audio.Track.SetExclusive(track);
                sourceClock = track;
            }

            sourceClock = (IAdjustableClock)track ?? new StopwatchClock();

            Schedule(() =>
            {
                sourceClock.Reset();
            });

            var beatmap = Beatmap.Beatmap;

            if (beatmap.BeatmapInfo?.Mode > PlayMode.Osu)
            {
                //we only support osu! mode for now because the hitobject parsing is crappy and needs a refactor.
                Exit();
                return;
            }

            PlayMode usablePlayMode = beatmap.BeatmapInfo?.Mode > PlayMode.Osu ? beatmap.BeatmapInfo.Mode : PreferredPlayMode;

            ruleset = Ruleset.GetRuleset(usablePlayMode);

            scoreOverlay = ruleset.CreateScoreOverlay();
            scoreOverlay.BindProcessor(scoreProcessor = ruleset.CreateScoreProcessor(beatmap.HitObjects.Count));

            pauseOverlay = new PauseOverlay
            {
                Depth    = -1,
                OnResume = delegate
                {
                    Delay(400);
                    Schedule(Resume);
                },
                OnRetry = Restart,
                OnQuit  = Exit
            };

            hitRenderer = ruleset.CreateHitRendererWith(beatmap);

            //bind HitRenderer to ScoreProcessor and ourselves (for a pass situation)
            hitRenderer.OnJudgement += scoreProcessor.AddJudgement;
            hitRenderer.OnAllJudged += onPass;

            //bind ScoreProcessor to ourselves (for a fail situation)
            scoreProcessor.Failed += onFail;

            if (Autoplay)
            {
                hitRenderer.Schedule(() => hitRenderer.DrawableObjects.ForEach(h => h.State = ArmedState.Hit));
            }

            Children = new Drawable[]
            {
                playerInputManager = new PlayerInputManager(game.Host)
                {
                    Clock       = new InterpolatingFramedClock(sourceClock),
                    PassThrough = false,
                    Children    = new Drawable[]
                    {
                        hitRenderer,
                        skipButton = new SkipButton {
                            Alpha = 0
                        },
                    }
                },
                scoreOverlay,
                pauseOverlay
            };
        }
Ejemplo n.º 7
0
        public HUDOverlay(ScoreProcessor scoreProcessor, HealthProcessor healthProcessor, DrawableRuleset drawableRuleset, IReadOnlyList <Mod> mods)
        {
            this.scoreProcessor  = scoreProcessor;
            this.healthProcessor = healthProcessor;
            this.drawableRuleset = drawableRuleset;
            this.mods            = mods;

            RelativeSizeAxes = Axes.Both;

            Children = new Drawable[]
            {
                FailingLayer        = CreateFailingLayer(),
                visibilityContainer = new Container
                {
                    RelativeSizeAxes = Axes.Both,
                    Child            = new GridContainer
                    {
                        RelativeSizeAxes = Axes.Both,
                        Content          = new[]
                        {
                            new Drawable[]
                            {
                                mainUIElements = new Container
                                {
                                    RelativeSizeAxes = Axes.Both,
                                    Children         = new Drawable[]
                                    {
                                        HealthDisplay   = CreateHealthDisplay(),
                                        AccuracyCounter = CreateAccuracyCounter(),
                                        ScoreCounter    = CreateScoreCounter(),
                                        ComboCounter    = CreateComboCounter(),
                                        HitErrorDisplay = CreateHitErrorDisplayOverlay(),
                                    }
                                },
                            },
                            new Drawable[]
                            {
                                Progress = CreateProgress(),
                            }
                        },
                        RowDimensions = new[]
                        {
                            new Dimension(),
                            new Dimension(GridSizeMode.AutoSize)
                        }
                    },
                },
                topRightElements = new FillFlowContainer
                {
                    Anchor       = Anchor.TopRight,
                    Origin       = Anchor.TopRight,
                    Margin       = new MarginPadding(10),
                    Spacing      = new Vector2(10),
                    AutoSizeAxes = Axes.Both,
                    Direction    = FillDirection.Vertical,
                    Children     = new Drawable[]
                    {
                        ModDisplay            = CreateModsContainer(),
                        PlayerSettingsOverlay = CreatePlayerSettingsOverlay(),
                    }
                },
                bottomRightElements = new FillFlowContainer
                {
                    Anchor         = Anchor.BottomRight,
                    Origin         = Anchor.BottomRight,
                    Margin         = new MarginPadding(10),
                    Spacing        = new Vector2(10),
                    AutoSizeAxes   = Axes.Both,
                    LayoutDuration = FADE_DURATION / 2,
                    LayoutEasing   = FADE_EASING,
                    Direction      = FillDirection.Vertical,
                    Children       = new Drawable[]
                    {
                        KeyCounter = CreateKeyCounter(),
                        HoldToQuit = CreateHoldForMenuButton(),
                    }
                }
            };
        }
Ejemplo n.º 8
0
        private void load(AudioManager audio, OsuConfigManager config, OsuGame game)
        {
            Mods.Value = base.Mods.Value.Select(m => m.CreateCopy()).ToArray();

            if (Beatmap.Value is DummyWorkingBeatmap)
            {
                return;
            }

            IBeatmap playableBeatmap = loadPlayableBeatmap();

            if (playableBeatmap == null)
            {
                return;
            }

            sampleRestart = audio.Samples.Get(@"Gameplay/restart");

            mouseWheelDisabled = config.GetBindable <bool>(OsuSetting.MouseDisableWheel);

            if (game != null)
            {
                LocalUserPlaying.BindTo(game.LocalUserPlaying);
            }

            DrawableRuleset = ruleset.CreateDrawableRulesetWith(playableBeatmap, Mods.Value);

            ScoreProcessor = ruleset.CreateScoreProcessor();
            ScoreProcessor.ApplyBeatmap(playableBeatmap);
            ScoreProcessor.Mods.BindTo(Mods);

            HealthProcessor = ruleset.CreateHealthProcessor(playableBeatmap.HitObjects[0].StartTime);
            HealthProcessor.ApplyBeatmap(playableBeatmap);

            if (!ScoreProcessor.Mode.Disabled)
            {
                config.BindWith(OsuSetting.ScoreDisplayMode, ScoreProcessor.Mode);
            }

            InternalChild = GameplayClockContainer = CreateGameplayClockContainer(Beatmap.Value, DrawableRuleset.GameplayStartTime);

            AddInternal(gameplayBeatmap  = new GameplayBeatmap(playableBeatmap));
            AddInternal(screenSuspension = new ScreenSuspensionHandler(GameplayClockContainer));

            dependencies.CacheAs(gameplayBeatmap);

            var beatmapSkinProvider = new BeatmapSkinProvidingContainer(Beatmap.Value.Skin);

            // the beatmapSkinProvider is used as the fallback source here to allow the ruleset-specific skin implementation
            // full access to all skin sources.
            var rulesetSkinProvider = new SkinProvidingContainer(ruleset.CreateLegacySkinProvider(beatmapSkinProvider, playableBeatmap));

            // load the skinning hierarchy first.
            // this is intentionally done in two stages to ensure things are in a loaded state before exposing the ruleset to skin sources.
            GameplayClockContainer.Add(beatmapSkinProvider.WithChild(rulesetSkinProvider));

            rulesetSkinProvider.AddRange(new[]
            {
                // underlay and gameplay should have access the to skinning sources.
                createUnderlayComponents(),
                createGameplayComponents(Beatmap.Value, playableBeatmap)
            });

            // also give the HUD a ruleset container to allow rulesets to potentially override HUD elements (used to disable combo counters etc.)
            // we may want to limit this in the future to disallow rulesets from outright replacing elements the user expects to be there.
            var hudRulesetContainer = new SkinProvidingContainer(ruleset.CreateLegacySkinProvider(beatmapSkinProvider, playableBeatmap));

            // add the overlay components as a separate step as they proxy some elements from the above underlay/gameplay components.
            GameplayClockContainer.Add(hudRulesetContainer.WithChild(createOverlayComponents(Beatmap.Value)));

            if (!DrawableRuleset.AllowGameplayOverlays)
            {
                HUDOverlay.ShowHud.Value    = false;
                HUDOverlay.ShowHud.Disabled = true;
                BreakOverlay.Hide();
                skipOverlay.Hide();
            }

            DrawableRuleset.FrameStableClock.WaitingOnFrames.BindValueChanged(waiting =>
            {
                if (waiting.NewValue)
                {
                    GameplayClockContainer.Stop();
                }
                else
                {
                    GameplayClockContainer.Start();
                }
            });

            DrawableRuleset.IsPaused.BindValueChanged(paused =>
            {
                updateGameplayState();
                updateSampleDisabledState();
            });

            DrawableRuleset.FrameStableClock.IsCatchingUp.BindValueChanged(_ => updateSampleDisabledState());

            DrawableRuleset.HasReplayLoaded.BindValueChanged(_ => updateGameplayState());

            DrawableRuleset.HasReplayLoaded.BindValueChanged(_ => updatePauseOnFocusLostState(), true);

            // bind clock into components that require it
            DrawableRuleset.IsPaused.BindTo(GameplayClockContainer.IsPaused);

            DrawableRuleset.OnNewResult += r =>
            {
                HealthProcessor.ApplyResult(r);
                ScoreProcessor.ApplyResult(r);
                gameplayBeatmap.ApplyResult(r);
            };

            DrawableRuleset.OnRevertResult += r =>
            {
                HealthProcessor.RevertResult(r);
                ScoreProcessor.RevertResult(r);
            };

            // Bind the judgement processors to ourselves
            ScoreProcessor.HasCompleted.ValueChanged += updateCompletionState;
            HealthProcessor.Failed += onFail;

            foreach (var mod in Mods.Value.OfType <IApplicableToScoreProcessor>())
            {
                mod.ApplyToScoreProcessor(ScoreProcessor);
            }

            foreach (var mod in Mods.Value.OfType <IApplicableToHealthProcessor>())
            {
                mod.ApplyToHealthProcessor(HealthProcessor);
            }

            IsBreakTime.BindTo(breakTracker.IsBreakTime);
            IsBreakTime.BindValueChanged(onBreakTimeChanged, true);
        }
Ejemplo n.º 9
0
        /// <summary>
        /// Run any recording / playback setup for replays.
        /// </summary>
        protected virtual void PrepareReplay()
        {
            DrawableRuleset.SetRecordTarget(recordingScore = new Score());

            ScoreProcessor.NewJudgement += result => ScoreProcessor.PopulateScore(recordingScore.ScoreInfo);
        }
Ejemplo n.º 10
0
 public SpectatingTrackedUserData(int userId, ScoreProcessor scoreProcessor)
     : base(userId, scoreProcessor)
 {
 }
Ejemplo n.º 11
0
 protected override TrackedUserData CreateUserData(int userId, ScoreProcessor scoreProcessor) => new SpectatingTrackedUserData(userId, scoreProcessor);
Ejemplo n.º 12
0
 public MultiSpectatorLeaderboard([NotNull] ScoreProcessor scoreProcessor, int[] userIds)
     : base(scoreProcessor, userIds)
 {
 }
Ejemplo n.º 13
0
 protected virtual bool FailCondition(ScoreProcessor scoreProcessor, JudgementResult result) => scoreProcessor.Combo.Value == 0 && result.Judgement.AffectsCombo;
Ejemplo n.º 14
0
 private void load(ScoreProcessor scoreProcessor)
 {
     Current.BindTo(scoreProcessor.Accuracy);
 }
Ejemplo n.º 15
0
        private void load(AudioManager audio, OsuConfigManager config)
        {
            Mods.Value = base.Mods.Value.Select(m => m.CreateCopy()).ToArray();

            if (Beatmap.Value is DummyWorkingBeatmap)
            {
                return;
            }

            IBeatmap playableBeatmap = loadPlayableBeatmap();

            if (playableBeatmap == null)
            {
                return;
            }

            sampleRestart = audio.Samples.Get(@"Gameplay/restart");

            mouseWheelDisabled = config.GetBindable <bool>(OsuSetting.MouseDisableWheel);

            DrawableRuleset = ruleset.CreateDrawableRulesetWith(playableBeatmap, Mods.Value);

            ScoreProcessor = ruleset.CreateScoreProcessor();
            ScoreProcessor.ApplyBeatmap(playableBeatmap);
            ScoreProcessor.Mods.BindTo(Mods);

            HealthProcessor = ruleset.CreateHealthProcessor(playableBeatmap.HitObjects[0].StartTime);
            HealthProcessor.ApplyBeatmap(playableBeatmap);

            if (!ScoreProcessor.Mode.Disabled)
            {
                config.BindWith(OsuSetting.ScoreDisplayMode, ScoreProcessor.Mode);
            }

            InternalChild = GameplayClockContainer = new GameplayClockContainer(Beatmap.Value, DrawableRuleset.GameplayStartTime);

            AddInternal(gameplayBeatmap  = new GameplayBeatmap(playableBeatmap));
            AddInternal(screenSuspension = new ScreenSuspensionHandler(GameplayClockContainer));

            dependencies.CacheAs(gameplayBeatmap);

            addUnderlayComponents(GameplayClockContainer);
            addGameplayComponents(GameplayClockContainer, Beatmap.Value, playableBeatmap);
            addOverlayComponents(GameplayClockContainer, Beatmap.Value);

            if (!DrawableRuleset.AllowGameplayOverlays)
            {
                HUDOverlay.ShowHud.Value    = false;
                HUDOverlay.ShowHud.Disabled = true;
                BreakOverlay.Hide();
                skipOverlay.Hide();
            }

            DrawableRuleset.HasReplayLoaded.BindValueChanged(_ => updatePauseOnFocusLostState(), true);

            // bind clock into components that require it
            DrawableRuleset.IsPaused.BindTo(GameplayClockContainer.IsPaused);

            DrawableRuleset.OnNewResult += r =>
            {
                HealthProcessor.ApplyResult(r);
                ScoreProcessor.ApplyResult(r);
                gameplayBeatmap.ApplyResult(r);
            };

            DrawableRuleset.OnRevertResult += r =>
            {
                HealthProcessor.RevertResult(r);
                ScoreProcessor.RevertResult(r);
            };

            // Bind the judgement processors to ourselves
            ScoreProcessor.HasCompleted.ValueChanged += updateCompletionState;
            HealthProcessor.Failed += onFail;

            foreach (var mod in Mods.Value.OfType <IApplicableToScoreProcessor>())
            {
                mod.ApplyToScoreProcessor(ScoreProcessor);
            }

            foreach (var mod in Mods.Value.OfType <IApplicableToHealthProcessor>())
            {
                mod.ApplyToHealthProcessor(HealthProcessor);
            }

            breakTracker.IsBreakTime.BindValueChanged(onBreakTimeChanged, true);
        }
Ejemplo n.º 16
0
        /// <summary>
        ///     Handles all key presses in the current replay frame.
        /// </summary>
        private void HandleKeyPressesInFrame()
        {
            // Retrieve a list of the key press states in integer form.
            var currentFramePressed  = Replay.KeyPressStateToLanes(Replay.Frames[CurrentFrame].Keys);
            var previousFramePressed = CurrentFrame > 0 ? Replay.KeyPressStateToLanes(Replay.Frames[CurrentFrame - 1].Keys) : new List <int>();

            // Update the key press state in the store.
            for (var i = 0; i < InputKeyStore.Count; i++)
            {
                InputKeyStore[i].Pressed = currentFramePressed.Contains(i);
            }

            // Check the difference in key press states for the current and previous frames.
            var keyDifferences = currentFramePressed.Except(previousFramePressed)
                                 .Concat(previousFramePressed.Except(currentFramePressed))
                                 .ToList();

            // Go through each frame and handle key presses/releases.
            foreach (var key in keyDifferences)
            {
                // This key was uniquely pressed during this frame.
                if (currentFramePressed.Contains(key))
                {
                    // Find the nearest object in the lane that the user has pressed.
                    var nearestObjectIndex = GetIndexOfNearestLaneObject(key + 1, Time);

                    if (nearestObjectIndex == -1)
                    {
                        continue;
                    }

                    // Grab the actual HitObject instance.
                    var hitObject = ActiveHitObjects[nearestObjectIndex];

                    // Calculate the hit difference.
                    var hitDifference = hitObject.StartTime - Time;

                    // Calculate Score.
                    var judgement = ScoreProcessor.CalculateScore(hitDifference, KeyPressType.Press);

                    switch (judgement)
                    {
                    // Don't handle ghost key presses, so just continue further.
                    case Judgement.Ghost:
                        continue;

                    // Object needs to be removed completely if it's a miss.
                    case Judgement.Miss:
                        // Add another miss for an LN (head and tail)
                        if (hitObject.IsLongNote)
                        {
                            ScoreProcessor.CalculateScore(Judgement.Miss);
                            ScoreProcessor.Stats.Add(new HitStat(HitStatType.Miss, KeyPressType.Press, hitObject, Time, Judgement.Miss, int.MinValue,
                                                                 ScoreProcessor.Accuracy, ScoreProcessor.Health));
                        }
                        break;

                    default:
                        // Long notes need to be changed to a held status.
                        if (hitObject.IsLongNote)
                        {
                            ActiveHeldLongNotes.Add(hitObject);
                        }
                        break;
                    }

                    // Add a new hit stat to the score processor.
                    var stat = new HitStat(HitStatType.Hit, KeyPressType.Press, hitObject, Time, judgement, hitDifference,
                                           ScoreProcessor.Accuracy, ScoreProcessor.Health);

                    ScoreProcessor.Stats.Add(stat);

                    // Object needs to be removed from ActiveObjects.
                    ActiveHitObjectsToRemove.Add(hitObject);
                }
                // This key was uniquely released during this frame.
                else if (previousFramePressed.Contains(key))
                {
                    // Find the index of the actual closest LN and handle the key release
                    // if so.
                    foreach (var hitObject in ActiveHeldLongNotes)
                    {
                        // Handle the release of the note.
                        if (hitObject.Lane != key + 1)
                        {
                            continue;
                        }

                        // Calculate the hit difference.
                        var hitDifference = hitObject.EndTime - Time;

                        // Calculate Score
                        var judgement = ScoreProcessor.CalculateScore(hitDifference, KeyPressType.Release);

                        // LN was released during a hit window.
                        if (judgement != Judgement.Ghost)
                        {
                            // Add a new hit stat to the score processor.
                            var stat = new HitStat(HitStatType.Hit, KeyPressType.Release, hitObject, Time, judgement, hitDifference,
                                                   ScoreProcessor.Accuracy, ScoreProcessor.Health);

                            ScoreProcessor.Stats.Add(stat);
                        }
                        // The LN was released too early (miss)
                        else
                        {
                            ScoreProcessor.CalculateScore(Judgement.Miss);

                            // Add a new stat to ScoreProcessor.
                            var stat = new HitStat(HitStatType.Hit, KeyPressType.Release, hitObject, Time, Judgement.Miss, hitDifference,
                                                   ScoreProcessor.Accuracy, ScoreProcessor.Health);

                            ScoreProcessor.Stats.Add(stat);
                        }

                        // Remove the object from its held state.
                        ActiveHeldLongNotesToRemove.Add(hitObject);
                    }
                }
            }

            // Remove all active objects after handling key presses/releases.
            ActiveHitObjectsToRemove.ForEach(x => ActiveHitObjects.Remove(x));
            ActiveHeldLongNotesToRemove.ForEach(x => ActiveHeldLongNotes.Remove(x));
        }
Ejemplo n.º 17
0
 public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor)
 {
     scoreProcessor.FailConditions += FailCondition;
 }
Ejemplo n.º 18
0
        private void load(AudioManager audio, OsuConfigManager config, OsuGameBase game)
        {
            var gameplayMods = Mods.Value.Select(m => m.DeepClone()).ToArray();

            if (Beatmap.Value is DummyWorkingBeatmap)
            {
                return;
            }

            IBeatmap playableBeatmap = loadPlayableBeatmap(gameplayMods);

            if (playableBeatmap == null)
            {
                return;
            }

            sampleRestart = audio.Samples.Get(@"Gameplay/restart");

            mouseWheelDisabled = config.GetBindable <bool>(OsuSetting.MouseDisableWheel);

            if (game != null)
            {
                gameActive.BindTo(game.IsActive);
            }

            if (game is OsuGame osuGame)
            {
                LocalUserPlaying.BindTo(osuGame.LocalUserPlaying);
            }

            DrawableRuleset = ruleset.CreateDrawableRulesetWith(playableBeatmap, gameplayMods);
            dependencies.CacheAs(DrawableRuleset);

            ScoreProcessor = ruleset.CreateScoreProcessor();
            ScoreProcessor.ApplyBeatmap(playableBeatmap);
            ScoreProcessor.Mods.Value = gameplayMods;

            dependencies.CacheAs(ScoreProcessor);

            HealthProcessor = ruleset.CreateHealthProcessor(playableBeatmap.HitObjects[0].StartTime);
            HealthProcessor.ApplyBeatmap(playableBeatmap);

            dependencies.CacheAs(HealthProcessor);

            if (!ScoreProcessor.Mode.Disabled)
            {
                config.BindWith(OsuSetting.ScoreDisplayMode, ScoreProcessor.Mode);
            }

            InternalChild = GameplayClockContainer = CreateGameplayClockContainer(Beatmap.Value, DrawableRuleset.GameplayStartTime);

            AddInternal(screenSuspension = new ScreenSuspensionHandler(GameplayClockContainer));

            Score = CreateScore(playableBeatmap);

            // ensure the score is in a consistent state with the current player.
            Score.ScoreInfo.BeatmapInfo = Beatmap.Value.BeatmapInfo;
            Score.ScoreInfo.Ruleset     = ruleset.RulesetInfo;
            if (ruleset.RulesetInfo.ID != null)
            {
                Score.ScoreInfo.RulesetID = ruleset.RulesetInfo.ID.Value;
            }
            Score.ScoreInfo.Mods = gameplayMods;

            dependencies.CacheAs(GameplayState = new GameplayState(playableBeatmap, ruleset, gameplayMods, Score));

            var rulesetSkinProvider = new RulesetSkinProvidingContainer(ruleset, playableBeatmap, Beatmap.Value.Skin);

            // load the skinning hierarchy first.
            // this is intentionally done in two stages to ensure things are in a loaded state before exposing the ruleset to skin sources.
            GameplayClockContainer.Add(rulesetSkinProvider);

            rulesetSkinProvider.AddRange(new Drawable[]
            {
                failAnimationLayer = new FailAnimation(DrawableRuleset)
                {
                    OnComplete = onFailComplete,
                    Children   = new[]
                    {
                        // underlay and gameplay should have access to the skinning sources.
                        createUnderlayComponents(),
                        createGameplayComponents(Beatmap.Value, playableBeatmap)
                    }
                },
                FailOverlay = new FailOverlay
                {
                    OnRetry = Restart,
                    OnQuit  = () => PerformExit(true),
                },
                new HotkeyExitOverlay
                {
                    Action = () =>
                    {
                        if (!this.IsCurrentScreen())
                        {
                            return;
                        }

                        fadeOut(true);
                        PerformExit(false);
                    },
                },
            });

            if (Configuration.AllowRestart)
            {
                rulesetSkinProvider.Add(new HotkeyRetryOverlay
                {
                    Action = () =>
                    {
                        if (!this.IsCurrentScreen())
                        {
                            return;
                        }

                        fadeOut(true);
                        Restart();
                    },
                });
            }

            // add the overlay components as a separate step as they proxy some elements from the above underlay/gameplay components.
            // also give the overlays the ruleset skin provider to allow rulesets to potentially override HUD elements (used to disable combo counters etc.)
            // we may want to limit this in the future to disallow rulesets from outright replacing elements the user expects to be there.
            failAnimationLayer.Add(createOverlayComponents(Beatmap.Value));

            if (!DrawableRuleset.AllowGameplayOverlays)
            {
                HUDOverlay.ShowHud.Value    = false;
                HUDOverlay.ShowHud.Disabled = true;
                BreakOverlay.Hide();
            }

            DrawableRuleset.FrameStableClock.WaitingOnFrames.BindValueChanged(waiting =>
            {
                if (waiting.NewValue)
                {
                    GameplayClockContainer.Stop();
                }
                else
                {
                    GameplayClockContainer.Start();
                }
            });

            DrawableRuleset.IsPaused.BindValueChanged(paused =>
            {
                updateGameplayState();
                updateSampleDisabledState();
            });

            DrawableRuleset.FrameStableClock.IsCatchingUp.BindValueChanged(_ => updateSampleDisabledState());

            DrawableRuleset.HasReplayLoaded.BindValueChanged(_ => updateGameplayState());

            // bind clock into components that require it
            DrawableRuleset.IsPaused.BindTo(GameplayClockContainer.IsPaused);

            DrawableRuleset.NewResult += r =>
            {
                HealthProcessor.ApplyResult(r);
                ScoreProcessor.ApplyResult(r);
                GameplayState.ApplyResult(r);
            };

            DrawableRuleset.RevertResult += r =>
            {
                HealthProcessor.RevertResult(r);
                ScoreProcessor.RevertResult(r);
            };

            DimmableStoryboard.HasStoryboardEnded.ValueChanged += storyboardEnded =>
            {
                if (storyboardEnded.NewValue)
                {
                    progressToResults(true);
                }
            };

            // Bind the judgement processors to ourselves
            ScoreProcessor.HasCompleted.BindValueChanged(scoreCompletionChanged);
            HealthProcessor.Failed += onFail;

            // Provide judgement processors to mods after they're loaded so that they're on the gameplay clock,
            // this is required for mods that apply transforms to these processors.
            ScoreProcessor.OnLoadComplete += _ =>
            {
                foreach (var mod in gameplayMods.OfType <IApplicableToScoreProcessor>())
                {
                    mod.ApplyToScoreProcessor(ScoreProcessor);
                }
            };

            HealthProcessor.OnLoadComplete += _ =>
            {
                foreach (var mod in gameplayMods.OfType <IApplicableToHealthProcessor>())
                {
                    mod.ApplyToHealthProcessor(HealthProcessor);
                }
            };

            IsBreakTime.BindTo(breakTracker.IsBreakTime);
            IsBreakTime.BindValueChanged(onBreakTimeChanged, true);
        }
Ejemplo n.º 19
0
 protected virtual bool FailCondition(ScoreProcessor scoreProcessor) => scoreProcessor.Combo.Value == 0;
 public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor)
 {
     // Default value of ScoreProcessor's Rank in Hidden Mod should be SS+
     scoreProcessor.Rank.Value = ScoreRank.XH;
 }
Ejemplo n.º 21
0
Archivo: Player.cs Proyecto: ekrctb/osu
 protected virtual void PrepareScoreForResults()
 {
     // perform one final population to ensure everything is up-to-date.
     ScoreProcessor.PopulateScore(Score.ScoreInfo);
 }
Ejemplo n.º 22
0
 public BreakOverlay(bool letterboxing, ScoreProcessor scoreProcessor)
     : this(letterboxing)
 {
     bindProcessor(scoreProcessor);
 }
Ejemplo n.º 23
0
 public BreakTracker(double gameplayStartTime = 0, ScoreProcessor scoreProcessor = null)
 {
     this.gameplayStartTime = gameplayStartTime;
     this.scoreProcessor    = scoreProcessor;
 }
Ejemplo n.º 24
0
        private void load(AudioManager audio, APIAccess api, OsuConfigManager config)
        {
            this.api = api;

            WorkingBeatmap working = Beatmap.Value;

            if (working is DummyWorkingBeatmap)
            {
                return;
            }

            sampleRestart = audio.Sample.Get(@"Gameplay/restart");

            mouseWheelDisabled = config.GetBindable <bool>(OsuSetting.MouseDisableWheel);
            userAudioOffset    = config.GetBindable <double>(OsuSetting.AudioOffset);

            Beatmap beatmap;

            try
            {
                beatmap = working.Beatmap;

                if (beatmap == null)
                {
                    throw new InvalidOperationException("Beatmap was not loaded");
                }

                ruleset = Ruleset.Value ?? beatmap.BeatmapInfo.Ruleset;
                var rulesetInstance = ruleset.CreateInstance();

                try
                {
                    RulesetContainer = rulesetInstance.CreateRulesetContainerWith(working, ruleset.ID == beatmap.BeatmapInfo.Ruleset.ID);
                }
                catch (BeatmapInvalidForRulesetException)
                {
                    // we may fail to create a RulesetContainer if the beatmap cannot be loaded with the user's preferred ruleset
                    // let's try again forcing the beatmap's ruleset.
                    ruleset          = beatmap.BeatmapInfo.Ruleset;
                    rulesetInstance  = ruleset.CreateInstance();
                    RulesetContainer = rulesetInstance.CreateRulesetContainerWith(Beatmap, true);
                }

                if (!RulesetContainer.Objects.Any())
                {
                    Logger.Error(new InvalidOperationException("Beatmap contains no hit objects!"), "Beatmap contains no hit objects!");
                    return;
                }
            }
            catch (Exception e)
            {
                Logger.Error(e, "Could not load beatmap sucessfully!");
                //couldn't load, hard abort!
                return;
            }

            sourceClock     = (IAdjustableClock)working.Track ?? new StopwatchClock();
            adjustableClock = new DecoupleableInterpolatingFramedClock {
                IsCoupled = false
            };

            var firstObjectTime = RulesetContainer.Objects.First().StartTime;

            adjustableClock.Seek(AllowLeadIn
                ? Math.Min(0, firstObjectTime - Math.Max(beatmap.ControlPointInfo.TimingPointAt(firstObjectTime).BeatLength * 4, beatmap.BeatmapInfo.AudioLeadIn))
                : firstObjectTime);

            adjustableClock.ProcessFrame();

            // the final usable gameplay clock with user-set offsets applied.
            var offsetClock = new FramedOffsetClock(adjustableClock);

            userAudioOffset.ValueChanged += v => offsetClock.Offset = v;
            userAudioOffset.TriggerChange();

            scoreProcessor = RulesetContainer.CreateScoreProcessor();

            Children = new Drawable[]
            {
                pauseContainer = new PauseContainer(offsetClock, adjustableClock)
                {
                    OnRetry       = Restart,
                    OnQuit        = Exit,
                    CheckCanPause = () => AllowPause && ValidForResume && !HasFailed && !RulesetContainer.HasReplayLoaded,
                    OnPause       = () =>
                    {
                        pauseContainer.Retries           = RestartCount;
                        hudOverlay.KeyCounter.IsCounting = pauseContainer.IsPaused;
                    },
                    OnResume = () => hudOverlay.KeyCounter.IsCounting = true,
                    Children = new Drawable[]
                    {
                        storyboardContainer = new Container
                        {
                            RelativeSizeAxes = Axes.Both,
                            Alpha            = 0,
                        },
                        new LocalSkinOverrideContainer(working.Skin)
                        {
                            RelativeSizeAxes = Axes.Both,
                            Child            = RulesetContainer
                        },
                        new SkipOverlay(firstObjectTime)
                        {
                            Clock = Clock, // skip button doesn't want to use the audio clock directly
                            ProcessCustomClock = false,
                            AdjustableClock    = adjustableClock,
                            FramedClock        = offsetClock,
                        },
                        hudOverlay = new HUDOverlay(scoreProcessor, RulesetContainer, working, offsetClock, adjustableClock)
                        {
                            Clock = Clock, // hud overlay doesn't want to use the audio clock directly
                            ProcessCustomClock = false,
                            Anchor             = Anchor.Centre,
                            Origin             = Anchor.Centre
                        },
                        new BreakOverlay(beatmap.BeatmapInfo.LetterboxInBreaks, scoreProcessor)
                        {
                            Anchor             = Anchor.Centre,
                            Origin             = Anchor.Centre,
                            ProcessCustomClock = false,
                            Breaks             = beatmap.Breaks
                        }
                    }
                },
                failOverlay = new FailOverlay
                {
                    OnRetry = Restart,
                    OnQuit  = Exit,
                },
                new HotkeyRetryOverlay
                {
                    Action = () =>
                    {
                        if (!IsCurrentScreen)
                        {
                            return;
                        }

                        //we want to hide the hitrenderer immediately (looks better).
                        //we may be able to remove this once the mouse cursor trail is improved.
                        RulesetContainer?.Hide();
                        Restart();
                    },
                }
            };

            if (ShowStoryboard)
            {
                initializeStoryboard(false);
            }

            // Bind ScoreProcessor to ourselves
            scoreProcessor.AllJudged += onCompletion;
            scoreProcessor.Failed    += onFail;

            foreach (var mod in Beatmap.Value.Mods.Value.OfType <IApplicableToScoreProcessor>())
            {
                mod.ApplyToScoreProcessor(scoreProcessor);
            }
        }
Ejemplo n.º 25
0
        protected override async Task PrepareScoreForResultsAsync(Score score)
        {
            await base.PrepareScoreForResultsAsync(score).ConfigureAwait(false);

            Score.ScoreInfo.TotalScore = (int)Math.Round(ScoreProcessor.GetStandardisedScore());
        }
Ejemplo n.º 26
0
Archivo: Player.cs Proyecto: rly907/osu
        private void load(AudioManager audio, IAPIProvider api, OsuConfigManager config)
        {
            this.api = api;

            Mods.Value = base.Mods.Value.Select(m => m.CreateCopy()).ToArray();

            if (Beatmap.Value is DummyWorkingBeatmap)
            {
                return;
            }

            IBeatmap playableBeatmap = loadPlayableBeatmap();

            if (playableBeatmap == null)
            {
                return;
            }

            sampleRestart = audio.Samples.Get(@"Gameplay/restart");

            mouseWheelDisabled = config.GetBindable <bool>(OsuSetting.MouseDisableWheel);

            DrawableRuleset = ruleset.CreateDrawableRulesetWith(playableBeatmap, Mods.Value);

            ScoreProcessor = ruleset.CreateScoreProcessor();
            ScoreProcessor.ApplyBeatmap(playableBeatmap);
            ScoreProcessor.Mods.BindTo(Mods);

            HealthProcessor = ruleset.CreateHealthProcessor(playableBeatmap.HitObjects[0].StartTime);
            HealthProcessor.ApplyBeatmap(playableBeatmap);

            if (!ScoreProcessor.Mode.Disabled)
            {
                config.BindWith(OsuSetting.ScoreDisplayMode, ScoreProcessor.Mode);
            }

            InternalChild = GameplayClockContainer = new GameplayClockContainer(Beatmap.Value, Mods.Value, DrawableRuleset.GameplayStartTime);

            addUnderlayComponents(GameplayClockContainer);
            addGameplayComponents(GameplayClockContainer, Beatmap.Value);
            addOverlayComponents(GameplayClockContainer, Beatmap.Value);

            DrawableRuleset.HasReplayLoaded.BindValueChanged(_ => updatePauseOnFocusLostState(), true);

            // bind clock into components that require it
            DrawableRuleset.IsPaused.BindTo(GameplayClockContainer.IsPaused);

            DrawableRuleset.OnNewResult += r =>
            {
                HealthProcessor.ApplyResult(r);
                ScoreProcessor.ApplyResult(r);
            };

            DrawableRuleset.OnRevertResult += r =>
            {
                HealthProcessor.RevertResult(r);
                ScoreProcessor.RevertResult(r);
            };

            // Bind the judgement processors to ourselves
            ScoreProcessor.AllJudged += onCompletion;
            HealthProcessor.Failed   += onFail;

            foreach (var mod in Mods.Value.OfType <IApplicableToScoreProcessor>())
            {
                mod.ApplyToScoreProcessor(ScoreProcessor);
            }

            foreach (var mod in Mods.Value.OfType <IApplicableToHealthProcessor>())
            {
                mod.ApplyToHealthProcessor(HealthProcessor);
            }

            BreakOverlay.IsBreakTime.ValueChanged += _ => updatePauseOnFocusLostState();
        }
Ejemplo n.º 27
0
 protected virtual TrackedUserData CreateUserData(MultiplayerRoomUser user, RulesetInfo ruleset, ScoreProcessor scoreProcessor) => new TrackedUserData(user, ruleset, scoreProcessor);
Ejemplo n.º 28
0
 public ComboEffects(ScoreProcessor processor)
 {
     this.processor = processor;
 }
Ejemplo n.º 29
0
        private void load(AudioManager audio, OsuConfigManager config, OsuGame osu)
        {
            dimLevel           = config.GetBindable <double>(OsuSetting.DimLevel);
            mouseWheelDisabled = config.GetBindable <bool>(OsuSetting.MouseDisableWheel);

            sampleRestart = audio.Sample.Get(@"Gameplay/restart");

            Ruleset rulesetInstance;

            try
            {
                if (Beatmap.Value.Beatmap == null)
                {
                    throw new InvalidOperationException("Beatmap was not loaded");
                }

                ruleset         = osu?.Ruleset.Value ?? Beatmap.Value.BeatmapInfo.Ruleset;
                rulesetInstance = ruleset.CreateInstance();

                try
                {
                    HitRenderer = rulesetInstance.CreateHitRendererWith(Beatmap, ruleset.ID == Beatmap.Value.BeatmapInfo.Ruleset.ID);
                }
                catch (BeatmapInvalidForRulesetException)
                {
                    // we may fail to create a HitRenderer if the beatmap cannot be loaded with the user's preferred ruleset
                    // let's try again forcing the beatmap's ruleset.
                    ruleset         = Beatmap.Value.BeatmapInfo.Ruleset;
                    rulesetInstance = ruleset.CreateInstance();
                    HitRenderer     = rulesetInstance.CreateHitRendererWith(Beatmap, true);
                }

                if (!HitRenderer.Objects.Any())
                {
                    throw new InvalidOperationException("Beatmap contains no hit objects!");
                }
            }
            catch (Exception e)
            {
                Logger.Log($"Could not load this beatmap sucessfully ({e})!", LoggingTarget.Runtime, LogLevel.Error);

                //couldn't load, hard abort!
                Exit();
                return;
            }

            Track track = Beatmap.Value.Track;

            if (track != null)
            {
                adjustableSourceClock = track;
            }

            adjustableSourceClock = (IAdjustableClock)track ?? new StopwatchClock();

            decoupledClock = new DecoupleableInterpolatingFramedClock {
                IsCoupled = false
            };

            var firstObjectTime = HitRenderer.Objects.First().StartTime;

            decoupledClock.Seek(Math.Min(0, firstObjectTime - Math.Max(Beatmap.Value.Beatmap.ControlPointInfo.TimingPointAt(firstObjectTime).BeatLength * 4, Beatmap.Value.BeatmapInfo.AudioLeadIn)));
            decoupledClock.ProcessFrame();

            offsetClock = new FramedOffsetClock(decoupledClock);

            userAudioOffset = config.GetBindable <double>(OsuSetting.AudioOffset);
            userAudioOffset.ValueChanged += v => offsetClock.Offset = v;
            userAudioOffset.TriggerChange();

            Schedule(() =>
            {
                adjustableSourceClock.Reset();

                foreach (var mod in Beatmap.Value.Mods.Value.OfType <IApplicableToClock>())
                {
                    mod.ApplyToClock(adjustableSourceClock);
                }

                decoupledClock.ChangeSource(adjustableSourceClock);
            });

            Children = new Drawable[]
            {
                pauseContainer = new PauseContainer
                {
                    AudioClock    = decoupledClock,
                    FramedClock   = offsetClock,
                    OnRetry       = Restart,
                    OnQuit        = Exit,
                    CheckCanPause = () => ValidForResume && !HasFailed && !HitRenderer.HasReplayLoaded,
                    Retries       = RestartCount,
                    OnPause       = () => {
                        hudOverlay.KeyCounter.IsCounting = pauseContainer.IsPaused;
                    },
                    OnResume = () => {
                        hudOverlay.KeyCounter.IsCounting = true;
                    },
                    Children = new Drawable[]
                    {
                        new SkipButton(firstObjectTime)
                        {
                            AudioClock = decoupledClock
                        },
                        new Container
                        {
                            RelativeSizeAxes = Axes.Both,
                            Clock            = offsetClock,
                            Children         = new Drawable[]
                            {
                                HitRenderer,
                            }
                        },
                        hudOverlay = new HUDOverlay
                        {
                            Anchor = Anchor.Centre,
                            Origin = Anchor.Centre
                        },
                    }
                },
                failOverlay = new FailOverlay
                {
                    OnRetry = Restart,
                    OnQuit  = Exit,
                },
                new HotkeyRetryOverlay
                {
                    Action = () => {
                        //we want to hide the hitrenderer immediately (looks better).
                        //we may be able to remove this once the mouse cursor trail is improved.
                        HitRenderer?.Hide();
                        Restart();
                    },
                }
            };

            scoreProcessor = HitRenderer.CreateScoreProcessor();

            hudOverlay.KeyCounter.AddRange(rulesetInstance.CreateGameplayKeys());
            hudOverlay.BindProcessor(scoreProcessor);
            hudOverlay.BindHitRenderer(HitRenderer);

            hudOverlay.Progress.Objects      = HitRenderer.Objects;
            hudOverlay.Progress.AudioClock   = decoupledClock;
            hudOverlay.Progress.AllowSeeking = HitRenderer.HasReplayLoaded;
            hudOverlay.Progress.OnSeek       = pos => decoupledClock.Seek(pos);

            hudOverlay.ModDisplay.Current.BindTo(Beatmap.Value.Mods);

            //bind HitRenderer to ScoreProcessor and ourselves (for a pass situation)
            HitRenderer.OnAllJudged += onCompletion;

            //bind ScoreProcessor to ourselves (for a fail situation)
            scoreProcessor.Failed += onFail;
        }
Ejemplo n.º 30
0
        private void load(AudioManager audio, BeatmapDatabase beatmaps, OsuConfigManager config)
        {
            var beatmap = Beatmap.Beatmap;

            if (beatmap.BeatmapInfo?.Mode > PlayMode.Osu)
            {
                //we only support osu! mode for now because the hitobject parsing is crappy and needs a refactor.
                Exit();
                return;
            }

            dimLevel           = config.GetBindable <int>(OsuConfig.DimLevel);
            mouseWheelDisabled = config.GetBindable <bool>(OsuConfig.MouseDisableWheel);

            try
            {
                if (Beatmap == null)
                {
                    Beatmap = beatmaps.GetWorkingBeatmap(BeatmapInfo, withStoryboard: true);
                }

                if ((Beatmap?.Beatmap?.HitObjects.Count ?? 0) == 0)
                {
                    throw new Exception("No valid objects were found!");
                }

                if (Beatmap == null)
                {
                    throw new Exception("Beatmap was not loaded");
                }
            }
            catch (Exception e)
            {
                Logger.Log($"Could not load this beatmap sucessfully ({e})!", LoggingTarget.Runtime, LogLevel.Error);

                //couldn't load, hard abort!
                Exit();
                return;
            }

            Track track = Beatmap.Track;

            if (track != null)
            {
                audio.Track.SetExclusive(track);
                sourceClock = track;
            }

            sourceClock             = (IAdjustableClock)track ?? new StopwatchClock();
            interpolatedSourceClock = new InterpolatingFramedClock(sourceClock);

            Schedule(() =>
            {
                sourceClock.Reset();
            });

            ruleset = Ruleset.GetRuleset(Beatmap.PlayMode);

            hudOverlay = new StandardHudOverlay();
            hudOverlay.KeyCounter.Add(ruleset.CreateGameplayKeys());
            hudOverlay.BindProcessor(scoreProcessor = ruleset.CreateScoreProcessor(beatmap.HitObjects.Count));

            pauseOverlay = new PauseOverlay
            {
                Depth    = -1,
                OnResume = delegate
                {
                    Delay(400);
                    Schedule(Resume);
                },
                OnRetry = Restart,
                OnQuit  = Exit
            };

            hitRenderer = ruleset.CreateHitRendererWith(Beatmap);

            if (ReplayInputHandler != null)
            {
                hitRenderer.InputManager.ReplayInputHandler = ReplayInputHandler;
            }

            hudOverlay.BindHitRenderer(hitRenderer);

            //bind HitRenderer to ScoreProcessor and ourselves (for a pass situation)
            hitRenderer.OnJudgement += scoreProcessor.AddJudgement;
            hitRenderer.OnAllJudged += onPass;

            //bind ScoreProcessor to ourselves (for a fail situation)
            scoreProcessor.Failed += onFail;

            Children = new Drawable[]
            {
                new Container
                {
                    RelativeSizeAxes = Axes.Both,
                    Clock            = interpolatedSourceClock,
                    Children         = new Drawable[]
                    {
                        hitRenderer,
                        skipButton = new SkipButton
                        {
                            Alpha = 0
                        },
                    }
                },
                hudOverlay,
                pauseOverlay
            };
        }
Ejemplo n.º 31
0
        private void load(AudioManager audio, APIAccess api, OsuConfigManager config)
        {
            this.api = api;

            WorkingBeatmap working = Beatmap.Value;

            if (working is DummyWorkingBeatmap)
            {
                return;
            }

            sampleRestart = audio.Sample.Get(@"Gameplay/restart");

            mouseWheelDisabled = config.GetBindable <bool>(OsuSetting.MouseDisableWheel);
            userAudioOffset    = config.GetBindable <double>(OsuSetting.AudioOffset);

            IBeatmap beatmap;

            try
            {
                beatmap = working.Beatmap;

                if (beatmap == null)
                {
                    throw new InvalidOperationException("Beatmap was not loaded");
                }

                ruleset = Ruleset.Value ?? beatmap.BeatmapInfo.Ruleset;
                var rulesetInstance = ruleset.CreateInstance();

                try
                {
                    RulesetContainer = rulesetInstance.CreateRulesetContainerWith(working);
                }
                catch (BeatmapInvalidForRulesetException)
                {
                    // we may fail to create a RulesetContainer if the beatmap cannot be loaded with the user's preferred ruleset
                    // let's try again forcing the beatmap's ruleset.
                    ruleset          = beatmap.BeatmapInfo.Ruleset;
                    rulesetInstance  = ruleset.CreateInstance();
                    RulesetContainer = rulesetInstance.CreateRulesetContainerWith(Beatmap.Value);
                }

                if (!RulesetContainer.Objects.Any())
                {
                    Logger.Log("Beatmap contains no hit objects!", level: LogLevel.Error);
                    return;
                }
            }
            catch (Exception e)
            {
                Logger.Error(e, "Could not load beatmap sucessfully!");
                //couldn't load, hard abort!
                return;
            }

            sourceClock     = (IAdjustableClock)working.Track ?? new StopwatchClock();
            adjustableClock = new DecoupleableInterpolatingFramedClock {
                IsCoupled = false
            };

            adjustableClock.Seek(AllowLeadIn
                ? Math.Min(0, RulesetContainer.GameplayStartTime - beatmap.BeatmapInfo.AudioLeadIn)
                : RulesetContainer.GameplayStartTime);

            adjustableClock.ProcessFrame();

            // Lazer's audio timings in general doesn't match stable. This is the result of user testing, albeit limited.
            // This only seems to be required on windows. We need to eventually figure out why, with a bit of luck.
            var platformOffsetClock = new FramedOffsetClock(adjustableClock)
            {
                Offset = RuntimeInfo.OS == RuntimeInfo.Platform.Windows ? 22 : 0
            };

            // the final usable gameplay clock with user-set offsets applied.
            var offsetClock = new FramedOffsetClock(platformOffsetClock);

            userAudioOffset.ValueChanged += v => offsetClock.Offset = v;
            userAudioOffset.TriggerChange();

            ScoreProcessor = RulesetContainer.CreateScoreProcessor();
            if (!ScoreProcessor.Mode.Disabled)
            {
                config.BindWith(OsuSetting.ScoreDisplayMode, ScoreProcessor.Mode);
            }

            InternalChildren = new Drawable[]
            {
                pauseContainer = new PauseContainer(offsetClock, adjustableClock)
                {
                    Retries       = RestartCount,
                    OnRetry       = Restart,
                    OnQuit        = performUserRequestedExit,
                    CheckCanPause = () => AllowPause && ValidForResume && !HasFailed && !RulesetContainer.HasReplayLoaded,
                    Children      = new[]
                    {
                        storyboardContainer = new Container
                        {
                            RelativeSizeAxes = Axes.Both,
                            Alpha            = 0,
                        },
                        new ScalingContainer(ScalingMode.Gameplay)
                        {
                            Child = new LocalSkinOverrideContainer(working.Skin)
                            {
                                RelativeSizeAxes = Axes.Both,
                                Child            = RulesetContainer
                            }
                        },
                        new BreakOverlay(beatmap.BeatmapInfo.LetterboxInBreaks, ScoreProcessor)
                        {
                            Anchor             = Anchor.Centre,
                            Origin             = Anchor.Centre,
                            ProcessCustomClock = false,
                            Breaks             = beatmap.Breaks
                        },
                        new ScalingContainer(ScalingMode.Gameplay)
                        {
                            Child = RulesetContainer.Cursor?.CreateProxy() ?? new Container(),
                        },
                        HUDOverlay = new HUDOverlay(ScoreProcessor, RulesetContainer, working, offsetClock, adjustableClock)
                        {
                            Clock = Clock, // hud overlay doesn't want to use the audio clock directly
                            ProcessCustomClock = false,
                            Anchor             = Anchor.Centre,
                            Origin             = Anchor.Centre
                        },
                        new SkipOverlay(RulesetContainer.GameplayStartTime)
                        {
                            Clock = Clock, // skip button doesn't want to use the audio clock directly
                            ProcessCustomClock = false,
                            AdjustableClock    = adjustableClock,
                            FramedClock        = offsetClock,
                        },
                    }
                },
                failOverlay = new FailOverlay
                {
                    OnRetry = Restart,
                    OnQuit  = performUserRequestedExit,
                },
                new HotkeyRetryOverlay
                {
                    Action = () =>
                    {
                        if (!this.IsCurrentScreen())
                        {
                            return;
                        }

                        fadeOut(true);
                        Restart();
                    },
                }
            };

            HUDOverlay.HoldToQuit.Action = performUserRequestedExit;
            HUDOverlay.KeyCounter.Visible.BindTo(RulesetContainer.HasReplayLoaded);

            RulesetContainer.IsPaused.BindTo(pauseContainer.IsPaused);

            if (ShowStoryboard)
            {
                initializeStoryboard(false);
            }

            // Bind ScoreProcessor to ourselves
            ScoreProcessor.AllJudged += onCompletion;
            ScoreProcessor.Failed    += onFail;

            foreach (var mod in Beatmap.Value.Mods.Value.OfType <IApplicableToScoreProcessor>())
            {
                mod.ApplyToScoreProcessor(ScoreProcessor);
            }
        }