Esempio n. 1
0
        internal void RecalculateSliderLengths()
        {
            if (hitObjectManager.hitObjectsCount == 0)
            {
                return;
            }

            changeManager.BeginAction(ChangeType.HitObject);

            foreach (HitObject h in hitObjectManager.hitObjects)
            {
                SliderOsu s = h as SliderOsu;

                if (s != null)
                {
                    changeManager.PushAction(ChangeType.HitObject, ActionType.ChangeLength, Modifier.VariableChanges);
                    changeManager.BackupData(s);
                    s.SpatialLength = 0;
                    s.UpdateCalculations();
                }
                editor.BreakDirty = true;
                editor.UpdateBreaks();
            }
            changeManager.FinishAction();
        }
        internal override bool SetBackupData()
        {
            if (!hasNewData)
            {
                throw new NotEnoughObjectsException();
            }

            if (initializeStorage)
            {
                startPositions = new List <Vector2>();
                sliderPoints   = new List <Vector2>();
                spatialData    = new List <double>();
            }

            BackupData(delegate(HitObject h)
            {
                startPositions.Add(h.BasePosition);

                SliderOsu s = h as SliderOsu;
                if (s != null)
                {
                    spatialData.Add(s.SpatialLength);
                    sliderPoints.AddRange(s.sliderCurvePoints);
                }
            });

            return(true);
        }
Esempio n. 3
0
        internal override bool SetBackupData()
        {
            if (!hasNewData)
            {
                throw new NotEnoughObjectsException();
            }

            if (initializeStorage)
            {
                soundAdditions     = new List <SoundAdditionData>();
                soundAdditionsList = new List <SoundAdditionData>();
            }

            backupSelectedIndexes();

            BackupData(delegate(HitObject h)
            {
                soundAdditions.Add(new SoundAdditionData(h.SoundType));

                SliderOsu s = h as SliderOsu;
                if (s != null)
                {
                    soundAdditionsList.Add(new SoundAdditionData(s.SoundTypeList));
                }
            });

            return(true);
        }
Esempio n. 4
0
        internal override void ChangeState(State undoState)
        {
            int index  = 0,
                sIndex = 0;

            foreach (HitObject h in changedObjects)
            {
                ListHelper.Swap(soundAdditions[index].SoundTypes, 0, ref h.SoundType);

                SliderOsu s = h as SliderOsu;
                if (s != null)
                {
                    SoundAdditionData soundData = soundAdditionsList[sIndex];
                    ListHelper.Swap(soundData.SoundTypes, s.SoundTypeList);

                    updateUnifiedStatus(s, s.SoundTypeList);
                    sIndex++;
                }
                index++;
            }

            ActiveManager.Editor.Compose.ReplaceSelection(changedObjects);

            if (selectedSliderIndexes != null)
            {
                ActiveManager.Editor.Compose.SelectIndexes((SliderOsu)changedObjects[0], selectedSliderIndexes);
            }
        }
Esempio n. 5
0
        internal override void UpdateFlashlight()
        {
            if (s_Flashlight.Texture == null || spriteManagerFlashlight.SpriteList.Count == 0 || !s_Flashlight.Texture.TextureGl.Loaded)
            {
                if (AllowSubmission)
                {
                    CurrentScore.InvalidateSubmission();
                    player.AddFlashlightFlag();

                    ConfigManager.PurgeHashCache();
                    GameBase.ChangeMode(OsuModes.Update);
                }
                return;
            }

            s_Flashlight.Position = GameBase.GameField.FieldToDisplay(catcher1.Position) / GameBase.WindowManager.Ratio;

            if (!Player.Paused)
            {
                SliderOsu s = player.ActiveHitObject as SliderOsu;
                if (s != null && s.IsSliding)
                {
                    if (!s_LightsOut.IsVisible)
                    {
                        s_LightsOut.Alpha = 0.8f;
                    }
                }
                else if (s_LightsOut.IsVisible)
                {
                    s_LightsOut.Alpha = 0;
                }

                float targetScale = 8.0f;

                if (!EventManager.BreakMode)
                {
                    if (ComboCounter.HitCombo < 100)
                    {
                        targetScale = 5.2f;
                    }
                    else if (ComboCounter.HitCombo < 200)
                    {
                        targetScale = 4.6f;
                    }
                    else
                    {
                        targetScale = 4.0f;
                    }
                }

                if (s_Flashlight.Scale > targetScale)
                {
                    s_Flashlight.Scale = (float)Math.Max(s_Flashlight.Scale - 0.1 * GameBase.FrameRatio, targetScale);
                }
                else if (s_Flashlight.Scale < targetScale)
                {
                    s_Flashlight.Scale = (float)Math.Min(s_Flashlight.Scale + 0.1 * GameBase.FrameRatio, targetScale);
                }
            }
        }
Esempio n. 6
0
            /// <summary>
            /// Special ctor that allows position to be set independently of slider point index.
            /// Currently not used due to issues with backup method.
            /// </summary>
            internal SliderPointData(SliderOsu slider, int sliderPointIndex, Vector2 position)
            {
                Slider           = slider;
                SliderPointIndex = sliderPointIndex;
                SliderPointCount = slider.sliderCurvePoints.Count;

                Position = position;
            }
Esempio n. 7
0
        /// <summary>
        /// Stores indexes of selected slider circles. checkAll = true checks selected indexes for all sliders in selection instead of checking only the first object
        /// </summary>
        protected void backupSelectedIndexes(bool checkAll = false)
        {
            selectedSliderIndexes = null;

            bool hasSelectionData = false;

            if (checkAll)
            {
                selectedSliderIndexes = new List <int>();
                foreach (HitObject h in changedObjects)
                {
                    SliderOsu s = h as SliderOsu;

                    if (s != null)
                    {
                        if (backupSelectedIndexes(s))
                        {
                            hasSelectionData = true;
                        }
                        selectedSliderIndexes.Add(-1); //Divides each slider
                    }
                }
            }
            else
            {
                if (changedObjects.Count > 1)
                {
                    return;                           //Currently the only way to change a slider's circle selection is if there is only 1 selected object
                }
                try
                {
                    SliderOsu s = changedObjects[0] as SliderOsu;

                    if (s != null)
                    {
                        selectedSliderIndexes = new List <int>();
                        if (backupSelectedIndexes(s))
                        {
                            hasSelectionData = true;
                        }
                    }
                }
                catch (IndexOutOfRangeException)
                {
#if DEBUG
                    string errorMsg = "Slider index out of range";
                    NotificationManager.ShowMessage(errorMsg);
                    Debug.Print(errorMsg);
#endif
                }
            }

            if (!hasSelectionData)
            {
                selectedSliderIndexes = null; //We should null this if no sliders are present.
            }
        }
        internal override void ChangeState(State undoState)
        {
            SliderOsu s = (SliderOsu)changedObjects[0];

            ListHelper.Swap(s.sliderCurvePoints, sliderBackup.sliderCurvePoints);

            s.ClearPositionOffset();
            sliderBackup.ClearPositionOffset();

            Vector2 position = s.BasePosition;

            s.sliderStartCircle.ModifyPosition(sliderBackup.Position);
            s.Position = s.BasePosition = sliderBackup.Position;

            sliderBackup.Position = sliderBackup.BasePosition = position;

            double spatialLength = s.SpatialLength;

            s.SpatialLength            = sliderBackup.SpatialLength;
            sliderBackup.SpatialLength = spatialLength;

            int startTime = s.StartTime;
            int endTime   = s.EndTime;

            s.ModifySliderTime(sliderBackup.StartTime, false);

            sliderBackup.StartTime = startTime;
            sliderBackup.EndTime   = endTime;

            bool newCombo = s.NewCombo;

            s.NewCombo            = sliderBackup.NewCombo;
            sliderBackup.NewCombo = newCombo;

            HitObjectSoundType soundType = s.SoundType;

            s.SoundType            = sliderBackup.SoundType;
            sliderBackup.SoundType = soundType;

            SampleSet ss  = s.SampleSet;
            SampleSet ssa = s.SampleSetAdditions;

            s.SampleSet          = sliderBackup.SampleSet;
            s.SampleSetAdditions = sliderBackup.SampleSetAdditions;

            sliderBackup.SampleSet          = ss;
            sliderBackup.SampleSetAdditions = ssa;

            ListHelper.Swap(s.SoundTypeList, sliderBackup.SoundTypeList);
            ListHelper.Swap(s.SampleSetList, sliderBackup.SampleSetList);
            ListHelper.Swap(s.SampleSetAdditionList, sliderBackup.SampleSetAdditionList);

            s.UpdateCalculations();

            ActiveManager.Editor.Compose.UpdateSamples();
            ActiveManager.Editor.Compose.UpdateSoundAdditions();
        }
Esempio n. 9
0
 void sliderBar_ValueChanged(bool final)
 {
     newAim = (int)(sliderBarUser.Current * AudioEngine.AudioLength);
     if (newAim.Value < AudioEngine.Time)
     {
         SliderOsu s = Player.Instance.hitObjectManager.hitObjects.Find(h => h is SliderOsu && h.StartTime <= newAim && h.EndTime >= newAim) as SliderOsu;
         if (s != null)
         {
             newAim = s.StartTime - 100;
         }
     }
 }
        internal override bool SetBackupData()
        {
            if (newObjectCount == 0)
            {
                return(false);
            }

            HitObject h = changedObjects[0] as HitObject;

            sliderBackup = h?.Clone() as SliderOsu;
            return(sliderBackup != null);
        }
Esempio n. 11
0
        internal override void ChangeState(State undoState)
        {
            int sIndex = 0;

            foreach (SliderPointData p in pointData)
            {
                SliderOsu      slider       = p.Slider;
                List <Vector2> sliderPoints = slider.sliderCurvePoints;

                int     pIndex   = p.SliderPointIndex;
                Vector2 position = sliderPoints[pIndex];

                float changeX = position.X - p.Position.X;
                float changeY = position.Y - p.Position.Y;

                Vector2 changeVector = new Vector2(changeX, changeY);
                Vector2 newPosition  = position - changeVector;

                sliderPoints[pIndex] = newPosition;

                if (p.IsRed) //Handle stacked points
                {
                    if (pIndex >= 1 && sliderPoints[pIndex - 1] == p.Position)
                    {
                        sliderPoints[pIndex - 1] = newPosition;
                    }
                    else
                    {
                        sliderPoints[pIndex + 1] = newPosition;
                    }
                }

                p.Position = position;

                if (pIndex == 0)
                {
                    slider.Position = slider.BasePosition -= changeVector;
                    slider.sliderStartCircle.ModifyPosition(slider.Position);
                }

                CurveTypes type = slider.CurveType;
                slider.CurveType      = slider.CurveTypeSavable = curveTypeData[sIndex];
                curveTypeData[sIndex] = type;

                slider.SpatialLength = 0;
                slider.UpdateCalculations();

                sIndex++;
            }
        }
        internal override bool SetBackupData()
        {
            SliderOsu s = changedObjects[0] as SliderOsu;

            if (s != null)
            {
                segmentCount = s.SegmentCount;
                //We don't know how the segment count will change, so backup all the lists that could be affected
                soundAdditions.Add(new SoundAdditionData(s.SoundTypeList));
                sliderSampleSets.AddRange(s.SampleSetList);
                sliderSampleSetAdditions.AddRange(s.SampleSetAdditionList);
                return(true);
            }
            return(false);
        }
Esempio n. 13
0
        protected bool backupSelectedIndexes(SliderOsu s)
        {
            bool hasSelected = false;
            int  i           = 0;

            foreach (HitCircleOsu c in s.sliderAllCircles)
            {
                if (c.Selected)
                {
                    hasSelected = true;
                    selectedSliderIndexes.Add(i);
                }
                i++;
            }
            return(hasSelected);
        }
        /// <summary>
        /// Account for changes in sample counts. List counts must remain equal. This should be called after values are swapped.
        /// </summary>
        /// <param name="s">Reference object</param>
        /// <param name="refList">This list belongs to given hitobject. If it is empty, clear related lists. Otherwise check that a different list belonging to s is empty</param>
        protected void updateUnifiedStatus <T>(SliderOsu s, List <T> refList)
        {
            if (refList.Count == 0)
            {
                s.SampleSetList.Clear();
                s.SoundTypeList.Clear();
                s.SampleSetAdditionList.Clear();
                s.unifiedSoundAddition = true;
            }
            else
            {
                List <SampleSet> sampleList;

                //Ensure that all sound lists are equal
                if (refList.Equals(s.SoundTypeList))
                {
                    if (s.SampleSetList.Count == 0)
                    {
                        for (int i = 0; i < s.SampleSetList.Count; i++)
                        {
                            s.SampleSetList.Add(s.SampleSet);
                            s.SampleSetAdditionList.Add(s.SampleSet);
                        }
                    }
                    s.unifiedSoundAddition = false;
                    return;
                }
                else if (refList.Equals(s.SampleSetList))
                {
                    sampleList = s.SampleSetAdditionList;
                }
                else
                {
                    sampleList = s.SampleSetList;
                }

                if (sampleList.Count == 0)
                {
                    for (int i = 0; i < s.SampleSetList.Count; i++)
                    {
                        sampleList.Add(s.SampleSet);
                        s.SoundTypeList.Add(s.SoundType);
                    }
                    s.unifiedSoundAddition = false;
                }
            }
        }
        internal override void ChangeState(State undoState)
        {
            Vector2 swapVector;
            int     index        = 0,
                    sLengthIndex = 0,
                    sPointIndex  = 0;

            foreach (HitObject h in changedObjects)
            {
                SliderOsu s = h as SliderOsu;
                if (s != null)
                {
                    //swap slider point data
                    for (int i = 0; i < s.sliderCurvePoints.Count; i++)
                    {
                        swapVector                = s.sliderCurvePoints[i];
                        s.sliderCurvePoints[i]    = sliderPoints[sPointIndex];
                        sliderPoints[sPointIndex] = swapVector;

                        sPointIndex++;
                    }

                    //swap position data
                    swapVector = s.BasePosition;
                    s.sliderStartCircle.ModifyPosition(startPositions[index]);
                    s.Position = s.BasePosition = startPositions[index];

                    //swap spatial length
                    ListHelper.Swap(spatialData, sLengthIndex, ref s.SpatialLength);

                    s.UpdateCalculations();
                    sLengthIndex++;
                }
                else
                {
                    swapVector = h.BasePosition;
                    h.ModifyPosition(startPositions[index]);
                }

                startPositions[index] = swapVector;
                index++;
            }
            // ActiveManager.Editor.Compose.ReplaceSelection(changedObjects);
            ActiveManager.hitObjectManager.Update();
        }
Esempio n. 16
0
        internal void BackupData(SliderOsu slider, int sliderPointIndex)
        {
            if (!allowBackup || CurrentChangeType != ChangeType.HitObject)
            {
                return;
            }

            if (slider == null)
            {
                Debug.Assert(false);
                return;
            }

            HitObjectRecord current = (HitObjectRecord)currentRecord;
            SliderPointData data    = new SliderPointData(slider, sliderPointIndex);

            current.BackupData(data, true);
        }
        internal override void ChangeState(State undoState)
        {
            SliderOsu s = changedObjects[0] as SliderOsu;

            if (s != null)
            {
                //Swap list values
                ListHelper.Swap(soundAdditions[0].SoundTypes, s.SoundTypeList);
                ListHelper.Swap(sliderSampleSets, s.SampleSetList);
                ListHelper.Swap(sliderSampleSetAdditions, s.SampleSetAdditionList);

                int segments = s.SegmentCount;
                s.SegmentCount = segmentCount;
                segmentCount   = segments;

                s.UpdateCalculations();
            }
        }
Esempio n. 18
0
            internal SliderPointData(SliderOsu slider, int sliderPointIndex)
            {
                Slider           = slider;
                SliderPointIndex = sliderPointIndex;

                if (!HasValidData())
                {
                    return;
                }

                List <Vector2> sliderPoints = slider.sliderCurvePoints;

                SliderPointCount = sliderPoints.Count;
                Position         = sliderPoints[sliderPointIndex];
                IsRed            = (sliderPointIndex > 0 &&
                                    sliderPoints[sliderPointIndex - 1] == Position) ||
                                   (sliderPointIndex + 1 < sliderPoints.Count &&
                                    sliderPoints[sliderPointIndex + 1] == Position);
            }
        internal override void ChangeState(State undoState)
        {
            SliderOsu      s            = removedPoint.Slider;
            List <Vector2> sliderPoints = s.sliderCurvePoints;
            int            pIndex       = removedPoint.SliderPointIndex;

            switch (undoState)
            {
            case State.Undo:
                if (sliderPoints.Count > pIndex)
                {
                    sliderPoints.Insert(pIndex, removedPoint.Position);
                }
                else
                {
                    pIndex = removedPoint.SliderPointIndex = sliderPoints.Count;
                    sliderPoints.Add(removedPoint.Position);
                }
                break;

            case State.Redo:
                sliderPoints.RemoveAt(pIndex);
                break;
            }

            if (pIndex == 0) //First slider point has been changed - the start circle needs to be updated
            {
                Vector2 position = s.Position;
                s.Position = s.BasePosition = removedPoint.Position;
                s.sliderStartCircle.ModifyPosition(s.Position);
                removedPoint.Position = position;
            }

            if (curveType != null)
            {
                CurveTypes type = s.CurveType;
                s.CurveType = s.CurveTypeSavable = (CurveTypes)curveType;
                curveType   = type;
            }

            s.SpatialLength = 0;
            s.UpdateCalculations();
        }
Esempio n. 20
0
        internal ConvertToStream(SliderOsu slider)
        {
            editor = Editor.Instance;
            editor.FinishDrag();

            InitializeComponent();
            m_slider        = slider;
            suppress_events = true;
            switch (editor.BeatSnapDivisor)
            {
            case 3:
            case 6:
                domainBeatSnap.SelectedIndex = 2;
                break;

            default:
                domainBeatSnap.SelectedIndex = 3;
                break;
            }

            suppress_events = false;
        }
        protected override void UpdateHitObject(HitObject currHitObject)
        {
            SliderOsu slider = currHitObject as SliderOsu;

            if (slider != null)
            {
                if (AudioEngine.Time >= slider.StartTime && AudioEngine.Time <= slider.EndTime)
                {
                    slider.InitSlide();
                    sliderActive = true;
                }
            }

            if (AudioEngine.AudioState == AudioStates.Playing && AudioEngine.FrameAverage < 300)
            {
                if ((currHitObject.StartTime <= AudioEngine.Time &&
                     currHitObject.StartTime >= AudioEngine.TimeLastFrame &&
                     !currHitObject.IsType(HitObjectType.Spinner))
                    ||
                    (currHitObject.EndTime <= AudioEngine.Time &&
                     currHitObject.EndTime >= AudioEngine.TimeLastFrame))
                {
                    if (!currHitObject.Sounded)
                    {
                        currHitObject.PlaySound();
                        currHitObject.Sounded = true;
                    }
                }
                else if (currHitObject.Sounded &&
                         Math.Abs(currHitObject.StartTime - AudioEngine.Time) > AudioEngine.FrameAverage * 4)
                {
                    currHitObject.Sounded = false;
                }
            }

            base.UpdateHitObject(currHitObject);
        }
        internal override void ChangeState(State undoState)
        {
            SliderOsu      s            = addedPoint.Slider;
            List <Vector2> sliderPoints = s.sliderCurvePoints;
            int            pIndex       = addedPoint.SliderPointIndex;

            ActiveManager.Editor.Compose.RefreshSelection();
            switch (undoState)
            {
            case State.Undo:
                sliderPoints.RemoveAt(pIndex);
                break;

            case State.Redo:
                if (sliderPoints.Count > pIndex)
                {
                    sliderPoints.Insert(pIndex, addedPoint.Position);
                }
                else
                {
                    addedPoint.SliderPointIndex = sliderPoints.Count;     //This should always be accurate, but reset value to be extra sure.
                    sliderPoints.Add(addedPoint.Position);
                }
                break;
            }

            if (curveType != null)
            {
                CurveTypes type = s.CurveType;
                s.CurveType = s.CurveTypeSavable = (CurveTypes)curveType;
                curveType   = type;
            }

            s.SpatialLength = 0;
            s.UpdateCalculations();
        }
Esempio n. 23
0
        internal override Slider CreateSlider(Vector2 startPosition, int startTime, bool newCombo,
                                              HitObjectSoundType soundType, CurveTypes curveType, int repeatCount, double sliderLength, List <Vector2> sliderPoints, List <HitObjectSoundType> soundTypes, int comboOffset,
                                              SampleSet sampleSet, SampleSet addSet, List <SampleSet> sampleSets, List <SampleSet> sampleSetAdditions, CustomSampleSet customSampleSet, int volume, string sampleFile)
        {
            SliderOsu s = new SliderOsu(hitObjectManager, startPosition, startTime, newCombo, soundType, curveType, repeatCount, sliderLength, sliderPoints, soundTypes, comboOffset);

            s.SampleSet          = sampleSet;
            s.SampleSetAdditions = addSet;

            s.SampleSetList         = sampleSets;
            s.SampleSetAdditionList = sampleSetAdditions;
            s.CustomSampleSet       = customSampleSet;
            s.SampleVolume          = volume;
            s.ProcessSampleFile(sampleFile);

            if (s.sliderStartCircle != null)
            {
                HitSoundInfo hitsoundInfo = s.GetHitSoundInfo(0);
                s.sliderStartCircle.SampleSet          = hitsoundInfo.SampleSet;
                s.sliderStartCircle.SampleSetAdditions = hitsoundInfo.SampleSetAdditions;
            }

            return(s);
        }
Esempio n. 24
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!";
        }
Esempio n. 25
0
        internal DifficultyHitObjectOsu(HitObject BaseHitObject, float CircleRadius)
        {
            this.BaseHitObject = BaseHitObject;

            if (BaseHitObject.IsType(HitObjectType.Slider))
            {
                SliderOsu Slider = (SliderOsu)BaseHitObject;
                MaxCombo = 1 + Slider.sliderScoreTimingPoints.Count;
            }
            else
            {
                MaxCombo = 1;
            }

            // We will scale everything by this factor, so we can assume a uniform CircleSize among beatmaps.
            float ScalingFactor = (52.0f / CircleRadius);

            if (CircleRadius < 30)
            {
                float smallCircleBonus = Math.Min(30.0f - CircleRadius, 5.0f) / 50.0f;
                ScalingFactor *= 1.0f + smallCircleBonus;
            }

            NormalizedStartPosition = BaseHitObject.Position * ScalingFactor;

            // Calculate approximation of lazy movement on the slider
            if (BaseHitObject.IsType(HitObjectType.Slider))
            {
                float SliderFollowCircleRadius = CircleRadius * 3;                                       // Not sure if this is correct, but here we do not need 100% exact values. This comes pretty darn close in my tests.

                int SegmentLength  = Math.Min(BaseHitObject.Length / BaseHitObject.SegmentCount, 60000); // We don't want infinite loops if someone decides to make a too long slider. (MillhioreF, I am talking about you! https://osu.ppy.sh/b/326585)
                int SegmentEndTime = BaseHitObject.StartTime + SegmentLength;

                // For simplifying this step we use actual osu! coordinates and simply scale the length, that we obtain by the ScalingFactor later
                Vector2 CursorPos = BaseHitObject.Position; //

                //Debug.Print("" + (BaseHitObject.StartTime + LAZY_SLIDER_STEP_LENGTH) + " " + SegmentEndTime + " " + BaseHitObject.EndTime + " " + BaseHitObject.SegmentCount);

                // Actual computation of the first lazy curve
                for (int Time = BaseHitObject.StartTime + LAZY_SLIDER_STEP_LENGTH; Time < SegmentEndTime; Time += LAZY_SLIDER_STEP_LENGTH)
                {
                    Vector2 Difference = BaseHitObject.PositionAtTime(Time) - CursorPos;
                    float   Distance   = Difference.Length();

                    // Did we move away too far?
                    if (Distance > SliderFollowCircleRadius)
                    {
                        // Yep, we need to move the cursor
                        Difference.Normalize();                         // Obtain the direction of difference. We do no longer need the actual difference
                        Distance              -= SliderFollowCircleRadius;
                        CursorPos             += Difference * Distance; // We move the cursor just as far as needed to stay in the follow circle
                        LazySliderLengthFirst += Distance;
                    }
                }

                LazySliderLengthFirst *= ScalingFactor;
                // If we have an odd amount of repetitions the current position will be the end of the slider. Note that this will -always- be triggered if
                // BaseHitObject.SegmentCount <= 1, because BaseHitObject.SegmentCount can not be smaller than 1. Therefore NormalizedEndPosition will always be initialized
                if (BaseHitObject.SegmentCount % 2 == 1)
                {
                    NormalizedEndPosition = CursorPos * ScalingFactor;
                }

                // If we have more than one segment, then we also need to compute the length ob subsequent lazy curves. They are different from the first one, since the first
                // one starts right at the beginning of the slider.
                if (BaseHitObject.SegmentCount > 1)
                {
                    // Use the next segment
                    SegmentEndTime += SegmentLength;

                    for (int Time = SegmentEndTime - SegmentLength + LAZY_SLIDER_STEP_LENGTH; Time < SegmentEndTime; Time += LAZY_SLIDER_STEP_LENGTH)
                    {
                        Vector2 Difference = BaseHitObject.PositionAtTime(Time) - CursorPos;
                        float   Distance   = Difference.Length();

                        // Did we move away too far?
                        if (Distance > SliderFollowCircleRadius)
                        {
                            // Yep, we need to move the cursor
                            Difference.Normalize();             // Obtain the direction of difference. We do no longer need the actual difference
                            Distance  -= SliderFollowCircleRadius;
                            CursorPos += Difference * Distance; // We move the cursor just as far as needed to stay in the follow circle
                            LazySliderLengthSubsequent += Distance;
                        }
                    }

                    LazySliderLengthSubsequent *= ScalingFactor;
                    // If we have an even amount of repetitions the current position will be the end of the slider
                    if (BaseHitObject.SegmentCount % 2 == 0)
                    {
                        NormalizedEndPosition = CursorPos * ScalingFactor;
                    }
                }
            }
            // We have a normal HitCircle or a spinner
            else
            {
                NormalizedEndPosition = NormalizedStartPosition;
            }
        }
        /// <summary>
        /// Calculates the hp drop rate via some insane looping through the map.
        /// Oh god.
        /// </summary>
        /// <returns></returns>
        internal virtual double CalculateHpDropRate()
        {
            double testDrop = 0.05;

            double lowestHpEver =
                hitObjectManager.MapDifficultyRange(BeatmapManager.Current.DifficultyHpDrainRate, 195, 160, 60);
            double lowestHpComboEnd =
                hitObjectManager.MapDifficultyRange(BeatmapManager.Current.DifficultyHpDrainRate, 198, 170, 80);
            double lowestHpEnd =
                hitObjectManager.MapDifficultyRange(BeatmapManager.Current.DifficultyHpDrainRate, 198, 180, 80);
            double HpRecoveryAvailable =
                hitObjectManager.MapDifficultyRange(BeatmapManager.Current.DifficultyHpDrainRate, 8, 4, 0);

            do
            {
                TotalHitsPossible = 0;

                HpBar.Reset();

                ComboCounter.HitCombo = 0;
                Player.currentScore.Reset();

                double lowestHp = HpBar.CurrentHp;
                int    lastTime = hitObjectManager.hitObjects[0].StartTime - hitObjectManager.PreEmpt;
                bool   fail     = false;

                int breakCount  = player.eventManager.eventBreaks.Count;
                int breakNumber = 0;

                int comboTooLowCount = 0;

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

                    //Find active break (between current and lastTime)
                    int localLastTime = lastTime;

                    int breakTime = 0;
                    if (breakCount > 0 && breakNumber < breakCount)
                    {
                        Event e = player.eventManager.eventBreaks[breakNumber];
                        if (e.StartTime >= localLastTime && e.EndTime <= h.StartTime)
                        {
                            // consider break start equal to object end time for version 8+ since drain stops during this time
                            breakTime = (BeatmapManager.Current.BeatmapVersion < 8) ? e.Length : e.EndTime - localLastTime;
                            breakNumber++;
                        }
                    }

                    HpBar.ReduceCurrentHp(testDrop * (h.StartTime - lastTime - breakTime));

                    lastTime = h.EndTime;


                    if (HpBar.CurrentHp < lowestHp)
                    {
                        lowestHp = HpBar.CurrentHp;
                    }

                    if (HpBar.CurrentHp <= lowestHpEver)
                    {
                        //Debug.Print("Overall score drops below " + lowestHpEver + " at " + lastTime + " (" + testDrop + ", lowest " + lowestHp + ")");
                        fail      = true;
                        testDrop *= 0.96;
                        break;
                    }

                    HpBar.ReduceCurrentHp(testDrop * (h.EndTime - h.StartTime));

                    if (h.IsType(HitObjectType.Slider))
                    {
                        SliderOsu s = (SliderOsu)h;

                        int repeatCount = s.sliderRepeatPoints.Count + 2;
                        int tickCount   = s.sliderScoreTimingPoints.Count - s.sliderRepeatPoints.Count - 1;
                        TotalHitsPossible += repeatCount + tickCount - 1;
                        for (int j = 0; j < repeatCount; j++)
                        {
                            IncreaseScoreHit(IncreaseScoreType.SliderRepeat, h);
                        }
                        for (int j = 0; j < tickCount; j++)
                        {
                            IncreaseScoreHit(IncreaseScoreType.SliderTick, h);
                        }
                    }
                    else if (h.IsType(HitObjectType.Spinner))
                    {
                        SpinnerOsu s = (SpinnerOsu)h;
                        for (int j = 0; j < s.rotationRequirement; j++)
                        {
                            IncreaseScoreHit(IncreaseScoreType.SpinnerSpinPoints, h);
                        }
                    }

                    if (i == hitObjectManager.hitObjectsCount - 1 || hitObjectManager.hitObjects[i + 1].NewCombo)
                    {
                        IncreaseScoreHit(IncreaseScoreType.Hit300g, h);
                        if (HpBar.CurrentHp < lowestHpComboEnd)
                        {
                            if (++comboTooLowCount > 2)
                            {
                                HpMultiplierComboEnd *= 1.07;
                                HpMultiplierNormal   *= 1.03;
                                fail = true;
                                break;
                            }
                        }
                    }
                    else
                    {
                        IncreaseScoreHit(IncreaseScoreType.Hit300, h);
                    }

                    TotalHitsPossible++;

                    h.MaxHp = HpBar.CurrentHp;
                }

                if (!fail && HpBar.CurrentHp < lowestHpEnd)
                {
                    //Debug.Print("Song ends on " + currentHp + " - being more lenient");
                    fail                  = true;
                    testDrop             *= 0.94;
                    HpMultiplierComboEnd *= 1.01;
                    HpMultiplierNormal   *= 1.01;
                }

                double recovery = (HpBar.CurrentHpUncapped - HP_BAR_MAXIMUM) / hitObjectManager.hitObjectsCount;
                if (!fail && recovery < HpRecoveryAvailable)
                {
                    //Debug.Print("Song has average " + recovery + " recovery - being more lenient");
                    fail                  = true;
                    testDrop             *= 0.96;
                    HpMultiplierComboEnd *= 1.02;
                    HpMultiplierNormal   *= 1.01;
                }

                if (fail)
                {
                    continue;
                }

                if (GameBase.TestMode)
                {
                    PlayerTest pt = player as PlayerTest;
                    if (pt != null)
                    {
                        pt.testMaxScore = Player.currentScore.TotalScore;
                        pt.testMaxCombo = ComboCounter.HitCombo;
                    }
                }

                Debug.Print("Loaded Beatmap " + BeatmapManager.Current.Filename);
                Debug.Print(string.Empty);
                Debug.Print("           stars: " + BeatmapManager.Current.DifficultyEyupStars);
                Debug.Print("           stars: " + BeatmapManager.Current.DifficultyEchoStars);
                Debug.Print("     slider rate: " + BeatmapManager.Current.DifficultySliderMultiplier);
                Debug.Print(" playable length: " + BeatmapManager.Current.DrainLength);
                Debug.Print("      hitobjects: " + BeatmapManager.Current.ObjectCount);
                Debug.Print("      hitcircles: " +
                            hitObjectManager.hitObjects.FindAll(h => h.IsType(HitObjectType.Normal)).Count);
                Debug.Print("         sliders: " +
                            hitObjectManager.hitObjects.FindAll(h => h.IsType(HitObjectType.Slider)).Count);
                Debug.Print("        spinners: " +
                            hitObjectManager.hitObjects.FindAll(h => h.IsType(HitObjectType.Spinner)).Count);
                Debug.Print("      drain rate: {0:00.00}%/s", (testDrop / 2 * 1000));
                Debug.Print("       lowest hp: {0:00.00}%", lowestHp / 2);
                Debug.Print("normal multiplier: {0:00.00}x", HpMultiplierNormal);
                Debug.Print("combo multiplier: {0:00.00}x", HpMultiplierComboEnd);
                Debug.Print(" excess hp recov: {0:00.00}%/hitobject",
                            (float)(HpBar.CurrentHpUncapped - HP_BAR_MAXIMUM) / 2 / hitObjectManager.hitObjects.Count);
                Debug.Print("    max final hp: {0:00.00}%", HpBar.CurrentHp / 2);
                Debug.Print("       max combo: " + TotalHitsPossible);
                Debug.Print(string.Empty);

                return(testDrop);
            } while (true);
        }
Esempio n. 27
0
        internal HitCircleSliderEnd(HitObjectManager hom, Vector2 pos, int startTime, int appearTime, bool firstRun, bool reverse, float angle, SliderOsu parent)
            : base(hom, pos, startTime, false, false, false, false, 0)
        {
            SpriteCollection.Remove(SpriteApproachCircle);
            SpriteCollection.Remove(SpriteHitCircleText);

            this.firstRun = firstRun;

            if (reverse)
            {
                int sortTime = firstRun ? parent.StartTime - 1 :
                               startTime - (int)(1000 * parent.SpatialLength / parent.Velocity) - 1;

                SpriteHitCircleText =
                    new pSprite(TextureManager.Load(@"reversearrow"), Fields.Gamefield,
                                Origins.Centre, Clocks.Audio, Position,
                                SpriteManager.drawOrderBwd(sortTime), false, Color.White);
                SpriteHitCircleText.Transformations.Add(new Transformation(TransformationType.Fade, 0, 1, appearTime, appearTime + 150));
                SpriteCollection.Add(SpriteHitCircleText);
                DimCollection.Add(SpriteHitCircleText);
            }

            bool hidden             = ModManager.CheckActive(hitObjectManager.ActiveMods, Mods.Hidden);
            int  hiddenFadedInTime  = parent.StartTime - (int)(hitObjectManager.PreEmpt * 0.6);
            int  hiddenFadedOutTime = parent.StartTime - (int)(hitObjectManager.PreEmpt * 0.3);

            var tr = SpriteHitCircle1.Transformations.Find(t => t.Type == TransformationType.Fade && t.StartFloat == 0);

            if (tr != null)
            {
                tr.Time1 = appearTime;
                tr.Time2 = hidden ? hiddenFadedInTime : Math.Min(startTime, appearTime + HitObjectManager.FadeIn);
            }

            tr = SpriteHitCircle2.Transformations.Find(t => t.Type == TransformationType.Fade && t.StartFloat == 0);
            if (tr != null)
            {
                tr.Time1 = appearTime;
                tr.Time2 = hidden ? hiddenFadedInTime : Math.Min(startTime, appearTime + HitObjectManager.FadeIn);
            }

            if (hidden)
            {
                if (firstRun)
                {
                    SpriteHitCircle1.Transformations.Add(new Transformation(TransformationType.Fade, 1, 0, hiddenFadedInTime, hiddenFadedOutTime));
                    SpriteHitCircle2.Transformations.Add(new Transformation(TransformationType.Fade, 1, 0, hiddenFadedInTime, hiddenFadedOutTime));
                }
                else
                {
                    SpriteCollection.Remove(SpriteHitCircle1);
                    SpriteCollection.Remove(SpriteHitCircle2);
                }
            }

            Disarm();

            if (reverse)
            {
                if (SkinManager.UseNewLayout)
                {
                    SpriteHitCircleText.Rotation = angle;
                    for (int i = appearTime; i < startTime; i += 300)
                    {
                        int length = Math.Min(300, startTime - i);
                        SpriteHitCircleText.Transformations.Add(new Transformation(TransformationType.Scale, 1.3f, 1, i, i + length, EasingTypes.Out));
                    }
                }
                else
                {
                    for (int i = appearTime; i < startTime; i += 300)
                    {
                        int length = Math.Min(300, startTime - i);
                        SpriteHitCircleText.Transformations.Add(new Transformation(TransformationType.Scale, 1.3F, 1, i, i + length));
                        SpriteHitCircleText.Transformations.Add(new Transformation(TransformationType.Rotation, angle + OsuMathHelper.Pi / 32, angle - OsuMathHelper.Pi / 32, i, i + length));
                    }
                }
            }
        }
        internal override bool SetBackupData()
        {
            if (HasModifier(Modifier.None))
            {
                throw new Exception("An action modifier must be specified");
            }

            if (!hasNewData)
            {
                throw new NotEnoughObjectsException();
            }

            backupSelectedIndexes();

            if (HasModifier(Modifier.ChangeAll))
            {
                if (initializeStorage)
                {
                    sampleSets             = new List <SampleAdditionData>();
                    sampleSetAdditions     = new List <SampleAdditionData>();
                    sampleSetsList         = new List <SampleAdditionData>();
                    sampleSetAdditionsList = new List <SampleAdditionData>();
                }

                BackupData(delegate(HitObject h)
                {
                    sampleSets.Add(new SampleAdditionData(h.SampleSet));
                    sampleSetAdditions.Add(new SampleAdditionData(h.SampleSetAdditions));

                    SliderOsu s = h as SliderOsu;
                    if (s != null)
                    {
                        sampleSetsList.Add(new SampleAdditionData(s.SampleSetList));
                        sampleSetAdditionsList.Add(new SampleAdditionData(s.SampleSetAdditionList));
                    }
                });
            }
            else if (HasModifier(Modifier.SampleSet))
            {
                if (initializeStorage)
                {
                    sampleSets     = new List <SampleAdditionData>();
                    sampleSetsList = new List <SampleAdditionData>();
                }

                BackupData(delegate(HitObject h)
                {
                    sampleSets.Add(new SampleAdditionData(h.SampleSet));

                    SliderOsu s = h as SliderOsu;
                    if (s != null)
                    {
                        sampleSetsList.Add(new SampleAdditionData(s.SampleSetList));
                    }
                });
            }
            else if (HasModifier(Modifier.SampleSetAddition))
            {
                if (initializeStorage)
                {
                    sampleSetAdditions     = new List <SampleAdditionData>();
                    sampleSetAdditionsList = new List <SampleAdditionData>();
                }

                BackupData(delegate(HitObject h)
                {
                    sampleSetAdditions.Add(new SampleAdditionData(h.SampleSetAdditions));

                    SliderOsu s = h as SliderOsu;
                    if (s != null)
                    {
                        sampleSetAdditionsList.Add(new SampleAdditionData(s.SampleSetAdditionList));
                    }
                });
            }

            return(true);
        }
        /// <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++;
            }
        }
        internal override void ChangeState(State undoState)
        {
            SampleAdditionData sampleSetData;

            int index  = 0,
                sIndex = 0;

            if (HasModifier(Modifier.ChangeAll))
            {
                foreach (HitObject h in changedObjects)
                {
                    ListHelper.Swap(sampleSets[index].SampleTypes, 0, ref h.SampleSet);
                    ListHelper.Swap(sampleSetAdditions[index].SampleTypes, 0, ref h.SampleSetAdditions);

                    SliderOsu s = h as SliderOsu;
                    if (s != null)
                    {
                        ListHelper.Swap(sampleSetsList[sIndex].SampleTypes, s.SampleSetList);
                        ListHelper.Swap(sampleSetAdditionsList[sIndex].SampleTypes, s.SampleSetAdditionList);
                        s.UpdateNodalSamples();
                        sIndex++;
                    }
                    index++;
                }
            }
            else if (HasModifier(Modifier.SampleSet))
            {
                foreach (HitObject h in changedObjects)
                {
                    ListHelper.Swap(sampleSets[index].SampleTypes, 0, ref h.SampleSet);

                    SliderOsu s = h as SliderOsu;
                    if (s != null)
                    {
                        sampleSetData = sampleSetsList[sIndex];
                        ListHelper.Swap(sampleSetData.SampleTypes, s.SampleSetList);

                        updateUnifiedStatus(s, s.SampleSetList);
                        sIndex++;
                    }
                    index++;
                }
            }
            else if (HasModifier(Modifier.SampleSetAddition))
            {
                foreach (HitObject h in changedObjects)
                {
                    ListHelper.Swap(sampleSetAdditions[index].SampleTypes, 0, ref h.SampleSetAdditions);

                    SliderOsu s = h as SliderOsu;
                    if (s != null)
                    {
                        sampleSetData = sampleSetAdditionsList[sIndex];
                        ListHelper.Swap(sampleSetData.SampleTypes, s.SampleSetAdditionList);

                        updateUnifiedStatus(s, s.SampleSetAdditionList);
                        sIndex++;
                    }
                    index++;
                }
            }

            //todo: decide if objects should be selected when restored
            ActiveManager.Editor.Compose.ReplaceSelection(changedObjects);

            //Restore slider circle selections.
            if (selectedSliderIndexes != null && selectedSliderIndexes.Count > 0)
            {
                ActiveManager.Editor.Compose.SelectIndexes((SliderOsu)changedObjects[0], selectedSliderIndexes);
            }

            ActiveManager.Editor.Compose.UpdateSamples();
        }