2022-03-27 05:43:17 +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 ;
using System.Collections.Generic ;
using System.Diagnostics ;
using System.Linq ;
using osu.Framework.Allocation ;
using osu.Framework.Bindables ;
using osu.Framework.Graphics ;
using osu.Framework.Graphics.Containers ;
using osu.Framework.Graphics.Shapes ;
using osu.Framework.Input.Events ;
using osu.Framework.Layout ;
using osu.Game.Configuration ;
using osu.Game.Graphics.Containers ;
using osu.Game.Graphics.UserInterface ;
using osu.Game.Rulesets.Mods ;
using osuTK ;
using osuTK.Input ;
namespace osu.Game.Overlays.Mods
{
public class ModSelectScreen : OsuFocusedOverlayContainer
{
[Cached]
private OverlayColourProvider colourProvider = new OverlayColourProvider ( OverlayColourScheme . Green ) ;
[Cached]
2022-03-27 06:21:17 +08:00
public Bindable < IReadOnlyList < Mod > > SelectedMods { get ; private set ; } = new Bindable < IReadOnlyList < Mod > > ( Array . Empty < Mod > ( ) ) ;
2022-03-27 05:43:17 +08:00
2022-04-05 02:02:47 +08:00
protected override bool StartHidden = > true ;
2022-03-27 05:43:17 +08:00
private readonly BindableBool customisationVisible = new BindableBool ( ) ;
private DifficultyMultiplierDisplay multiplierDisplay ;
private ModSettingsArea modSettingsArea ;
private FillFlowContainer < ModColumn > columnFlow ;
private GridContainer grid ;
private Container mainContent ;
2022-04-04 14:45:44 +08:00
private PopupScreenTitle header ;
private Container footer ;
2022-03-27 05:43:17 +08:00
[BackgroundDependencyLoader]
private void load ( )
{
RelativeSizeAxes = Axes . Both ;
RelativePositionAxes = Axes . Both ;
InternalChildren = new Drawable [ ]
{
mainContent = new Container
{
Origin = Anchor . BottomCentre ,
Anchor = Anchor . BottomCentre ,
RelativeSizeAxes = Axes . Both ,
Children = new Drawable [ ]
{
grid = new GridContainer
{
RelativeSizeAxes = Axes . Both ,
RowDimensions = new [ ]
{
new Dimension ( GridSizeMode . AutoSize ) ,
new Dimension ( GridSizeMode . AutoSize ) ,
new Dimension ( ) ,
new Dimension ( GridSizeMode . Absolute , 75 ) ,
} ,
Content = new [ ]
{
new Drawable [ ]
{
2022-04-04 14:45:44 +08:00
header = new PopupScreenTitle
2022-03-27 05:43:17 +08:00
{
Anchor = Anchor . TopCentre ,
Origin = Anchor . TopCentre ,
Title = "Mod Select" ,
Description = "Mods provide different ways to enjoy gameplay. Some have an effect on the score you can achieve during ranked play. Others are just for fun." ,
Close = Hide
}
} ,
new Drawable [ ]
{
new Container
{
RelativeSizeAxes = Axes . X ,
AutoSizeAxes = Axes . Y ,
Padding = new MarginPadding { Horizontal = 100 , Vertical = 10 } ,
Child = multiplierDisplay = new DifficultyMultiplierDisplay
{
Anchor = Anchor . CentreRight ,
Origin = Anchor . CentreRight
}
}
} ,
new Drawable [ ]
{
2022-04-04 19:42:12 +08:00
new Container
2022-03-27 05:43:17 +08:00
{
RelativeSizeAxes = Axes . Both ,
RelativePositionAxes = Axes . Both ,
Children = new Drawable [ ]
{
new OsuScrollContainer ( Direction . Horizontal )
{
RelativeSizeAxes = Axes . Both ,
Masking = false ,
2022-04-05 15:40:09 +08:00
ClampExtension = 100 ,
2022-03-27 05:43:17 +08:00
ScrollbarOverlapsContent = false ,
Child = columnFlow = new ModColumnContainer
{
2022-04-05 16:14:59 +08:00
Direction = FillDirection . Horizontal ,
2022-03-27 05:43:17 +08:00
RelativeSizeAxes = Axes . Y ,
AutoSizeAxes = Axes . X ,
Spacing = new Vector2 ( 10 , 0 ) ,
2022-04-05 15:39:02 +08:00
Margin = new MarginPadding { Horizontal = 70 } ,
2022-03-27 05:43:17 +08:00
Children = new [ ]
{
new ModColumn ( ModType . DifficultyReduction , false , new [ ] { Key . Q , Key . W , Key . E , Key . R , Key . T , Key . Y , Key . U , Key . I , Key . O , Key . P } ) ,
new ModColumn ( ModType . DifficultyIncrease , false , new [ ] { Key . A , Key . S , Key . D , Key . F , Key . G , Key . H , Key . J , Key . K , Key . L } ) ,
new ModColumn ( ModType . Automation , false , new [ ] { Key . Z , Key . X , Key . C , Key . V , Key . B , Key . N , Key . M } ) ,
new ModColumn ( ModType . Conversion , false ) ,
new ModColumn ( ModType . Fun , false )
}
}
}
}
}
} ,
new [ ] { Empty ( ) }
}
} ,
2022-04-04 14:45:44 +08:00
footer = new Container
2022-03-27 05:43:17 +08:00
{
RelativeSizeAxes = Axes . X ,
AutoSizeAxes = Axes . Y ,
Anchor = Anchor . BottomCentre ,
Origin = Anchor . BottomCentre ,
Children = new Drawable [ ]
{
new Box
{
RelativeSizeAxes = Axes . X ,
Height = 50 ,
Anchor = Anchor . BottomCentre ,
Origin = Anchor . BottomCentre ,
Colour = colourProvider . Background5
} ,
new ShearedToggleButton ( 200 )
{
Anchor = Anchor . BottomLeft ,
Origin = Anchor . BottomLeft ,
Margin = new MarginPadding { Vertical = 14 , Left = 70 } ,
Text = "Mod Customisation" ,
Active = { BindTarget = customisationVisible }
}
}
} ,
new ClickToReturnContainer
{
RelativeSizeAxes = Axes . Both ,
HandleMouse = { BindTarget = customisationVisible } ,
OnClicked = ( ) = > customisationVisible . Value = false
}
}
} ,
modSettingsArea = new ModSettingsArea
{
Anchor = Anchor . BottomCentre ,
2022-04-05 02:02:47 +08:00
Origin = Anchor . BottomCentre ,
Height = 0
2022-03-27 05:43:17 +08:00
}
} ;
columnFlow . Shear = new Vector2 ( ModPanel . SHEAR_X , 0 ) ;
}
protected override void LoadComplete ( )
{
base . LoadComplete ( ) ;
2022-03-27 06:21:17 +08:00
( ( IBindable < IReadOnlyList < Mod > > ) modSettingsArea . SelectedMods ) . BindTo ( SelectedMods ) ;
2022-03-28 06:16:10 +08:00
2022-03-27 06:21:17 +08:00
SelectedMods . BindValueChanged ( val = >
2022-03-27 05:43:17 +08:00
{
updateMultiplier ( ) ;
updateCustomisation ( val ) ;
2022-03-28 06:16:10 +08:00
updateSelectionFromBindable ( ) ;
2022-03-27 05:43:17 +08:00
} , true ) ;
2022-03-28 06:16:10 +08:00
foreach ( var column in columnFlow )
{
column . SelectedMods . BindValueChanged ( _ = > updateBindableFromSelection ( ) ) ;
}
2022-03-27 05:43:17 +08:00
customisationVisible . BindValueChanged ( _ = > updateCustomisationVisualState ( ) , true ) ;
}
private void updateMultiplier ( )
{
double multiplier = 1.0 ;
2022-03-27 06:21:17 +08:00
foreach ( var mod in SelectedMods . Value )
2022-03-27 05:43:17 +08:00
multiplier * = mod . ScoreMultiplier ;
multiplierDisplay . Current . Value = multiplier ;
}
private void updateCustomisation ( ValueChangedEvent < IReadOnlyList < Mod > > valueChangedEvent )
{
bool anyCustomisableMod = false ;
bool anyModWithRequiredCustomisationAdded = false ;
2022-03-27 06:21:17 +08:00
foreach ( var mod in SelectedMods . Value )
2022-03-27 05:43:17 +08:00
{
anyCustomisableMod | = mod . GetSettingsSourceProperties ( ) . Any ( ) ;
anyModWithRequiredCustomisationAdded | = ! valueChangedEvent . OldValue . Contains ( mod ) & & mod . RequiresConfiguration ;
}
if ( anyCustomisableMod )
{
customisationVisible . Disabled = false ;
if ( anyModWithRequiredCustomisationAdded & & ! customisationVisible . Value )
customisationVisible . Value = true ;
}
else
{
if ( customisationVisible . Value )
customisationVisible . Value = false ;
customisationVisible . Disabled = true ;
}
}
private void updateCustomisationVisualState ( )
{
2022-04-05 15:56:24 +08:00
const double transition_duration = 300 ;
2022-04-04 14:50:40 +08:00
grid . FadeColour ( customisationVisible . Value ? Colour4 . Gray : Colour4 . White , transition_duration , Easing . InOutCubic ) ;
2022-03-27 05:43:17 +08:00
float modAreaHeight = customisationVisible . Value ? ModSettingsArea . HEIGHT : 0 ;
2022-04-04 14:50:40 +08:00
modSettingsArea . ResizeHeightTo ( modAreaHeight , transition_duration , Easing . InOutCubic ) ;
mainContent . TransformTo ( nameof ( Margin ) , new MarginPadding { Bottom = modAreaHeight } , transition_duration , Easing . InOutCubic ) ;
2022-03-27 05:43:17 +08:00
}
2022-03-28 06:16:10 +08:00
private bool selectionBindableSyncInProgress ;
private void updateSelectionFromBindable ( )
{
if ( selectionBindableSyncInProgress )
return ;
selectionBindableSyncInProgress = true ;
foreach ( var column in columnFlow )
column . SelectedMods . Value = SelectedMods . Value . Where ( mod = > mod . Type = = column . ModType ) . ToArray ( ) ;
selectionBindableSyncInProgress = false ;
}
private void updateBindableFromSelection ( )
{
if ( selectionBindableSyncInProgress )
return ;
selectionBindableSyncInProgress = true ;
SelectedMods . Value = columnFlow . SelectMany ( column = > column . SelectedMods . Value ) . ToArray ( ) ;
selectionBindableSyncInProgress = false ;
}
2022-03-27 05:43:17 +08:00
protected override void PopIn ( )
{
2022-04-04 14:45:44 +08:00
const double fade_in_duration = 500 ;
2022-03-27 05:43:17 +08:00
base . PopIn ( ) ;
2022-04-04 14:45:44 +08:00
header . MoveToY ( 0 , fade_in_duration , Easing . OutQuint ) ;
footer . MoveToY ( 0 , fade_in_duration , Easing . OutQuint ) ;
this . FadeIn ( fade_in_duration , Easing . OutQuint ) ;
2022-03-27 05:43:17 +08:00
}
protected override void PopOut ( )
{
2022-04-04 14:45:44 +08:00
const double fade_out_duration = 500 ;
2022-03-27 05:43:17 +08:00
base . PopOut ( ) ;
2022-04-04 14:45:44 +08:00
header . MoveToY ( - header . DrawHeight , fade_out_duration , Easing . OutQuint ) ;
footer . MoveToY ( footer . DrawHeight , fade_out_duration , Easing . OutQuint ) ;
this . FadeOut ( fade_out_duration , Easing . OutQuint ) ;
2022-03-27 05:43:17 +08:00
}
private class ModColumnContainer : FillFlowContainer < ModColumn >
{
private readonly LayoutValue drawSizeLayout = new LayoutValue ( Invalidation . DrawSize ) ;
public ModColumnContainer ( )
{
AddLayout ( drawSizeLayout ) ;
}
public override void Add ( ModColumn column )
{
base . Add ( column ) ;
Debug . Assert ( column ! = null ) ;
column . Shear = Vector2 . Zero ;
}
protected override void Update ( )
{
base . Update ( ) ;
if ( ! drawSizeLayout . IsValid )
{
Padding = new MarginPadding
{
Left = DrawHeight * ModPanel . SHEAR_X ,
Bottom = 10
} ;
drawSizeLayout . Validate ( ) ;
}
}
}
private class ClickToReturnContainer : Container
{
public BindableBool HandleMouse { get ; } = new BindableBool ( ) ;
public Action OnClicked { get ; set ; }
protected override bool Handle ( UIEvent e )
{
if ( ! HandleMouse . Value )
return base . Handle ( e ) ;
switch ( e )
{
case ClickEvent _ :
OnClicked ? . Invoke ( ) ;
return true ;
case MouseEvent _ :
return true ;
}
return base . Handle ( e ) ;
}
}
}
}