1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-14 20:32:55 +08:00

Deselect old incompatible mods if any on user mod select screen

This commit is contained in:
Bartłomiej Dach 2022-04-20 23:34:43 +02:00
parent 32722adba9
commit b7c11cdb8e
No known key found for this signature in database
GPG Key ID: BCECCD4FA41F6497
2 changed files with 36 additions and 12 deletions

View File

@ -168,7 +168,7 @@ namespace osu.Game.Overlays.Mods
foreach (var column in columnFlow) foreach (var column in columnFlow)
{ {
column.SelectedMods.BindValueChanged(_ => updateBindableFromSelection()); column.SelectedMods.BindValueChanged(updateBindableFromSelection);
} }
customisationVisible.BindValueChanged(_ => updateCustomisationVisualState(), true); customisationVisible.BindValueChanged(_ => updateCustomisationVisualState(), true);
@ -237,33 +237,36 @@ namespace osu.Game.Overlays.Mods
TopLevelContent.MoveToY(-modAreaHeight, transition_duration, Easing.InOutCubic); TopLevelContent.MoveToY(-modAreaHeight, transition_duration, Easing.InOutCubic);
} }
private bool selectionBindableSyncInProgress;
private void updateSelectionFromBindable() private void updateSelectionFromBindable()
{ {
if (selectionBindableSyncInProgress) // note that selectionBindableSyncInProgress is purposefully not checked here.
return; // this is because in the case of mod selection in solo gameplay, a user selection of a mod can actually lead to deselection of other incompatible mods.
// to synchronise state correctly, updateBindableFromSelection() computes the final mods (including incompatibility rules) and updates SelectedMods,
selectionBindableSyncInProgress = true; // and this method then runs unconditionally again to make sure the new visual selection accurately reflects the final set of selected mods.
// selectionBindableSyncInProgress ensures that mutual infinite recursion does not happen after that unconditional call.
foreach (var column in columnFlow) foreach (var column in columnFlow)
column.SelectedMods.Value = SelectedMods.Value.Where(mod => mod.Type == column.ModType).ToArray(); column.SelectedMods.Value = SelectedMods.Value.Where(mod => mod.Type == column.ModType).ToArray();
selectionBindableSyncInProgress = false;
} }
private void updateBindableFromSelection() private bool selectionBindableSyncInProgress;
private void updateBindableFromSelection(ValueChangedEvent<IReadOnlyList<Mod>> modSelectionChange)
{ {
if (selectionBindableSyncInProgress) if (selectionBindableSyncInProgress)
return; return;
selectionBindableSyncInProgress = true; selectionBindableSyncInProgress = true;
SelectedMods.Value = columnFlow.SelectMany(column => column.SelectedMods.Value).ToArray(); SelectedMods.Value = ComputeNewModsFromSelection(
modSelectionChange.NewValue.Except(modSelectionChange.OldValue),
modSelectionChange.OldValue.Except(modSelectionChange.NewValue));
selectionBindableSyncInProgress = false; selectionBindableSyncInProgress = false;
} }
protected virtual IReadOnlyList<Mod> ComputeNewModsFromSelection(IEnumerable<Mod> addedMods, IEnumerable<Mod> removedMods)
=> columnFlow.SelectMany(column => column.SelectedMods.Value).ToArray();
protected override void PopIn() protected override void PopIn()
{ {
const double fade_in_duration = 400; const double fade_in_duration = 400;

View File

@ -1,8 +1,11 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations; using JetBrains.Annotations;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Utils;
using osuTK.Input; using osuTK.Input;
namespace osu.Game.Overlays.Mods namespace osu.Game.Overlays.Mods
@ -11,6 +14,24 @@ namespace osu.Game.Overlays.Mods
{ {
protected override ModColumn CreateModColumn(ModType modType, Key[] toggleKeys = null) => new UserModColumn(modType, false, toggleKeys); protected override ModColumn CreateModColumn(ModType modType, Key[] toggleKeys = null) => new UserModColumn(modType, false, toggleKeys);
protected override IReadOnlyList<Mod> ComputeNewModsFromSelection(IEnumerable<Mod> addedMods, IEnumerable<Mod> removedMods)
{
IEnumerable<Mod> modsAfterRemoval = SelectedMods.Value.Except(removedMods).ToList();
// the preference is that all new mods should override potential incompatible old mods.
// in general that's a bit difficult to compute if more than one mod is added at a time,
// so be conservative and just remove all mods that aren't compatible with any one added mod.
foreach (var addedMod in addedMods)
{
if (!ModUtils.CheckCompatibleSet(modsAfterRemoval.Append(addedMod), out var invalidMods))
modsAfterRemoval = modsAfterRemoval.Except(invalidMods);
modsAfterRemoval = modsAfterRemoval.Append(addedMod).ToList();
}
return modsAfterRemoval.ToList();
}
private class UserModColumn : ModColumn private class UserModColumn : ModColumn
{ {
public UserModColumn(ModType modType, bool allowBulkSelection, [CanBeNull] Key[] toggleKeys = null) public UserModColumn(ModType modType, bool allowBulkSelection, [CanBeNull] Key[] toggleKeys = null)