Exemplo n.º 1
0
        /// <summary>
        /// When we have latency involved, we need to look at changing the autoplay replay
        /// to act as if a player was hitting notes.  So we should check for any non-300s
        /// and apply then to the replay by warping some frames.
        /// </summary>
        private void AlterReplay(IncreaseScoreType type)
        {
            List <bReplayFrame> replay = autoScore.Replay;
            int currentFrame           = InputManager.ReplayFrame + 1;

            while (currentFrame < replay.Count - 2)
            {
                bReplayFrame prev = replay[currentFrame - 1];
                bReplayFrame curr = replay[currentFrame];
                bReplayFrame next = replay[currentFrame + 1];

                if (curr.mouseLeft)
                {
                    if (prev.mouseLeft || next.mouseLeft || isLocalUserActiveAt(curr.time))
                    {
                        currentFrame++;
                        continue; //slider or spinner.
                    }

                    switch (type)
                    {
                    case IncreaseScoreType.Miss:
                        curr.SetButtonStates(pButtonState.None);
                        return;

                    case IncreaseScoreType.Hit50:
                    {
                        int aim = (int)(player.hitObjectManager.HitWindow100 * 1.1f);
                        if (curr.time - prev.time > aim)
                        {
                            curr.time -= aim;
                            return;
                        }
                    }
                    break;

                    case IncreaseScoreType.Hit100:
                    {
                        int aim = (int)(player.hitObjectManager.HitWindow300 * 1.1f);
                        if (curr.time - prev.time > aim)
                        {
                            curr.time -= aim;
                            return;
                        }
                    }
                    break;
                    }
                }

                currentFrame++;
            }
        }
Exemplo n.º 2
0
        public static void PushNewFrame(bReplayFrame f)
        {
            if (!HasSpectators && waitingOutgoingFrames.Count > 0)
            {
                waitingOutgoingFrames.Clear();
                return;
            }

            waitingOutgoingFrames.Add(f);

            if (waitingOutgoingFrames.Count >= 30)
            {
                PurgeFrames(queuedAction);
                queuedAction = ReplayAction.Standard;
            }
        }
Exemplo n.º 3
0
        internal void ReadReplayData(string replayData)
        {
            if (replayData.Length > 0)
            {
                string[] replaylines = replayData.Split(',');

                bReplayFrame lastF;
                if (Replay.Count > 0)
                {
                    lastF = Replay[Replay.Count - 1];
                }
                else
                {
                    lastF = new bReplayFrame(0, 0, 0, pButtonState.None);
                }

                foreach (string replayline in replaylines)
                {
                    if (replayline.Length == 0)
                    {
                        continue;
                    }

                    string[] data = replayline.Split('|');

                    if (data.Length < 4)
                    {
                        continue;
                    }
                    if (data[0] == @"-12345")
                    {
                        Seed = int.Parse(data[3]);
                        continue;
                    }

                    pButtonState buttons = (pButtonState)Enum.Parse(typeof(pButtonState), data[3]);

                    bReplayFrame newF = new bReplayFrame(int.Parse(data[0]) + lastF.time,
                                                         float.Parse(data[1], GameBase.nfi),
                                                         float.Parse(data[2], GameBase.nfi),
                                                         buttons);
                    Replay.Add(newF);

                    lastF = newF;
                }
            }
        }
Exemplo n.º 4
0
        private static int FindInsertionIndex(List <bReplayFrame> replay, bReplayFrame frame)
        {
            int index = replay.BinarySearch(frame, replayFrameComparer);

            if (index < 0)
            {
                index = ~index;
            }
            else
            {
                // Go to the first index which is actually bigger
                while (index < replay.Count && frame.time == replay[index].time)
                {
                    ++index;
                }
            }

            return(index);
        }
Exemplo n.º 5
0
        internal override void Update()
        {
            HitObjectManager hitObjectManager = player.hitObjectManager;

            if (InputManager.ReplayMode && !player.Passed && AudioEngine.Time < lastRewindTime + hitObjectManager.HitWindow50)
            {
                return;
            }

            int activePlayerSlot = activePlayerSlotId;

            bool localUserActive = activePlayerSlot == player.localPlayerMatchId || isLocalUserActive;

            if (scoreboardActiveUser != activePlayerSlot || InputManager.ReplayMode == localUserActive)
            {
                scoreboardActiveUser = activePlayerSlot;

                UpdateRankingText();

                InputManager.s_Cursor.FadeColour(localUserActive ? Color.White : Color.Gray, 50);

                if (activePlayerSlot == -2) //all players (spinner)
                {
                    scoreboard.SetActiveAll();
                }
                else
                {
                    scoreboard.SetActivePlayer(activePlayerSlot == -1 ? null : player.ScoreEntries[activePlayerSlot]);
                }

                if (InputManager.ReplayMode == localUserActive)
                {
                    InputManager.ReplayMode = !localUserActive;
                    player.UpdateChecksum(); //Must update checksum here else it will think we are hacking.

                    if (InputManager.ReplayMode)
                    {
                        InputManager.ReplayScore = autoScore;

                        lastRewindTime = AudioEngine.Time;

                        HitObject h =
                            player.hitObjectManager.hitObjectsMinimal.Find(
                                ho =>
                                ho.StartTime >= AudioEngine.Time - hitObjectManager.HitWindow50 &&
                                ho.TagNumeric == scoreboardActiveUser &&
                                !ho.IsHit);
                        //If we accidentally bypassed a possible hit/miss situation, we don't want to skip directly to the current time.
                        //We need to let the replay start a few frames earlier (in practice this shouldn't cause any problems with repeated
                        //events... I hope).

                        int rewindTime = h != null ? h.StartTime - hitObjectManager.HitWindow50 : (int)AudioEngine.Time;

                        //Bypass any unnecessary frames.
                        while (InputManager.ReplayFrame < InputManager.ReplayScore.Replay.Count - 1 &&
                               InputManager.ReplayScore.Replay[InputManager.ReplayFrame].time < rewindTime)
                        {
                            InputManager.ReplayFrame++;
                        }

                        //Then go back to check for any clicks we may have missed..
                        if (InputManager.ReplayScore.Replay[InputManager.ReplayFrame].mouseLeft)
                        {
                            while (InputManager.ReplayScore.Replay[InputManager.ReplayFrame].time > rewindTime &&
                                   InputManager.ReplayScore.Replay[InputManager.ReplayFrame - 1].mouseLeft)
                            {
                                InputManager.ReplayFrame--;
                            }
                        }
                        else
                        {
                            while (InputManager.ReplayScore.Replay[InputManager.ReplayFrame].time > rewindTime &&
                                   !InputManager.ReplayScore.Replay[InputManager.ReplayFrame].mouseLeft)
                            {
                                InputManager.ReplayFrame--;
                            }
                        }

                        rewindTime = InputManager.ReplayScore.Replay[InputManager.ReplayFrame].time;

                        h = player.hitObjectManager.hitObjectsMinimal.Find(ho => ho.StartTime == rewindTime);
                        if (h != null && h.TagNumeric != scoreboardActiveUser)
                        {
                            //Found a frame for a circle that isn't owned by this user - skip forward a frame and play.
                            InputManager.ReplayFrame++;
                            //lastRewindTime = InputManager.ReplayScore.replay[InputManager.ReplayFrame].time;
                        }

                        //Insert a dud frame to clear the mouse buttons.
                        bReplayFrame cf = InputManager.ReplayScore.Replay[InputManager.ReplayFrame];

                        bReplayFrame frame = new bReplayFrame(cf.time - 1, cf.mouseX, cf.mouseY, pButtonState.None);
                        InputManager.ReplayScore.Replay.Insert(InputManager.ReplayFrame, frame);
                        InputManager.ReplayScore.Replay.Insert(InputManager.ReplayFrame, frame);
                    }
                    else
                    {
                        //Force an update to make sure we dont accidentally accept a false positive click.
                        InputManager.UpdateButtonsLocalPlayer();
                    }
                }
            }

            base.Update();
        }
Exemplo n.º 6
0
        /// <summary>
        /// When we have latency involved, we need to look at changing the autoplay replay
        /// to act as if a player was hitting notes.  So we should check for any non-300s
        /// and apply then to the replay by warping some frames.
        /// </summary>
        private void AlterReplaySlider(IncreaseScoreType type)
        {
            //First check if we just want to increase the tick count...

            List <bReplayFrame> replay = autoScore.Replay;
            int currentFrame           = InputManager.ReplayFrame + 1;

            int replayCount = replay.Count;

            if (currentFrame > replayCount - 1)
            {
                return;
            }

            int sliderCheckFrame = Math.Max(safeNoClickFrame, currentFrame);

            //Even if we already are in the middle of a slider, we should
            //rewind back to the start and utilise the rest of it.
            while (sliderCheckFrame < replayCount - 2 && replay[sliderCheckFrame + 1].mouseLeft)
            {
                sliderCheckFrame++;
            }

            safeNoClickFrame = sliderCheckFrame;

            while (currentFrame < replayCount - 2)
            {
                bReplayFrame prev = replay[currentFrame - 1];
                bReplayFrame curr = replay[currentFrame];
                bReplayFrame next = replay[currentFrame + 1];

                if (curr.mouseLeft && next.mouseLeft && !prev.mouseLeft && !isLocalUserActiveAt(curr.time))
                {
                    //Found the start of a slider or spinner.
                    //We need to confirm this is actually a slider though.

                    //bool firstMatchRewindFound = sliderCheckFrame != currentFrame && first;
                    //Is the first check in this loop and rewinding has been utilised.

                    //if (!firstMatchRewindFound) //Rewinding current slider not used.  Use currentFrame.
                    //    sliderCheckFrame = currentFrame;

                    sliderCheckFrame = currentFrame;

                    int searchTime = replay[sliderCheckFrame].time;

                    SliderOsu found =
                        player.hitObjectManager.hitObjects.Find(ho => ho.StartTime == searchTime) as SliderOsu;

                    if (found == null)
                    {
                        currentFrame++;
                        continue; //No slider or spinner.
                    }

                    //At this point we know we have a slider.
                    //Now we need to choose how to change the replay depending on the type of
                    //penalty we are planning to deal.

                    switch (type)
                    {
                    case IncreaseScoreType.Miss:
                        //The slider should be TOTALLY missed.

                        //We can't do this if we have already started on a slider that
                        //has already had a non-miss...

                        /*if (firstMatchRewindFound && found.sliderTicksMissed == 0)
                         * {
                         *  currentFrame++;
                         *  continue;
                         * }*/

                        while (curr.mouseLeft)
                        {
                            //No mouse control over the whole slider is required.
                            curr.SetButtonStates(pButtonState.None);

                            if (currentFrame == replayCount - 1)
                            {
                                return;
                            }

                            curr = replay[++currentFrame];
                        }

                        return;

                    case IncreaseScoreType.Hit50:
                    case IncreaseScoreType.Hit100:
                    case IncreaseScoreType.Hit300:
                    {
                        int possibleCount;

                        if (type == IncreaseScoreType.Hit50)
                        {
                            //In case of a 50, we can miss a minimum of 50%.
                            possibleCount = (found.sliderScoreTimingPoints.Count + 1) / 2 ==
                                            (found.sliderScoreTimingPoints.Count + 1) / 2f
                                                        ?
                                            (found.sliderScoreTimingPoints.Count + 1) / 2
                                                        : (found.sliderScoreTimingPoints.Count + 1) / 2 + 1;

                            //possibleCount = found.sliderScoreTimingPoints.Count;
                        }
                        else if (type == IncreaseScoreType.Hit100)
                        {
                            //In case of a 100, we can miss a maximum of 50%.
                            possibleCount = 1;         //TOO LENIANT
                        }
                        else
                        {
                            return;
                        }

                        //If the slider is already partway through, we should remove any ticks which have already been consumed.
                        possibleCount -= found.sliderTicksHit;

                        if (possibleCount <= 0)
                        {
                            currentFrame++;
                            continue;
                        }

                        bool firstTick = true;

                        int firstTickEndTime = curr.time +
                                               Math.Min(found.sliderScoreTimingPoints[0],
                                                        player.hitObjectManager.HitWindow50);

                        while (curr.time <= found.EndTime && possibleCount > 0)
                        {
                            if (firstTick)
                            {
                                if (curr.time < firstTickEndTime)
                                {
                                    curr.SetButtonStates(pButtonState.None);
                                }
                                else
                                {
                                    firstTick = false;
                                    possibleCount--;
                                }
                            }

                            if (!firstTick)
                            {
                                bool containsTimingPoint =
                                    found.sliderScoreTimingPoints.FindIndex(
                                        tp => curr.time <= tp && next.time > tp) >= 0;

                                if (containsTimingPoint)
                                {
                                    if (possibleCount > 0)
                                    {
                                        possibleCount--;
                                        curr.SetButtonStates(pButtonState.None);
                                        next.SetButtonStates(pButtonState.None);
                                        Debug.Print("  Consumed 1 tick (remaining to consume " + possibleCount + ")");
                                    }
                                    else
                                    {
                                        return;
                                    }
                                }
                            }

                            if (currentFrame == replayCount - 1)
                            {
                                break;
                            }

                            curr = replay[++currentFrame];
                            next = replay[currentFrame + 1];
                        }
                    }

                        Debug.Print("  Slider exhausted...");
                        return;
                    }
                }

                currentFrame++;
            }
        }
Exemplo n.º 7
0
        internal override void CreateAutoplayReplay()
        {
            int buttonIndex = 0;

            bool        delayedMovements = ModManager.CheckActive(Mods.Relax2);
            EasingTypes preferredEasing  = delayedMovements ? EasingTypes.InOutCubic : EasingTypes.Out;

            InputManager.ReplayScore.Replay = new List <bReplayFrame>();
            List <bReplayFrame> replay = InputManager.ReplayScore.Replay;

            AddFrameToReplay(replay, new bReplayFrame(-100000, 256, 500, pButtonState.None));
            AddFrameToReplay(replay, new bReplayFrame(hitObjectManager.hitObjects[0].StartTime - 1500, 256, 500, pButtonState.None));
            AddFrameToReplay(replay, new bReplayFrame(hitObjectManager.hitObjects[0].StartTime - 1000, 256, 192, pButtonState.None));

            // We are using ApplyModsToRate and not ApplyModsToTime to counteract the speed up / slow down from HalfTime / DoubleTime so that we remain at a constant framerate of 60 fps.
            float       frameDelay    = (float)HitObjectManager.ApplyModsToRate(1000.0 / 60.0);
            Vector2     spinnerCentre = new Vector2(256, 192);
            const float spinnerRadius = 50;

            // Already superhuman, but still somewhat realistic
            int reactionTime = (int)HitObjectManager.ApplyModsToRate(100);


            for (int i = 0; i < hitObjectManager.hitObjectsCount; i++)
            {
                HitObject h = hitObjectManager.hitObjects[i];

                if (h.EndTime < InputManager.ReplayStartTime)
                {
                    h.IsHit = true;
                    continue;
                }

                int endDelay = h is SpinnerOsu ? 1 : 0;

                if (delayedMovements && i > 0)
                {
                    HitObject last = hitObjectManager.hitObjects[i - 1];

                    //Make the cursor stay at a hitObject as long as possible (mainly for autopilot).
                    if (h.StartTime - HitObjectManager.HITTABLE_RANGE > last.EndTime + hitObjectManager.HitWindow50 + 50)
                    {
                        if (!(last is Spinner) && h.StartTime - last.EndTime < 1000)
                        {
                            AddFrameToReplay(replay, new bReplayFrame(last.EndTime + hitObjectManager.HitWindow50, last.EndPosition.X, last.EndPosition.Y, pButtonState.None));
                        }
                        if (!(h is Spinner))
                        {
                            AddFrameToReplay(replay, new bReplayFrame(h.StartTime - HitObjectManager.HITTABLE_RANGE, h.Position.X, h.Position.Y, pButtonState.None));
                        }
                    }
                    else if (h.StartTime - hitObjectManager.HitWindow50 > last.EndTime + hitObjectManager.HitWindow50 + 50)
                    {
                        if (!(last is Spinner) && h.StartTime - last.EndTime < 1000)
                        {
                            AddFrameToReplay(replay, new bReplayFrame(last.EndTime + hitObjectManager.HitWindow50, last.EndPosition.X, last.EndPosition.Y, pButtonState.None));
                        }
                        if (!(h is Spinner))
                        {
                            AddFrameToReplay(replay, new bReplayFrame(h.StartTime - hitObjectManager.HitWindow50, h.Position.X, h.Position.Y, pButtonState.None));
                        }
                    }
                    else if (h.StartTime - hitObjectManager.HitWindow100 > last.EndTime + hitObjectManager.HitWindow100 + 50)
                    {
                        if (!(last is Spinner) && h.StartTime - last.EndTime < 1000)
                        {
                            AddFrameToReplay(replay, new bReplayFrame(last.EndTime + hitObjectManager.HitWindow100, last.EndPosition.X, last.EndPosition.Y, pButtonState.None));
                        }
                        if (!(h is Spinner))
                        {
                            AddFrameToReplay(replay, new bReplayFrame(h.StartTime - hitObjectManager.HitWindow100, h.Position.X, h.Position.Y, pButtonState.None));
                        }
                    }
                }


                Vector2     targetPosition   = h.Position;
                EasingTypes easing           = preferredEasing;
                float       spinnerDirection = -1;

                if (h is Spinner)
                {
                    targetPosition.X = replay[replay.Count - 1].mouseX;
                    targetPosition.Y = replay[replay.Count - 1].mouseY;

                    Vector2 difference = spinnerCentre - targetPosition;

                    float differenceLength = difference.Length();
                    float newLength        = (float)Math.Sqrt(differenceLength * differenceLength - spinnerRadius * spinnerRadius);

                    if (differenceLength > spinnerRadius)
                    {
                        float angle = (float)Math.Asin(spinnerRadius / differenceLength);

                        if (angle > 0)
                        {
                            spinnerDirection = -1;
                        }
                        else
                        {
                            spinnerDirection = 1;
                        }

                        difference.X = difference.X * (float)Math.Cos(angle) - difference.Y * (float)Math.Sin(angle);
                        difference.Y = difference.X * (float)Math.Sin(angle) + difference.Y * (float)Math.Cos(angle);

                        difference.Normalize();
                        difference *= newLength;

                        targetPosition += difference;

                        easing = EasingTypes.In;
                    }
                    else if (difference.Length() > 0)
                    {
                        targetPosition = spinnerCentre - difference * (spinnerRadius / difference.Length());
                    }
                    else
                    {
                        targetPosition = spinnerCentre + new Vector2(0, -spinnerRadius);
                    }
                }


                // Do some nice easing for cursor movements
                if (replay.Count > 0)
                {
                    bReplayFrame lastFrame = replay[replay.Count - 1];

                    // Wait until Auto could "see and react" to the next note.
                    int waitTime = h.StartTime - (int)Math.Max(0.0, hitObjectManager.PreEmpt - reactionTime);
                    if (waitTime > lastFrame.time)
                    {
                        lastFrame = new bReplayFrame(waitTime, lastFrame.mouseX, lastFrame.mouseY, lastFrame.buttonState);
                        AddFrameToReplay(replay, lastFrame);
                    }

                    Vector2 lastPosition = new Vector2(lastFrame.mouseX, lastFrame.mouseY);

                    HitObjectManagerOsu hom = hitObjectManager as HitObjectManagerOsu;
                    double timeDifference   = HitObjectManager.ApplyModsToTime(h.StartTime - lastFrame.time, ModManager.ModStatus);

                    // Only "snap" to hitcircles if they are far enough apart. As the time between hitcircles gets shorter the snapping threshold goes up.
                    if (hom != null && timeDifference > 0 &&                                                                // Sanity checks
                        ((lastPosition - targetPosition).Length() > hom.HitObjectRadius * (1.5 + 100.0 / timeDifference) || // Either the distance is big enough
                         timeDifference >= 266))                                                                            // ... or the beats are slow enough to tap anyway.
                    {
                        // Perform eased movement
                        for (float time = lastFrame.time + frameDelay; time < h.StartTime; time += frameDelay)
                        {
                            Vector2 currentPosition = OsuMathHelper.TweenValues(lastPosition, targetPosition, time, lastFrame.time, h.StartTime, easing);
                            AddFrameToReplay(replay, new bReplayFrame((int)time, currentPosition.X, currentPosition.Y, lastFrame.buttonState));
                        }

                        buttonIndex = 0;
                    }
                    else
                    {
                        buttonIndex++;
                    }
                }

                pButtonState button         = buttonIndex % 2 == 0 ? pButtonState.Left1 : pButtonState.Right1;
                pButtonState previousButton = pButtonState.None;

                bReplayFrame newFrame = new bReplayFrame(h.StartTime, targetPosition.X, targetPosition.Y, button);
                bReplayFrame endFrame = new bReplayFrame(h.EndTime + endDelay, h.EndPosition.X, h.EndPosition.Y, pButtonState.None);

                // Decrement because we want the previous frame, not the next one
                int index = FindInsertionIndex(replay, newFrame) - 1;

                // Do we have a previous frame? No need to check for < replay.Count since we decremented!
                if (index >= 0)
                {
                    bReplayFrame previousFrame = replay[index];
                    previousButton = previousFrame.buttonState;

                    // If a button is already held, then we simply alternate
                    if (previousButton != pButtonState.None)
                    {
                        Debug.Assert(previousButton != (pButtonState.Left1 | pButtonState.Right1));

                        // Force alternation if we have the same button. Otherwise we can just keep the naturally to us assigned button.
                        if (previousButton == button)
                        {
                            button = (pButtonState.Left1 | pButtonState.Right1) & ~button;
                            newFrame.SetButtonStates(button);
                        }

                        // We always follow the most recent slider / spinner, so remove any other frames that occur while it exists.
                        int endIndex = FindInsertionIndex(replay, endFrame);

                        if (index < replay.Count - 1)
                        {
                            replay.RemoveRange(index + 1, Math.Max(0, endIndex - (index + 1)));
                        }

                        // After alternating we need to keep holding the other button in the future rather than the previous one.
                        for (int j = index + 1; j < replay.Count; ++j)
                        {
                            // Don't affect frames which stop pressing a button!
                            if (j < replay.Count - 1 || replay[j].buttonState == previousButton)
                            {
                                replay[j].SetButtonStates(button);
                            }
                        }
                    }
                }

                AddFrameToReplay(replay, newFrame);

                // We add intermediate frames for spinning / following a slider here.
                if (h is SpinnerOsu)
                {
                    Vector2 difference = targetPosition - spinnerCentre;

                    float radius = difference.Length();
                    float angle  = radius == 0 ? 0 : (float)Math.Atan2(difference.Y, difference.X);

                    float t;

                    for (float j = h.StartTime + frameDelay; j < h.EndTime; j += frameDelay)
                    {
                        t = (float)HitObjectManager.ApplyModsToTime(j - h.StartTime) * spinnerDirection;

                        Vector2 pos = spinnerCentre + CirclePosition(t / 20 + angle, spinnerRadius);
                        AddFrameToReplay(replay, new bReplayFrame((int)j, pos.X, pos.Y, button));
                    }

                    t = (float)HitObjectManager.ApplyModsToTime(h.EndTime - h.StartTime) * spinnerDirection;
                    Vector2 endPosition = spinnerCentre + CirclePosition(t / 20 + angle, spinnerRadius);

                    AddFrameToReplay(replay, new bReplayFrame(h.EndTime, endPosition.X, endPosition.Y, button));

                    endFrame.mouseX = endPosition.X;
                    endFrame.mouseY = endPosition.Y;
                }
                else if (h is SliderOsu)
                {
                    SliderOsu s        = h as SliderOsu;
                    int       lastTime = 0;

                    foreach (
                        Transformation t in
                        s.sliderFollower.Transformations.FindAll(
                            tr => tr.Type == TransformationType.Movement))
                    {
                        if (lastTime != 0 && t.Time1 - lastTime < frameDelay)
                        {
                            continue;
                        }

                        AddFrameToReplay(replay, new bReplayFrame(t.Time1, t.StartVector.X, t.StartVector.Y,
                                                                  button));
                        lastTime = t.Time1;
                    }

                    AddFrameToReplay(replay, new bReplayFrame(h.EndTime, h.EndPosition.X, h.EndPosition.Y, button));
                }

                // We only want to let go of our button if we are at the end of the current replay. Otherwise something is still going on after us so we need to keep the button pressed!
                if (replay[replay.Count - 1].time <= endFrame.time)
                {
                    AddFrameToReplay(replay, endFrame);
                }
            }

            Player.currentScore.Replay     = InputManager.ReplayScore.Replay;
            Player.currentScore.PlayerName = "osu!";
        }
Exemplo n.º 8
0
 private static void AddFrameToReplay(List <bReplayFrame> replay, bReplayFrame frame)
 {
     replay.Insert(FindInsertionIndex(replay, frame), frame);
 }