2019-12-23 09:35:40 +00: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 ;
2020-07-20 13:26:58 +00:00
using System.Linq ;
2019-12-23 09:35:40 +00:00
using NUnit.Framework ;
using osu.Framework.Screens ;
using osu.Game.Beatmaps ;
using osu.Game.Beatmaps.ControlPoints ;
using osu.Game.Replays ;
using osu.Game.Rulesets.Judgements ;
using osu.Game.Rulesets.Mania.Objects ;
using osu.Game.Rulesets.Mania.Replays ;
2020-07-20 13:26:58 +00:00
using osu.Game.Rulesets.Mania.Scoring ;
using osu.Game.Rulesets.Objects ;
2019-12-23 09:35:40 +00:00
using osu.Game.Rulesets.Replays ;
using osu.Game.Rulesets.Scoring ;
using osu.Game.Scoring ;
using osu.Game.Screens.Play ;
using osu.Game.Tests.Visual ;
namespace osu.Game.Rulesets.Mania.Tests
{
2022-11-22 08:09:10 +00:00
/// <summary>
/// Diagrams in this class are represented as:
/// - : time
/// O : note
/// [ ] : hold note
///
/// x : button press
/// o : button release
/// </summary>
2019-12-23 09:35:40 +00:00
public partial class TestSceneHoldNoteInput : RateAdjustedBeatmapTestScene
{
private const double time_before_head = 250 ;
private const double time_head = 1500 ;
private const double time_during_hold_1 = 2500 ;
private const double time_tail = 4000 ;
private const double time_after_tail = 5250 ;
2023-05-08 07:55:29 +00:00
private List < JudgementResult > judgementResults = new List < JudgementResult > ( ) ;
2019-12-23 09:35:40 +00:00
/// <summary>
/// -----[ ]-----
/// o o
/// </summary>
[Test]
public void TestNoInput ( )
{
performTest ( new List < ReplayFrame >
{
new ManiaReplayFrame ( time_before_head ) ,
new ManiaReplayFrame ( time_after_tail ) ,
} ) ;
assertHeadJudgement ( HitResult . Miss ) ;
assertTailJudgement ( HitResult . Miss ) ;
2022-10-25 10:48:33 +00:00
assertNoteJudgement ( HitResult . IgnoreMiss ) ;
2019-12-23 09:35:40 +00:00
}
2023-05-08 08:49:41 +00:00
/// <summary>
/// -----[ ]-----
/// x o
/// </summary>
[Test]
public void TestCorrectInput ( )
{
performTest ( new List < ReplayFrame >
{
new ManiaReplayFrame ( time_head , ManiaAction . Key1 ) ,
new ManiaReplayFrame ( time_tail ) ,
} ) ;
assertHeadJudgement ( HitResult . Perfect ) ;
assertTailJudgement ( HitResult . Perfect ) ;
assertNoteJudgement ( HitResult . IgnoreHit ) ;
}
/// <summary>
/// -----[ ]-----
/// x o
/// </summary>
[Test]
public void TestLateRelease ( )
{
performTest ( new List < ReplayFrame >
{
new ManiaReplayFrame ( time_head , ManiaAction . Key1 ) ,
new ManiaReplayFrame ( time_after_tail ) ,
} ) ;
assertHeadJudgement ( HitResult . Perfect ) ;
assertTailJudgement ( HitResult . Miss ) ;
assertNoteJudgement ( HitResult . IgnoreMiss ) ;
}
2019-12-23 09:35:40 +00:00
/// <summary>
/// -----[ ]-----
/// x o
/// </summary>
[Test]
public void TestPressTooEarlyAndReleaseAfterTail ( )
{
performTest ( new List < ReplayFrame >
{
new ManiaReplayFrame ( time_before_head , ManiaAction . Key1 ) ,
new ManiaReplayFrame ( time_after_tail , ManiaAction . Key1 ) ,
} ) ;
assertHeadJudgement ( HitResult . Miss ) ;
assertTailJudgement ( HitResult . Miss ) ;
}
/// <summary>
/// -----[ ]-----
/// x o
/// </summary>
[Test]
public void TestPressTooEarlyAndReleaseAtTail ( )
{
performTest ( new List < ReplayFrame >
{
new ManiaReplayFrame ( time_before_head , ManiaAction . Key1 ) ,
new ManiaReplayFrame ( time_tail ) ,
} ) ;
assertHeadJudgement ( HitResult . Miss ) ;
assertTailJudgement ( HitResult . Miss ) ;
}
/// <summary>
/// -----[ ]-----
/// xo x o
/// </summary>
[Test]
public void TestPressTooEarlyThenPressAtStartAndReleaseAfterTail ( )
{
performTest ( new List < ReplayFrame >
{
new ManiaReplayFrame ( time_before_head , ManiaAction . Key1 ) ,
new ManiaReplayFrame ( time_before_head + 10 ) ,
new ManiaReplayFrame ( time_head , ManiaAction . Key1 ) ,
new ManiaReplayFrame ( time_after_tail ) ,
} ) ;
assertHeadJudgement ( HitResult . Perfect ) ;
assertTailJudgement ( HitResult . Miss ) ;
}
/// <summary>
/// -----[ ]-----
/// xo x o
/// </summary>
[Test]
public void TestPressTooEarlyThenPressAtStartAndReleaseAtTail ( )
{
performTest ( new List < ReplayFrame >
{
new ManiaReplayFrame ( time_before_head , ManiaAction . Key1 ) ,
new ManiaReplayFrame ( time_before_head + 10 ) ,
new ManiaReplayFrame ( time_head , ManiaAction . Key1 ) ,
new ManiaReplayFrame ( time_tail ) ,
} ) ;
assertHeadJudgement ( HitResult . Perfect ) ;
assertTailJudgement ( HitResult . Perfect ) ;
}
/// <summary>
/// -----[ ]-----
/// xo o
/// </summary>
[Test]
public void TestPressAtStartAndBreak ( )
{
performTest ( new List < ReplayFrame >
{
new ManiaReplayFrame ( time_head , ManiaAction . Key1 ) ,
new ManiaReplayFrame ( time_head + 10 ) ,
new ManiaReplayFrame ( time_after_tail ) ,
} ) ;
assertHeadJudgement ( HitResult . Perfect ) ;
assertTailJudgement ( HitResult . Miss ) ;
}
2023-08-31 07:31:54 +00:00
/// <summary>
/// -----[ ]-----
/// xox o
/// </summary>
[Test]
public void TestPressAtStartThenReleaseAndImmediatelyRepress ( )
{
performTest ( new List < ReplayFrame >
{
new ManiaReplayFrame ( time_head , ManiaAction . Key1 ) ,
new ManiaReplayFrame ( time_head + 1 ) ,
new ManiaReplayFrame ( time_head + 2 , ManiaAction . Key1 ) ,
new ManiaReplayFrame ( time_tail ) ,
} ) ;
assertHeadJudgement ( HitResult . Perfect ) ;
2023-12-20 10:57:42 +00:00
assertComboAtJudgement ( 0 , 1 ) ;
2023-08-31 07:31:54 +00:00
assertTailJudgement ( HitResult . Meh ) ;
2023-12-20 10:57:42 +00:00
assertComboAtJudgement ( 1 , 0 ) ;
assertComboAtJudgement ( 3 , 1 ) ;
2023-08-31 07:31:54 +00:00
}
2019-12-23 09:35:40 +00:00
/// <summary>
/// -----[ ]-----
/// xo x o
/// </summary>
[Test]
public void TestPressAtStartThenBreakThenRepressAndReleaseAfterTail ( )
{
performTest ( new List < ReplayFrame >
{
new ManiaReplayFrame ( time_head , ManiaAction . Key1 ) ,
new ManiaReplayFrame ( time_head + 10 ) ,
new ManiaReplayFrame ( time_during_hold_1 , ManiaAction . Key1 ) ,
new ManiaReplayFrame ( time_after_tail ) ,
} ) ;
assertHeadJudgement ( HitResult . Perfect ) ;
assertTailJudgement ( HitResult . Miss ) ;
}
/// <summary>
/// -----[ ]-----
/// xo x o o
/// </summary>
[Test]
public void TestPressAtStartThenBreakThenRepressAndReleaseAtTail ( )
{
performTest ( new List < ReplayFrame >
{
new ManiaReplayFrame ( time_head , ManiaAction . Key1 ) ,
new ManiaReplayFrame ( time_head + 10 ) ,
new ManiaReplayFrame ( time_during_hold_1 , ManiaAction . Key1 ) ,
new ManiaReplayFrame ( time_tail ) ,
} ) ;
assertHeadJudgement ( HitResult . Perfect ) ;
assertTailJudgement ( HitResult . Meh ) ;
}
/// <summary>
/// -----[ ]-----
/// x o
/// </summary>
[Test]
public void TestPressDuringNoteAndReleaseAfterTail ( )
{
performTest ( new List < ReplayFrame >
{
new ManiaReplayFrame ( time_during_hold_1 , ManiaAction . Key1 ) ,
new ManiaReplayFrame ( time_after_tail ) ,
} ) ;
assertHeadJudgement ( HitResult . Miss ) ;
assertTailJudgement ( HitResult . Miss ) ;
}
/// <summary>
/// -----[ ]-----
/// x o o
/// </summary>
[Test]
public void TestPressDuringNoteAndReleaseAtTail ( )
{
performTest ( new List < ReplayFrame >
{
new ManiaReplayFrame ( time_during_hold_1 , ManiaAction . Key1 ) ,
new ManiaReplayFrame ( time_tail ) ,
} ) ;
assertHeadJudgement ( HitResult . Miss ) ;
assertTailJudgement ( HitResult . Meh ) ;
}
2022-11-22 08:09:10 +00:00
/// <summary>
/// -----[ ]-O-------------
/// xo o
/// </summary>
[Test]
public void TestPressAndReleaseJustBeforeTailWithNearbyNoteAndCloseByHead ( )
{
Note note ;
const int duration = 50 ;
var beatmap = new Beatmap < ManiaHitObject >
{
HitObjects =
{
// hold note is very short, to make the head still in range
new HoldNote
{
StartTime = time_head ,
Duration = duration ,
Column = 0 ,
} ,
{
// Next note within tail lenience
note = new Note
{
StartTime = time_head + duration + 10
}
}
} ,
BeatmapInfo =
{
Difficulty = new BeatmapDifficulty { SliderTickRate = 4 } ,
Ruleset = new ManiaRuleset ( ) . RulesetInfo
} ,
} ;
performTest ( new List < ReplayFrame >
{
new ManiaReplayFrame ( time_head + duration , ManiaAction . Key1 ) ,
new ManiaReplayFrame ( time_head + duration + 10 ) ,
} , beatmap ) ;
assertHeadJudgement ( HitResult . Good ) ;
assertTailJudgement ( HitResult . Perfect ) ;
assertHitObjectJudgement ( note , HitResult . Miss ) ;
}
/// <summary>
/// -----[ ]--O--
/// xo o
/// </summary>
[Test]
public void TestPressAndReleaseJustBeforeTailWithNearbyNote ( )
{
Note note ;
var beatmap = new Beatmap < ManiaHitObject >
{
HitObjects =
{
new HoldNote
{
StartTime = time_head ,
Duration = time_tail - time_head ,
Column = 0 ,
} ,
{
// Next note within tail lenience
note = new Note
{
StartTime = time_tail + 50
}
}
} ,
BeatmapInfo =
{
Difficulty = new BeatmapDifficulty { SliderTickRate = 4 } ,
Ruleset = new ManiaRuleset ( ) . RulesetInfo
} ,
} ;
performTest ( new List < ReplayFrame >
{
new ManiaReplayFrame ( time_tail - 10 , ManiaAction . Key1 ) ,
new ManiaReplayFrame ( time_tail ) ,
} , beatmap ) ;
assertHeadJudgement ( HitResult . Miss ) ;
assertTailJudgement ( HitResult . Miss ) ;
assertHitObjectJudgement ( note , HitResult . Good ) ;
}
/// <summary>
/// -----[ ]--O--
/// xo o
/// </summary>
[Test]
public void TestPressAndReleaseJustAfterTailWithNearbyNote ( )
{
2023-10-16 06:16:10 +00:00
// Next note within tail lenience
Note note = new Note { StartTime = time_tail + 50 } ;
2022-11-22 08:09:10 +00:00
var beatmap = new Beatmap < ManiaHitObject >
{
HitObjects =
{
new HoldNote
{
StartTime = time_head ,
Duration = time_tail - time_head ,
Column = 0 ,
} ,
2023-10-16 06:16:10 +00:00
note
2022-11-22 08:09:10 +00:00
} ,
BeatmapInfo =
{
Difficulty = new BeatmapDifficulty { SliderTickRate = 4 } ,
Ruleset = new ManiaRuleset ( ) . RulesetInfo
} ,
} ;
performTest ( new List < ReplayFrame >
{
new ManiaReplayFrame ( time_tail + 10 , ManiaAction . Key1 ) ,
new ManiaReplayFrame ( time_tail + 20 ) ,
} , beatmap ) ;
assertHeadJudgement ( HitResult . Miss ) ;
assertTailJudgement ( HitResult . Miss ) ;
assertHitObjectJudgement ( note , HitResult . Great ) ;
}
2019-12-23 09:35:40 +00:00
/// <summary>
/// -----[ ]-----
/// xo o
/// </summary>
[Test]
public void TestPressAndReleaseAtTail ( )
{
performTest ( new List < ReplayFrame >
{
new ManiaReplayFrame ( time_tail , ManiaAction . Key1 ) ,
new ManiaReplayFrame ( time_tail + 10 ) ,
} ) ;
assertHeadJudgement ( HitResult . Miss ) ;
assertTailJudgement ( HitResult . Meh ) ;
}
2020-07-20 13:26:58 +00:00
[Test]
public void TestMissReleaseAndHitSecondRelease ( )
{
var windows = new ManiaHitWindows ( ) ;
windows . SetDifficulty ( 10 ) ;
var beatmap = new Beatmap < ManiaHitObject >
{
HitObjects =
{
new HoldNote
{
StartTime = 1000 ,
Duration = 500 ,
Column = 0 ,
} ,
new HoldNote
{
StartTime = 1000 + 500 + windows . WindowFor ( HitResult . Miss ) + 10 ,
Duration = 500 ,
Column = 0 ,
} ,
} ,
BeatmapInfo =
{
2022-01-18 13:57:39 +00:00
Difficulty = new BeatmapDifficulty
2020-07-20 13:26:58 +00:00
{
SliderTickRate = 4 ,
OverallDifficulty = 10 ,
} ,
Ruleset = new ManiaRuleset ( ) . RulesetInfo
} ,
} ;
performTest ( new List < ReplayFrame >
{
new ManiaReplayFrame ( beatmap . HitObjects [ 1 ] . StartTime , ManiaAction . Key1 ) ,
new ManiaReplayFrame ( beatmap . HitObjects [ 1 ] . GetEndTime ( ) ) ,
} , beatmap ) ;
AddAssert ( "first hold note missed" , ( ) = > judgementResults . Where ( j = > beatmap . HitObjects [ 0 ] . NestedHitObjects . Contains ( j . HitObject ) )
2020-09-29 08:48:44 +00:00
. All ( j = > ! j . Type . IsHit ( ) ) ) ;
2020-07-20 13:26:58 +00:00
2024-06-29 08:08:40 +00:00
AddAssert ( "second hold note hit" , ( ) = > judgementResults . Where ( j = > beatmap . HitObjects [ 1 ] . NestedHitObjects . Contains ( j . HitObject ) )
2024-06-29 08:46:16 +00:00
. All ( j = > j . Type . IsHit ( ) ) ) ;
2020-07-20 13:26:58 +00:00
}
2021-04-22 09:59:57 +00:00
[Test]
public void TestZeroLength ( )
{
var beatmap = new Beatmap < ManiaHitObject >
{
HitObjects =
{
new HoldNote
{
StartTime = 1000 ,
Duration = 0 ,
Column = 0 ,
} ,
} ,
BeatmapInfo = { Ruleset = new ManiaRuleset ( ) . RulesetInfo } ,
} ;
performTest ( new List < ReplayFrame >
{
new ManiaReplayFrame ( beatmap . HitObjects [ 0 ] . StartTime , ManiaAction . Key1 ) ,
new ManiaReplayFrame ( beatmap . HitObjects [ 0 ] . GetEndTime ( ) + 1 ) ,
} , beatmap ) ;
AddAssert ( "hold note hit" , ( ) = > judgementResults . Where ( j = > beatmap . HitObjects [ 0 ] . NestedHitObjects . Contains ( j . HitObject ) )
. All ( j = > j . Type . IsHit ( ) ) ) ;
}
2022-11-22 08:09:10 +00:00
private void assertHitObjectJudgement ( HitObject hitObject , HitResult result )
= > AddAssert ( $"object judged as {result}" , ( ) = > judgementResults . First ( j = > j . HitObject = = hitObject ) . Type , ( ) = > Is . EqualTo ( result ) ) ;
2019-12-23 09:35:40 +00:00
private void assertHeadJudgement ( HitResult result )
2022-11-22 08:09:10 +00:00
= > AddAssert ( $"head judged as {result}" , ( ) = > judgementResults . First ( j = > j . HitObject is Note ) . Type , ( ) = > Is . EqualTo ( result ) ) ;
2019-12-23 09:35:40 +00:00
private void assertTailJudgement ( HitResult result )
2022-11-22 08:09:10 +00:00
= > AddAssert ( $"tail judged as {result}" , ( ) = > judgementResults . Single ( j = > j . HitObject is TailNote ) . Type , ( ) = > Is . EqualTo ( result ) ) ;
2019-12-23 09:35:40 +00:00
private void assertNoteJudgement ( HitResult result )
2022-11-22 08:09:10 +00:00
= > AddAssert ( $"hold note judged as {result}" , ( ) = > judgementResults . Single ( j = > j . HitObject is HoldNote ) . Type , ( ) = > Is . EqualTo ( result ) ) ;
2019-12-23 09:35:40 +00:00
2023-10-09 23:32:35 +00:00
private void assertComboAtJudgement ( int judgementIndex , int combo )
= > AddAssert ( $"combo at judgement {judgementIndex} is {combo}" , ( ) = > judgementResults . ElementAt ( judgementIndex ) . ComboAfterJudgement , ( ) = > Is . EqualTo ( combo ) ) ;
2023-09-01 11:42:27 +00:00
2023-05-08 07:55:29 +00:00
private ScoreAccessibleReplayPlayer currentPlayer = null ! ;
2019-12-23 09:35:40 +00:00
2023-05-08 07:55:29 +00:00
private void performTest ( List < ReplayFrame > frames , Beatmap < ManiaHitObject > ? beatmap = null )
2019-12-23 09:35:40 +00:00
{
2020-07-20 13:26:58 +00:00
if ( beatmap = = null )
2019-12-23 09:35:40 +00:00
{
2020-07-20 13:26:58 +00:00
beatmap = new Beatmap < ManiaHitObject >
2019-12-23 09:35:40 +00:00
{
HitObjects =
{
new HoldNote
{
StartTime = time_head ,
Duration = time_tail - time_head ,
Column = 0 ,
}
} ,
BeatmapInfo =
{
2022-01-18 13:57:39 +00:00
Difficulty = new BeatmapDifficulty { SliderTickRate = 4 } ,
2019-12-23 09:35:40 +00:00
Ruleset = new ManiaRuleset ( ) . RulesetInfo
} ,
2020-07-20 13:26:58 +00:00
} ;
2021-09-06 13:04:51 +00:00
beatmap . ControlPointInfo . Add ( 0 , new EffectControlPoint { ScrollSpeed = 0.1f } ) ;
2020-07-20 13:26:58 +00:00
}
2019-12-23 09:35:40 +00:00
2020-07-20 13:26:58 +00:00
AddStep ( "load player" , ( ) = >
{
Beatmap . Value = CreateWorkingBeatmap ( beatmap ) ;
2019-12-23 09:35:40 +00:00
var p = new ScoreAccessibleReplayPlayer ( new Score { Replay = new Replay { Frames = frames } } ) ;
p . OnLoadComplete + = _ = >
{
p . ScoreProcessor . NewJudgement + = result = >
{
if ( currentPlayer = = p ) judgementResults . Add ( result ) ;
} ;
} ;
LoadScreen ( currentPlayer = p ) ;
judgementResults = new List < JudgementResult > ( ) ;
} ) ;
AddUntilStep ( "Beatmap at 0" , ( ) = > Beatmap . Value . Track . CurrentTime = = 0 ) ;
AddUntilStep ( "Wait until player is loaded" , ( ) = > currentPlayer . IsCurrentScreen ( ) ) ;
2021-03-21 17:39:52 +00:00
2023-05-08 07:55:29 +00:00
AddUntilStep ( "Wait for completion" , ( ) = > currentPlayer . ScoreProcessor . HasCompleted . Value ) ;
2019-12-23 09:35:40 +00:00
}
private partial class ScoreAccessibleReplayPlayer : ReplayPlayer
{
public new ScoreProcessor ScoreProcessor = > base . ScoreProcessor ;
protected override bool PauseOnFocusLost = > false ;
public ScoreAccessibleReplayPlayer ( Score score )
2020-12-23 08:39:08 +00:00
: base ( score , new PlayerConfiguration
{
AllowPause = false ,
ShowResults = false ,
} )
2019-12-23 09:35:40 +00:00
{
}
}
}
}