// Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Objects.Drawables; namespace osu.Game.Rulesets.Timing { /// /// A collection of s. /// /// /// This container redirects any 's added to it to the /// which provides the speed adjustment active at the start time of the hit object. Furthermore, this container provides the /// necessary for the contained s. /// /// public class SpeedAdjustmentCollection : Container { private readonly BindableDouble visibleTimeRange = new BindableDouble(); /// /// Gets or sets the range of time that is visible by the length of this container. /// For example, only hit objects with start time less than or equal to 1000 will be visible with = 1000. /// public Bindable VisibleTimeRange { get { return visibleTimeRange; } set { visibleTimeRange.BindTo(value); } } /// /// Adds a hit object to the which provides the speed adjustment /// active at the start time of the hit object. /// /// The hit object to add. public void Add(DrawableHitObject hitObject) { var target = adjustmentContainerFor(hitObject); if (target == null) throw new ArgumentException("No speed adjustment could be found that can contain the hit object.", nameof(hitObject)); target.Add(hitObject); } public override void Add(SpeedAdjustmentContainer speedAdjustment) { speedAdjustment.VisibleTimeRange.BindTo(VisibleTimeRange); base.Add(speedAdjustment); } protected override IComparer DepthComparer => new SpeedAdjustmentContainerReverseStartTimeComparer(); /// /// Finds the which provides the speed adjustment active at the start time /// of a hit object. If there is no active at the start time of the hit object, /// then the first (time-wise) speed adjustment is returned. /// /// The hit object to find the active for. /// The active at 's start time. Null if there are no speed adjustments. private SpeedAdjustmentContainer adjustmentContainerFor(DrawableHitObject hitObject) => Children.FirstOrDefault(c => c.CanContain(hitObject)) ?? Children.LastOrDefault(); /// /// Compares two speed adjustment containers by their control point start time, falling back to creation order // if their control point start time is equal. This will compare the two speed adjustment containers in reverse order. /// private class SpeedAdjustmentContainerReverseStartTimeComparer : ReverseCreationOrderDepthComparer { public override int Compare(Drawable x, Drawable y) { var speedAdjustmentX = x as SpeedAdjustmentContainer; var speedAdjustmentY = y as SpeedAdjustmentContainer; // If either of the two drawables are not hit objects, fall back to the base comparer if (speedAdjustmentX?.MultiplierControlPoint == null || speedAdjustmentY?.MultiplierControlPoint == null) return base.Compare(x, y); // Compare by start time int i = speedAdjustmentY.MultiplierControlPoint.StartTime.CompareTo(speedAdjustmentX.MultiplierControlPoint.StartTime); return i != 0 ? i : base.Compare(x, y); } } } }