// Copyright (c) ppy Pty Ltd . 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.Linq; using osu.Game.Rulesets.Mods; #nullable enable namespace osu.Game.Utils { /// /// A set of utilities to handle combinations. /// public static class ModUtils { /// /// Checks that all s are compatible with each-other, and that all appear within a set of allowed types. /// /// /// The allowed types must contain exact types for the respective s to be allowed. /// /// The s to check. /// The set of allowed types. /// Whether all s are compatible with each-other and appear in the set of allowed types. public static bool CheckCompatibleSetAndAllowed(IEnumerable combination, IEnumerable allowedTypes) { // Prevent multiple-enumeration. var combinationList = combination as ICollection ?? combination.ToArray(); return CheckCompatibleSet(combinationList, out _) && CheckAllowed(combinationList, allowedTypes); } /// /// Checks that all s in a combination are compatible with each-other. /// /// The combination to check. /// Whether all s in the combination are compatible with each-other. public static bool CheckCompatibleSet(IEnumerable combination) => CheckCompatibleSet(combination, out _); /// /// Checks that all s in a combination are compatible with each-other. /// /// The combination to check. /// Any invalid mods in the set. /// Whether all s in the combination are compatible with each-other. public static bool CheckCompatibleSet(IEnumerable combination, out List? invalidMods) { combination = FlattenMods(combination); invalidMods = null; foreach (var mod in combination) { foreach (var type in mod.IncompatibleMods) { foreach (var invalid in combination.Where(m => type.IsInstanceOfType(m))) { invalidMods ??= new List(); invalidMods.Add(invalid); } } } return invalidMods == null; } /// /// Checks that all s in a combination appear within a set of allowed types. /// /// /// The set of allowed types must contain exact types for the respective s to be allowed. /// /// The combination to check. /// The set of allowed types. /// Whether all s in the combination are allowed. public static bool CheckAllowed(IEnumerable combination, IEnumerable allowedTypes) { var allowedSet = new HashSet(allowedTypes); return combination.SelectMany(FlattenMod) .All(m => allowedSet.Contains(m.GetType())); } /// /// Flattens a set of s, returning a new set with all s removed. /// /// The set of s to flatten. /// The new set, containing all s in recursively with all s removed. public static IEnumerable FlattenMods(IEnumerable mods) => mods.SelectMany(FlattenMod); /// /// Flattens a , returning a set of s in-place of any s. /// /// The to flatten. /// A set of singular "flattened" s public static IEnumerable FlattenMod(Mod mod) { if (mod is MultiMod multi) { foreach (var m in multi.Mods.SelectMany(FlattenMod)) yield return m; } else yield return mod; } } }