2020-04-24 12:59:05 +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.
using System.Collections.Generic ;
using System.Linq ;
2020-05-01 03:41:57 +08:00
using Humanizer ;
2020-04-24 12:59:05 +08:00
using NUnit.Framework ;
using osu.Framework.Allocation ;
2020-04-30 06:14:27 +08:00
using osu.Framework.Extensions.IEnumerableExtensions ;
2020-05-01 06:24:39 +08:00
using osu.Framework.Graphics ;
2020-07-25 18:13:19 +08:00
using osu.Framework.Graphics.Animations ;
2020-04-24 12:59:05 +08:00
using osu.Framework.Testing ;
2020-04-27 07:40:57 +08:00
using osu.Game.Beatmaps ;
2020-04-24 12:59:05 +08:00
using osu.Game.Beatmaps.ControlPoints ;
using osu.Game.Rulesets.Judgements ;
using osu.Game.Rulesets.Objects ;
using osu.Game.Rulesets.Scoring ;
using osu.Game.Rulesets.Taiko.Judgements ;
using osu.Game.Rulesets.Taiko.Objects ;
2020-05-01 03:41:57 +08:00
using osu.Game.Rulesets.Taiko.Scoring ;
2020-04-24 12:59:05 +08:00
using osu.Game.Rulesets.Taiko.UI ;
using osu.Game.Rulesets.UI.Scrolling ;
using osu.Game.Tests.Visual ;
namespace osu.Game.Rulesets.Taiko.Tests.Skinning
{
[TestFixture]
public partial class TestSceneDrawableTaikoMascot : TaikoSkinnableTestScene
{
[Cached(typeof(IScrollingInfo))]
private ScrollingTestContainer . TestScrollingInfo info = new ScrollingTestContainer . TestScrollingInfo
{
Direction = { Value = ScrollingDirection . Left } ,
TimeRange = { Value = 5000 } ,
} ;
2022-08-17 16:36:41 +08:00
private TaikoScoreProcessor scoreProcessor = null ! ;
2020-05-01 03:41:57 +08:00
2020-04-30 03:27:02 +08:00
private IEnumerable < DrawableTaikoMascot > mascots = > this . ChildrenOfType < DrawableTaikoMascot > ( ) ;
2020-07-25 18:13:19 +08:00
private IEnumerable < DrawableTaikoMascot > animatedMascots = >
mascots . Where ( mascot = > mascot . ChildrenOfType < TextureAnimation > ( ) . All ( animation = > animation . FrameCount > 0 ) ) ;
2020-04-29 05:22:50 +08:00
private IEnumerable < TaikoPlayfield > playfields = > this . ChildrenOfType < TaikoPlayfield > ( ) ;
2020-04-28 05:17:19 +08:00
2020-05-01 03:41:57 +08:00
[SetUp]
public void SetUp ( )
{
scoreProcessor = new TaikoScoreProcessor ( ) ;
}
2020-04-24 12:59:05 +08:00
[Test]
2020-04-30 03:27:02 +08:00
public void TestStateAnimations ( )
2020-04-24 12:59:05 +08:00
{
2020-04-30 02:28:46 +08:00
AddStep ( "set beatmap" , ( ) = > setBeatmap ( ) ) ;
2020-04-24 12:59:05 +08:00
2021-06-02 15:04:53 +08:00
AddStep ( "clear state" , ( ) = > SetContents ( _ = > new TaikoMascotAnimation ( TaikoMascotAnimationState . Clear ) ) ) ;
AddStep ( "idle state" , ( ) = > SetContents ( _ = > new TaikoMascotAnimation ( TaikoMascotAnimationState . Idle ) ) ) ;
AddStep ( "kiai state" , ( ) = > SetContents ( _ = > new TaikoMascotAnimation ( TaikoMascotAnimationState . Kiai ) ) ) ;
AddStep ( "fail state" , ( ) = > SetContents ( _ = > new TaikoMascotAnimation ( TaikoMascotAnimationState . Fail ) ) ) ;
2020-04-24 12:59:05 +08:00
}
2020-05-01 04:03:39 +08:00
[Test]
public void TestInitialState ( )
{
2022-08-17 22:03:39 +08:00
AddStep ( "set beatmap" , ( ) = > setBeatmap ( ) ) ;
2021-06-02 15:04:53 +08:00
AddStep ( "create mascot" , ( ) = > SetContents ( _ = > new DrawableTaikoMascot { RelativeSizeAxes = Axes . Both } ) ) ;
2020-05-01 04:03:39 +08:00
AddAssert ( "mascot initially idle" , ( ) = > allMascotsIn ( TaikoMascotAnimationState . Idle ) ) ;
}
2020-04-30 06:14:27 +08:00
[Test]
public void TestClearStateTransition ( )
{
AddStep ( "set beatmap" , ( ) = > setBeatmap ( ) ) ;
2021-06-02 15:04:53 +08:00
AddStep ( "create mascot" , ( ) = > SetContents ( _ = > new DrawableTaikoMascot { RelativeSizeAxes = Axes . Both } ) ) ;
2020-04-30 06:14:27 +08:00
2020-05-12 04:53:05 +08:00
AddStep ( "set clear state" , ( ) = > mascots . ForEach ( mascot = > mascot . State . Value = TaikoMascotAnimationState . Clear ) ) ;
AddStep ( "miss" , ( ) = > mascots . ForEach ( mascot = > mascot . LastResult . Value = new JudgementResult ( new Hit ( ) , new TaikoJudgement ( ) ) { Type = HitResult . Miss } ) ) ;
2020-07-25 18:13:19 +08:00
AddAssert ( "skins with animations remain in clear state" , ( ) = > animatedMascotsIn ( TaikoMascotAnimationState . Clear ) ) ;
2020-05-01 06:24:39 +08:00
AddUntilStep ( "state reverts to fail" , ( ) = > allMascotsIn ( TaikoMascotAnimationState . Fail ) ) ;
2020-04-30 06:14:27 +08:00
2020-05-12 04:53:05 +08:00
AddStep ( "set clear state again" , ( ) = > mascots . ForEach ( mascot = > mascot . State . Value = TaikoMascotAnimationState . Clear ) ) ;
2020-07-25 18:13:19 +08:00
AddAssert ( "skins with animations change to clear" , ( ) = > animatedMascotsIn ( TaikoMascotAnimationState . Clear ) ) ;
2020-04-30 06:14:27 +08:00
}
2020-04-24 12:59:05 +08:00
[Test]
2020-05-01 03:41:57 +08:00
public void TestIdleState ( )
2020-04-24 12:59:05 +08:00
{
2022-08-17 22:03:39 +08:00
prepareDrawableRulesetAndBeatmap ( false ) ;
2020-04-24 12:59:05 +08:00
2020-05-01 03:41:57 +08:00
assertStateAfterResult ( new JudgementResult ( new Hit ( ) , new TaikoJudgement ( ) ) { Type = HitResult . Great } , TaikoMascotAnimationState . Idle ) ;
2020-12-13 19:59:46 +08:00
assertStateAfterResult ( new JudgementResult ( new Hit . StrongNestedHit ( ) , new TaikoStrongJudgement ( ) ) { Type = HitResult . IgnoreMiss } , TaikoMascotAnimationState . Idle ) ;
2020-05-01 03:41:57 +08:00
}
2020-04-27 07:40:57 +08:00
2020-05-01 03:41:57 +08:00
[Test]
public void TestKiaiState ( )
{
2022-08-17 22:03:39 +08:00
prepareDrawableRulesetAndBeatmap ( true ) ;
2020-04-30 03:42:28 +08:00
2020-09-29 16:16:55 +08:00
assertStateAfterResult ( new JudgementResult ( new Hit ( ) , new TaikoJudgement ( ) ) { Type = HitResult . Ok } , TaikoMascotAnimationState . Kiai ) ;
2020-09-29 14:36:08 +08:00
assertStateAfterResult ( new JudgementResult ( new Hit ( ) , new TaikoStrongJudgement ( ) ) { Type = HitResult . IgnoreMiss } , TaikoMascotAnimationState . Kiai ) ;
2020-05-01 03:41:57 +08:00
assertStateAfterResult ( new JudgementResult ( new Hit ( ) , new TaikoJudgement ( ) ) { Type = HitResult . Miss } , TaikoMascotAnimationState . Fail ) ;
2020-04-27 07:40:57 +08:00
}
[Test]
2020-05-01 03:41:57 +08:00
public void TestMissState ( )
2020-04-27 07:40:57 +08:00
{
2022-08-17 22:03:39 +08:00
prepareDrawableRulesetAndBeatmap ( false ) ;
2020-04-27 07:40:57 +08:00
2020-05-01 03:41:57 +08:00
assertStateAfterResult ( new JudgementResult ( new Hit ( ) , new TaikoJudgement ( ) ) { Type = HitResult . Great } , TaikoMascotAnimationState . Idle ) ;
assertStateAfterResult ( new JudgementResult ( new Hit ( ) , new TaikoJudgement ( ) ) { Type = HitResult . Miss } , TaikoMascotAnimationState . Fail ) ;
2020-09-29 16:16:55 +08:00
assertStateAfterResult ( new JudgementResult ( new Hit ( ) , new TaikoJudgement ( ) ) { Type = HitResult . Ok } , TaikoMascotAnimationState . Idle ) ;
2020-05-01 03:41:57 +08:00
}
2020-04-27 07:40:57 +08:00
2020-05-01 03:41:57 +08:00
[TestCase(true)]
[TestCase(false)]
public void TestClearStateOnComboMilestone ( bool kiai )
{
2022-08-17 22:03:39 +08:00
prepareDrawableRulesetAndBeatmap ( kiai ) ;
2020-05-01 03:41:57 +08:00
AddRepeatStep ( "reach 49 combo" , ( ) = > applyNewResult ( new JudgementResult ( new Hit ( ) , new TaikoJudgement ( ) ) { Type = HitResult . Great } ) , 49 ) ;
2020-09-29 16:16:55 +08:00
assertStateAfterResult ( new JudgementResult ( new Hit ( ) , new TaikoJudgement ( ) ) { Type = HitResult . Ok } , TaikoMascotAnimationState . Clear ) ;
2020-05-01 03:41:57 +08:00
}
2020-05-01 04:16:25 +08:00
[TestCase(true, TaikoMascotAnimationState.Kiai)]
[TestCase(false, TaikoMascotAnimationState.Idle)]
public void TestClearStateOnClearedSwell ( bool kiai , TaikoMascotAnimationState expectedStateAfterClear )
2020-05-01 03:41:57 +08:00
{
2022-08-17 22:03:39 +08:00
prepareDrawableRulesetAndBeatmap ( kiai ) ;
2020-04-24 12:59:05 +08:00
2020-05-01 03:41:57 +08:00
assertStateAfterResult ( new JudgementResult ( new Swell ( ) , new TaikoSwellJudgement ( ) ) { Type = HitResult . Great } , TaikoMascotAnimationState . Clear ) ;
2022-06-20 20:39:47 +08:00
AddUntilStep ( $"state reverts to {expectedStateAfterClear.ToString().ToLowerInvariant()}" , ( ) = > allMascotsIn ( expectedStateAfterClear ) ) ;
2020-04-27 07:40:57 +08:00
}
private void setBeatmap ( bool kiai = false )
{
var controlPointInfo = new ControlPointInfo ( ) ;
controlPointInfo . Add ( 0 , new TimingControlPoint { BeatLength = 90 } ) ;
if ( kiai )
controlPointInfo . Add ( 0 , new EffectControlPoint { KiaiMode = true } ) ;
2020-04-29 05:24:21 +08:00
Beatmap . Value = CreateWorkingBeatmap ( new Beatmap
2020-04-27 07:40:57 +08:00
{
HitObjects = new List < HitObject > { new Hit { Type = HitType . Centre } } ,
BeatmapInfo = new BeatmapInfo
{
2022-01-18 21:57:39 +08:00
Difficulty = new BeatmapDifficulty ( ) ,
2020-04-27 07:40:57 +08:00
Metadata = new BeatmapMetadata
{
2020-05-01 03:41:57 +08:00
Artist = "Unknown" ,
Title = "Sample Beatmap" ,
2022-01-18 22:30:40 +08:00
Author = { Username = "Craftplacer" } ,
2020-04-27 07:40:57 +08:00
} ,
Ruleset = new TaikoRuleset ( ) . RulesetInfo
} ,
ControlPointInfo = controlPointInfo
} ) ;
2020-05-01 03:41:57 +08:00
scoreProcessor . ApplyBeatmap ( Beatmap . Value . Beatmap ) ;
}
2022-08-17 22:03:39 +08:00
private void prepareDrawableRulesetAndBeatmap ( bool kiai )
2020-05-01 03:41:57 +08:00
{
2022-08-17 16:36:41 +08:00
AddStep ( "set beatmap" , ( ) = > setBeatmap ( kiai ) ) ;
2020-05-01 03:41:57 +08:00
AddStep ( "create drawable ruleset" , ( ) = >
{
2021-06-02 15:04:53 +08:00
SetContents ( _ = >
2020-05-01 03:41:57 +08:00
{
var ruleset = new TaikoRuleset ( ) ;
return new DrawableTaikoRuleset ( ruleset , Beatmap . Value . GetPlayableBeatmap ( ruleset . RulesetInfo ) ) ;
} ) ;
} ) ;
2022-08-17 16:36:41 +08:00
AddUntilStep ( "wait for track to be loaded" , ( ) = > MusicController . TrackLoaded ) ;
AddStep ( "start track" , ( ) = > MusicController . CurrentTrack . Restart ( ) ) ;
AddUntilStep ( "wait for track started" , ( ) = > MusicController . IsPlaying ) ;
2020-05-01 03:41:57 +08:00
}
private void assertStateAfterResult ( JudgementResult judgementResult , TaikoMascotAnimationState expectedState )
{
2022-08-17 16:36:41 +08:00
TaikoMascotAnimationState [ ] mascotStates = null ! ;
2020-07-25 18:13:19 +08:00
2022-06-20 20:39:47 +08:00
AddStep ( $"{judgementResult.Type.ToString().ToLowerInvariant()} result for {judgementResult.Judgement.GetType().Name.Humanize(LetterCasing.LowerCase)}" ,
2020-07-25 18:13:19 +08:00
( ) = >
{
applyNewResult ( judgementResult ) ;
// store the states as soon as possible, so that the delay between steps doesn't incorrectly fail the test
// due to not checking if the state changed quickly enough.
Schedule ( ( ) = > mascotStates = animatedMascots . Select ( mascot = > mascot . State . Value ) . ToArray ( ) ) ;
} ) ;
2020-05-01 03:41:57 +08:00
2022-08-17 16:36:41 +08:00
AddAssert ( $"state is {expectedState.ToString().ToLowerInvariant()}" , ( ) = > mascotStates . Distinct ( ) , ( ) = > Is . EquivalentTo ( new [ ] { expectedState } ) ) ;
2020-04-27 07:40:57 +08:00
}
2020-05-01 03:41:57 +08:00
private void applyNewResult ( JudgementResult judgementResult )
2020-04-27 07:40:57 +08:00
{
2020-05-01 03:58:05 +08:00
scoreProcessor . ApplyResult ( judgementResult ) ;
2020-04-27 07:40:57 +08:00
foreach ( var playfield in playfields )
{
2020-05-01 03:41:57 +08:00
var hit = new DrawableTestHit ( new Hit ( ) , judgementResult . Type ) ;
2020-09-26 23:18:50 +08:00
playfield . Add ( hit ) ;
2020-04-27 07:40:57 +08:00
2020-05-01 03:41:57 +08:00
playfield . OnNewResult ( hit , judgementResult ) ;
2020-04-27 07:40:57 +08:00
}
2020-05-12 04:53:05 +08:00
foreach ( var mascot in mascots )
{
mascot . LastResult . Value = judgementResult ;
}
2020-04-24 12:59:05 +08:00
}
2020-04-30 06:14:27 +08:00
private bool allMascotsIn ( TaikoMascotAnimationState state ) = > mascots . All ( d = > d . State . Value = = state ) ;
2020-07-25 18:13:19 +08:00
private bool animatedMascotsIn ( TaikoMascotAnimationState state ) = > animatedMascots . Any ( d = > d . State . Value = = state ) ;
2020-04-24 12:59:05 +08:00
}
}