mirror of
https://github.com/ppy/osu.git
synced 2024-12-15 04:13:00 +08:00
Merge branch 'master' into add-internal-legacy-offsets
This commit is contained in:
commit
65d510baf2
@ -1 +1 @@
|
||||
Subproject commit e8ae207769ec26fb7ddd67a2433514fcda354ecd
|
||||
Subproject commit 71900dc350bcebbb60d912d4023a1d2a6bbbc3c1
|
@ -91,8 +91,6 @@ namespace osu.Game.Rulesets.UI
|
||||
|
||||
#region Clock control
|
||||
|
||||
protected override bool ShouldProcessClock => false; // We handle processing the clock ourselves
|
||||
|
||||
private ManualClock clock;
|
||||
private IFrameBasedClock parentClock;
|
||||
|
||||
@ -103,6 +101,7 @@ namespace osu.Game.Rulesets.UI
|
||||
//our clock will now be our parent's clock, but we want to replace this to allow manual control.
|
||||
parentClock = Clock;
|
||||
|
||||
ProcessCustomClock = false;
|
||||
Clock = new FramedClock(clock = new ManualClock
|
||||
{
|
||||
CurrentTime = parentClock.CurrentTime,
|
||||
|
@ -41,7 +41,7 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
private static bool hasShownNotificationOnce;
|
||||
|
||||
public HUDOverlay(ScoreProcessor scoreProcessor, RulesetContainer rulesetContainer, DecoupleableInterpolatingFramedClock decoupledClock, WorkingBeatmap working, IAdjustableClock adjustableSourceClock)
|
||||
public HUDOverlay(ScoreProcessor scoreProcessor, RulesetContainer rulesetContainer, WorkingBeatmap working, IClock offsetClock, IAdjustableClock adjustableClock)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
@ -66,13 +66,13 @@ namespace osu.Game.Screens.Play
|
||||
BindRulesetContainer(rulesetContainer);
|
||||
|
||||
Progress.Objects = rulesetContainer.Objects;
|
||||
Progress.AudioClock = decoupledClock;
|
||||
Progress.AudioClock = offsetClock;
|
||||
Progress.AllowSeeking = rulesetContainer.HasReplayLoaded;
|
||||
Progress.OnSeek = pos => decoupledClock.Seek(pos);
|
||||
Progress.OnSeek = pos => adjustableClock.Seek(pos);
|
||||
|
||||
ModDisplay.Current.BindTo(working.Mods);
|
||||
|
||||
PlayerSettingsOverlay.PlaybackSettings.AdjustableClock = adjustableSourceClock;
|
||||
PlayerSettingsOverlay.PlaybackSettings.AdjustableClock = adjustableClock;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
|
@ -44,14 +44,22 @@ namespace osu.Game.Screens.Play
|
||||
public Action OnResume;
|
||||
public Action OnPause;
|
||||
|
||||
public IAdjustableClock AudioClock;
|
||||
public FramedClock FramedClock;
|
||||
private readonly IAdjustableClock adjustableClock;
|
||||
private readonly FramedClock framedClock;
|
||||
|
||||
public PauseContainer()
|
||||
public PauseContainer(FramedClock framedClock, IAdjustableClock adjustableClock)
|
||||
{
|
||||
this.framedClock = framedClock;
|
||||
this.adjustableClock = adjustableClock;
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
AddInternal(content = new Container { RelativeSizeAxes = Axes.Both });
|
||||
AddInternal(content = new Container
|
||||
{
|
||||
Clock = this.framedClock,
|
||||
ProcessCustomClock = false,
|
||||
RelativeSizeAxes = Axes.Both
|
||||
});
|
||||
|
||||
AddInternal(pauseOverlay = new PauseOverlay
|
||||
{
|
||||
@ -65,47 +73,37 @@ namespace osu.Game.Screens.Play
|
||||
});
|
||||
}
|
||||
|
||||
public void Pause(bool force = false)
|
||||
public void Pause(bool force = false) => Schedule(() => // Scheduled to ensure a stable position in execution order, no matter how it was called.
|
||||
{
|
||||
if (!CanPause && !force) return;
|
||||
|
||||
if (IsPaused) return;
|
||||
|
||||
// stop the decoupled clock (stops the audio eventually)
|
||||
AudioClock.Stop();
|
||||
|
||||
// stop processing updatess on the offset clock (instantly freezes time for all our components)
|
||||
FramedClock.ProcessSourceClockFrames = false;
|
||||
|
||||
// stop the seekable clock (stops the audio eventually)
|
||||
adjustableClock.Stop();
|
||||
IsPaused = true;
|
||||
|
||||
// we need to do a final check after all of our children have processed up to the paused clock time.
|
||||
// this is to cover cases where, for instance, the player fails in the current processing frame.
|
||||
Schedule(() =>
|
||||
{
|
||||
if (!CanPause) return;
|
||||
OnPause?.Invoke();
|
||||
pauseOverlay.Show();
|
||||
|
||||
lastPauseActionTime = Time.Current;
|
||||
|
||||
OnPause?.Invoke();
|
||||
pauseOverlay.Show();
|
||||
});
|
||||
}
|
||||
lastPauseActionTime = Time.Current;
|
||||
});
|
||||
|
||||
public void Resume()
|
||||
{
|
||||
if (!IsPaused) return;
|
||||
|
||||
IsPaused = false;
|
||||
FramedClock.ProcessSourceClockFrames = true;
|
||||
|
||||
IsResuming = false;
|
||||
lastPauseActionTime = Time.Current;
|
||||
|
||||
OnResume?.Invoke();
|
||||
// seek back to the time of the framed clock.
|
||||
// this accounts for the audio clock potentially taking time to enter a completely stopped state.
|
||||
adjustableClock.Seek(framedClock.CurrentTime);
|
||||
adjustableClock.Start();
|
||||
|
||||
OnResume?.Invoke();
|
||||
pauseOverlay.Hide();
|
||||
AudioClock.Start();
|
||||
IsResuming = false;
|
||||
}
|
||||
|
||||
private OsuGameBase game;
|
||||
@ -122,6 +120,9 @@ namespace osu.Game.Screens.Play
|
||||
if (!game.IsActive && CanPause)
|
||||
Pause();
|
||||
|
||||
if (!IsPaused)
|
||||
framedClock.ProcessFrame();
|
||||
|
||||
base.Update();
|
||||
}
|
||||
|
||||
|
@ -56,9 +56,12 @@ namespace osu.Game.Screens.Play
|
||||
public CursorContainer Cursor => RulesetContainer.Cursor;
|
||||
public bool ProvidingUserCursor => RulesetContainer?.Cursor != null && !RulesetContainer.HasReplayLoaded.Value;
|
||||
|
||||
private IAdjustableClock adjustableSourceClock;
|
||||
private FramedOffsetClock offsetClock;
|
||||
private DecoupleableInterpolatingFramedClock decoupledClock;
|
||||
private IAdjustableClock sourceClock;
|
||||
|
||||
/// <summary>
|
||||
/// The decoupled clock used for gameplay. Should be used for seeks and clock control.
|
||||
/// </summary>
|
||||
private DecoupleableInterpolatingFramedClock adjustableClock;
|
||||
|
||||
private PauseContainer pauseContainer;
|
||||
|
||||
@ -140,17 +143,18 @@ namespace osu.Game.Screens.Play
|
||||
return;
|
||||
}
|
||||
|
||||
adjustableSourceClock = (IAdjustableClock)working.Track ?? new StopwatchClock();
|
||||
decoupledClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false };
|
||||
sourceClock = (IAdjustableClock)working.Track ?? new StopwatchClock();
|
||||
adjustableClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false };
|
||||
|
||||
var firstObjectTime = RulesetContainer.Objects.First().StartTime;
|
||||
decoupledClock.Seek(AllowLeadIn
|
||||
adjustableClock.Seek(AllowLeadIn
|
||||
? Math.Min(0, firstObjectTime - Math.Max(beatmap.ControlPointInfo.TimingPointAt(firstObjectTime).BeatLength * 4, beatmap.BeatmapInfo.AudioLeadIn))
|
||||
: firstObjectTime);
|
||||
|
||||
decoupledClock.ProcessFrame();
|
||||
adjustableClock.ProcessFrame();
|
||||
|
||||
offsetClock = new FramedOffsetClock(decoupledClock);
|
||||
// the final usable gameplay clock with user-set offsets applied.
|
||||
var offsetClock = new FramedOffsetClock(adjustableClock);
|
||||
|
||||
userAudioOffset = config.GetBindable<double>(OsuSetting.AudioOffset);
|
||||
userAudioOffset.ValueChanged += v => offsetClock.Offset = v;
|
||||
@ -160,16 +164,8 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
storyboardContainer = new Container
|
||||
pauseContainer = new PauseContainer(offsetClock, adjustableClock)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Clock = offsetClock,
|
||||
Alpha = 0,
|
||||
},
|
||||
pauseContainer = new PauseContainer
|
||||
{
|
||||
AudioClock = decoupledClock,
|
||||
FramedClock = offsetClock,
|
||||
OnRetry = Restart,
|
||||
OnQuit = Exit,
|
||||
CheckCanPause = () => AllowPause && ValidForResume && !HasFailed && !RulesetContainer.HasReplayLoaded,
|
||||
@ -181,15 +177,23 @@ namespace osu.Game.Screens.Play
|
||||
OnResume = () => hudOverlay.KeyCounter.IsCounting = true,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
storyboardContainer = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Clock = offsetClock,
|
||||
Child = RulesetContainer,
|
||||
Alpha = 0,
|
||||
},
|
||||
new SkipButton(firstObjectTime) { AudioClock = decoupledClock },
|
||||
hudOverlay = new HUDOverlay(scoreProcessor, RulesetContainer, decoupledClock, working, adjustableSourceClock)
|
||||
RulesetContainer,
|
||||
new SkipButton(firstObjectTime)
|
||||
{
|
||||
Clock = Clock, // skip button doesn't want to use the audio clock directly
|
||||
ProcessCustomClock = false,
|
||||
AdjustableClock = adjustableClock,
|
||||
FramedClock = offsetClock,
|
||||
},
|
||||
hudOverlay = new HUDOverlay(scoreProcessor, RulesetContainer, working, offsetClock, adjustableClock)
|
||||
{
|
||||
Clock = Clock, // hud overlay doesn't want to use the audio clock directly
|
||||
ProcessCustomClock = false,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre
|
||||
},
|
||||
@ -197,7 +201,7 @@ namespace osu.Game.Screens.Play
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Clock = decoupledClock,
|
||||
ProcessCustomClock = false,
|
||||
Breaks = beatmap.Breaks
|
||||
}
|
||||
}
|
||||
@ -234,11 +238,11 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
private void applyRateFromMods()
|
||||
{
|
||||
if (adjustableSourceClock == null) return;
|
||||
if (sourceClock == null) return;
|
||||
|
||||
adjustableSourceClock.Rate = 1;
|
||||
sourceClock.Rate = 1;
|
||||
foreach (var mod in Beatmap.Value.Mods.Value.OfType<IApplicableToClock>())
|
||||
mod.ApplyToClock(adjustableSourceClock);
|
||||
mod.ApplyToClock(sourceClock);
|
||||
}
|
||||
|
||||
private void initializeStoryboard(bool asyncLoad)
|
||||
@ -297,7 +301,7 @@ namespace osu.Game.Screens.Play
|
||||
if (Beatmap.Value.Mods.Value.OfType<IApplicableFailOverride>().Any(m => !m.AllowFail))
|
||||
return false;
|
||||
|
||||
decoupledClock.Stop();
|
||||
adjustableClock.Stop();
|
||||
|
||||
HasFailed = true;
|
||||
failOverlay.Retries = RestartCount;
|
||||
@ -326,17 +330,19 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
adjustableSourceClock.Reset();
|
||||
sourceClock.Reset();
|
||||
|
||||
Schedule(() =>
|
||||
{
|
||||
decoupledClock.ChangeSource(adjustableSourceClock);
|
||||
adjustableClock.ChangeSource(sourceClock);
|
||||
applyRateFromMods();
|
||||
|
||||
this.Delay(750).Schedule(() =>
|
||||
{
|
||||
if (!pauseContainer.IsPaused)
|
||||
decoupledClock.Start();
|
||||
{
|
||||
adjustableClock.Start();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -363,9 +369,7 @@ namespace osu.Game.Screens.Play
|
||||
}
|
||||
|
||||
if (loadedSuccessfully)
|
||||
{
|
||||
pauseContainer?.Pause();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -24,7 +24,9 @@ namespace osu.Game.Screens.Play
|
||||
public class SkipButton : OverlayContainer, IKeyBindingHandler<GlobalAction>
|
||||
{
|
||||
private readonly double startTime;
|
||||
public IAdjustableClock AudioClock;
|
||||
|
||||
public IAdjustableClock AdjustableClock;
|
||||
public IFrameBasedClock FramedClock;
|
||||
|
||||
private Button button;
|
||||
private Box remainingTimeBox;
|
||||
@ -60,8 +62,11 @@ namespace osu.Game.Screens.Play
|
||||
{
|
||||
var baseClock = Clock;
|
||||
|
||||
if (AudioClock != null)
|
||||
Clock = new FramedClock(AudioClock) { ProcessSourceClockFrames = false };
|
||||
if (FramedClock != null)
|
||||
{
|
||||
Clock = FramedClock;
|
||||
ProcessCustomClock = false;
|
||||
}
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
@ -109,7 +114,7 @@ namespace osu.Game.Screens.Play
|
||||
using (BeginAbsoluteSequence(beginFadeTime))
|
||||
this.FadeOut(fade_time);
|
||||
|
||||
button.Action = () => AudioClock?.Seek(startTime - skip_required_cutoff - fade_time);
|
||||
button.Action = () => AdjustableClock?.Seek(startTime - skip_required_cutoff - fade_time);
|
||||
|
||||
displayTime = Time.Current;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user