// 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 osu.Framework.Audio.Track; using osu.Framework.Graphics.Textures; using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Rulesets; using osuTK; namespace osu.Game.Tests.Beatmaps { public class TestWorkingBeatmap : WorkingBeatmap { private readonly TrackVirtualManual track; private readonly IBeatmap beatmap; /// /// Create an instance which creates a for the provided ruleset when requested. /// /// The target ruleset. /// A clock which should be used instead of a stopwatch for virtual time progression. public TestWorkingBeatmap(RulesetInfo ruleset, IFrameBasedClock referenceClock) : this(new TestBeatmap(ruleset), referenceClock) { } /// /// Create an instance which provides the when requested. /// /// The beatmap /// An optional clock which should be used instead of a stopwatch for virtual time progression. public TestWorkingBeatmap(IBeatmap beatmap, IFrameBasedClock referenceClock = null) : base(beatmap.BeatmapInfo) { this.beatmap = beatmap; if (referenceClock != null) track = new TrackVirtualManual(referenceClock); } protected override IBeatmap GetBeatmap() => beatmap; protected override Texture GetBackground() => null; protected override Track GetTrack() => track; /// /// A virtual track which tracks a reference clock. /// public class TrackVirtualManual : Track { private readonly IFrameBasedClock referenceClock; private readonly ManualClock clock = new ManualClock(); private bool running; /// /// Local offset added to the reference clock to resolve correct time. /// private double offset; public TrackVirtualManual(IFrameBasedClock referenceClock) { this.referenceClock = referenceClock; Length = double.PositiveInfinity; } public override bool Seek(double seek) { offset = MathHelper.Clamp(seek, 0, Length); lastReferenceTime = null; return offset == seek; } public override void Start() { running = true; } public override void Reset() { Seek(0); base.Reset(); } public override void Stop() { if (running) { running = false; // on stopping, the current value should be transferred out of the clock, as we can no longer rely on // the referenceClock (which will still be counting time). offset = clock.CurrentTime; lastReferenceTime = null; } } public override bool IsRunning => running; private double? lastReferenceTime; public override double CurrentTime => clock.CurrentTime; protected override void UpdateState() { base.UpdateState(); if (running) { double refTime = referenceClock.CurrentTime; if (!lastReferenceTime.HasValue) { // if the clock just started running, the current value should be transferred to the offset // (to zero the progression of time). offset -= refTime; } lastReferenceTime = refTime; } clock.CurrentTime = Math.Min((lastReferenceTime ?? 0) + offset, Length); if (CurrentTime >= Length) { Stop(); RaiseCompleted(); } } } } }