2019-01-24 16:43: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.
2018-04-13 17:19:50 +08:00
2018-05-11 20:40:48 +08:00
using System ;
2018-04-13 17:19:50 +08:00
using System.Collections.Generic ;
2019-03-25 00:02:36 +08:00
using System.Linq ;
2018-08-07 15:45:18 +08:00
using NUnit.Framework ;
2019-03-25 00:02:36 +08:00
using osu.Framework.Allocation ;
2019-02-21 18:04:31 +08:00
using osu.Framework.Bindables ;
2019-03-25 00:02:36 +08:00
using osu.Framework.Graphics ;
2021-02-04 18:55:15 +08:00
using osu.Framework.Graphics.Containers ;
2019-12-13 19:21:35 +08:00
using osu.Framework.Testing ;
2018-04-13 17:19:50 +08:00
using osu.Game.Graphics.Sprites ;
2019-03-25 00:02:36 +08:00
using osu.Game.Graphics.UserInterface ;
using osu.Game.Overlays.Mods ;
using osu.Game.Rulesets ;
2020-07-15 18:45:48 +08:00
using osu.Game.Rulesets.Mania ;
using osu.Game.Rulesets.Mania.Mods ;
2019-03-25 00:02:36 +08:00
using osu.Game.Rulesets.Mods ;
2019-12-13 19:21:35 +08:00
using osu.Game.Rulesets.Osu ;
2019-03-25 00:02:36 +08:00
using osu.Game.Rulesets.Osu.Mods ;
using osu.Game.Screens.Play.HUD ;
using osuTK ;
2018-11-20 15:51:59 +08:00
using osuTK.Graphics ;
2018-04-13 17:19:50 +08:00
2019-03-25 00:02:36 +08:00
namespace osu.Game.Tests.Visual.UserInterface
2018-04-13 17:19:50 +08:00
{
[Description("mod select and icon display")]
2019-06-20 00:40:58 +08:00
public class TestSceneModSelectOverlay : OsuTestScene
2018-04-13 17:19:50 +08:00
{
private RulesetStore rulesets ;
private ModDisplay modDisplay ;
private TestModSelectOverlay modSelect ;
[BackgroundDependencyLoader]
private void load ( RulesetStore rulesets )
{
this . rulesets = rulesets ;
2019-12-13 19:21:35 +08:00
}
2018-04-13 17:19:50 +08:00
2019-12-13 19:21:35 +08:00
[SetUp]
2021-02-10 14:29:55 +08:00
public void SetUp ( ) = > Schedule ( ( ) = >
{
SelectedMods . Value = Array . Empty < Mod > ( ) ;
createDisplay ( ( ) = > new TestModSelectOverlay ( ) ) ;
} ) ;
2019-12-13 19:21:35 +08:00
[SetUpSteps]
public void SetUpSteps ( )
{
AddStep ( "show" , ( ) = > modSelect . Show ( ) ) ;
2018-04-13 17:19:50 +08:00
}
2021-02-09 12:54:13 +08:00
[Test]
public void TestSettingsResetOnDeselection ( )
{
var osuModDoubleTime = new OsuModDoubleTime { SpeedChange = { Value = 1.2 } } ;
changeRuleset ( 0 ) ;
AddStep ( "set dt mod with custom rate" , ( ) = > { SelectedMods . Value = new [ ] { osuModDoubleTime } ; } ) ;
AddAssert ( "selected mod matches" , ( ) = > ( SelectedMods . Value . Single ( ) as OsuModDoubleTime ) ? . SpeedChange . Value = = 1.2 ) ;
AddStep ( "deselect" , ( ) = > modSelect . DeselectAllButton . Click ( ) ) ;
AddAssert ( "selected mods empty" , ( ) = > SelectedMods . Value . Count = = 0 ) ;
AddStep ( "reselect" , ( ) = > modSelect . GetModButton ( osuModDoubleTime ) . Click ( ) ) ;
AddAssert ( "selected mod has default value" , ( ) = > ( SelectedMods . Value . Single ( ) as OsuModDoubleTime ) ? . SpeedChange . IsDefault = = true ) ;
}
2021-02-04 18:55:15 +08:00
[Test]
public void TestAnimationFlushOnClose ( )
{
changeRuleset ( 0 ) ;
AddStep ( "Select all fun mods" , ( ) = >
{
modSelect . ModSectionsContainer
. Single ( c = > c . ModType = = ModType . DifficultyIncrease )
. SelectAll ( ) ;
} ) ;
AddUntilStep ( "many mods selected" , ( ) = > modDisplay . Current . Value . Count > = 5 ) ;
AddStep ( "trigger deselect and close overlay" , ( ) = >
{
modSelect . ModSectionsContainer
. Single ( c = > c . ModType = = ModType . DifficultyIncrease )
. DeselectAll ( ) ;
modSelect . Hide ( ) ;
} ) ;
AddAssert ( "all mods deselected" , ( ) = > modDisplay . Current . Value . Count = = 0 ) ;
}
2018-08-07 15:45:18 +08:00
[Test]
public void TestOsuMods ( )
2018-04-13 17:19:50 +08:00
{
2019-12-13 19:21:35 +08:00
changeRuleset ( 0 ) ;
2018-08-07 15:45:18 +08:00
2019-12-13 19:21:35 +08:00
var osu = new OsuRuleset ( ) ;
2018-08-07 15:45:18 +08:00
2019-12-13 19:21:35 +08:00
var easierMods = osu . GetModsFor ( ModType . DifficultyReduction ) ;
var harderMods = osu . GetModsFor ( ModType . DifficultyIncrease ) ;
2020-02-08 12:08:44 +08:00
var conversionMods = osu . GetModsFor ( ModType . Conversion ) ;
2018-04-13 17:19:50 +08:00
2019-12-13 19:21:35 +08:00
var noFailMod = osu . GetModsFor ( ModType . DifficultyReduction ) . FirstOrDefault ( m = > m is OsuModNoFail ) ;
2018-04-13 17:19:50 +08:00
var hiddenMod = harderMods . FirstOrDefault ( m = > m is OsuModHidden ) ;
var doubleTimeMod = harderMods . OfType < MultiMod > ( ) . FirstOrDefault ( m = > m . Mods . Any ( a = > a is OsuModDoubleTime ) ) ;
2020-02-08 12:08:44 +08:00
var targetMod = conversionMods . FirstOrDefault ( m = > m is OsuModTarget ) ;
2018-04-13 17:19:50 +08:00
var easy = easierMods . FirstOrDefault ( m = > m is OsuModEasy ) ;
var hardRock = harderMods . FirstOrDefault ( m = > m is OsuModHardRock ) ;
testSingleMod ( noFailMod ) ;
testMultiMod ( doubleTimeMod ) ;
testIncompatibleMods ( easy , hardRock ) ;
testDeselectAll ( easierMods . Where ( m = > ! ( m is MultiMod ) ) ) ;
2019-12-13 19:21:35 +08:00
testMultiplierTextColour ( noFailMod , ( ) = > modSelect . LowMultiplierColour ) ;
testMultiplierTextColour ( hiddenMod , ( ) = > modSelect . HighMultiplierColour ) ;
2018-04-13 17:19:50 +08:00
2020-02-08 12:07:21 +08:00
testUnimplementedMod ( targetMod ) ;
2018-04-13 17:19:50 +08:00
}
2018-08-07 15:45:18 +08:00
[Test]
public void TestManiaMods ( )
2018-04-13 17:19:50 +08:00
{
2019-12-13 19:21:35 +08:00
changeRuleset ( 3 ) ;
2020-07-15 18:45:48 +08:00
var mania = new ManiaRuleset ( ) ;
testModsWithSameBaseType (
mania . GetAllMods ( ) . Single ( m = > m . GetType ( ) = = typeof ( ManiaModFadeIn ) ) ,
mania . GetAllMods ( ) . Single ( m = > m . GetType ( ) = = typeof ( ManiaModHidden ) ) ) ;
2018-08-07 15:45:18 +08:00
}
[Test]
public void TestRulesetChanges ( )
{
2019-12-13 19:21:35 +08:00
changeRuleset ( 0 ) ;
2018-08-07 15:45:18 +08:00
2019-12-13 19:21:35 +08:00
var noFailMod = new OsuRuleset ( ) . GetModsFor ( ModType . DifficultyReduction ) . FirstOrDefault ( m = > m is OsuModNoFail ) ;
2018-08-07 15:45:18 +08:00
2019-12-13 20:45:38 +08:00
AddStep ( "set mods externally" , ( ) = > { SelectedMods . Value = new [ ] { noFailMod } ; } ) ;
2018-08-07 15:45:18 +08:00
2019-12-13 19:21:35 +08:00
changeRuleset ( 0 ) ;
2018-08-07 15:45:18 +08:00
AddAssert ( "ensure mods still selected" , ( ) = > modDisplay . Current . Value . Single ( m = > m is OsuModNoFail ) ! = null ) ;
2019-12-13 19:21:35 +08:00
changeRuleset ( 3 ) ;
2018-08-07 15:45:18 +08:00
2019-12-13 19:21:35 +08:00
AddAssert ( "ensure mods not selected" , ( ) = > modDisplay . Current . Value . Count = = 0 ) ;
2018-08-07 15:45:18 +08:00
2019-12-13 19:21:35 +08:00
changeRuleset ( 0 ) ;
2018-08-07 15:45:18 +08:00
2019-12-13 19:21:35 +08:00
AddAssert ( "ensure mods not selected" , ( ) = > modDisplay . Current . Value . Count = = 0 ) ;
2018-04-13 17:19:50 +08:00
}
2021-01-01 08:47:18 +08:00
[Test]
public void TestExternallySetCustomizedMod ( )
{
2021-01-21 12:30:53 +08:00
changeRuleset ( 0 ) ;
2021-01-01 08:47:18 +08:00
AddStep ( "set customized mod externally" , ( ) = > SelectedMods . Value = new [ ] { new OsuModDoubleTime { SpeedChange = { Value = 1.01 } } } ) ;
AddAssert ( "ensure button is selected and customized accordingly" , ( ) = >
{
var button = modSelect . GetModButton ( SelectedMods . Value . Single ( ) ) ;
return ( ( OsuModDoubleTime ) button . SelectedMod ) . SpeedChange . Value = = 1.01 ;
} ) ;
}
2021-02-10 13:44:37 +08:00
[Test]
public void TestSettingsAreRetainedOnReload ( )
{
changeRuleset ( 0 ) ;
AddStep ( "set customized mod externally" , ( ) = > SelectedMods . Value = new [ ] { new OsuModDoubleTime { SpeedChange = { Value = 1.01 } } } ) ;
AddAssert ( "setting remains" , ( ) = > ( SelectedMods . Value . SingleOrDefault ( ) as OsuModDoubleTime ) ? . SpeedChange . Value = = 1.01 ) ;
AddStep ( "create overlay" , ( ) = > createDisplay ( ( ) = > new TestNonStackedModSelectOverlay ( ) ) ) ;
AddAssert ( "setting remains" , ( ) = > ( SelectedMods . Value . SingleOrDefault ( ) as OsuModDoubleTime ) ? . SpeedChange . Value = = 1.01 ) ;
}
2021-02-10 13:34:45 +08:00
[Test]
public void TestExternallySetModIsReplacedByOverlayInstance ( )
{
Mod external = new OsuModDoubleTime ( ) ;
Mod overlayButtonMod = null ;
changeRuleset ( 0 ) ;
AddStep ( "set mod externally" , ( ) = > { SelectedMods . Value = new [ ] { external } ; } ) ;
AddAssert ( "ensure button is selected" , ( ) = >
{
var button = modSelect . GetModButton ( SelectedMods . Value . Single ( ) ) ;
overlayButtonMod = button . SelectedMod ;
return overlayButtonMod . GetType ( ) = = external . GetType ( ) ;
} ) ;
// Right now, when an external change occurs, the ModSelectOverlay will replace the global instance with its own
AddAssert ( "mod instance doesn't match" , ( ) = > external ! = overlayButtonMod ) ;
AddAssert ( "one mod present in global selected" , ( ) = > SelectedMods . Value . Count = = 1 ) ;
AddAssert ( "globally selected matches button's mod instance" , ( ) = > SelectedMods . Value . Contains ( overlayButtonMod ) ) ;
AddAssert ( "globally selected doesn't contain original external change" , ( ) = > ! SelectedMods . Value . Contains ( external ) ) ;
}
2021-02-02 19:27:41 +08:00
[Test]
public void TestNonStacked ( )
{
changeRuleset ( 0 ) ;
AddStep ( "create overlay" , ( ) = > createDisplay ( ( ) = > new TestNonStackedModSelectOverlay ( ) ) ) ;
AddStep ( "show" , ( ) = > modSelect . Show ( ) ) ;
AddAssert ( "ensure all buttons are spread out" , ( ) = > modSelect . ChildrenOfType < ModButton > ( ) . All ( m = > m . Mods . Length < = 1 ) ) ;
}
2021-02-02 19:47:50 +08:00
[Test]
public void TestChangeIsValidChangesButtonVisibility ( )
{
changeRuleset ( 0 ) ;
AddAssert ( "double time visible" , ( ) = > modSelect . ChildrenOfType < ModButton > ( ) . Any ( b = > b . Mods . Any ( m = > m is OsuModDoubleTime ) ) ) ;
AddStep ( "make double time invalid" , ( ) = > modSelect . IsValidMod = m = > ! ( m is OsuModDoubleTime ) ) ;
2021-02-04 18:29:48 +08:00
AddUntilStep ( "double time not visible" , ( ) = > modSelect . ChildrenOfType < ModButton > ( ) . All ( b = > ! b . Mods . Any ( m = > m is OsuModDoubleTime ) ) ) ;
2021-02-02 19:47:50 +08:00
AddAssert ( "nightcore still visible" , ( ) = > modSelect . ChildrenOfType < ModButton > ( ) . Any ( b = > b . Mods . Any ( m = > m is OsuModNightcore ) ) ) ;
AddStep ( "make double time valid again" , ( ) = > modSelect . IsValidMod = m = > true ) ;
2021-02-04 18:29:48 +08:00
AddUntilStep ( "double time visible" , ( ) = > modSelect . ChildrenOfType < ModButton > ( ) . Any ( b = > b . Mods . Any ( m = > m is OsuModDoubleTime ) ) ) ;
2021-02-02 19:47:50 +08:00
AddAssert ( "nightcore still visible" , ( ) = > modSelect . ChildrenOfType < ModButton > ( ) . Any ( b = > b . Mods . Any ( m = > m is OsuModNightcore ) ) ) ;
}
2021-02-02 19:50:54 +08:00
[Test]
public void TestChangeIsValidPreservesSelection ( )
{
changeRuleset ( 0 ) ;
AddStep ( "select DT + HD" , ( ) = > SelectedMods . Value = new Mod [ ] { new OsuModDoubleTime ( ) , new OsuModHidden ( ) } ) ;
AddAssert ( "DT + HD selected" , ( ) = > modSelect . ChildrenOfType < ModButton > ( ) . Count ( b = > b . Selected ) = = 2 ) ;
AddStep ( "make NF invalid" , ( ) = > modSelect . IsValidMod = m = > ! ( m is ModNoFail ) ) ;
AddAssert ( "DT + HD still selected" , ( ) = > modSelect . ChildrenOfType < ModButton > ( ) . Count ( b = > b . Selected ) = = 2 ) ;
}
2018-04-13 17:19:50 +08:00
private void testSingleMod ( Mod mod )
{
selectNext ( mod ) ;
checkSelected ( mod ) ;
selectPrevious ( mod ) ;
checkNotSelected ( mod ) ;
selectNext ( mod ) ;
selectNext ( mod ) ;
checkNotSelected ( mod ) ;
selectPrevious ( mod ) ;
selectPrevious ( mod ) ;
checkNotSelected ( mod ) ;
}
private void testMultiMod ( MultiMod multiMod )
{
foreach ( var mod in multiMod . Mods )
{
selectNext ( mod ) ;
checkSelected ( mod ) ;
}
for ( int index = multiMod . Mods . Length - 1 ; index > = 0 ; index - - )
selectPrevious ( multiMod . Mods [ index ] ) ;
foreach ( var mod in multiMod . Mods )
checkNotSelected ( mod ) ;
}
2018-07-05 10:32:09 +08:00
private void testUnimplementedMod ( Mod mod )
2018-04-13 17:19:50 +08:00
{
selectNext ( mod ) ;
checkNotSelected ( mod ) ;
}
private void testIncompatibleMods ( Mod modA , Mod modB )
{
selectNext ( modA ) ;
checkSelected ( modA ) ;
checkNotSelected ( modB ) ;
selectNext ( modB ) ;
checkSelected ( modB ) ;
checkNotSelected ( modA ) ;
selectPrevious ( modB ) ;
checkNotSelected ( modA ) ;
checkNotSelected ( modB ) ;
}
private void testDeselectAll ( IEnumerable < Mod > mods )
{
foreach ( var mod in mods )
selectNext ( mod ) ;
AddAssert ( "check for any selection" , ( ) = > modSelect . SelectedMods . Value . Any ( ) ) ;
2019-12-13 19:21:35 +08:00
AddStep ( "deselect all" , ( ) = > modSelect . DeselectAllButton . Action . Invoke ( ) ) ;
2018-04-13 17:19:50 +08:00
AddAssert ( "check for no selection" , ( ) = > ! modSelect . SelectedMods . Value . Any ( ) ) ;
}
2019-12-13 19:21:35 +08:00
private void testMultiplierTextColour ( Mod mod , Func < Color4 > getCorrectColour )
2018-04-13 17:19:50 +08:00
{
2019-12-13 19:21:35 +08:00
checkLabelColor ( ( ) = > Color4 . White ) ;
2018-04-13 17:19:50 +08:00
selectNext ( mod ) ;
2019-03-19 16:24:26 +08:00
AddWaitStep ( "wait for changing colour" , 1 ) ;
2019-12-13 19:21:35 +08:00
checkLabelColor ( getCorrectColour ) ;
2018-04-13 17:19:50 +08:00
selectPrevious ( mod ) ;
2019-03-19 16:24:26 +08:00
AddWaitStep ( "wait for changing colour" , 1 ) ;
2019-12-13 19:21:35 +08:00
checkLabelColor ( ( ) = > Color4 . White ) ;
2018-04-13 17:19:50 +08:00
}
2020-07-15 18:45:48 +08:00
private void testModsWithSameBaseType ( Mod modA , Mod modB )
{
selectNext ( modA ) ;
checkSelected ( modA ) ;
selectNext ( modB ) ;
checkSelected ( modB ) ;
// Backwards
selectPrevious ( modA ) ;
checkSelected ( modA ) ;
}
2018-04-13 17:19:50 +08:00
private void selectNext ( Mod mod ) = > AddStep ( $"left click {mod.Name}" , ( ) = > modSelect . GetModButton ( mod ) ? . SelectNext ( 1 ) ) ;
private void selectPrevious ( Mod mod ) = > AddStep ( $"right click {mod.Name}" , ( ) = > modSelect . GetModButton ( mod ) ? . SelectNext ( - 1 ) ) ;
private void checkSelected ( Mod mod )
{
AddAssert ( $"check {mod.Name} is selected" , ( ) = >
{
var button = modSelect . GetModButton ( mod ) ;
return modSelect . SelectedMods . Value . Single ( m = > m . Name = = mod . Name ) ! = null & & button . SelectedMod . GetType ( ) = = mod . GetType ( ) & & button . Selected ;
} ) ;
}
2019-12-13 19:21:35 +08:00
private void changeRuleset ( int? id )
2019-06-20 23:12:39 +08:00
{
2019-12-13 19:35:34 +08:00
AddStep ( $"change ruleset to {(id?.ToString() ?? " none ")}" , ( ) = > { Ruleset . Value = rulesets . AvailableRulesets . FirstOrDefault ( r = > r . ID = = id ) ; } ) ;
2019-06-21 16:51:25 +08:00
waitForLoad ( ) ;
2019-06-20 23:12:39 +08:00
}
2019-06-21 16:51:25 +08:00
private void waitForLoad ( ) = >
AddUntilStep ( "wait for icons to load" , ( ) = > modSelect . AllLoaded ) ;
2018-04-13 17:19:50 +08:00
private void checkNotSelected ( Mod mod )
{
AddAssert ( $"check {mod.Name} is not selected" , ( ) = >
{
var button = modSelect . GetModButton ( mod ) ;
return modSelect . SelectedMods . Value . All ( m = > m . GetType ( ) ! = mod . GetType ( ) ) & & button . SelectedMod ? . GetType ( ) ! = mod . GetType ( ) ;
} ) ;
}
2019-12-13 19:21:35 +08:00
private void checkLabelColor ( Func < Color4 > getColour ) = > AddAssert ( "check label has expected colour" , ( ) = > modSelect . MultiplierLabel . Colour . AverageColour = = getColour ( ) ) ;
2018-04-13 17:19:50 +08:00
2021-02-02 19:27:41 +08:00
private void createDisplay ( Func < TestModSelectOverlay > createOverlayFunc )
{
Children = new Drawable [ ]
{
modSelect = createOverlayFunc ( ) . With ( d = >
{
d . Origin = Anchor . BottomCentre ;
d . Anchor = Anchor . BottomCentre ;
d . SelectedMods . BindTarget = SelectedMods ;
} ) ,
modDisplay = new ModDisplay
{
Anchor = Anchor . TopRight ,
Origin = Anchor . TopRight ,
AutoSizeAxes = Axes . Both ,
Position = new Vector2 ( - 5 , 25 ) ,
Current = { BindTarget = modSelect . SelectedMods }
}
} ;
}
2021-02-05 15:42:35 +08:00
private class TestModSelectOverlay : LocalPlayerModSelectOverlay
2018-04-13 17:19:50 +08:00
{
2019-04-10 16:13:12 +08:00
public new Bindable < IReadOnlyList < Mod > > SelectedMods = > base . SelectedMods ;
2018-08-07 13:49:44 +08:00
2019-06-20 23:12:39 +08:00
public bool AllLoaded = > ModSectionsContainer . Children . All ( c = > c . ModIconsLoaded ) ;
2021-02-04 18:55:15 +08:00
public new FillFlowContainer < ModSection > ModSectionsContainer = >
base . ModSectionsContainer ;
2018-04-13 17:19:50 +08:00
public ModButton GetModButton ( Mod mod )
{
var section = ModSectionsContainer . Children . Single ( s = > s . ModType = = mod . Type ) ;
return section . ButtonsContainer . OfType < ModButton > ( ) . Single ( b = > b . Mods . Any ( m = > m . GetType ( ) = = mod . GetType ( ) ) ) ;
}
public new OsuSpriteText MultiplierLabel = > base . MultiplierLabel ;
public new TriangleButton DeselectAllButton = > base . DeselectAllButton ;
public new Color4 LowMultiplierColour = > base . LowMultiplierColour ;
public new Color4 HighMultiplierColour = > base . HighMultiplierColour ;
}
2021-02-02 19:27:41 +08:00
private class TestNonStackedModSelectOverlay : TestModSelectOverlay
{
protected override bool Stacked = > false ;
}
2018-04-13 17:19:50 +08:00
}
}