mirror of
https://github.com/ppy/osu.git
synced 2025-01-13 09:23:06 +08:00
Allow skipping storyboard outro
Reuses SkipOverlay by calculating the endtime of the storyboard and using that as a "start point". Upon skipping the outro the score is instantly shown. When the end of the storyboard is reached the score screen automatically shows up. If the player holds ESC (pause) during the outro, the score is displayed The storyboard endtime is calculated by getting the latest endtime of the storyboard's elements, or simply returning 0 if there is no storyboard. Co-Authored-By: Marlina José <marlina@umich.edu>
This commit is contained in:
parent
16d34bcc0a
commit
25b8c2f257
100
osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs
Normal file
100
osu.Game.Tests/Visual/Gameplay/TestSceneStoryboardWithOutro.cs
Normal file
@ -0,0 +1,100 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens.Ranking;
|
||||
using osu.Game.Storyboards;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
public class TestSceneStoryboardWithOutro : PlayerTestScene
|
||||
{
|
||||
protected new OutroPlayer Player => (OutroPlayer)base.Player;
|
||||
|
||||
private Storyboard storyboard;
|
||||
|
||||
private const double storyboard_duration = 2000;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuConfigManager config)
|
||||
{
|
||||
config.SetValue(OsuSetting.ShowStoryboard, true);
|
||||
storyboard = new Storyboard();
|
||||
var sprite = new StoryboardSprite("unknown", Anchor.TopLeft, Vector2.Zero);
|
||||
sprite.TimelineGroup.Alpha.Add(Easing.None, 0, storyboard_duration, 1, 0);
|
||||
storyboard.GetLayer("Background").Add(sprite);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStoryboardSkipOutro()
|
||||
{
|
||||
AddUntilStep("storyboard loaded", () => Player.Beatmap.Value.StoryboardLoaded);
|
||||
AddUntilStep("completion set by processor", () => Player.ScoreProcessor.HasCompleted.Value);
|
||||
AddStep("skip outro", () => InputManager.Key(osuTK.Input.Key.Space));
|
||||
AddAssert("score shown", () => Player.IsScoreShown);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStoryboardNoSkipOutro()
|
||||
{
|
||||
AddUntilStep("storyboard loaded", () => Player.Beatmap.Value.StoryboardLoaded);
|
||||
AddUntilStep("storyboard ends", () => Player.HUDOverlay.Progress.ReferenceClock.CurrentTime >= storyboard_duration);
|
||||
AddWaitStep("wait for score", 10);
|
||||
AddAssert("score shown", () => Player.IsScoreShown);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStoryboardExitToSkipOutro()
|
||||
{
|
||||
AddUntilStep("storyboard loaded", () => Player.Beatmap.Value.StoryboardLoaded);
|
||||
AddUntilStep("completion set by processor", () => Player.ScoreProcessor.HasCompleted.Value);
|
||||
AddStep("exit via pause", () => Player.ExitViaPause());
|
||||
AddAssert("score shown", () => Player.IsScoreShown);
|
||||
}
|
||||
|
||||
protected override bool AllowFail => false;
|
||||
|
||||
protected override Ruleset CreatePlayerRuleset() => new OsuRuleset();
|
||||
|
||||
protected override TestPlayer CreatePlayer(Ruleset ruleset) => new OutroPlayer();
|
||||
|
||||
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
|
||||
{
|
||||
var beatmap = new Beatmap();
|
||||
beatmap.HitObjects.Add(new HitCircle());
|
||||
return beatmap;
|
||||
}
|
||||
|
||||
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null) =>
|
||||
new ClockBackedTestWorkingBeatmap(beatmap, storyboard ?? this.storyboard, Clock, Audio);
|
||||
|
||||
protected class OutroPlayer : TestPlayer
|
||||
{
|
||||
public void ExitViaPause() => PerformExit(true);
|
||||
|
||||
public bool IsScoreShown => !this.IsCurrentScreen() && this.GetChildScreen() is ResultsScreen;
|
||||
|
||||
public OutroPlayer()
|
||||
: base(false)
|
||||
{
|
||||
}
|
||||
|
||||
protected override Task ImportScore(Score score)
|
||||
{
|
||||
// avoid database errors from trying to store the score
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -49,6 +49,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
AllowPause = false,
|
||||
AllowRestart = false,
|
||||
AllowSkippingIntro = false,
|
||||
AllowSkippingOutro = false,
|
||||
})
|
||||
{
|
||||
this.userIds = userIds;
|
||||
|
@ -228,6 +228,11 @@ namespace osu.Game.Screens.Play
|
||||
adjustableClock.ChangeSource(track);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the endtime of the last element in the storyboard in ms, or start time of the last hitobject if there's no storyboard.
|
||||
/// </summary>
|
||||
public double StoryboardEndTime => beatmap.Storyboard.LatestEventTime ?? 0;
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
if (!IsPaused.Value)
|
||||
@ -235,6 +240,8 @@ namespace osu.Game.Screens.Play
|
||||
userOffsetClock.ProcessFrame();
|
||||
}
|
||||
|
||||
updateHasStoryboardEnded();
|
||||
|
||||
base.Update();
|
||||
}
|
||||
|
||||
@ -296,5 +303,23 @@ namespace osu.Game.Screens.Play
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
# region Storyboard outro logic
|
||||
|
||||
public IBindable<bool> HasStoryboardEnded => hasStoryboardEnded;
|
||||
|
||||
public bool HasTimeLeftInStoryboard => GameplayClock.CurrentTime <= StoryboardEndTime;
|
||||
|
||||
private readonly BindableBool hasStoryboardEnded = new BindableBool(true);
|
||||
|
||||
private void updateHasStoryboardEnded()
|
||||
{
|
||||
if (StoryboardEndTime == 0)
|
||||
return;
|
||||
|
||||
hasStoryboardEnded.Value = GameplayClock.CurrentTime >= StoryboardEndTime;
|
||||
}
|
||||
|
||||
# endregion
|
||||
}
|
||||
}
|
||||
|
@ -74,6 +74,8 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
private Bindable<bool> mouseWheelDisabled;
|
||||
|
||||
private Bindable<bool> storyboardEnabled;
|
||||
|
||||
private readonly Bindable<bool> storyboardReplacesBackground = new Bindable<bool>();
|
||||
|
||||
protected readonly Bindable<bool> LocalUserPlaying = new Bindable<bool>();
|
||||
@ -106,6 +108,8 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
private SkipOverlay skipOverlay;
|
||||
|
||||
private SkipOverlay skipOutroOverlay;
|
||||
|
||||
protected ScoreProcessor ScoreProcessor { get; private set; }
|
||||
|
||||
protected HealthProcessor HealthProcessor { get; private set; }
|
||||
@ -190,6 +194,8 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
mouseWheelDisabled = config.GetBindable<bool>(OsuSetting.MouseDisableWheel);
|
||||
|
||||
storyboardEnabled = config.GetBindable<bool>(OsuSetting.ShowStoryboard);
|
||||
|
||||
if (game != null)
|
||||
gameActive.BindTo(game.IsActive);
|
||||
|
||||
@ -285,6 +291,9 @@ namespace osu.Game.Screens.Play
|
||||
ScoreProcessor.HasCompleted.ValueChanged += updateCompletionState;
|
||||
HealthProcessor.Failed += onFail;
|
||||
|
||||
// Keep track of whether the storyboard ended after the playable portion
|
||||
GameplayClockContainer.HasStoryboardEnded.ValueChanged += updateCompletionState;
|
||||
|
||||
foreach (var mod in Mods.Value.OfType<IApplicableToScoreProcessor>())
|
||||
mod.ApplyToScoreProcessor(ScoreProcessor);
|
||||
|
||||
@ -360,6 +369,10 @@ namespace osu.Game.Screens.Play
|
||||
{
|
||||
RequestSkip = performUserRequestedSkip
|
||||
},
|
||||
skipOutroOverlay = new SkipOverlay(GameplayClockContainer.StoryboardEndTime)
|
||||
{
|
||||
RequestSkip = scheduleCompletion
|
||||
},
|
||||
FailOverlay = new FailOverlay
|
||||
{
|
||||
OnRetry = Restart,
|
||||
@ -389,6 +402,9 @@ namespace osu.Game.Screens.Play
|
||||
if (!Configuration.AllowSkippingIntro)
|
||||
skipOverlay.Expire();
|
||||
|
||||
if (!Configuration.AllowSkippingOutro)
|
||||
skipOutroOverlay.Expire();
|
||||
|
||||
if (Configuration.AllowRestart)
|
||||
{
|
||||
container.Add(new HotkeyRetryOverlay
|
||||
@ -403,6 +419,8 @@ namespace osu.Game.Screens.Play
|
||||
});
|
||||
}
|
||||
|
||||
skipOutroOverlay.Hide();
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
@ -523,6 +541,14 @@ namespace osu.Game.Screens.Play
|
||||
Pause();
|
||||
return;
|
||||
}
|
||||
|
||||
// show the score if in storyboard outro (score has been set)
|
||||
bool scoreReady = prepareScoreForDisplayTask != null && prepareScoreForDisplayTask.IsCompleted;
|
||||
|
||||
if (scoreReady)
|
||||
{
|
||||
scheduleCompletion();
|
||||
}
|
||||
}
|
||||
|
||||
this.Exit();
|
||||
@ -611,6 +637,14 @@ namespace osu.Game.Screens.Play
|
||||
return score.ScoreInfo;
|
||||
});
|
||||
|
||||
// show skip overlay if storyboard is enabled and has an outro
|
||||
if (storyboardEnabled.Value && GameplayClockContainer.HasTimeLeftInStoryboard)
|
||||
{
|
||||
skipOutroOverlay.Show();
|
||||
completionProgressDelegate = null;
|
||||
return;
|
||||
}
|
||||
|
||||
using (BeginDelayedSequence(RESULTS_DISPLAY_DELAY))
|
||||
scheduleCompletion();
|
||||
}
|
||||
|
@ -24,5 +24,10 @@ namespace osu.Game.Screens.Play
|
||||
/// Whether the player should be allowed to skip the intro, advancing to the start of gameplay.
|
||||
/// </summary>
|
||||
public bool AllowSkippingIntro { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the player should be allowed to skip the outro, advancing to the end of a storyboard.
|
||||
/// </summary>
|
||||
public bool AllowSkippingOutro { get; set; } = true;
|
||||
}
|
||||
}
|
||||
|
@ -45,6 +45,7 @@ namespace osu.Game.Storyboards
|
||||
/// Videos and samples return StartTime as their EndTIme.
|
||||
/// </remarks>
|
||||
public double? LatestEventTime => Layers.SelectMany(l => l.Elements).OrderByDescending(e => e.EndTime).FirstOrDefault()?.EndTime;
|
||||
|
||||
/// <summary>
|
||||
/// Depth of the currently front-most storyboard layer, excluding the overlay layer.
|
||||
/// </summary>
|
||||
|
Loading…
Reference in New Issue
Block a user