1
0
mirror of https://github.com/ppy/osu.git synced 2025-02-15 16:22:55 +08:00

Merge pull request #17853 from bdach/mod-overlay/extension-points

Add extension points to new mod select screen required to replace old design
This commit is contained in:
Dean Herbert 2022-04-21 13:07:18 +09:00 committed by GitHub
commit fa590c1df1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 183 additions and 47 deletions

View File

@ -0,0 +1,37 @@
// 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.Linq;
using NUnit.Framework;
using osu.Framework.Graphics.Containers;
using osu.Framework.Testing;
using osu.Game.Overlays.Mods;
using osu.Game.Screens.OnlinePlay;
namespace osu.Game.Tests.Visual.Multiplayer
{
public class TestSceneFreeModSelectScreen : MultiplayerTestScene
{
[Test]
public void TestFreeModSelect()
{
FreeModSelectScreen freeModSelectScreen = null;
AddStep("create free mod select screen", () => Child = freeModSelectScreen = new FreeModSelectScreen
{
State = { Value = Visibility.Visible }
});
AddAssert("all visible mods are playable",
() => this.ChildrenOfType<ModPanel>()
.Where(panel => panel.IsPresent)
.All(panel => panel.Mod.HasImplementation && panel.Mod.UserPlayable));
AddToggleStep("toggle visibility", visible =>
{
if (freeModSelectScreen != null)
freeModSelectScreen.State.Value = visible ? Visibility.Visible : Visibility.Hidden;
});
}
}
}

View File

@ -23,7 +23,7 @@ namespace osu.Game.Tests.Visual.UserInterface
[Resolved]
private RulesetStore rulesetStore { get; set; }
private ModSelectScreen modSelectScreen;
private UserModSelectScreen modSelectScreen;
[SetUpSteps]
public void SetUpSteps()
@ -35,7 +35,7 @@ namespace osu.Game.Tests.Visual.UserInterface
private void createScreen()
{
AddStep("create screen", () => Child = modSelectScreen = new ModSelectScreen
AddStep("create screen", () => Child = modSelectScreen = new UserModSelectScreen
{
RelativeSizeAxes = Axes.Both,
State = { Value = Visibility.Visible },

View File

@ -37,7 +37,9 @@ namespace osu.Game.Overlays.Mods
private void updateIncompatibility()
{
incompatible.Value = selectedMods.Value.Count > 0 && !selectedMods.Value.Contains(Mod) && !ModUtils.CheckCompatibleSet(selectedMods.Value.Append(Mod));
incompatible.Value = selectedMods.Value.Count > 0
&& selectedMods.Value.All(selected => selected.GetType() != Mod.GetType())
&& !ModUtils.CheckCompatibleSet(selectedMods.Value.Append(Mod));
}
protected override void UpdateState()
@ -46,8 +48,8 @@ namespace osu.Game.Overlays.Mods
if (incompatible.Value)
{
Colour4 backgroundColour = ColourProvider.Background5;
Colour4 textBackgroundColour = ColourProvider.Background4;
Colour4 backgroundColour = ColourProvider.Background6;
Colour4 textBackgroundColour = ColourProvider.Background5;
Content.TransformTo(nameof(BorderColour), ColourInfo.GradientVertical(backgroundColour, textBackgroundColour), TRANSITION_DURATION, Easing.OutQuint);
Background.FadeColour(backgroundColour, TRANSITION_DURATION, Easing.OutQuint);

View File

@ -54,6 +54,8 @@ namespace osu.Game.Overlays.Mods
public Bindable<IReadOnlyList<Mod>> SelectedMods = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>());
protected virtual ModPanel CreateModPanel(Mod mod) => new ModPanel(mod);
private readonly Key[]? toggleKeys;
private readonly Bindable<Dictionary<ModType, IReadOnlyList<Mod>>> availableMods = new Bindable<Dictionary<ModType, IReadOnlyList<Mod>>>();
@ -258,10 +260,7 @@ namespace osu.Game.Overlays.Mods
cancellationTokenSource?.Cancel();
var panels = newMods.Select(mod => new ModPanel(mod)
{
Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0)
});
var panels = newMods.Select(mod => CreateModPanel(mod).With(panel => panel.Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0)));
Task? loadTask;

View File

@ -158,7 +158,7 @@ namespace osu.Game.Overlays.Mods
playStateChangeSamples();
UpdateState();
});
Filtered.BindValueChanged(_ => updateFilterState());
Filtered.BindValueChanged(_ => updateFilterState(), true);
UpdateState();
FinishTransforms(true);

View File

@ -1,6 +1,8 @@
// 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.
#nullable enable
using System;
using System.Collections.Generic;
using System.Diagnostics;
@ -20,18 +22,44 @@ using osuTK.Input;
namespace osu.Game.Overlays.Mods
{
public class ModSelectScreen : ShearedOverlayContainer
public abstract class ModSelectScreen : ShearedOverlayContainer
{
protected override OverlayColourScheme ColourScheme => OverlayColourScheme.Green;
[Cached]
public Bindable<IReadOnlyList<Mod>> SelectedMods { get; private set; } = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>());
private Func<Mod, bool> isValidMod = m => true;
public Func<Mod, bool> IsValidMod
{
get => isValidMod;
set
{
isValidMod = value ?? throw new ArgumentNullException(nameof(value));
if (IsLoaded)
updateAvailableMods();
}
}
/// <summary>
/// Whether configurable <see cref="Mod"/>s can be configured by the local user.
/// </summary>
protected virtual bool AllowCustomisation => true;
/// <summary>
/// Whether the total score multiplier calculated from the current selected set of mods should be shown.
/// </summary>
protected virtual bool ShowTotalMultiplier => true;
protected virtual ModColumn CreateModColumn(ModType modType, Key[]? toggleKeys = null) => new ModColumn(modType, false, toggleKeys);
private readonly BindableBool customisationVisible = new BindableBool();
private DifficultyMultiplierDisplay multiplierDisplay;
private ModSettingsArea modSettingsArea;
private FillFlowContainer<ModColumn> columnFlow;
private DifficultyMultiplierDisplay? multiplierDisplay;
private ModSettingsArea modSettingsArea = null!;
private FillFlowContainer<ModColumn> columnFlow = null!;
[BackgroundDependencyLoader]
private void load()
@ -57,29 +85,12 @@ namespace osu.Game.Overlays.Mods
MainAreaContent.AddRange(new Drawable[]
{
new Container
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
AutoSizeAxes = Axes.X,
Height = DifficultyMultiplierDisplay.HEIGHT,
Margin = new MarginPadding
{
Horizontal = 100,
},
Child = multiplierDisplay = new DifficultyMultiplierDisplay
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre
}
},
new Container
{
Padding = new MarginPadding
{
Top = DifficultyMultiplierDisplay.HEIGHT + PADDING,
Top = (ShowTotalMultiplier ? DifficultyMultiplierDisplay.HEIGHT : 0) + PADDING,
},
Depth = float.MaxValue,
RelativeSizeAxes = Axes.Both,
RelativePositionAxes = Axes.Both,
Children = new Drawable[]
@ -100,11 +111,11 @@ namespace osu.Game.Overlays.Mods
Margin = new MarginPadding { Right = 70 },
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)
CreateModColumn(ModType.DifficultyReduction, new[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P }),
CreateModColumn(ModType.DifficultyIncrease, new[] { Key.A, Key.S, Key.D, Key.F, Key.G, Key.H, Key.J, Key.K, Key.L }),
CreateModColumn(ModType.Automation, new[] { Key.Z, Key.X, Key.C, Key.V, Key.B, Key.N, Key.M }),
CreateModColumn(ModType.Conversion),
CreateModColumn(ModType.Fun)
}
}
}
@ -112,14 +123,34 @@ namespace osu.Game.Overlays.Mods
}
});
Footer.Add(new ShearedToggleButton(200)
if (ShowTotalMultiplier)
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Margin = new MarginPadding { Vertical = PADDING, Left = 70 },
Text = "Mod Customisation",
Active = { BindTarget = customisationVisible }
});
MainAreaContent.Add(new Container
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
AutoSizeAxes = Axes.X,
Height = DifficultyMultiplierDisplay.HEIGHT,
Margin = new MarginPadding { Horizontal = 100 },
Child = multiplierDisplay = new DifficultyMultiplierDisplay
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre
},
});
}
if (AllowCustomisation)
{
Footer.Add(new ShearedToggleButton(200)
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Margin = new MarginPadding { Vertical = PADDING, Left = 70 },
Text = "Mod Customisation",
Active = { BindTarget = customisationVisible }
});
}
}
protected override void LoadComplete()
@ -141,10 +172,15 @@ namespace osu.Game.Overlays.Mods
}
customisationVisible.BindValueChanged(_ => updateCustomisationVisualState(), true);
updateAvailableMods();
}
private void updateMultiplier()
{
if (multiplierDisplay == null)
return;
double multiplier = 1.0;
foreach (var mod in SelectedMods.Value)
@ -153,8 +189,17 @@ namespace osu.Game.Overlays.Mods
multiplierDisplay.Current.Value = multiplier;
}
private void updateAvailableMods()
{
foreach (var column in columnFlow)
column.Filter = isValidMod;
}
private void updateCustomisation(ValueChangedEvent<IReadOnlyList<Mod>> valueChangedEvent)
{
if (!AllowCustomisation)
return;
bool anyCustomisableMod = false;
bool anyModWithRequiredCustomisationAdded = false;
@ -225,7 +270,7 @@ namespace osu.Game.Overlays.Mods
base.PopIn();
multiplierDisplay
multiplierDisplay?
.Delay(fade_in_duration * 0.65f)
.FadeIn(fade_in_duration / 2, Easing.OutQuint)
.ScaleTo(1, fade_in_duration, Easing.OutElastic);
@ -245,7 +290,7 @@ namespace osu.Game.Overlays.Mods
base.PopOut();
multiplierDisplay
multiplierDisplay?
.FadeOut(fade_out_duration / 2, Easing.OutQuint)
.ScaleTo(0.75f, fade_out_duration, Easing.OutQuint);
@ -297,7 +342,7 @@ namespace osu.Game.Overlays.Mods
{
public BindableBool HandleMouse { get; } = new BindableBool();
public Action OnClicked { get; set; }
public Action? OnClicked { get; set; }
protected override bool Handle(UIEvent e)
{

View File

@ -0,0 +1,24 @@
// 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 JetBrains.Annotations;
using osu.Game.Rulesets.Mods;
using osuTK.Input;
namespace osu.Game.Overlays.Mods
{
public class UserModSelectScreen : ModSelectScreen
{
protected override ModColumn CreateModColumn(ModType modType, Key[] toggleKeys = null) => new UserModColumn(modType, false, toggleKeys);
private class UserModColumn : ModColumn
{
public UserModColumn(ModType modType, bool allowBulkSelection, [CanBeNull] Key[] toggleKeys = null)
: base(modType, allowBulkSelection, toggleKeys)
{
}
protected override ModPanel CreateModPanel(Mod mod) => new IncompatibilityDisplayingModPanel(mod);
}
}
}

View File

@ -0,0 +1,29 @@
// 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 osu.Game.Overlays.Mods;
using osu.Game.Rulesets.Mods;
using osuTK.Input;
namespace osu.Game.Screens.OnlinePlay
{
public class FreeModSelectScreen : ModSelectScreen
{
protected override bool AllowCustomisation => false;
protected override bool ShowTotalMultiplier => false;
public new Func<Mod, bool> IsValidMod
{
get => base.IsValidMod;
set => base.IsValidMod = m => m.HasImplementation && m.UserPlayable && value.Invoke(m);
}
public FreeModSelectScreen()
{
IsValidMod = _ => true;
}
protected override ModColumn CreateModColumn(ModType modType, Key[] toggleKeys = null) => new ModColumn(modType, true, toggleKeys);
}
}