// 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; using osu.Game.Rulesets.Timing.Drawables; namespace osu.Game.Rulesets.Timing { /// /// A collection of s. /// /// /// This container provides for the s. /// /// public class SpeedAdjustmentCollection : Container { private readonly BindableDouble visibleTimeRange = new BindableDouble(); /// /// The amount of time visible by span 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 most applicable timing section in this container. /// /// The hit object to add. public void Add(DrawableHitObject hitObject) { var target = timingSectionFor(hitObject); if (target == null) throw new ArgumentException("No timing section 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 TimingSectionReverseStartTimeComparer(); /// /// Finds the most applicable timing section that can contain a hit object. If the hit object occurs before the first (time-wise) /// timing section, then the timing section returned is the first (time-wise) timing section. /// /// The hit object to contain. /// The last (time-wise) timing section which can contain . Null if no timing section exists. private SpeedAdjustmentContainer timingSectionFor(DrawableHitObject hitObject) => Children.FirstOrDefault(c => c.CanContain(hitObject)) ?? Children.LastOrDefault(); /// /// Compares two timing sections by their start time, falling back to creation order if their start time is equal. /// This will compare the two timing sections in reverse order. /// private class TimingSectionReverseStartTimeComparer : ReverseCreationOrderDepthComparer { public override int Compare(Drawable x, Drawable y) { var timingChangeX = x as SpeedAdjustmentContainer; var timingChangeY = y as SpeedAdjustmentContainer; // If either of the two drawables are not hit objects, fall back to the base comparer if (timingChangeX?.TimingSection == null || timingChangeY?.TimingSection == null) return base.Compare(x, y); // Compare by start time int i = timingChangeY.TimingSection.Time.CompareTo(timingChangeX.TimingSection.Time); return i != 0 ? i : base.Compare(x, y); } } } }