/// <summary> /// Calculates the movement time, effective distance and other details for the movement from objPrev to objCurr. /// </summary> /// <param name="fourthLastObject">Hit object four objects ago, relative to <paramref name="currentObject"/>.</param> /// <param name="secondLastObject">Hit object immediately preceding <paramref name="lastObject"/></param> /// <param name="lastObject">Hit object immediately preceding <paramref name="currentObject"/>.</param> /// <param name="currentObject">The hit object being currently considered.</param> /// <param name="nextObject">Hit object immediately succeeding <paramref name="currentObject"/>.</param> /// <param name="tapStrain">The tap strain of the current object.</param> TODO: does this have to be passed down? maybe store in the object? /// <param name="noteDensity">The visual note density of the current object.</param> TODO: above /// <param name="gameplayRate">The current rate of the gameplay clock.</param> /// <param name="hidden">Whether the hidden mod is active.</param> /// <returns>List of movements performed in attempt to hit the current object.</returns> public static List <OsuMovement> Extract( [CanBeNull] OsuHitObject secondLastObject, OsuHitObject lastObject, OsuHitObject currentObject, [CanBeNull] OsuHitObject nextObject, double tapStrain, double gameplayRate, bool hidden, double noteDensity, [CanBeNull] OsuHitObject fourthLastObject = null) { var movement = new OsuMovement(); var parameters = new MovementExtractionParameters(fourthLastObject, secondLastObject, lastObject, currentObject, nextObject, gameplayRate); movement.RawMovementTime = parameters.LastToCurrent.TimeDelta; movement.StartTime = currentObject.StartTime / 1000.0; if (currentObject is Spinner || lastObject is Spinner) { movement.Throughput = 0; movement.Distance = 0; movement.MovementTime = 1; movement.Cheesablility = 0; movement.CheeseWindow = 0; return(new List <OsuMovement> { movement }); } movement.EndsOnSlider = currentObject is Slider; double movementThroughput = FittsLaw.Throughput(parameters.LastToCurrent.RelativeLength, parameters.LastToCurrent.TimeDelta); movement.Throughput = movementThroughput; movement.Distance = correctMovementDistance(parameters, movementThroughput, tapStrain, hidden, noteDensity); calculateCheeseWindow(parameters, movementThroughput); movement.MovementTime = parameters.LastToCurrent.TimeDelta; movement.Cheesablility = parameters.Cheesability; movement.CheeseWindow = parameters.CheeseWindow; var movementWithNested = new List <OsuMovement> { movement }; // add zero difficulty movements corresponding to slider ticks/slider ends so combo is reflected properly int extraNestedCount = currentObject.NestedHitObjects.Count - 1; for (int i = 0; i < extraNestedCount; i++) { movementWithNested.Add(OsuMovement.Empty(movement.StartTime)); } return(movementWithNested); }
/// <summary> /// Extracts movement (only for the first object in a beatmap). /// </summary> public static List <OsuMovement> ExtractFirst(OsuHitObject obj) { var movement = OsuMovement.Empty(obj.StartTime / 1000.0); var movementWithNested = new List <OsuMovement> { movement }; // add zero difficulty movements corresponding to slider ticks/slider ends so combo is reflected properly int extraNestedCount = obj.NestedHitObjects.Count - 1; for (int i = 0; i < extraNestedCount; i++) { movementWithNested.Add(OsuMovement.Empty(movement.StartTime)); } return(movementWithNested); }