Beispiel #1
0
        /// <summary>
        ///     Handles completely missed HitObjects.
        /// </summary>
        private void HandleMissedHitObjects()
        {
            // Handle missed notes.
            foreach (var hitObject in ActiveHitObjects)
            {
                if (Time > hitObject.StartTime + ScoreProcessor.JudgementWindow[Judgement.Okay])
                {
                    // Add a miss to the score.
                    ScoreProcessor.CalculateScore(Judgement.Miss);

                    // Create a new HitStat to add to the ScoreProcessor.
                    var stat = new HitStat(HitStatType.Miss, KeyPressType.None, hitObject, hitObject.StartTime, Judgement.Miss, int.MinValue,
                                           ScoreProcessor.Accuracy, ScoreProcessor.Health);

                    ScoreProcessor.Stats.Add(stat);

                    // Long notes count as two misses, so add another one if the object is one.
                    if (hitObject.IsLongNote)
                    {
                        ScoreProcessor.CalculateScore(Judgement.Miss);
                        ScoreProcessor.Stats.Add(stat);
                    }

                    ActiveHitObjectsToRemove.Add(hitObject);
                }
                else
                {
                    break;
                }
            }

            // Remove all objects
            ActiveHitObjectsToRemove.ForEach(x => ActiveHitObjects.Remove(x));
            ActiveHeldLongNotesToRemove.ForEach(x => ActiveHeldLongNotes.Remove(x));
        }
Beispiel #2
0
        /// <summary>
        ///     Handles the replay frames for missed long notes.
        /// </summary>
        private void HandleMissedLongNoteReleases()
        {
            // Handle missed LN releases.
            foreach (var hitObject in ActiveHeldLongNotes)
            {
                var releaseWindow = ScoreProcessor.JudgementWindow[Judgement.Okay] * ScoreProcessor.WindowReleaseMultiplier[Judgement.Okay];

                // Check if the LN's release was missed.
                if (!(Time > hitObject.EndTime + releaseWindow))
                {
                    continue;
                }

                ScoreProcessor.CalculateScore(Judgement.Okay);

                // Add new miss stat.
                var stat = new HitStat(HitStatType.Miss, KeyPressType.None, hitObject, hitObject.EndTime, Judgement.Okay, int.MinValue,
                                       ScoreProcessor.Accuracy, ScoreProcessor.Health);

                ScoreProcessor.Stats.Add(stat);

                // Queue the object to be removed.
                ActiveHeldLongNotesToRemove.Add(hitObject);
            }

            ActiveHeldLongNotesToRemove.ForEach(x => ActiveHeldLongNotes.Remove(x));
        }
        /// <summary>
        ///     Filters out only those stats that we care about and computes earliest and latest hit time.
        /// </summary>
        private void FilterHitStats()
        {
            StatsWithHitDifference    = new List <HitStat>();
            StatsWithoutHitDifference = new List <HitStat>();
            EarliestHitTime           = int.MaxValue;
            LatestHitTime             = int.MinValue;

            foreach (var breakdown in Processor.Stats)
            {
                EarliestHitTime = Math.Min(EarliestHitTime, breakdown.SongPosition);
                LatestHitTime   = Math.Max(LatestHitTime, breakdown.SongPosition);

                var hitDifference = breakdown.HitDifference;
                if (breakdown.KeyPressType == KeyPressType.Release && breakdown.Judgement != Judgement.Miss)
                {
                    // Scale LN release hit errors to match regular hits.
                    hitDifference = (int)(breakdown.HitDifference /
                                          Processor.WindowReleaseMultiplier[breakdown.Judgement]);
                }

                var hitStat = new HitStat(breakdown.Type, breakdown.KeyPressType, breakdown.HitObject,
                                          breakdown.SongPosition, breakdown.Judgement, hitDifference, breakdown.Accuracy,
                                          breakdown.Health);

                // No need to check for Type == Miss as all of them have hitDifference == int.MinValue.
                if (hitDifference != int.MinValue && Math.Abs(hitDifference) <= LargestHitWindow)
                {
                    StatsWithHitDifference.Add(hitStat);
                }
                else
                {
                    StatsWithoutHitDifference.Add(hitStat);
                }
            }
        }
Beispiel #4
0
        /// <summary>
        ///     Updates the active objects in the pool + adds to score when applicable.
        /// </summary>
        private void UpdateAndScoreActiveObjects()
        {
            // Add more hit objects to the pool if necessary
            foreach (var lane in HitObjectQueueLanes)
            {
                while (lane.Count > 0 && CurrentTrackPosition - GetPositionFromTime(lane.Peek().StartTime) > CreateObjectPosition)
                {
                    CreatePoolObject(lane.Dequeue());
                }
            }

            // Check to see if the player missed any active notes
            foreach (var lane in ActiveNoteLanes)
            {
                while (lane.Count > 0 && CurrentAudioPosition > lane.Peek().Info.StartTime + Ruleset.ScoreProcessor.JudgementWindow[Judgement.Okay])
                {
                    // Current hit object
                    var hitObject = lane.Dequeue();

                    // Update scoreboard for simulated plays
                    var screenView = (GameplayScreenView)Ruleset.Screen.View;
                    screenView.UpdateScoreboardUsers();

                    // Add new hit stat data and update score
                    var stat = new HitStat(HitStatType.Miss, KeyPressType.None, hitObject.Info, (int)Ruleset.Screen.Timing.Time, Judgement.Miss,
                                           int.MinValue, Ruleset.ScoreProcessor.Accuracy, Ruleset.ScoreProcessor.Health);
                    Ruleset.ScoreProcessor.Stats.Add(stat);
                    Ruleset.ScoreProcessor.CalculateScore(Judgement.Miss);

                    // Perform Playfield animations
                    var playfield = (GameplayPlayfieldKeys)Ruleset.Playfield;
                    playfield.Stage.ComboDisplay.MakeVisible();
                    playfield.Stage.JudgementHitBurst.PerformJudgementAnimation(Judgement.Miss);

                    // If HitObject is an LN, kill it and count it as another miss because of the tail.
                    // - missing an LN counts as two misses
                    if (hitObject.Info.IsLongNote)
                    {
                        KillPoolObject(hitObject);
                        Ruleset.ScoreProcessor.CalculateScore(Judgement.Miss);
                        Ruleset.ScoreProcessor.Stats.Add(stat);
                        screenView.UpdateScoreboardUsers();
                    }
                    // Otherwise just kill the object.
                    else
                    {
                        KillPoolObject(hitObject);
                    }
                }
            }

            // Update active objects.
            foreach (var lane in ActiveNoteLanes)
            {
                foreach (var hitObject in lane)
                {
                    hitObject.UpdateSpritePositions(CurrentTrackPosition);
                }
            }
        }
Beispiel #5
0
        /// <summary>
        ///     Updates the held long note objects in the pool + adds to score when applicable.
        /// </summary>
        private void UpdateAndScoreHeldObjects()
        {
            // The release window. (Window * Multiplier)
            var window = Ruleset.ScoreProcessor.JudgementWindow[Judgement.Okay] * Ruleset.ScoreProcessor.WindowReleaseMultiplier[Judgement.Okay];

            // Check to see if any LN releases were missed (Counts as an okay instead of a miss.)
            foreach (var lane in HeldLongNoteLanes)
            {
                while (lane.Count > 0 && CurrentAudioPosition > lane.Peek().Info.EndTime + window)
                {
                    // Current hit object
                    var hitObject = lane.Dequeue();

                    // The judgement that is given when a user completely fails to release.
                    const Judgement missedJudgement = Judgement.Okay;

                    // Add new hit stat data and update score
                    var stat = new HitStat(HitStatType.Miss, KeyPressType.None, hitObject.Info, hitObject.Info.EndTime, Judgement.Okay,
                                           int.MinValue, Ruleset.ScoreProcessor.Accuracy, Ruleset.ScoreProcessor.Health);
                    Ruleset.ScoreProcessor.Stats.Add(stat);

                    var im = Ruleset.InputManager as KeysInputManager;

                    if (im?.ReplayInputManager == null)
                    {
                        Ruleset.ScoreProcessor.CalculateScore(missedJudgement);
                    }

                    // Update scoreboard for simulated plays
                    var screenView = (GameplayScreenView)Ruleset.Screen.View;
                    screenView.UpdateScoreboardUsers();
                    screenView.UpdateScoreAndAccuracyDisplays();

                    // Perform Playfield animations
                    var stage = ((GameplayPlayfieldKeys)Ruleset.Playfield).Stage;

                    if (im?.ReplayInputManager == null)
                    {
                        stage.ComboDisplay.MakeVisible();
                        stage.JudgementHitBurst.PerformJudgementAnimation(missedJudgement);
                    }

                    stage.HitLightingObjects[hitObject.Info.Lane - 1].StopHolding();

                    // Update Pooling
                    KillHoldPoolObject(hitObject);
                }
            }

            // Update the currently held long notes.
            foreach (var lane in HeldLongNoteLanes)
            {
                foreach (var hitObject in lane)
                {
                    hitObject.UpdateSpritePositions(CurrentTrackPosition);
                }
            }
        }
Beispiel #6
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));
        }