// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; using System.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Graphics.Transforms; namespace osu.Game.Storyboards.Commands { public class StoryboardLoopingGroup : StoryboardCommandGroup { private readonly double loopStartTime; /// /// The total number of times this loop is played back. Always greater than zero. /// public readonly int TotalIterations; /// /// Construct a new command loop. /// /// The start time of the loop. /// The number of times the loop should repeat. Should be greater than zero. Zero means a single playback. public StoryboardLoopingGroup(double startTime, int repeatCount) { if (repeatCount < 0) throw new ArgumentException("Repeat count must be zero or above.", nameof(repeatCount)); loopStartTime = startTime; TotalIterations = repeatCount + 1; } protected override void AddCommand(ICollection> list, StoryboardCommand command) => base.AddCommand(list, new StoryboardLoopingCommand(command, this)); public override string ToString() => $"{loopStartTime} x{TotalIterations}"; private class StoryboardLoopingCommand : StoryboardCommand { private readonly StoryboardCommand command; private readonly StoryboardLoopingGroup loopingGroup; public StoryboardLoopingCommand(StoryboardCommand command, StoryboardLoopingGroup loopingGroup) // In an ideal world, we would multiply the command duration by TotalIterations in command end time. // Unfortunately this would clash with how stable handled end times, and results in some storyboards playing outro // sequences for minutes or hours. : base(loopingGroup.loopStartTime + command.StartTime, loopingGroup.loopStartTime + command.EndTime, command.StartValue, command.EndValue, command.Easing) { this.command = command; this.loopingGroup = loopingGroup; } public override string PropertyName => command.PropertyName; public override void ApplyInitialValue(Drawable d) => command.ApplyInitialValue(d); public override TransformSequence ApplyTransforms(Drawable d) { if (loopingGroup.TotalIterations == 0) return command.ApplyTransforms(d); double loopingGroupDuration = loopingGroup.Duration; return command.ApplyTransforms(d).Loop(loopingGroupDuration - Duration, loopingGroup.TotalIterations); } } } }