2021-04-14 12:04:03 +08:00
// 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.
2022-06-17 15:37:17 +08:00
#nullable disable
2021-04-19 11:26:50 +08:00
using System ;
2021-05-05 16:49:33 +08:00
using System.Linq ;
2021-04-14 12:04:03 +08:00
using System.Threading.Tasks ;
using NUnit.Framework ;
using osu.Framework.Graphics ;
2021-04-19 11:26:50 +08:00
using osu.Framework.Graphics.Containers ;
2021-04-14 12:04:03 +08:00
using osu.Framework.Screens ;
2021-04-16 12:59:10 +08:00
using osu.Framework.Testing ;
2021-04-14 12:04:03 +08:00
using osu.Game.Beatmaps ;
using osu.Game.Configuration ;
2023-03-29 13:00:07 +08:00
using osu.Game.Graphics.Containers ;
2021-04-14 12:04:03 +08:00
using osu.Game.Rulesets ;
2021-04-19 11:26:50 +08:00
using osu.Game.Rulesets.Judgements ;
2021-04-14 12:04:03 +08:00
using osu.Game.Rulesets.Osu ;
using osu.Game.Rulesets.Osu.Objects ;
2021-04-19 11:26:50 +08:00
using osu.Game.Rulesets.Scoring ;
2021-04-14 12:04:03 +08:00
using osu.Game.Scoring ;
2021-04-19 11:26:50 +08:00
using osu.Game.Screens.Play ;
2021-04-14 12:04:03 +08:00
using osu.Game.Screens.Ranking ;
using osu.Game.Storyboards ;
using osuTK ;
namespace osu.Game.Tests.Visual.Gameplay
{
public partial class TestSceneStoryboardWithOutro : PlayerTestScene
{
2021-04-19 11:26:50 +08:00
protected override bool HasCustomSteps = > true ;
2024-02-29 20:34:38 +08:00
protected override bool AllowBackwardsSeeks = > true ;
2021-04-14 12:04:03 +08:00
protected new OutroPlayer Player = > ( OutroPlayer ) base . Player ;
2022-10-02 20:29:53 +08:00
private double currentBeatmapDuration ;
2021-04-19 11:26:50 +08:00
private double currentStoryboardDuration ;
private bool showResults = true ;
private event Func < HealthProcessor , JudgementResult , bool > currentFailConditions ;
2021-04-14 12:04:03 +08:00
2021-04-16 12:59:10 +08:00
[SetUpSteps]
public override void SetUpSteps ( )
{
base . SetUpSteps ( ) ;
2021-04-18 01:57:32 +08:00
AddStep ( "enable storyboard" , ( ) = > LocalConfig . SetValue ( OsuSetting . ShowStoryboard , true ) ) ;
AddStep ( "set dim level to 0" , ( ) = > LocalConfig . SetValue < double > ( OsuSetting . DimLevel , 0 ) ) ;
2022-06-24 20:25:23 +08:00
AddStep ( "reset fail conditions" , ( ) = > currentFailConditions = ( _ , _ ) = > false ) ;
2022-10-02 20:29:53 +08:00
AddStep ( "set beatmap duration to 0s" , ( ) = > currentBeatmapDuration = 0 ) ;
AddStep ( "set storyboard duration to 8s" , ( ) = > currentStoryboardDuration = 8000 ) ;
2021-04-19 11:26:50 +08:00
AddStep ( "set ShowResults = true" , ( ) = > showResults = true ) ;
2021-04-16 12:59:10 +08:00
}
2021-04-14 12:04:03 +08:00
[Test]
public void TestStoryboardSkipOutro ( )
{
2022-06-27 16:13:46 +08:00
AddStep ( "set storyboard duration to long" , ( ) = > currentStoryboardDuration = 200000 ) ;
2022-06-28 14:19:02 +08:00
CreateTest ( ) ;
2021-04-14 12:04:03 +08:00
AddUntilStep ( "completion set by processor" , ( ) = > Player . ScoreProcessor . HasCompleted . Value ) ;
AddStep ( "skip outro" , ( ) = > InputManager . Key ( osuTK . Input . Key . Space ) ) ;
2022-06-27 16:13:46 +08:00
AddUntilStep ( "player is no longer current screen" , ( ) = > ! Player . IsCurrentScreen ( ) ) ;
2021-05-27 18:45:53 +08:00
AddUntilStep ( "wait for score shown" , ( ) = > Player . IsScoreShown ) ;
2021-04-14 12:04:03 +08:00
}
[Test]
public void TestStoryboardNoSkipOutro ( )
{
2022-06-28 14:19:02 +08:00
CreateTest ( ) ;
2022-08-15 16:06:24 +08:00
AddUntilStep ( "storyboard ends" , ( ) = > Player . GameplayClockContainer . CurrentTime > = currentStoryboardDuration ) ;
2021-04-16 12:59:10 +08:00
AddUntilStep ( "wait for score shown" , ( ) = > Player . IsScoreShown ) ;
2021-04-14 12:04:03 +08:00
}
[Test]
2023-10-12 18:26:32 +08:00
public void TestStoryboardExitDuringOutroProgressesToResults ( )
2021-04-14 12:04:03 +08:00
{
2022-06-28 14:19:02 +08:00
CreateTest ( ) ;
2021-04-14 12:04:03 +08:00
AddUntilStep ( "completion set by processor" , ( ) = > Player . ScoreProcessor . HasCompleted . Value ) ;
AddStep ( "exit via pause" , ( ) = > Player . ExitViaPause ( ) ) ;
2023-10-12 18:26:32 +08:00
AddUntilStep ( "reached results screen" , ( ) = > Stack . CurrentScreen is ResultsScreen ) ;
2021-04-14 12:04:03 +08:00
}
2021-04-18 09:49:29 +08:00
[TestCase(false)]
[TestCase(true)]
public void TestStoryboardToggle ( bool enabledAtBeginning )
{
2022-06-28 14:19:02 +08:00
CreateTest ( ) ;
2021-04-18 09:49:29 +08:00
AddStep ( $"{(enabledAtBeginning ? " enable " : " disable ")} storyboard" , ( ) = > LocalConfig . SetValue ( OsuSetting . ShowStoryboard , enabledAtBeginning ) ) ;
2021-04-18 10:13:28 +08:00
AddStep ( "toggle storyboard" , ( ) = > LocalConfig . SetValue ( OsuSetting . ShowStoryboard , ! enabledAtBeginning ) ) ;
2021-04-18 09:49:29 +08:00
AddUntilStep ( "wait for score shown" , ( ) = > Player . IsScoreShown ) ;
}
2021-04-19 11:26:50 +08:00
[Test]
public void TestOutroEndsDuringFailAnimation ( )
{
CreateTest ( ( ) = >
{
2022-06-24 20:25:23 +08:00
AddStep ( "fail on first judgement" , ( ) = > currentFailConditions = ( _ , _ ) = > true ) ;
2021-10-13 12:47:49 +08:00
// Fail occurs at 164ms with the provided beatmap.
// Fail animation runs for 2.5s realtime but the gameplay time change is *variable* due to the frequency transform being applied, so we need a bit of lenience.
AddStep ( "set storyboard duration to 0.6s" , ( ) = > currentStoryboardDuration = 600 ) ;
2021-04-19 11:26:50 +08:00
} ) ;
2021-10-13 12:47:49 +08:00
2022-01-26 00:45:11 +08:00
AddUntilStep ( "wait for fail" , ( ) = > Player . GameplayState . HasFailed ) ;
2022-08-15 16:06:24 +08:00
AddUntilStep ( "storyboard ends" , ( ) = > Player . GameplayClockContainer . CurrentTime > = currentStoryboardDuration ) ;
2021-04-19 11:26:50 +08:00
AddUntilStep ( "wait for fail overlay" , ( ) = > Player . FailOverlay . State . Value = = Visibility . Visible ) ;
}
2023-03-29 13:00:07 +08:00
[Test]
public void TestSaveFailedReplayWithStoryboardEndedDoesNotProgress ( )
{
CreateTest ( ( ) = >
{
AddStep ( "fail on first judgement" , ( ) = > currentFailConditions = ( _ , _ ) = > true ) ;
AddStep ( "set storyboard duration to 0s" , ( ) = > currentStoryboardDuration = 0 ) ;
} ) ;
AddUntilStep ( "storyboard ends" , ( ) = > Player . GameplayClockContainer . CurrentTime > = currentStoryboardDuration ) ;
AddUntilStep ( "wait for fail" , ( ) = > Player . GameplayState . HasFailed ) ;
AddUntilStep ( "wait for fail overlay" , ( ) = > Player . FailOverlay . State . Value = = Visibility . Visible ) ;
AddUntilStep ( "wait for button clickable" , ( ) = > Player . ChildrenOfType < SaveFailedScoreButton > ( ) . First ( ) . ChildrenOfType < OsuClickableContainer > ( ) . First ( ) . Enabled . Value ) ;
AddStep ( "click save button" , ( ) = > Player . ChildrenOfType < SaveFailedScoreButton > ( ) . First ( ) . ChildrenOfType < OsuClickableContainer > ( ) . First ( ) . TriggerClick ( ) ) ;
// Test a regression where importing the fail replay would cause progression to results screen in a failed state.
AddWaitStep ( "wait some" , 10 ) ;
AddAssert ( "player is still current screen" , ( ) = > Player . IsCurrentScreen ( ) ) ;
}
2021-04-19 11:26:50 +08:00
[Test]
public void TestShowResultsFalse ( )
{
CreateTest ( ( ) = >
{
AddStep ( "set ShowResults = false" , ( ) = > showResults = false ) ;
} ) ;
2022-08-15 16:06:24 +08:00
AddUntilStep ( "storyboard ends" , ( ) = > Player . GameplayClockContainer . CurrentTime > = currentStoryboardDuration ) ;
2021-04-19 11:26:50 +08:00
AddWaitStep ( "wait" , 10 ) ;
AddAssert ( "no score shown" , ( ) = > ! Player . IsScoreShown ) ;
}
[Test]
public void TestStoryboardEndsBeforeCompletion ( )
{
CreateTest ( ( ) = > AddStep ( "set storyboard duration to .1s" , ( ) = > currentStoryboardDuration = 100 ) ) ;
2022-08-15 16:06:24 +08:00
AddUntilStep ( "storyboard ends" , ( ) = > Player . GameplayClockContainer . CurrentTime > = currentStoryboardDuration ) ;
2021-04-19 11:26:50 +08:00
AddUntilStep ( "completion set by processor" , ( ) = > Player . ScoreProcessor . HasCompleted . Value ) ;
AddUntilStep ( "wait for score shown" , ( ) = > Player . IsScoreShown ) ;
}
2021-05-05 10:23:36 +08:00
[Test]
public void TestStoryboardRewind ( )
{
2021-05-05 16:49:33 +08:00
SkipOverlay . FadeContainer fadeContainer ( ) = > Player . ChildrenOfType < SkipOverlay . FadeContainer > ( ) . First ( ) ;
2022-06-28 14:19:02 +08:00
CreateTest ( ) ;
2021-05-05 10:23:36 +08:00
AddUntilStep ( "completion set by processor" , ( ) = > Player . ScoreProcessor . HasCompleted . Value ) ;
2021-05-05 16:49:33 +08:00
AddUntilStep ( "skip overlay content becomes visible" , ( ) = > fadeContainer ( ) . State = = Visibility . Visible ) ;
2021-05-05 10:23:36 +08:00
AddStep ( "rewind" , ( ) = > Player . GameplayClockContainer . Seek ( - 1000 ) ) ;
2021-05-05 16:49:33 +08:00
AddUntilStep ( "skip overlay content not visible" , ( ) = > fadeContainer ( ) . State = = Visibility . Hidden ) ;
AddUntilStep ( "skip overlay content becomes visible" , ( ) = > fadeContainer ( ) . State = = Visibility . Visible ) ;
2022-08-15 16:06:24 +08:00
AddUntilStep ( "storyboard ends" , ( ) = > Player . GameplayClockContainer . CurrentTime > = currentStoryboardDuration ) ;
2021-05-05 10:23:36 +08:00
}
2021-05-08 08:59:20 +08:00
[Test]
public void TestPerformExitNoOutro ( )
{
2022-06-28 14:19:02 +08:00
CreateTest ( ) ;
2021-05-08 08:59:20 +08:00
AddStep ( "disable storyboard" , ( ) = > LocalConfig . SetValue ( OsuSetting . ShowStoryboard , false ) ) ;
AddUntilStep ( "completion set by processor" , ( ) = > Player . ScoreProcessor . HasCompleted . Value ) ;
AddStep ( "exit via pause" , ( ) = > Player . ExitViaPause ( ) ) ;
2023-10-12 13:49:07 +08:00
AddUntilStep ( "reached results screen" , ( ) = > Stack . CurrentScreen is ResultsScreen ) ;
2021-05-08 08:59:20 +08:00
}
2022-10-02 20:29:53 +08:00
[Test]
public void TestPerformExitAfterOutro ( )
{
CreateTest ( ( ) = >
{
AddStep ( "set beatmap duration to 4s" , ( ) = > currentBeatmapDuration = 4000 ) ;
AddStep ( "set storyboard duration to 1s" , ( ) = > currentStoryboardDuration = 1000 ) ;
} ) ;
AddUntilStep ( "storyboard ends" , ( ) = > Player . GameplayClockContainer . CurrentTime > = currentStoryboardDuration ) ;
AddStep ( "exit via pause" , ( ) = > Player . ExitViaPause ( ) ) ;
AddAssert ( "player paused" , ( ) = > ! Player . IsResuming ) ;
AddStep ( "resume player" , ( ) = > Player . Resume ( ) ) ;
AddUntilStep ( "completion set by processor" , ( ) = > Player . ScoreProcessor . HasCompleted . Value ) ;
AddUntilStep ( "wait for score shown" , ( ) = > Player . IsScoreShown ) ;
}
2021-04-19 11:26:50 +08:00
protected override bool AllowFail = > true ;
2021-04-14 12:04:03 +08:00
protected override Ruleset CreatePlayerRuleset ( ) = > new OsuRuleset ( ) ;
2021-04-19 11:26:50 +08:00
protected override TestPlayer CreatePlayer ( Ruleset ruleset ) = > new OutroPlayer ( currentFailConditions , showResults ) ;
2021-04-14 12:04:03 +08:00
protected override IBeatmap CreateBeatmap ( RulesetInfo ruleset )
{
var beatmap = new Beatmap ( ) ;
2022-10-02 20:29:53 +08:00
beatmap . HitObjects . Add ( new HitCircle { StartTime = currentBeatmapDuration } ) ;
2021-04-14 12:04:03 +08:00
return beatmap ;
}
2021-04-18 01:57:32 +08:00
protected override WorkingBeatmap CreateWorkingBeatmap ( IBeatmap beatmap , Storyboard storyboard = null )
{
2021-04-19 11:26:50 +08:00
return base . CreateWorkingBeatmap ( beatmap , createStoryboard ( currentStoryboardDuration ) ) ;
}
2021-04-18 01:57:32 +08:00
2021-04-19 11:26:50 +08:00
private Storyboard createStoryboard ( double duration )
{
var storyboard = new Storyboard ( ) ;
var sprite = new StoryboardSprite ( "unknown" , Anchor . TopLeft , Vector2 . Zero ) ;
sprite . TimelineGroup . Alpha . Add ( Easing . None , 0 , duration , 1 , 0 ) ;
storyboard . GetLayer ( "Background" ) . Add ( sprite ) ;
return storyboard ;
2021-04-18 01:57:32 +08:00
}
2021-04-14 12:04:03 +08:00
protected partial class OutroPlayer : TestPlayer
{
public void ExitViaPause ( ) = > PerformExit ( true ) ;
2021-04-19 11:26:50 +08:00
public new FailOverlay FailOverlay = > base . FailOverlay ;
2021-04-14 12:04:03 +08:00
public bool IsScoreShown = > ! this . IsCurrentScreen ( ) & & this . GetChildScreen ( ) is ResultsScreen ;
2021-04-19 11:26:50 +08:00
private event Func < HealthProcessor , JudgementResult , bool > failConditions ;
public OutroPlayer ( Func < HealthProcessor , JudgementResult , bool > failConditions , bool showResults = true )
2022-10-02 20:29:53 +08:00
: base ( showResults : showResults )
2021-04-19 11:26:50 +08:00
{
this . failConditions = failConditions ;
}
protected override void LoadComplete ( )
2021-04-14 12:04:03 +08:00
{
2021-04-19 11:26:50 +08:00
base . LoadComplete ( ) ;
HealthProcessor . FailConditions + = failConditions ;
2021-04-14 12:04:03 +08:00
}
protected override Task ImportScore ( Score score )
{
return Task . CompletedTask ;
}
}
}
}