mirror of
https://github.com/ppy/osu.git
synced 2025-02-21 00:53:31 +08:00
Merge branch 'master' into fix-mania-diffcalc
This commit is contained in:
commit
ef2e2894a2
@ -39,6 +39,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
typeof(ManiaModKey7),
|
||||
typeof(ManiaModKey8),
|
||||
typeof(ManiaModKey9),
|
||||
typeof(ManiaModKey10),
|
||||
}.Except(new[] { GetType() }).ToArray();
|
||||
}
|
||||
}
|
||||
|
@ -94,6 +94,52 @@ namespace osu.Game.Tests.NonVisual
|
||||
Assert.IsTrue(combinations[2] is ModIncompatibleWithAofA);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMultiModFlattening()
|
||||
{
|
||||
var combinations = new TestLegacyDifficultyCalculator(new ModA(), new MultiMod(new ModB(), new ModC())).CreateDifficultyAdjustmentModCombinations();
|
||||
|
||||
Assert.AreEqual(4, combinations.Length);
|
||||
Assert.IsTrue(combinations[0] is ModNoMod);
|
||||
Assert.IsTrue(combinations[1] is ModA);
|
||||
Assert.IsTrue(combinations[2] is MultiMod);
|
||||
Assert.IsTrue(combinations[3] is MultiMod);
|
||||
|
||||
Assert.IsTrue(((MultiMod)combinations[2]).Mods[0] is ModA);
|
||||
Assert.IsTrue(((MultiMod)combinations[2]).Mods[1] is ModB);
|
||||
Assert.IsTrue(((MultiMod)combinations[2]).Mods[2] is ModC);
|
||||
Assert.IsTrue(((MultiMod)combinations[3]).Mods[0] is ModB);
|
||||
Assert.IsTrue(((MultiMod)combinations[3]).Mods[1] is ModC);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestIncompatibleThroughMultiMod()
|
||||
{
|
||||
var combinations = new TestLegacyDifficultyCalculator(new ModA(), new MultiMod(new ModB(), new ModIncompatibleWithA())).CreateDifficultyAdjustmentModCombinations();
|
||||
|
||||
Assert.AreEqual(3, combinations.Length);
|
||||
Assert.IsTrue(combinations[0] is ModNoMod);
|
||||
Assert.IsTrue(combinations[1] is ModA);
|
||||
Assert.IsTrue(combinations[2] is MultiMod);
|
||||
|
||||
Assert.IsTrue(((MultiMod)combinations[2]).Mods[0] is ModB);
|
||||
Assert.IsTrue(((MultiMod)combinations[2]).Mods[1] is ModIncompatibleWithA);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestIncompatibleWithSameInstanceViaMultiMod()
|
||||
{
|
||||
var combinations = new TestLegacyDifficultyCalculator(new ModA(), new MultiMod(new ModA(), new ModB())).CreateDifficultyAdjustmentModCombinations();
|
||||
|
||||
Assert.AreEqual(3, combinations.Length);
|
||||
Assert.IsTrue(combinations[0] is ModNoMod);
|
||||
Assert.IsTrue(combinations[1] is ModA);
|
||||
Assert.IsTrue(combinations[2] is MultiMod);
|
||||
|
||||
Assert.IsTrue(((MultiMod)combinations[2]).Mods[0] is ModA);
|
||||
Assert.IsTrue(((MultiMod)combinations[2]).Mods[1] is ModB);
|
||||
}
|
||||
|
||||
private class ModA : Mod
|
||||
{
|
||||
public override string Name => nameof(ModA);
|
||||
@ -112,6 +158,13 @@ namespace osu.Game.Tests.NonVisual
|
||||
public override Type[] IncompatibleMods => new[] { typeof(ModIncompatibleWithAAndB) };
|
||||
}
|
||||
|
||||
private class ModC : Mod
|
||||
{
|
||||
public override string Name => nameof(ModC);
|
||||
public override string Acronym => nameof(ModC);
|
||||
public override double ScoreMultiplier => 1;
|
||||
}
|
||||
|
||||
private class ModIncompatibleWithA : Mod
|
||||
{
|
||||
public override string Name => $"Incompatible With {nameof(ModA)}";
|
||||
|
@ -95,6 +95,24 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
AddAssert("copy has original value", () => Precision.AlmostEquals(1.5, copy.SpeedChange.Value));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMultiModSettingsUnboundWhenCopied()
|
||||
{
|
||||
MultiMod original = null;
|
||||
MultiMod copy = null;
|
||||
|
||||
AddStep("create mods", () =>
|
||||
{
|
||||
original = new MultiMod(new OsuModDoubleTime());
|
||||
copy = (MultiMod)original.CreateCopy();
|
||||
});
|
||||
|
||||
AddStep("change property", () => ((OsuModDoubleTime)original.Mods[0]).SpeedChange.Value = 2);
|
||||
|
||||
AddAssert("original has new value", () => Precision.AlmostEquals(2.0, ((OsuModDoubleTime)original.Mods[0]).SpeedChange.Value));
|
||||
AddAssert("copy has original value", () => Precision.AlmostEquals(1.5, ((OsuModDoubleTime)copy.Mods[0]).SpeedChange.Value));
|
||||
}
|
||||
|
||||
private void createModSelect()
|
||||
{
|
||||
AddStep("create mod select", () =>
|
||||
|
@ -113,10 +113,11 @@ namespace osu.Game.Rulesets.Difficulty
|
||||
/// </summary>
|
||||
public Mod[] CreateDifficultyAdjustmentModCombinations()
|
||||
{
|
||||
return createDifficultyAdjustmentModCombinations(Array.Empty<Mod>(), DifficultyAdjustmentMods).ToArray();
|
||||
return createDifficultyAdjustmentModCombinations(DifficultyAdjustmentMods, Array.Empty<Mod>()).ToArray();
|
||||
|
||||
IEnumerable<Mod> createDifficultyAdjustmentModCombinations(IEnumerable<Mod> currentSet, Mod[] adjustmentSet, int currentSetCount = 0, int adjustmentSetStart = 0)
|
||||
static IEnumerable<Mod> createDifficultyAdjustmentModCombinations(ReadOnlyMemory<Mod> remainingMods, IEnumerable<Mod> currentSet, int currentSetCount = 0)
|
||||
{
|
||||
// Return the current set.
|
||||
switch (currentSetCount)
|
||||
{
|
||||
case 0:
|
||||
@ -136,18 +137,43 @@ namespace osu.Game.Rulesets.Difficulty
|
||||
break;
|
||||
}
|
||||
|
||||
// Apply mods in the adjustment set recursively. Using the entire adjustment set would result in duplicate multi-mod mod
|
||||
// combinations in further recursions, so a moving subset is used to eliminate this effect
|
||||
for (int i = adjustmentSetStart; i < adjustmentSet.Length; i++)
|
||||
// Apply the rest of the remaining mods recursively.
|
||||
for (int i = 0; i < remainingMods.Length; i++)
|
||||
{
|
||||
var adjustmentMod = adjustmentSet[i];
|
||||
if (currentSet.Any(c => c.IncompatibleMods.Any(m => m.IsInstanceOfType(adjustmentMod))))
|
||||
var (nextSet, nextCount) = flatten(remainingMods.Span[i]);
|
||||
|
||||
// Check if any mods in the next set are incompatible with any of the current set.
|
||||
if (currentSet.SelectMany(m => m.IncompatibleMods).Any(c => nextSet.Any(c.IsInstanceOfType)))
|
||||
continue;
|
||||
|
||||
foreach (var combo in createDifficultyAdjustmentModCombinations(currentSet.Append(adjustmentMod), adjustmentSet, currentSetCount + 1, i + 1))
|
||||
// Check if any mods in the next set are the same type as the current set. Mods of the exact same type are not incompatible with themselves.
|
||||
if (currentSet.Any(c => nextSet.Any(n => c.GetType() == n.GetType())))
|
||||
continue;
|
||||
|
||||
// If all's good, attach the next set to the current set and recurse further.
|
||||
foreach (var combo in createDifficultyAdjustmentModCombinations(remainingMods.Slice(i + 1), currentSet.Concat(nextSet), currentSetCount + nextCount))
|
||||
yield return combo;
|
||||
}
|
||||
}
|
||||
|
||||
// Flattens a mod hierarchy (through MultiMod) as an IEnumerable<Mod>
|
||||
static (IEnumerable<Mod> set, int count) flatten(Mod mod)
|
||||
{
|
||||
if (!(mod is MultiMod multi))
|
||||
return (mod.Yield(), 1);
|
||||
|
||||
IEnumerable<Mod> set = Enumerable.Empty<Mod>();
|
||||
int count = 0;
|
||||
|
||||
foreach (var nested in multi.Mods)
|
||||
{
|
||||
var (nestedSet, nestedCount) = flatten(nested);
|
||||
set = set.Concat(nestedSet);
|
||||
count += nestedCount;
|
||||
}
|
||||
|
||||
return (set, count);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -6,7 +6,7 @@ using System.Linq;
|
||||
|
||||
namespace osu.Game.Rulesets.Mods
|
||||
{
|
||||
public class MultiMod : Mod
|
||||
public sealed class MultiMod : Mod
|
||||
{
|
||||
public override string Name => string.Empty;
|
||||
public override string Acronym => string.Empty;
|
||||
@ -20,6 +20,8 @@ namespace osu.Game.Rulesets.Mods
|
||||
Mods = mods;
|
||||
}
|
||||
|
||||
public override Mod CreateCopy() => new MultiMod(Mods.Select(m => m.CreateCopy()).ToArray());
|
||||
|
||||
public override Type[] IncompatibleMods => Mods.SelectMany(m => m.IncompatibleMods).ToArray();
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user