1
0
mirror of https://github.com/ppy/osu.git synced 2024-09-21 16:07:24 +08:00

Merge pull request #27214 from Givikap120/freemod_mapinfo_fix

Fix mod selection in online-play rooms not accounting for mods of selected item
This commit is contained in:
Bartłomiej Dach 2024-03-07 12:45:14 +01:00 committed by GitHub
commit fba44e67a0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 140 additions and 27 deletions

View File

@ -22,6 +22,7 @@ using osu.Game.Overlays;
using osu.Game.Overlays.Dialog;
using osu.Game.Overlays.Mods;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Taiko;
@ -286,6 +287,41 @@ namespace osu.Game.Tests.Visual.Multiplayer
});
}
[Test]
[FlakyTest] // See above
public void TestModSelectOverlay()
{
AddStep("add playlist item", () =>
{
SelectedRoom.Value.Playlist.Add(new PlaylistItem(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo)
{
RulesetID = new OsuRuleset().RulesetInfo.OnlineID,
RequiredMods = new[]
{
new APIMod(new OsuModDoubleTime { SpeedChange = { Value = 2.0 } }),
new APIMod(new OsuModStrictTracking()),
},
AllowedMods = new[]
{
new APIMod(new OsuModFlashlight()),
}
});
});
ClickButtonWhenEnabled<MultiplayerMatchSettingsOverlay.CreateOrUpdateButton>();
AddUntilStep("wait for join", () => RoomJoined);
ClickButtonWhenEnabled<RoomSubScreen.UserModSelectButton>();
AddAssert("mod select shows unranked", () => screen.UserModsSelectOverlay.ChildrenOfType<RankingInformationDisplay>().Single().Ranked.Value == false);
AddAssert("score multiplier = 1.20", () => screen.UserModsSelectOverlay.ChildrenOfType<RankingInformationDisplay>().Single().ModMultiplier.Value, () => Is.EqualTo(1.2).Within(0.01));
AddStep("select flashlight", () => screen.UserModsSelectOverlay.ChildrenOfType<ModPanel>().Single(m => m.Mod is ModFlashlight).TriggerClick());
AddAssert("score multiplier = 1.35", () => screen.UserModsSelectOverlay.ChildrenOfType<RankingInformationDisplay>().Single().ModMultiplier.Value, () => Is.EqualTo(1.35).Within(0.01));
AddStep("change flashlight setting", () => ((OsuModFlashlight)screen.UserModsSelectOverlay.SelectedMods.Value.Single()).FollowDelay.Value = 1200);
AddAssert("score multiplier = 1.20", () => screen.UserModsSelectOverlay.ChildrenOfType<RankingInformationDisplay>().Single().ModMultiplier.Value, () => Is.EqualTo(1.2).Within(0.01));
}
private partial class TestMultiplayerMatchSubScreen : MultiplayerMatchSubScreen
{
[Resolved(canBeNull: true)]

View File

@ -57,6 +57,7 @@ namespace osu.Game.Tests.Visual.UserInterface
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Mods = { BindTarget = SelectedMods },
});
AddStep("set beatmap", () =>

View File

@ -40,8 +40,7 @@ namespace osu.Game.Overlays.Mods
public Bindable<IBeatmapInfo?> BeatmapInfo { get; } = new Bindable<IBeatmapInfo?>();
[Resolved]
private Bindable<IReadOnlyList<Mod>> mods { get; set; } = null!;
public Bindable<IReadOnlyList<Mod>> Mods { get; } = new Bindable<IReadOnlyList<Mod>>();
public BindableBool Collapsed { get; } = new BindableBool(true);
@ -53,7 +52,7 @@ namespace osu.Game.Overlays.Mods
[Resolved]
private OsuGameBase game { get; set; } = null!;
private IBindable<RulesetInfo> gameRuleset = null!;
protected IBindable<RulesetInfo> GameRuleset = null!;
private CancellationTokenSource? cancellationSource;
private IBindable<StarDifficulty?> starDifficulty = null!;
@ -101,15 +100,15 @@ namespace osu.Game.Overlays.Mods
{
base.LoadComplete();
mods.BindValueChanged(_ =>
Mods.BindValueChanged(_ =>
{
modSettingChangeTracker?.Dispose();
modSettingChangeTracker = new ModSettingChangeTracker(mods.Value);
modSettingChangeTracker = new ModSettingChangeTracker(Mods.Value);
modSettingChangeTracker.SettingChanged += _ => updateValues();
updateValues();
}, true);
BeatmapInfo.BindValueChanged(_ => updateValues(), true);
BeatmapInfo.BindValueChanged(_ => updateValues());
Collapsed.BindValueChanged(_ =>
{
@ -118,11 +117,12 @@ namespace osu.Game.Overlays.Mods
updateCollapsedState();
});
gameRuleset = game.Ruleset.GetBoundCopy();
gameRuleset.BindValueChanged(_ => updateValues());
GameRuleset = game.Ruleset.GetBoundCopy();
GameRuleset.BindValueChanged(_ => updateValues());
BeatmapInfo.BindValueChanged(_ => updateValues(), true);
BeatmapInfo.BindValueChanged(_ => updateValues());
updateValues();
updateCollapsedState();
}
@ -166,17 +166,17 @@ namespace osu.Game.Overlays.Mods
});
double rate = 1;
foreach (var mod in mods.Value.OfType<IApplicableToRate>())
foreach (var mod in Mods.Value.OfType<IApplicableToRate>())
rate = mod.ApplyToRate(0, rate);
bpmDisplay.Current.Value = FormatUtils.RoundBPM(BeatmapInfo.Value.BPM, rate);
BeatmapDifficulty originalDifficulty = new BeatmapDifficulty(BeatmapInfo.Value.Difficulty);
foreach (var mod in mods.Value.OfType<IApplicableToDifficulty>())
foreach (var mod in Mods.Value.OfType<IApplicableToDifficulty>())
mod.ApplyToDifficulty(originalDifficulty);
Ruleset ruleset = gameRuleset.Value.CreateInstance();
Ruleset ruleset = GameRuleset.Value.CreateInstance();
BeatmapDifficulty adjustedDifficulty = ruleset.GetRateAdjustedDisplayDifficulty(originalDifficulty, rate);
TooltipContent = new AdjustedAttributesTooltip.Data(originalDifficulty, adjustedDifficulty);
@ -195,7 +195,7 @@ namespace osu.Game.Overlays.Mods
RightContent.FadeTo(Collapsed.Value && !IsHovered ? 0 : 1, transition_duration, Easing.OutQuint);
}
private partial class BPMDisplay : RollingCounter<int>
public partial class BPMDisplay : RollingCounter<int>
{
protected override double RollingDuration => 250;

View File

@ -43,6 +43,14 @@ namespace osu.Game.Overlays.Mods
[Cached]
public Bindable<IReadOnlyList<Mod>> SelectedMods { get; private set; } = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>());
/// <summary>
/// Contains a list of mods which <see cref="ModSelectOverlay"/> should read from to display effects on the selected beatmap.
/// </summary>
/// <remarks>
/// This is different from <see cref="SelectedMods"/> in screens like online-play rooms, where there are required mods activated from the playlist.
/// </remarks>
public Bindable<IReadOnlyList<Mod>> ActiveMods { get; private set; } = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>());
/// <summary>
/// Contains a dictionary with the current <see cref="ModState"/> of all mods applicable for the current ruleset.
/// </summary>
@ -97,6 +105,8 @@ namespace osu.Game.Overlays.Mods
protected virtual IReadOnlyList<Mod> ComputeNewModsFromSelection(IReadOnlyList<Mod> oldSelection, IReadOnlyList<Mod> newSelection) => newSelection;
protected virtual IReadOnlyList<Mod> ComputeActiveMods() => SelectedMods.Value;
protected virtual IEnumerable<ShearedButton> CreateFooterButtons()
{
if (AllowCustomisation)
@ -279,7 +289,7 @@ namespace osu.Game.Overlays.Mods
{
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
BeatmapInfo = { Value = beatmap?.BeatmapInfo }
BeatmapInfo = { Value = Beatmap?.BeatmapInfo },
},
}
});
@ -316,20 +326,26 @@ namespace osu.Game.Overlays.Mods
SelectedMods.BindValueChanged(_ =>
{
updateRankingInformation();
updateFromExternalSelection();
updateCustomisation();
ActiveMods.Value = ComputeActiveMods();
}, true);
ActiveMods.BindValueChanged(_ =>
{
updateOverlayInformation();
modSettingChangeTracker?.Dispose();
if (AllowCustomisation)
{
// Importantly, use SelectedMods.Value here (and not the ValueChanged NewValue) as the latter can
// Importantly, use ActiveMods.Value here (and not the ValueChanged NewValue) as the latter can
// potentially be stale, due to complexities in the way change trackers work.
//
// See https://github.com/ppy/osu/pull/23284#issuecomment-1529056988
modSettingChangeTracker = new ModSettingChangeTracker(SelectedMods.Value);
modSettingChangeTracker.SettingChanged += _ => updateRankingInformation();
modSettingChangeTracker = new ModSettingChangeTracker(ActiveMods.Value);
modSettingChangeTracker.SettingChanged += _ => updateOverlayInformation();
}
}, true);
@ -454,18 +470,25 @@ namespace osu.Game.Overlays.Mods
modState.ValidForSelection.Value = modState.Mod.Type != ModType.System && modState.Mod.HasImplementation && IsValidMod.Invoke(modState.Mod);
}
private void updateRankingInformation()
/// <summary>
/// Updates any information displayed on the overlay regarding the effects of the active mods.
/// This reads from <see cref="ActiveMods"/> instead of <see cref="SelectedMods"/>.
/// </summary>
private void updateOverlayInformation()
{
if (rankingInformationDisplay == null)
return;
if (rankingInformationDisplay != null)
{
double multiplier = 1.0;
double multiplier = 1.0;
foreach (var mod in ActiveMods.Value)
multiplier *= mod.ScoreMultiplier;
foreach (var mod in SelectedMods.Value)
multiplier *= mod.ScoreMultiplier;
rankingInformationDisplay.ModMultiplier.Value = multiplier;
rankingInformationDisplay.Ranked.Value = ActiveMods.Value.All(m => m.Ranked);
}
rankingInformationDisplay.ModMultiplier.Value = multiplier;
rankingInformationDisplay.Ranked.Value = SelectedMods.Value.All(m => m.Ranked);
if (beatmapAttributesDisplay != null)
beatmapAttributesDisplay.Mods.Value = ActiveMods.Value;
}
private void updateCustomisation()

View File

@ -0,0 +1,53 @@
// 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;
using System.Diagnostics;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Game.Online.Rooms;
using osu.Game.Overlays;
using osu.Game.Overlays.Mods;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Screens.OnlinePlay.Match
{
public partial class RoomModSelectOverlay : UserModSelectOverlay
{
[Resolved]
private IBindable<PlaylistItem> selectedItem { get; set; } = null!;
[Resolved]
private RulesetStore rulesets { get; set; } = null!;
private readonly List<Mod> roomRequiredMods = new List<Mod>();
public RoomModSelectOverlay()
: base(OverlayColourScheme.Plum)
{
}
protected override void LoadComplete()
{
base.LoadComplete();
selectedItem.BindValueChanged(v =>
{
roomRequiredMods.Clear();
if (v.NewValue is PlaylistItem item)
{
var rulesetInstance = rulesets.GetRuleset(item.RulesetID)?.CreateInstance();
Debug.Assert(rulesetInstance != null);
roomRequiredMods.AddRange(item.RequiredMods.Select(m => m.ToMod(rulesetInstance)));
}
ActiveMods.Value = ComputeActiveMods();
}, true);
}
protected override IReadOnlyList<Mod> ComputeActiveMods() => roomRequiredMods.Concat(base.ComputeActiveMods()).ToList();
}
}

View File

@ -241,7 +241,7 @@ namespace osu.Game.Screens.OnlinePlay.Match
}
};
LoadComponent(UserModsSelectOverlay = new UserModSelectOverlay(OverlayColourScheme.Plum)
LoadComponent(UserModsSelectOverlay = new RoomModSelectOverlay
{
SelectedMods = { BindTarget = UserMods },
IsValidMod = _ => false