Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of Mutators v1.0.3
Mutators.dll
Decompiled 2 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using ExitGames.Client.Photon; using HarmonyLib; using Microsoft.CodeAnalysis; using Mutators.Announcements; using Mutators.Assets; using Mutators.Assets.Loaders; using Mutators.Enums; using Mutators.Extensions; using Mutators.Logging.Loggers; using Mutators.Managers; using Mutators.Mutators; using Mutators.Mutators.Behaviours; using Mutators.Mutators.Behaviours.HurtColliders; using Mutators.Mutators.Behaviours.Markers; using Mutators.Mutators.Behaviours.UI; using Mutators.Mutators.MultiMutators; using Mutators.Mutators.Patches; using Mutators.Network; using Mutators.Patches; using Mutators.Providers.Random; using Mutators.Providers.Semiwork; using Mutators.Rules.Loaders; using Mutators.Rules.Loaders.Json; using Mutators.Rules.Loaders.Strategies; using Mutators.Rules.Registries; using Mutators.Services.Selection; using Mutators.Services.Selection.Strategies; using Mutators.Settings; using Mutators.Settings.Specific; using Mutators.Utility; using Mutators.Utility.Config; using Newtonsoft.Json; using Photon.Pun; using Photon.Realtime; using REPOLib.Modules; using ScalerCore; using TMPro; using Unity.VisualScripting; using UnityEngine; using UnityEngine.Events; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("Xepos")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.3.0")] [assembly: AssemblyInformationalVersion("1.0.3+da0a5261df0daa082b4c5feafc5e4a019daf68b4")] [assembly: AssemblyProduct("Mutators")] [assembly: AssemblyTitle("Mutators")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/Justin-dr/REPO-Mutators.git")] [assembly: InternalsVisibleTo("Mutators.Tests")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] [CompilerGenerated] internal sealed class <>z__ReadOnlyArray<T> : IEnumerable, ICollection, IList, IEnumerable<T>, IReadOnlyCollection<T>, IReadOnlyList<T>, ICollection<T>, IList<T> { int ICollection.Count => _items.Length; bool ICollection.IsSynchronized => false; object ICollection.SyncRoot => this; object IList.this[int index] { get { return _items[index]; } set { throw new NotSupportedException(); } } bool IList.IsFixedSize => true; bool IList.IsReadOnly => true; int IReadOnlyCollection<T>.Count => _items.Length; T IReadOnlyList<T>.this[int index] => _items[index]; int ICollection<T>.Count => _items.Length; bool ICollection<T>.IsReadOnly => true; T IList<T>.this[int index] { get { return _items[index]; } set { throw new NotSupportedException(); } } public <>z__ReadOnlyArray(T[] items) { _items = items; } IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)_items).GetEnumerator(); } void ICollection.CopyTo(Array array, int index) { ((ICollection)_items).CopyTo(array, index); } int IList.Add(object value) { throw new NotSupportedException(); } void IList.Clear() { throw new NotSupportedException(); } bool IList.Contains(object value) { return ((IList)_items).Contains(value); } int IList.IndexOf(object value) { return ((IList)_items).IndexOf(value); } void IList.Insert(int index, object value) { throw new NotSupportedException(); } void IList.Remove(object value) { throw new NotSupportedException(); } void IList.RemoveAt(int index) { throw new NotSupportedException(); } IEnumerator<T> IEnumerable<T>.GetEnumerator() { return ((IEnumerable<T>)_items).GetEnumerator(); } void ICollection<T>.Add(T item) { throw new NotSupportedException(); } void ICollection<T>.Clear() { throw new NotSupportedException(); } bool ICollection<T>.Contains(T item) { return ((ICollection<T>)_items).Contains(item); } void ICollection<T>.CopyTo(T[] array, int arrayIndex) { ((ICollection<T>)_items).CopyTo(array, arrayIndex); } bool ICollection<T>.Remove(T item) { throw new NotSupportedException(); } int IList<T>.IndexOf(T item) { return ((IList<T>)_items).IndexOf(item); } void IList<T>.Insert(int index, T item) { throw new NotSupportedException(); } void IList<T>.RemoveAt(int index) { throw new NotSupportedException(); } } [CompilerGenerated] internal sealed class <>z__ReadOnlySingleElementList<T> : IEnumerable, ICollection, IList, IEnumerable<T>, IReadOnlyCollection<T>, IReadOnlyList<T>, ICollection<T>, IList<T> { private sealed class Enumerator : IDisposable, IEnumerator, IEnumerator<T> { object IEnumerator.Current => _item; T IEnumerator<T>.Current => _item; public Enumerator(T item) { _item = item; } bool IEnumerator.MoveNext() { if (!_moveNextCalled) { return _moveNextCalled = true; } return false; } void IEnumerator.Reset() { _moveNextCalled = false; } void IDisposable.Dispose() { } } int ICollection.Count => 1; bool ICollection.IsSynchronized => false; object ICollection.SyncRoot => this; object IList.this[int index] { get { if (index != 0) { throw new IndexOutOfRangeException(); } return _item; } set { throw new NotSupportedException(); } } bool IList.IsFixedSize => true; bool IList.IsReadOnly => true; int IReadOnlyCollection<T>.Count => 1; T IReadOnlyList<T>.this[int index] { get { if (index != 0) { throw new IndexOutOfRangeException(); } return _item; } } int ICollection<T>.Count => 1; bool ICollection<T>.IsReadOnly => true; T IList<T>.this[int index] { get { if (index != 0) { throw new IndexOutOfRangeException(); } return _item; } set { throw new NotSupportedException(); } } public <>z__ReadOnlySingleElementList(T item) { _item = item; } IEnumerator IEnumerable.GetEnumerator() { return new Enumerator(_item); } void ICollection.CopyTo(Array array, int index) { array.SetValue(_item, index); } int IList.Add(object value) { throw new NotSupportedException(); } void IList.Clear() { throw new NotSupportedException(); } bool IList.Contains(object value) { return EqualityComparer<T>.Default.Equals(_item, (T)value); } int IList.IndexOf(object value) { if (!EqualityComparer<T>.Default.Equals(_item, (T)value)) { return -1; } return 0; } void IList.Insert(int index, object value) { throw new NotSupportedException(); } void IList.Remove(object value) { throw new NotSupportedException(); } void IList.RemoveAt(int index) { throw new NotSupportedException(); } IEnumerator<T> IEnumerable<T>.GetEnumerator() { return new Enumerator(_item); } void ICollection<T>.Add(T item) { throw new NotSupportedException(); } void ICollection<T>.Clear() { throw new NotSupportedException(); } bool ICollection<T>.Contains(T item) { return EqualityComparer<T>.Default.Equals(_item, item); } void ICollection<T>.CopyTo(T[] array, int arrayIndex) { array[arrayIndex] = _item; } bool ICollection<T>.Remove(T item) { throw new NotSupportedException(); } int IList<T>.IndexOf(T item) { if (!EqualityComparer<T>.Default.Equals(_item, item)) { return -1; } return 0; } void IList<T>.Insert(int index, T item) { throw new NotSupportedException(); } void IList<T>.RemoveAt(int index) { throw new NotSupportedException(); } } namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace Mutators { [BepInDependency("Vippy.ScalerCore", "1.0.2")] [BepInDependency("REPOLib", "4.2.0")] [BepInPlugin("Xepos.REPO-Mutators", "Mutators", "1.0.3")] public class RepoMutators : BaseUnityPlugin { internal const string MainScenePath = "Assets/Scenes/Main/Main.unity"; internal const string NETWORKMANAGER_NAME = "MutatorsNetworkManager"; internal const string MUTATOR_OVERRIDES = "mutatorOverrides"; public static string ConfigPath => Path.Combine(Paths.ConfigPath, "Mutators"); internal static RepoMutators Instance { get; private set; } = null; internal static ModSettings Settings { get; private set; } = null; internal static IMutatorsLogger Logger { get; private set; } = new NullMutatorsLogger(); internal Harmony? Harmony { get; set; } public static event Action<IRuleLoader<Func<IReadOnlyCollection<string>, string, bool>>>? OnLoadMultiMutatorRules; public static event Action<IRuleLoader<Predicate<string>>>? OnLoadSingleMutatorRules; private RepoMutators() { } private void Awake() { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Expected O, but got Unknown Instance = this; Logger = new MutatorsLogger(((BaseUnityPlugin)this).Logger); ((Component)this).gameObject.transform.parent = null; ((Object)((Component)this).gameObject).hideFlags = (HideFlags)61; Settings = new ModSettings(((BaseUnityPlugin)this).Config); MutatorSettings.Initialize(((BaseUnityPlugin)this).Config); InitializeDependencyContainer(new NopMutator(MutatorSettings.NopMutator)); GameObject val = new GameObject("RepoMutatorsPrefab") { hideFlags = (HideFlags)61 }; val.SetActive(false); val.AddComponent<PhotonView>(); val.AddComponent<MutatorsNetworkManager>(); string myPrefabId = "Xepos.REPO-Mutators/MutatorsNetworkManager"; NetworkPrefabs.RegisterNetworkPrefab(myPrefabId, val); ScheduleAssetLoad(); SceneManager.sceneLoaded += delegate(Scene scene, LoadSceneMode _) { //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) if (!(((Scene)(ref scene)).path != "Assets/Scenes/Main/Main.unity") && SemiFunc.IsMasterClientOrSingleplayer()) { if ((Object)(object)RunManager.instance.levelCurrent == (Object)(object)RunManager.instance.levelMainMenu) { if ((Object)(object)MutatorsNetworkManager.Instance != (Object)null && (Object)(object)((Component)MutatorsNetworkManager.Instance).gameObject != (Object)null) { Object.Destroy((Object)(object)((Component)MutatorsNetworkManager.Instance).gameObject); } } else if (!((Object)(object)MutatorsNetworkManager.Instance != (Object)null) && SemiFunc.RunIsLobbyMenu()) { Logger.LogDebug("Reviving network manager"); PrefabRef val2 = default(PrefabRef); if (!NetworkPrefabs.TryGetNetworkPrefabRef(myPrefabId, ref val2)) { throw new Exception("Unable to establish Mutators NetworkManager: Could not find PrefabRef with id: " + myPrefabId); } NetworkPrefabs.SpawnNetworkPrefab(val2, Vector3.zero, Quaternion.identity, (byte)0, (object[])null); MutatorManager instance = MutatorManager.Instance; Logger.LogInfo(string.Join(", ", instance.RegisteredMutators.Select<KeyValuePair<string, IMutator>, string>((KeyValuePair<string, IMutator> x) => $"{x.Key}: {x.Value.Settings.Weight}")) ?? ""); IMutator weightedMutator = instance.GetWeightedMutator(); Logger.LogDebug("Picked weighted mutator: " + weightedMutator.Name); instance.CurrentMutator = weightedMutator; if (weightedMutator is IMultiMutator multiMutator) { (IList<string>, IDictionary<string, object>) tuple = multiMutator.Format(); MutatorsNetworkManager.Instance.SendActiveMutators(multiMutator, tuple.Item2); } else { MutatorsNetworkManager.Instance.SendActiveMutator(weightedMutator.NamespacedName, weightedMutator.Settings.AsMetadata()); } Logger.LogDebug("Mutator set: " + weightedMutator.Name); } } }; MutatorManager.Instance.GameStateChanged += delegate(MutatorsGameState gameState) { Logger.LogDebug($"Changed Mutators gamestate to {gameState}"); }; Patch(); Logger.LogInfo($"{((BaseUnityPlugin)this).Info.Metadata.GUID} v{((BaseUnityPlugin)this).Info.Metadata.Version} has loaded!"); Logger.LogDebug("Initializing default mutators."); MutatorManager.Instance.InitializeDefaultMutators(); OnLoadSingleMutatorRules += delegate(IRuleLoader<Predicate<string>> ruleLoader) { ruleLoader.AddRuleStrategy(new ExclusionRuleLoadingStrategy()); ruleLoader.AddDefaultRule(new JsonMutatorRule("exclude-null-signal", "Exclusion", new string[1] { MutatorSettings.NullSignal.NamespacedName })); }; OnLoadMultiMutatorRules += delegate(IRuleLoader<Func<IReadOnlyCollection<string>, string, bool>> ruleLoader) { ruleLoader.AddRuleStrategy(new MultiExclusionRuleLoadingStrategy()); ruleLoader.AddRuleStrategy(new MutualExclusionRuleLoadingStrategy()); ruleLoader.AddRuleStrategy(new RequiresAmountOfOtherMutatorsRuleLoadingStrategy()); IDictionary<string, object> arguments = new Dictionary<string, object> { { "other-mutators-required-amount", 2 } }; ruleLoader.AddDefaultRules(new JsonMutatorRule("handle-with-care-less-is-more", "Mutual Exclusion", new string[2] { MutatorSettings.HandleWithCare.NamespacedName, MutatorSettings.LessIsMore.NamespacedName }), new JsonMutatorRule("protect-the-weak-protect-the-president", "Mutual Exclusion", new string[2] { MutatorSettings.ProtectTheWeak.NamespacedName, MutatorSettings.ProtectThePresident.NamespacedName }), new JsonMutatorRule("the-floor-is-lava-one-shot-one-kill", "Mutual Exclusion", new string[2] { MutatorSettings.TheFloorIsLava.NamespacedName, MutatorSettings.OneShotOneKill.NamespacedName }), new JsonMutatorRule("the-floor-is-lava-rusty-servos", "Mutual Exclusion", new string[2] { MutatorSettings.TheFloorIsLava.NamespacedName, MutatorSettings.RustyServos.NamespacedName }), new JsonMutatorRule("size-matters-rusty-servos", "Mutual Exclusion", new string[2] { MutatorSettings.SizeMatters.NamespacedName, MutatorSettings.RustyServos.NamespacedName }), new JsonMutatorRule("null-signal-required-amount-of-other-mutators", "Requires Amount Of Other Mutators", new string[1] { MutatorSettings.NullSignal.NamespacedName }, null, arguments)); }; } private void InitializeDependencyContainer(NopMutator nopMutator) { DI.Container.AddSingleton(Logger).AddSingleton<IRandomProvider, RandomProvider>().AddSingleton<ISemiFuncProvider, SemiFuncProvider>() .AddSingleton(new GeneratedMultiMutatorSelectionRulesRegistry()) .AddSingleton<SingleMutatorSelectionRulesRegistry>() .AddSingleton<IRepeatSelectionTracker, RepeatSelectionTracker>() .AddSingleton(Settings.MoonMutatorSettings) .AddSingleton(Settings.RandomMutatorSettings) .AddSingleton(nopMutator) .AddSingleton<NoneScalingSelectionStrategy>() .AddSingleton<MoonScalingSelectionStrategy>() .AddSingleton<RandomScalingSelectionStrategy>() .AddSingleton<IMutatorSelectionService, MutatorSelectionService>() .AddSingleton<MutatorManager>(); } private static void ScheduleAssetLoad() { UnityAction<Scene, LoadSceneMode> sceneLoaded = null; sceneLoaded = delegate(Scene scene, LoadSceneMode _) { if (!(((Scene)(ref scene)).path != "Assets/Scenes/Main/Main.unity")) { List<BaseGameAssetLoader> list = new List<BaseGameAssetLoader>(2) { new LaserAssetLoader(), new ExplosionAssetLoader() }; list.ForEach(delegate(BaseGameAssetLoader loader) { loader.Load(); }); SceneManager.sceneLoaded -= sceneLoaded; } }; SceneManager.sceneLoaded += sceneLoaded; } private void LoadRules() { SingleMutatorRuleLoader singleMutatorRuleLoader = new SingleMutatorRuleLoader(); MultiMutatorRuleLoader multiMutatorRuleLoader = new MultiMutatorRuleLoader(); RepoMutators.OnLoadSingleMutatorRules?.Invoke(singleMutatorRuleLoader); RepoMutators.OnLoadMultiMutatorRules?.Invoke(multiMutatorRuleLoader); IDictionary<string, Predicate<string>> source = singleMutatorRuleLoader.Load(); IDictionary<string, Func<IReadOnlyCollection<string>, string, bool>> source2 = multiMutatorRuleLoader.Load(); SingleMutatorSelectionRulesRegistry singleRegistry = DI.Container.Resolve<SingleMutatorSelectionRulesRegistry>(); GeneratedMultiMutatorSelectionRulesRegistry multiRegistry = DI.Container.Resolve<GeneratedMultiMutatorSelectionRulesRegistry>(); source.ForEach(delegate(KeyValuePair<string, Predicate<string>> rule) { singleRegistry.Register(rule.Key, rule.Value); }); source2.ForEach(delegate(KeyValuePair<string, Func<IReadOnlyCollection<string>, string, bool>> rule) { multiRegistry.Register(rule.Key, rule.Value); }); RepoMutators.OnLoadSingleMutatorRules = null; RepoMutators.OnLoadMultiMutatorRules = null; } private void Start() { MutatorManager.Instance.InitializeMultiMutators(); LoadRules(); } private void Patch() { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Expected O, but got Unknown //IL_0054: Expected O, but got Unknown bool flag = Chainloader.PluginInfos.Values.Any((PluginInfo x) => x.Metadata.GUID == "soundedsquash.spawnmanager"); if (Harmony == null) { Harmony val = new Harmony(((BaseUnityPlugin)this).Info.Metadata.GUID); Harmony val2 = val; Harmony = val; } Harmony.PatchAll(typeof(RunManagerPatch)); Harmony.PatchAll(typeof(LoadingUIPatch)); Harmony.PatchAll(typeof(MapToolControllerPatch)); Harmony.PatchAll(typeof(SemiFuncPatch)); Harmony.PatchAll(typeof(MenuPagePatch)); Harmony.PatchAll(typeof(SpectateCameraPatch)); Harmony.PatchAll(typeof(LevelGeneratorPatch)); Harmony.PatchAll(typeof(MenuManagerPatch)); Harmony.PatchAll(typeof(HealthUIPatch)); Harmony.PatchAll(typeof(MapBacktrackPatch)); if (!flag) { Harmony.PatchAll(typeof(EnemyDirectorPatch)); } } internal void Unpatch() { Harmony? harmony = Harmony; if (harmony != null) { harmony.UnpatchSelf(); } foreach (IMutator value in MutatorManager.Instance.RegisteredMutators.Values) { value.Unpatch(); } } } public static class MyPluginInfo { public const string PLUGIN_GUID = "Xepos.REPO-Mutators"; public const string PLUGIN_NAME = "Mutators"; public const string PLUGIN_VERSION = "1.0.3"; } internal class DI { private IDictionary<Type, Type> _registry = new Dictionary<Type, Type>(); private IDictionary<Type, object> _singletons = new Dictionary<Type, object>(); public static DI Container { get; } = new DI(); public IReadOnlyCollection<Type> RegisteredTypes => new ReadOnlyCollection<Type>(_registry.Keys.ToList()); public DI AddSingleton<T, I>() where I : T { Type typeFromHandle = typeof(T); Type typeFromHandle2 = typeof(I); if (_registry.ContainsKey(typeFromHandle)) { throw new InvalidOperationException($"A type of {typeFromHandle} has already been registered."); } if (IsStatic(typeFromHandle2)) { throw new ArgumentException("Concrete type " + typeFromHandle2.FullName + " may not be static."); } if (typeFromHandle2.IsInterface || typeFromHandle2.IsAbstract) { throw new ArgumentException(typeFromHandle2.FullName + " must be a concrete type."); } GetConstructor(typeFromHandle2); _registry[typeFromHandle] = typeFromHandle2; return this; } public DI AddSingleton<T>() where T : class { return AddSingleton<T, T>(); } public DI AddSingleton<T>(T instance) where T : class { if (instance == null) { throw new ArgumentNullException("instance"); } Type typeFromHandle = typeof(T); if (_registry.ContainsKey(typeFromHandle)) { throw new InvalidOperationException($"A type of {typeFromHandle} has already been registered."); } _registry[typeFromHandle] = instance.GetType(); _singletons[typeFromHandle] = instance; return this; } public T Resolve<T>() { return (T)Resolve(typeof(T), new List<Type>()); } private object Resolve(Type serviceType, IList<Type> resolutionPath) { if (_singletons.TryGetValue(serviceType, out var value)) { return value; } int num = resolutionPath.IndexOf(serviceType); if (num >= 0) { string text = string.Join(" -> ", from type in resolutionPath.Skip(num).Append(serviceType) select type.Name); throw new InvalidOperationException("Circular dependency detected: " + text); } if (!_registry.TryGetValue(serviceType, out var value2)) { throw new ArgumentException("Could not find registered implementation for " + serviceType.FullName + "."); } ConstructorInfo constructor = GetConstructor(value2); resolutionPath.Add(serviceType); try { object[] parameters = (from parameter in constructor.GetParameters() select Resolve(parameter.ParameterType, resolutionPath)).ToArray(); object obj = constructor.Invoke(parameters); _singletons[serviceType] = obj; return obj; } finally { resolutionPath.RemoveAt(resolutionPath.Count - 1); } } private static ConstructorInfo GetConstructor(Type concreteType) { ConstructorInfo[] constructors = concreteType.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); ConstructorInfo[] array = constructors.Where((ConstructorInfo c) => c.IsDefined(typeof(InjectAttribute))).ToArray(); if (array.Length > 1) { throw new ArgumentException("Type " + concreteType.FullName + " has multiple constructors marked with [Inject]."); } if (array.Length == 1) { return array[0]; } return constructors.OrderByDescending((ConstructorInfo c) => c.GetParameters().Length).FirstOrDefault(); } private static bool IsStatic(Type type) { if ((object)type != null && type.IsClass && type.IsSealed) { return type.IsAbstract; } return false; } } [AttributeUsage(AttributeTargets.Constructor)] internal class InjectAttribute : Attribute { } } namespace Mutators.Utility { internal static class LevelRemovalUtils { internal static void RemoveLevels(bool lobbyMenu = false) { IMutator currentMutator = MutatorManager.Instance.CurrentMutator; if (currentMutator is IMultiMutator multiMutator) { { foreach (KeyValuePair<IMutator, IDictionary<string, object>> subMutator in multiMutator.SubMutators) { subMutator.Key.Settings.ClearRuntimeOverrides(); subMutator.Key.Settings.ApplyRuntimeOverrides(subMutator.Value); if (subMutator.Key.Settings is ILevelRemovingMutatorSettings settings) { settings.RemoveLevels(lobbyMenu); } } return; } } if (currentMutator.Settings is ILevelRemovingMutatorSettings settings2) { settings2.RemoveLevels(lobbyMenu); } } } public static class TemporaryItemUtils { public static void RemoveMarkedItems(string marker) { StatsManager instance = StatsManager.instance; foreach (string item in (from x in instance.item where x.Key.Contains("(" + marker + ")") select x.Key).ToList()) { instance.item.Remove(item); instance.itemStatBattery.Remove(item); } } public static void DropAndRemoveMarkedItems(string marker) { //IL_0053: Unknown result type (might be due to invalid IL or missing references) Inventory instance = Inventory.instance; foreach (InventorySpot inventorySpot in instance.inventorySpots) { ItemEquippable currentItem = inventorySpot.CurrentItem; if (Object.op_Implicit((Object)(object)currentItem) && Object.op_Implicit((Object)(object)((Component)currentItem).gameObject.GetComponent<TemporaryLevelItemBehaviour>())) { ((Component)currentItem).GetComponent<ItemEquippable>().ForceUnequip(instance.playerAvatar.PlayerVisionTarget.VisionTransform.position, SemiFunc.IsMultiplayer() ? instance.physGrabber.photonView.ViewID : (-1)); } } RemoveMarkedItems(marker); } } } namespace Mutators.Utility.Config { public class ConfigRange<T> where T : struct, IComparable, IComparable<T>, IConvertible, IEquatable<T>, IFormattable { private readonly ConfigEntry<T> _min; private readonly ConfigEntry<T> _max; public T MinimumValue => _min.Value; public T MaximumValue => _max.Value; internal ConfigRange(ConfigEntry<T> min, ConfigEntry<T> max) { _min = min; _max = max; } } internal class PositiveIntValue : AcceptableValueBase { private const int MinValue = 0; public PositiveIntValue() : base(typeof(int)) { } public override object Clamp(object value) { if (!(value is int num)) { return 0; } return (num >= 0) ? num : 0; } public override bool IsValid(object value) { if (value is int) { return (int)value >= 0; } return false; } public override string ToDescriptionString() { return "# Acceptable values are integers of 0 or higher"; } } } namespace Mutators.Settings { public abstract class AbstractMutatorSettings { protected const string WeightConfigKey = "Weight"; protected const string MinimumLevelConfigKey = "Minimum level"; protected const string MaximumLevelConfigKey = "Maximum level"; private readonly IDictionary<string, object> _runtimeOverrides = new Dictionary<string, object>(); public string NamespacedName { get; } public string MutatorName { get; } public string MutatorDescription { get; } public abstract int Weight { get; } public abstract int MinimumLevel { get; } public abstract int MaximumLevel { get; } public AbstractMutatorSettings(string @namespace, string name, string description) { NamespacedName = name.ToSlug(@namespace); MutatorName = name; MutatorDescription = description; } public virtual void ApplyRuntimeOverrides(IDictionary<string, object>? overrides) { if (overrides == null || overrides.Count == 0) { return; } foreach (KeyValuePair<string, object> @override in overrides) { _runtimeOverrides[@override.Key] = @override.Value; } } public virtual void ClearRuntimeOverrides() { _runtimeOverrides.Clear(); } protected T GetRuntimeOverride<T>(string key, T fallback) { if (!_runtimeOverrides.TryGetValue(key, out object value)) { return fallback; } if (value.TryCoerce(typeof(T), out object coerced) && coerced is T) { return (T)coerced; } return fallback; } protected IList<T> GetRuntimeOverrideList<T>(string key, IList<T> fallback) { if (!_runtimeOverrides.TryGetValue(key, out object value)) { return fallback; } if (value is IList<T> result) { return result; } if (value is IEnumerable<T> source) { return source.ToList(); } if (value is string text && typeof(T) == typeof(string)) { return (from text2 in text.Split(",") select text2.Trim() into text2 where text2.Length > 0 select text2).Cast<T>().ToList(); } if (value is IEnumerable<object> enumerable) { IList<T> list = new List<T>(); { foreach (object item3 in enumerable) { if (item3.TryCoerce(typeof(T), out object coerced) && coerced is T item) { list.Add(item); continue; } return fallback; } return list; } } if (!value.TryCoerce(typeof(T), out object coerced2) || !(coerced2 is T item2)) { return fallback; } return new List<T>(1) { item2 }; } public virtual bool IsEligibleForSelection() { int levelsCompleted = RunManager.instance.levelsCompleted; if (MaximumLevel > 0 && MinimumLevel > MaximumLevel) { RepoMutators.Logger.LogWarning(MutatorName + " was configured with a minimum level larger than the maximum level!"); RepoMutators.Logger.LogWarning("This configuration is consider invalid, the level bounds will be ignored."); return true; } if (MaximumLevel == 0) { return levelsCompleted >= MinimumLevel; } if (levelsCompleted >= MinimumLevel) { return levelsCompleted <= MaximumLevel; } return false; } public IDictionary<string, object>? AsMetadata() { return CreateMetadata()?.WithMutator(NamespacedName); } protected virtual IDictionary<string, object>? CreateMetadata() { return null; } } public class EnemyDisablingMutatorSettings : GenericMutatorSettings { public const string ExcludedEnemiesKey = "excluded-enemies"; private readonly ConfigEntry<string> _excludedEnemies; private IList<string> _excludedEnemiesList = new List<string>(); public IList<string> ExcludedEnemies => GetRuntimeOverrideList("excluded-enemies", _excludedEnemiesList); public EnemyDisablingMutatorSettings(string @namespace, string name, string description, int weight, ConfigFile config, params string[] defaultDisabledEnemies) : base(@namespace, name, description, weight, config) { _excludedEnemies = config.Bind<string>(GenericMutatorSettings.GetSection(name), "Excluded enemies", string.Join(", ", defaultDisabledEnemies), "Enemies that cannot be spawned by the " + name + " Mutator. (Comma separated e.g. Apex Predator,Huntsman)"); _excludedEnemies.SettingChanged += delegate { CacheEnemies(); }; CacheEnemies(); } public EnemyDisablingMutatorSettings(string @namespace, string name, string description, ConfigFile config, params string[] defaultDisabledEnemies) : this(@namespace, name, description, 100, config, defaultDisabledEnemies) { } internal void CacheEnemies() { _excludedEnemiesList = (from value in _excludedEnemies.Value.Split(",") select value.Trim()).ToList(); } } public class GenericMutatorSettings : AbstractMutatorSettings { private readonly ConfigEntry<int> _weight; private readonly ConfigEntry<int> _minimumLevel; private readonly ConfigEntry<int> _maximumLevel; public override int Weight => _weight.Value; public override int MinimumLevel => _minimumLevel.Value; public override int MaximumLevel => _maximumLevel.Value; public GenericMutatorSettings(string @namespace, string name, string description, int weight, ConfigFile config) : base(@namespace, name, description) { _weight = config.BindPositive(GetSection(name), "Weight", weight, "Weighted chance for the " + name + " Mutator to be active."); _minimumLevel = config.BindPositive(GetSection(name), "Minimum level", 0, "The minimum level on which the " + name + " Mutator can show up"); _maximumLevel = config.BindPositive(GetSection(name), "Maximum level", 1000, "The maximum level on which the " + name + " mutator can show up (0 = no upper bound)"); } public GenericMutatorSettings(string @namespace, string name, string description, ConfigFile config) : this(@namespace, name, description, 100, config) { } protected T GetClampedRuntimeOverride<T>(string key, ConfigEntry<T> entry) { T runtimeOverride = GetRuntimeOverride(key, entry.Value); if (((ConfigEntryBase)entry).Description.AcceptableValues == null) { return runtimeOverride; } return (T)((ConfigEntryBase)entry).Description.AcceptableValues.Clamp((object)runtimeOverride); } protected static string GetSection(string name) { return name + " Mutator"; } } internal interface ILevelRemovingMutatorSettings { bool AllowCustomLevels { get; } IList<string> ExcludedLevels { get; } } internal class ModSettings { internal enum MutatorNameToggleType { Keybind, WithDescription } internal enum MultiMutatorScalingType { None, Random, Moon } internal sealed class MoonSetting(ConfigRange<int> multiMutatorMoonRange, ConfigEntry<int> generatedChance) { private ConfigRange<int> MultiMutatorMoonRange => multiMutatorMoonRange; internal int GeneratedChance => generatedChance.Value; internal int MaximumMutators => MultiMutatorMoonRange.MaximumValue; internal int MinimumMutators => MultiMutatorMoonRange.MinimumValue; } internal sealed class MoonSettings { private readonly IDictionary<int, MoonSetting> _multiMutatorMoonRanges = new Dictionary<int, MoonSetting>(); private int _moonCount; internal void LateBindMoonConfig(ConfigFile config, int moonCount) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown _moonCount = moonCount; for (int i = 0; i <= _moonCount; i++) { ConfigEntry<int> generatedChance = config.Bind<int>(GetMoonSection(i), "Generated Multi-Mutator Chance (%)", 50, new ConfigDescription($"The chance that a Generated Multi-Mutator will occur when the moon level is {i}", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>())); ConfigRange<int> multiMutatorMoonRange = new ConfigRange<int>(CreateMoonRange(config, i, min: true), CreateMoonRange(config, i, min: false)); _multiMutatorMoonRanges[i] = new MoonSetting(multiMutatorMoonRange, generatedChance); } RepoMutators.Logger.LogDebug($"Late-bound moon config with {moonCount} moons."); } internal MoonSetting GetMultiMutatorMoonRange(int moon) { int key = Math.Clamp(moon, 0, _moonCount); return _multiMutatorMoonRanges[key]; } } internal sealed class RandomSettings { private const string ConfigSection = "Multi-Mutators - Random"; private readonly ConfigEntry<int> _minimumAmount; private readonly ConfigEntry<int> _maximumAmount; private readonly IDictionary<byte, ConfigEntry<int>> _weight = new Dictionary<byte, ConfigEntry<int>>(MaximumGeneratedActiveSubMutators); private readonly IDictionary<byte, ConfigEntry<int>> _generatedChance = new Dictionary<byte, ConfigEntry<int>>(MaximumGeneratedActiveSubMutators); internal int MinimumAmount => _minimumAmount.Value; internal int MaximumAmount => _maximumAmount.Value; internal RandomSettings(ConfigFile config) { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Expected O, but got Unknown //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Expected O, but got Unknown //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Expected O, but got Unknown _minimumAmount = config.Bind<int>("Multi-Mutators - Random", "Minimum amount of Mutators", 1, new ConfigDescription("The minimum amount of Mutators that can be selected when using random scaling.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, (int)MaximumGeneratedActiveSubMutators), Array.Empty<object>())); _maximumAmount = config.Bind<int>("Multi-Mutators - Random", "Maximum amount of Mutators", 4, new ConfigDescription("The maximum amount of Mutators that can be selected when using random scaling.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, (int)MaximumGeneratedActiveSubMutators), Array.Empty<object>())); for (byte b = 1; b <= MaximumGeneratedActiveSubMutators; b++) { _weight[b] = config.Bind<int>("Multi-Mutators - Random", $"{b} Mutators - Weight", 50, new ConfigDescription(string.Format("The weighted chance for {0} {1} to be selected when using random scaling.", b, (b == 1) ? "Mutator" : "Mutators"), (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1000), Array.Empty<object>())); if (b > 1) { _generatedChance[b] = config.Bind<int>("Multi-Mutators - Random", $"{b} Mutators - Generated Multi-Mutator Chance (%)", 50, new ConfigDescription($"The chance that a Generated Multi-Mutator will occur when picking {b} Mutators.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>())); } } } internal int GetWeight(int amountOfMutators) { if (amountOfMutators < 0 || amountOfMutators > MaximumGeneratedActiveSubMutators) { throw new ArgumentException($"Amount of Mutators should be between 0 and {MaximumGeneratedActiveSubMutators}", "amountOfMutators"); } return _weight[(byte)amountOfMutators].Value; } internal int GetGeneratedChance(int amountOfMutators) { if (amountOfMutators < 2) { return 0; } if (amountOfMutators <= MaximumGeneratedActiveSubMutators) { return _generatedChance[(byte)amountOfMutators].Value; } throw new ArgumentException($"Amount of Mutators should not be greater than {MaximumGeneratedActiveSubMutators}", "amountOfMutators"); } } internal static readonly byte MaximumGeneratedActiveSubMutators = 10; private readonly ConfigEntry<bool> _extendedLogging; private readonly ConfigEntry<float> _mutatorDisplayY; private readonly ConfigEntry<float> _mutatorDisplaySize; private readonly ConfigEntry<string> _mutatorDisplayToggleKey; private readonly ConfigEntry<string> _mutatorDisplayToggleType; private readonly ConfigEntry<float> _mutatorDescriptionDisplayY; private readonly ConfigEntry<float> _mutatorDescriptionDisplaySize; private readonly ConfigEntry<float> _mutatorDescriptionInitialDisplayTime; private readonly ConfigEntry<bool> _mutatorDescriptionPinned; private readonly ConfigEntry<bool> _mutatorDescriptionInMapTool; private readonly ConfigEntry<float> _targetDisplaySize; private readonly ConfigEntry<float> _specialActionY; private readonly ConfigEntry<string> _specialActionKey; private readonly ConfigEntry<string> _multiMutatorSelectionMode; public RandomSettings RandomMutatorSettings { get; } public MoonSettings MoonMutatorSettings { get; } public bool ExtendedLogging => _extendedLogging.Value; public float MutatorDisplayY => _mutatorDisplayY.Value; public float MutatorDisplaySize => _mutatorDisplaySize.Value; public KeyCode MutatorDisplayToggleKey { get; private set; } public MutatorNameToggleType MutatorDisplayToggleType { get; private set; } public float MutatorDescriptionDisplayY => _mutatorDescriptionDisplayY.Value; public float MutatorDescriptionDisplaySize => _mutatorDescriptionDisplaySize.Value; public float MutatorDescriptionInitialDisplayTime => _mutatorDescriptionInitialDisplayTime.Value; public bool MutatorDescriptionPinned => _mutatorDescriptionPinned.Value; public bool MutatorDescriptionInMapTool => _mutatorDescriptionInMapTool.Value; public float TargetDisplaySize => _targetDisplaySize.Value; public float SpecialActionY => _specialActionY.Value; public KeyCode SpecialActionKey { get; private set; } public MultiMutatorScalingType MutatorScalingType { get; private set; } internal ModSettings(ConfigFile config) { //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Expected O, but got Unknown //IL_01fd: Unknown result type (might be due to invalid IL or missing references) //IL_0207: Expected O, but got Unknown _extendedLogging = config.Bind<bool>("Logging", "Extended Logging", false, "Enable extended logging."); _mutatorDisplayY = config.Bind<float>("Mutator Interface", "Y position", -75f, "The Y position of the active Mutator overlay"); _mutatorDisplaySize = config.Bind<float>("Mutator Interface", "Size", 30f, "The size of the active Mutator overlay"); _mutatorDisplayToggleType = config.Bind<string>("Mutator Interface", "Mutator display toggle type", "Keybind", new ConfigDescription("The method used to toggle the active Mutator overlay", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[2] { "Keybind", "With Description" }), Array.Empty<object>())); _mutatorDisplayToggleKey = config.Bind<string>("Mutator Interface", "Mutator display toggle key", "H", "The key use to toggle the active Mutator overlay"); _mutatorDescriptionDisplayY = config.Bind<float>("Mutator Interface", "Description Y position", -110f, "The Y position of the active Mutator's description overlay"); _mutatorDescriptionDisplaySize = config.Bind<float>("Mutator Interface", "Description size", 20f, "The size of the active Mutator's description overlay"); _mutatorDescriptionInitialDisplayTime = config.Bind<float>("Mutator Interface", "Description initial display time", 8f, "The time for which the mutator's description is on screen when starting the level"); _mutatorDescriptionPinned = config.Bind<bool>("Mutator Interface", "Description always active", false, "If true, \"Description initial display time\" will be ignored and the description will always stay on screen"); _mutatorDescriptionInMapTool = config.Bind<bool>("Mutator Interface", "Description active in map tool", true, "If true, the mutator's description will be displayed when opening the map tool"); _targetDisplaySize = config.Bind<float>("Mutator Interface", "Target Size", 40f, "The size of the target (e.g. president health) overlay"); _specialActionY = config.Bind<float>("Special Action", "Special Action Y position", -50f, "The Y position of the Special Action overlay"); _specialActionKey = config.Bind<string>("Special Action", "Special Action Key", "R", "Keybind that activates the Special Action"); _multiMutatorSelectionMode = config.Bind<string>("Multi-Mutators", "Scaling Mode", "Moon", new ConfigDescription("The method used to scale the amount of mutators. None = No scaling, Random = Random scaling, Moon = Scaling based on moon", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[3] { "None", "Random", "Moon" }), Array.Empty<object>())); RandomMutatorSettings = new RandomSettings(config); MoonMutatorSettings = new MoonSettings(); CacheKeys(); config.SettingChanged += delegate { CacheKeys(); }; } internal void LateBindMoonConfig(ConfigFile config, int moonCount) { MoonMutatorSettings.LateBindMoonConfig(config, moonCount); } internal void CacheKeys() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) MutatorDisplayToggleKey = (KeyCode)(Enum.TryParse(typeof(KeyCode), _mutatorDisplayToggleKey.Value, out object result) ? ((int)(KeyCode)result) : 0); MutatorDisplayToggleType = (Enum.TryParse(typeof(MutatorNameToggleType), _mutatorDisplayToggleType.Value.Replace(" ", ""), out object result2) ? ((MutatorNameToggleType)result2) : MutatorNameToggleType.Keybind); SpecialActionKey = (KeyCode)(Enum.TryParse(typeof(KeyCode), _specialActionKey.Value, out object result3) ? ((int)(KeyCode)result3) : 0); MutatorScalingType = (Enum.TryParse(typeof(MultiMutatorScalingType), _multiMutatorSelectionMode.Value, out object result4) ? ((MultiMutatorScalingType)result4) : MultiMutatorScalingType.None); } private static ConfigEntry<int> CreateMoonRange(ConfigFile config, int moon, bool min) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Expected O, but got Unknown return config.Bind<int>(GetMoonSection(moon), min ? "Minimum Mutators" : "Maximum Mutators", GetMoonDefaultValue(moon, min), new ConfigDescription(string.Format("The {0} amount of mutators that can be selected for moon {1}", min ? "minimum" : "maximum", moon), (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, (int)MaximumGeneratedActiveSubMutators), Array.Empty<object>())); } private static int GetMoonDefaultValue(int moon, bool min) { if (!min) { return Math.Min(4, moon + 1); } return Math.Max(moon, 1); } private static string GetMoonSection(int moon) { return "Multi-Mutators - " + ((moon == 0) ? "No Moon" : $"Moon {moon}"); } } internal class MultiMutatorSettings : AbstractMutatorSettings { public override int Weight { get; } public override int MinimumLevel { get; } public override int MaximumLevel { get; } public MultiMutatorSettings(string @namespace, string name, string description, int weight = 0, int minimumLevel = 0, int maximumLevel = 1000) : base(@namespace, name, description) { Weight = weight; MinimumLevel = minimumLevel; MaximumLevel = maximumLevel; } } public static class MutatorSettings { public static NopMutatorSettings NopMutator { get; private set; } public static ApolloElevenMutatorSettings ApolloEleven { get; private set; } public static OutWithABangMutatorSettings OutWithABang { get; private set; } public static DuckThisMutatorSettings DuckThis { get; private set; } public static UltraViolenceMutatorSettings UltraViolence { get; private set; } public static ProtectThePresidentMutatorSettings ProtectThePresident { get; private set; } public static OneShotOneKillMutatorSettings OneShotOneKill { get; private set; } public static GenericMutatorSettings RustyServos { get; private set; } public static HandleWithCareMutatorSettings HandleWithCare { get; private set; } public static EnemyDisablingMutatorSettings HuntingSeason { get; private set; } public static ThereCanOnlyBeOneMutatorSettings ThereCanOnlyBeOne { get; private set; } public static GenericMutatorSettings VolatileCargo { get; private set; } public static SealedAwayMutatorSettings SealedAway { get; private set; } public static GenericMutatorSettings ProtectTheWeak { get; private set; } public static FiringMyLaserMutatorSettings FiringMyLaser { get; private set; } public static VoiceoverMutatorSettings Voiceover { get; private set; } public static TheFloorIsLavaMutatorSettings TheFloorIsLava { get; private set; } public static LessIsMoreMutatorSettings LessIsMore { get; private set; } public static AmalgamMutatorSettings Amalgam { get; private set; } public static GenericMutatorSettings NullSignal { get; private set; } public static SizeMattersMutatorSettings SizeMatters { get; private set; } internal static void Initialize(ConfigFile config) { NopMutator = new NopMutatorSettings(config); ApolloEleven = new ApolloElevenMutatorSettings("Apollo 11", "Level-wide Zero-Gravity", config); OutWithABang = new OutWithABangMutatorSettings("Out With a Bang!", "Monsters explode on death", config); DuckThis = new DuckThisMutatorSettings("Duck This", "Ducks aggro on sight instead of on interaction\nAlways spawn at least 1 duck", config); UltraViolence = new UltraViolenceMutatorSettings("Ultra-Violence", "Immediately activates the final extraction phase", config); ProtectThePresident = new ProtectThePresidentMutatorSettings("Protect the President", "A random player becomes the \"President\"\nIf they die, everyone else self-destructs", config); OneShotOneKill = new OneShotOneKillMutatorSettings("One Shot, One Kill", "Any damage taken by a player is lethal", config); RustyServos = new GenericMutatorSettings("Xepos.REPO-Mutators", "Rusty Servos", "Players cannot jump\n+3 Grab Range", config); HandleWithCare = new HandleWithCareMutatorSettings("Handle With Care", "Valuables are worth more but break on any impact", config); HuntingSeason = new EnemyDisablingMutatorSettings("Xepos.REPO-Mutators", "Hunting Season", "No valuables spawn, weapons spawn instead\nEnemy respawn time reduced to 10 seconds", config, "Voodoo", "Weeping Angel"); ThereCanOnlyBeOne = new ThereCanOnlyBeOneMutatorSettings("There Can Only Be One", "All monster spawns are of the same type", config); VolatileCargo = new GenericMutatorSettings("Xepos.REPO-Mutators", "Volatile Cargo", "Valuables explode on destruction\nExplosion radius and strength based on value", config); SealedAway = new SealedAwayMutatorSettings("Sealed Away", "Breaking valuables has a chance to spawn monsters", config); ProtectTheWeak = new ProtectTheWeakMutatorSettings("Protect the Weak", "Protect your weaker friends!", config); FiringMyLaser = new FiringMyLaserMutatorSettings("Firing My Laser", "Fire your laser by pressing {specialActionKey}\nUncontrollably fire your laser when taking damage", config); Voiceover = new VoiceoverMutatorSettings("Voiceover", "Player voices are shuffled", config); TheFloorIsLava = new TheFloorIsLavaMutatorSettings("The Floor Is Lava", "You take damage while standing on the floor", config); LessIsMore = new LessIsMoreMutatorSettings("Less Is More", "Valuables are worth less but gain value when hit\nNormal breaking mechanics apply", config); Amalgam = new AmalgamMutatorSettings("Amalgam", "You’ve been here before — just not all at once", config); NullSignal = new GenericMutatorSettings("Xepos.REPO-Mutators", "Null Signal", "Active Mutator information is hidden", config); SizeMatters = new SizeMattersMutatorSettings("Size Matters", "All players are shrunk", config); } } } namespace Mutators.Settings.Specific { public class AmalgamMutatorSettings : GenericMutatorSettings, ILevelRemovingMutatorSettings { public const string ExcludedLevelsKey = "excluded-levels"; private readonly ConfigEntry<string> _excludedLevels; private IList<string> _excludedLevelsList; public bool AllowCustomLevels => true; public IList<string> ExcludedLevels => GetExcludedLevelsWithDefault(GetRuntimeOverrideList("excluded-levels", _excludedLevelsList)); internal AmalgamMutatorSettings(string name, string description, ConfigFile config) : base("Xepos.REPO-Mutators", name, description, config) { _excludedLevels = config.Bind<string>(GenericMutatorSettings.GetSection(name), "Excluded levels", "", "Levels that should be excluded from the " + name + " Mutator. These can neither be picked as a base, nor will their rooms be used in the level generation"); _excludedLevelsList = ExcludedLevelsAsList(); _excludedLevels.SettingChanged += SettingChanged; } private void SettingChanged(object sender, EventArgs e) { _excludedLevelsList = ExcludedLevelsAsList(); } private IList<string> ExcludedLevelsAsList() { return (from value in _excludedLevels.Value.Split(",") select value.Trim()).ToList(); } private static IList<string> GetExcludedLevelsWithDefault(IList<string> excluded) { IList<string> list = excluded.ToList(); if (!list.Contains("Backrooms")) { list.Add("Backrooms"); } if (!list.Contains("PeachCastle")) { list.Add("PeachCastle"); } return list; } } public class ApolloElevenMutatorSettings : GenericMutatorSettings { public const string ApplyToEnemiesKey = "apply-to-enemies"; public const string ApplyInCartKey = "apply-in-cart"; private readonly ConfigEntry<bool> _applyToEnemies; private readonly ConfigEntry<bool> _applyInCart; private readonly ConfigEntry<string> _downwardsKey; public bool ApplyToEnemies => GetRuntimeOverride("apply-to-enemies", _applyToEnemies.Value); public bool ApplyInCart => GetRuntimeOverride("apply-in-cart", _applyInCart.Value); public KeyCode DownwardsKey { get; private set; } internal ApolloElevenMutatorSettings(string name, string description, ConfigFile config) : base("Xepos.REPO-Mutators", name, description, config) { _applyToEnemies = config.Bind<bool>(GenericMutatorSettings.GetSection(name), "Apply to monsters", false, "If true, Zero-Gravity will also be applied to monsters while the " + name + " Mutator is active."); _applyInCart = config.Bind<bool>(GenericMutatorSettings.GetSection(name), "Apply in cart/extraction", true, "If true, Zero-Gravity will also apply to valuables in the cart and extraction points."); _downwardsKey = config.Bind<string>(GenericMutatorSettings.GetSection(name), "Downwards momentum keybind", "LeftControl", "(Client sided) If bound, this key can be used to control yourself downwards while the " + name + " Mutator is active"); CacheKey(); } internal void CacheKey() { //IL_0020: Unknown result type (might be due to invalid IL or missing references) if (Enum.TryParse(typeof(KeyCode), _downwardsKey.Value, out object result)) { DownwardsKey = (KeyCode)result; } else { DownwardsKey = (KeyCode)0; } } } public class DuckThisMutatorSettings : GenericMutatorSettings { public const string AggroCooldownKey = "aggro-cooldown"; private readonly ConfigEntry<float> _duckAggroCooldown; public float AggroCooldown => GetClampedRuntimeOverride<float>("aggro-cooldown", _duckAggroCooldown); internal DuckThisMutatorSettings(string name, string description, ConfigFile config) : base("Xepos.REPO-Mutators", name, description, config) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Expected O, but got Unknown _duckAggroCooldown = config.Bind<float>(GenericMutatorSettings.GetSection(name), "Duck aggro cooldown", 50f, new ConfigDescription("The cooldown between duck aggro while the Duck This Mutator is active.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 300f), Array.Empty<object>())); } } public class FiringMyLaserMutatorSettings : GenericMutatorSettings { public const string LaserActionCooldownKey = "laser-action-cooldown"; public const string LaserActionEnemyDamageKey = "laser-enemy-damage"; public const string LaserActionEnabledKey = "laser-action-enabled"; public const string LaserOnHurtEnabledKey = "laser-on-hurt-enabled"; private readonly ConfigEntry<int> _laserActionCooldown; private readonly ConfigEntry<int> _laserActionEnemyDamage; private readonly ConfigEntry<bool> _laserActionEnabled; private readonly ConfigEntry<bool> _laserOnHurtEnabled; public int LaserActionCooldown => GetClampedRuntimeOverride<int>("laser-action-cooldown", _laserActionCooldown); public int LaserActionEnemyDamage => GetClampedRuntimeOverride<int>("laser-enemy-damage", _laserActionEnemyDamage); public bool LaserActionEnabled => GetRuntimeOverride("laser-action-enabled", _laserActionEnabled.Value); public bool LaserOnHurtEnabled => GetRuntimeOverride("laser-on-hurt-enabled", _laserOnHurtEnabled.Value); internal FiringMyLaserMutatorSettings(string name, string description, ConfigFile config) : base("Xepos.REPO-Mutators", name, description, config) { //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Expected O, but got Unknown //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Expected O, but got Unknown _laserActionEnabled = config.Bind<bool>(GenericMutatorSettings.GetSection(name), "Allow manual laser action", true, "If true, players can manually use their laser action while the cooldown is over. Otherwise, only use the laser when getting hit"); _laserOnHurtEnabled = config.Bind<bool>(GenericMutatorSettings.GetSection(name), "Fire laser on hurt", true, "If true, players will automatically fire their laser when getting hurt."); _laserActionCooldown = config.Bind<int>(GenericMutatorSettings.GetSection(name), "Laser action cooldown", 60, new ConfigDescription("The amount of seconds before the laser special action can be used again.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(5, 10000), Array.Empty<object>())); _laserActionEnemyDamage = config.Bind<int>(GenericMutatorSettings.GetSection(name), "Laser action enemy damage", 30, new ConfigDescription("The amount of damage the laser special action deals to enemies per tick.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 200), Array.Empty<object>())); } protected override IDictionary<string, object> CreateMetadata() { return new Dictionary<string, object> { { "laser-action-enabled", LaserActionEnabled }, { "laser-on-hurt-enabled", LaserOnHurtEnabled } }; } } public class HandleWithCareMutatorSettings : GenericMutatorSettings { public const string ValueMultiplierKey = "value-multiplier"; public const string InstantlyDestroySurplusKey = "instantly-destroy-surplus"; public const string MultiplySurplusValueKey = "multiply-surplus-value"; private readonly ConfigEntry<float> _valueMultiplier; private readonly ConfigEntry<bool> _instaDestroySurplus; private readonly ConfigEntry<bool> _multiplySurplus; public float ValueMultiplier => GetClampedRuntimeOverride<float>("value-multiplier", _valueMultiplier); public bool InstantlyDestroySurplus => GetClampedRuntimeOverride<bool>("instantly-destroy-surplus", _instaDestroySurplus); public bool MultiplySurplusValue => GetClampedRuntimeOverride<bool>("multiply-surplus-value", _multiplySurplus); internal HandleWithCareMutatorSettings(string name, string description, ConfigFile config) : base("Xepos.REPO-Mutators", name, description, config) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown _valueMultiplier = config.Bind<float>(GenericMutatorSettings.GetSection(name), "Value Multiplier", 2f, new ConfigDescription("The amount by which the value of valuables should be multiplier when " + name + " is active.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10f), Array.Empty<object>())); _instaDestroySurplus = config.Bind<bool>(GenericMutatorSettings.GetSection(name), "Instantly Destroy Surplus on Damage", false, "If true, while the " + name + " Mutator is active, the surplus will instantly be destroyed when taking any damage, just like other valuables."); _multiplySurplus = config.Bind<bool>(GenericMutatorSettings.GetSection(name), "Multiply Surplus Value", false, "If true, while the " + name + " Mutator is active, the surplus value will be multiplied, just like other valuables."); } } public class LessIsMoreMutatorSettings : GenericMutatorSettings { public const string StrongDivisionFactorKey = "strong-division-factor"; public const string WeakDivisionFactorKey = "weak-division-factor"; private readonly ConfigEntry<float> _strongDivisionFactor; private readonly ConfigEntry<float> _weakDivisionFactor; private readonly ConfigEntry<float> _valueGainMultiplier; public float StrongDivisionFactor => GetClampedRuntimeOverride<float>("strong-division-factor", _strongDivisionFactor); public float WeakDivisionFactor => GetClampedRuntimeOverride<float>("weak-division-factor", _weakDivisionFactor); public float ValueGainMultiplier => _valueGainMultiplier.Value; internal LessIsMoreMutatorSettings(string name, string description, ConfigFile config) : base("Xepos.REPO-Mutators", name, description, config) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Expected O, but got Unknown //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Expected O, but got Unknown _weakDivisionFactor = config.Bind<float>(GenericMutatorSettings.GetSection(name), "Weak Durability Division Factor", 1f, new ConfigDescription("The amount by which the value of weak durability valuables should be divided when the " + name + " Mutator is active. Acts as an lower bound.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 5f), Array.Empty<object>())); _strongDivisionFactor = config.Bind<float>(GenericMutatorSettings.GetSection(name), "Strong Durability Division Factor", 2f, new ConfigDescription("The amount by which the value of strong durability valuables should be divided when the " + name + " Mutator is active. Acts as a upper bound.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 5f), Array.Empty<object>())); _valueGainMultiplier = config.Bind<float>(GenericMutatorSettings.GetSection(name), "Value Gain Multiplier", 2f, new ConfigDescription("The amount by which the normal value gain should be multiplied when the " + name + " Mutator is active.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10f), Array.Empty<object>())); } protected override IDictionary<string, object>? CreateMetadata() { return new Dictionary<string, object> { { "value-gain-multiplier", ValueGainMultiplier } }; } } public sealed class NopMutatorSettings : AbstractMutatorSettings { private readonly ConfigEntry<int> _weight; private readonly ConfigEntry<int> _minimumLevel; private readonly ConfigEntry<int> _maximumLevel; public override int Weight => _weight.Value; public override int MinimumLevel => _minimumLevel.Value; public override int MaximumLevel => _maximumLevel.Value; internal NopMutatorSettings(ConfigFile config) : base("Xepos.REPO-Mutators", "None", "A normal run, no special effects") { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown _weight = config.Bind<int>("No Mutator", "Chance (%)", 50, new ConfigDescription("Percentage chance for no mutator to be active. Unlike other mutators, this one is not weighted", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>())); _minimumLevel = config.BindPositive("No Mutator", "Minimum level", 0, "The minimum level on which no mutator can show up"); _maximumLevel = config.BindPositive("No Mutator", "Maximum level", 1000, "The maximum level on which no mutator can show up (0 = no upper bound)"); } } public class OneShotOneKillMutatorSettings : EnemyDisablingMutatorSettings { public const string InstaReviveInTruckOrExtractionKey = "revive-in-truck-or-extraction"; public const string InstaReviveHealthKey = "revive-health"; private readonly ConfigEntry<bool> _instaReviveInTruckOrExtraction; private readonly ConfigEntry<int> _instaReviveHealth; public bool InstaReviveInTruckOrExtraction => GetRuntimeOverride("revive-in-truck-or-extraction", _instaReviveInTruckOrExtraction.Value); public int InstaReviveHealth => GetClampedRuntimeOverride<int>("revive-health", _instaReviveHealth); internal OneShotOneKillMutatorSettings(string name, string description, ConfigFile config) : base("Xepos.REPO-Mutators", name, description, config, "Peeper") { _instaReviveInTruckOrExtraction = config.Bind<bool>(GenericMutatorSettings.GetSection(name), "Instant revive in truck or extraction", false, "If true, while the " + name + " Mutator is active, players are instantly revived with full head if their head is brought to the truck or an active extraction point."); _instaReviveHealth = config.BindPositive(GenericMutatorSettings.GetSection(name), "Instant revive health", 0, "The amount of health the player should be revived with when Instant revive is enabled. (0 = Revive with max health)"); } } public class OutWithABangMutatorSettings : GenericMutatorSettings { public const string Tier1Radius = "tier-1-enemy-explosion-radius"; public const string Tier1Damage = "tier-1-enemy-explosion-damage"; public const string Tier2Radius = "tier-2-enemy-explosion-radius"; public const string Tier2Damage = "tier-2-enemy-explosion-damage"; public const string Tier3Radius = "tier-3-enemy-explosion-radius"; public const string Tier3Damage = "tier-3-enemy-explosion-damage"; private readonly ConfigEntry<float> _tier1ExplosionRadius; private readonly ConfigEntry<int> _tier1ExplosionDamage; private readonly ConfigEntry<float> _tier2ExplosionRadius; private readonly ConfigEntry<int> _tier2ExplosionDamage; private readonly ConfigEntry<float> _tier3ExplosionRadius; private readonly ConfigEntry<int> _tier3ExplosionDamage; public float Tier1ExplosionRadius => _tier1ExplosionRadius.Value; public int Tier1ExplosionDamage => _tier1ExplosionDamage.Value; public float Tier2ExplosionRadius => _tier2ExplosionRadius.Value; public int Tier2ExplosionDamage => _tier2ExplosionDamage.Value; public float Tier3ExplosionRadius => _tier3ExplosionRadius.Value; public int Tier3ExplosionDamage => _tier3ExplosionDamage.Value; internal OutWithABangMutatorSettings(string name, string description, ConfigFile config) : base("Xepos.REPO-Mutators", name, description, config) { _tier1ExplosionRadius = config.Bind<float>(GenericMutatorSettings.GetSection(name), "Tier 1 Enemy Explosion Radius", 1f, "The on-death explosion radius of Tier 1 enemies during the " + name + " Mutator."); _tier1ExplosionDamage = config.BindPositive(GenericMutatorSettings.GetSection(name), "Tier 1 Enemy Explosion Damage", 50, "The on-death explosion damage of Tier 1 enemies during the " + name + " Mutator."); _tier2ExplosionRadius = config.Bind<float>(GenericMutatorSettings.GetSection(name), "Tier 2 Enemy Explosion Radius", 2f, "The on-death explosion radius of Tier 2 enemies during the " + name + " Mutator."); _tier2ExplosionDamage = config.BindPositive(GenericMutatorSettings.GetSection(name), "Tier 2 Enemy Explosion Damage", 100, "The on-death explosion damage of Tier 2 enemies during the " + name + " Mutator."); _tier3ExplosionRadius = config.Bind<float>(GenericMutatorSettings.GetSection(name), "Tier 3 Enemy Explosion Radius", 3f, "The on-death explosion radius of Tier 3 enemies during the " + name + " Mutator."); _tier3ExplosionDamage = config.BindPositive(GenericMutatorSettings.GetSection(name), "Tier 3 Enemy Explosion Damage", 200, "The on-death explosion damage of Tier 3 enemies during the " + name + " Mutator."); } protected override IDictionary<string, object>? CreateMetadata() { return new Dictionary<string, object> { { "tier-1-enemy-explosion-radius", Tier1ExplosionRadius }, { "tier-1-enemy-explosion-damage", Tier1ExplosionDamage }, { "tier-2-enemy-explosion-radius", Tier2ExplosionRadius }, { "tier-2-enemy-explosion-damage", Tier2ExplosionDamage }, { "tier-3-enemy-explosion-radius", Tier3ExplosionRadius }, { "tier-3-enemy-explosion-damage", Tier3ExplosionDamage } }; } } public class ProtectThePresidentMutatorSettings : GenericMutatorSettings { private readonly ConfigEntry<int> _minimumPlayerCount; public byte MinimumPlayerCount => (byte)_minimumPlayerCount.Value; internal ProtectThePresidentMutatorSettings(string name, string description, ConfigFile config) : base("Xepos.REPO-Mutators", name, description, config) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Expected O, but got Unknown _minimumPlayerCount = config.Bind<int>(GenericMutatorSettings.GetSection(name), "Minimum player amount requirement", 3, new ConfigDescription("The minimum amount of players required for the " + name + " Mutator to be available for selection.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 20), Array.Empty<object>())); } public override bool IsEligibleForSelection() { if (base.IsEligibleForSelection()) { Room currentRoom = PhotonNetwork.CurrentRoom; if (currentRoom != null) { return currentRoom.PlayerCount >= MinimumPlayerCount; } return false; } return false; } } public class ProtectTheWeakMutatorSettings : GenericMutatorSettings { private readonly ConfigEntry<int> _minimumPlayerCount; public byte MinimumPlayerCount => (byte)_minimumPlayerCount.Value; internal ProtectTheWeakMutatorSettings(string name, string description, ConfigFile config) : base("Xepos.REPO-Mutators", name, description, config) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Expected O, but got Unknown _minimumPlayerCount = config.Bind<int>(GenericMutatorSettings.GetSection(name), "Minimum player amount requirement", 3, new ConfigDescription("The minimum amount of players required for the " + name + " Mutator to be available for selection.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 20), Array.Empty<object>())); } public override bool IsEligibleForSelection() { if (base.IsEligibleForSelection()) { Room currentRoom = PhotonNetwork.CurrentRoom; if (currentRoom != null) { return currentRoom.PlayerCount >= MinimumPlayerCount; } return false; } return false; } } public class SealedAwayMutatorSettings : EnemyDisablingMutatorSettings { public const string MaximumMonsterSpawnsKey = "maximum-monster-spawns"; public const string MonsterSpawnChanceKey = "monster-spawn-chance"; private readonly ConfigEntry<int> _maximumMonsterSpawns; private readonly ConfigEntry<float> _monsterSpawnChance; public int MaximumMonsterSpawns => GetClampedRuntimeOverride<int>("maximum-monster-spawns", _maximumMonsterSpawns); public float MonsterSpawnChance => GetClampedRuntimeOverride<float>("monster-spawn-chance", _monsterSpawnChance); internal SealedAwayMutatorSettings(string name, string description, ConfigFile config) : base("Xepos.REPO-Mutators", name, description, config, "Voodoo", "Shadow Child") { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Expected O, but got Unknown //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Expected O, but got Unknown _maximumMonsterSpawns = config.Bind<int>(GenericMutatorSettings.GetSection(name), "Maximum monster spawns", 5, new ConfigDescription("Maximum amount of extra monsters that can be spawned by the " + name + " Mutator per level.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 15), Array.Empty<object>())); _monsterSpawnChance = config.Bind<float>(GenericMutatorSettings.GetSection(name), "Monster spawn chance", 65f, new ConfigDescription("The chance that a monster is spawned when breaking a valuable when the " + name + " Mutator is active.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>())); } } public class SizeMattersMutatorSettings : GenericMutatorSettings { public const string ScaleValuablesKey = "scale-valuables"; public const string ScaleEnemiesKey = "scale-enemies"; public const string ScaleCartsKey = "scale-carts"; private readonly ConfigEntry<bool> _scaleValuables; private readonly ConfigEntry<bool> _scaleEnemies; private readonly ConfigEntry<bool> _scaleCart; public bool ScaleValuables => GetRuntimeOverride("scale-valuables", _scaleValuables.Value); public bool ScaleEnemies => GetRuntimeOverride("scale-enemies", _scaleEnemies.Value); public bool ScaleCarts => GetRuntimeOverride("scale-carts", _scaleCart.Value); internal SizeMattersMutatorSettings(string name, string description, ConfigFile config) : base("Xepos.REPO-Mutators", name, description, config) { _scaleValuables = config.Bind<bool>(GenericMutatorSettings.GetSection(name), "Scale valuables", false, "If true, valuables will also be scaled."); _scaleEnemies = config.Bind<bool>(GenericMutatorSettings.GetSection(name), "Scale enemies", false, "If true, enemies will also be scaled."); _scaleCart = config.Bind<bool>(GenericMutatorSettings.GetSection(name), "Scale carts", false, "If true, carts will also be scaled."); } protected override IDictionary<string, object> CreateMetadata() { return new Dictionary<string, object> { { "scale-valuables", ScaleValuables }, { "scale-enemies", ScaleEnemies }, { "scale-carts", ScaleCarts } }; } } public class TheFloorIsLavaMutatorSettings : GenericMutatorSettings, ILevelRemovingMutatorSettings { public const string ImmunePlayerCountKey = "immune-player-amount"; public const string AllowCustomLevelsKey = "allow-custom-levels"; public const string DisableEnemiesKey = "disable-enemies"; private readonly ConfigEntry<int> _immunePlayerCount; private readonly ConfigEntry<bool> _usePercentageDamage; private readonly ConfigEntry<bool> _allowCustomLevels; private readonly ConfigEntry<bool> _disableEnemies; private readonly ConfigEntry<float> _reviveImmunityDuration; public int DamagePerTick { get; private set; } public int ImmunePlayerCount => GetClampedRuntimeOverride<int>("immune-player-amount", _immunePlayerCount); public bool UsePercentageDamage => _usePercentageDamage.Value; public bool AllowCustomLevels => GetRuntimeOverride("allow-custom-levels", _allowCustomLevels.Value); public bool DisableEnemies => GetRuntimeOverride("disable-enemies", _disableEnemies.Value); public float ReviveImmunityDuration => _reviveImmunityDuration.Value; public IList<string> ExcludedLevels => new List<string>(); internal TheFloorIsLavaMutatorSettings(string name, string description, ConfigFile config) : base("Xepos.REPO-Mutators", name, description, 0, config) { //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Expected O, but got Unknown ConfigEntry<int> val = config.BindPositive(GenericMutatorSettings.GetSection(name), "Damage", 1, "The amount of damage that players receive per second when standing on floor tiles."); _usePercentageDamage = config.Bind<bool>(GenericMutatorSettings.GetSection(name), "Use percentage damage", false, "If true, players will receive percentage max health damage instead of a flat amount."); _immunePlayerCount = config.Bind<int>(GenericMutatorSettings.GetSection(name), "Immune player count", 0, new ConfigDescription("The amount of players that are immune to " + name + " damage.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 20), Array.Empty<object>())); _allowCustomLevels = config.Bind<bool>(GenericMutatorSettings.GetSection(name), "Allow custom levels", true, "If false, custom levels cannot be picked while the " + name + " Mutator is active."); _disableEnemies = config.Bind<bool>(GenericMutatorSettings.GetSection(name), "Disable enemies", false, "If true, no enemies will spawn while the " + name + " Mutator is active."); _reviveImmunityDuration = config.Bind<float>(GenericMutatorSettings.GetSection(name), "Revive immunity duration", 20f, "The time in seconds for which players are immune to lava damage after reviving while the " + name + " Mutator is active."); DamagePerTick = Math.Clamp(val.Value, 1, int.MaxValue); } protected override IDictionary<string, object>? CreateMetadata() { return new Dictionary<string, object> { { "damage", DamagePerTick }, { "is-percentage-damage", UsePercentageDamage }, { "revive-immunity-duration", ReviveImmunityDuration } }; } } public class ThereCanOnlyBeOneMutatorSettings : EnemyDisablingMutatorSettings { public const string GroupSpawnsThresholdKey = "group-spawns-threshold"; private readonly ConfigEntry<int> _groupSpawnsThreshold; public int GroupSpawnsThreshold => GetClampedRuntimeOverride<int>("group-spawns-threshold", _groupSpawnsThreshold); internal ThereCanOnlyBeOneMutatorSettings(string name, string description, ConfigFile config) : base("Xepos.REPO-Mutators", name, description, config) { _groupSpawnsThreshold = config.BindPositive(GenericMutatorSettings.GetSection(name), "Group spawn minimum level", 8, "The minimum level from which enemy groups (e.g. 3 mentalists or 10 gnomes) can be spawned by the " + name + " Mutator."); } } public class UltraViolenceMutatorSettings : EnemyDisablingMutatorSettings { private readonly ConfigEntry<bool> _keepOnLight; private readonly ConfigEntry<int> _minimumPlayerCount; public bool KeepOnLight => _keepOnLight.Value; public byte MinimumPlayerCount => (byte)_minimumPlayerCount.Value; internal UltraViolenceMutatorSettings(string name, string description, ConfigFile config) : base("Xepos.REPO-Mutators", name, description, 0, config) { //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Expected O, but got Unknown _keepOnLight = config.Bind<bool>(GenericMutatorSettings.GetSection(name), "Keep on lights", false, "Keep on the level lighting while the " + name + " Mutator is active."); _minimumPlayerCount = config.Bind<int>(GenericMutatorSettings.GetSection(name), "Minimum player amount requirement", 0, new ConfigDescription("The minimum amount of players required for the " + name + " Mutator to be available for selection.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 20), Array.Empty<object>())); } public override bool IsEligibleForSelection() { if (base.IsEligibleForSelection()) { Room currentRoom = PhotonNetwork.CurrentRoom; if (currentRoom != null) { return currentRoom.PlayerCount >= MinimumPlayerCount; } return false; } return false; } protected override IDictionary<string, object>? CreateMetadata() { return new Dictionary<string, object> { { "keep-lights-on", KeepOnLight } }; } } public class VoiceoverMutatorSettings : GenericMutatorSettings { private readonly ConfigEntry<int> _minimumPlayerCount; public byte MinimumPlayerCount => (byte)_minimumPlayerCount.Value; internal VoiceoverMutatorSettings(string name, string description, ConfigFile config) : base("Xepos.REPO-Mutators", name, description, config) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Expected O, but got Unknown _minimumPlayerCount = config.Bind<int>(GenericMutatorSettings.GetSection(name), "Minimum player amount requirement", 3, new ConfigDescription("The minimum amount of players required for the " + name + " Mutator to be available for selection.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 20), Array.Empty<object>())); } public override bool IsEligibleForSelection() { if (base.IsEligibleForSelection()) { Room currentRoom = PhotonNetwork.CurrentRoom; if (currentRoom != null) { return currentRoom.PlayerCount >= MinimumPlayerCount; } return false; } return false; } } } namespace Mutators.Services.Selection { public interface IMutatorSelectionService { IMutator GetWeightedMutator(ICollection<IMutator> mutators); } internal interface IRepeatSelectionTracker { IMutator? PreviousMutator { get; } bool ShouldBlockRepeat(IMutator mutator, float probability); IMutator TrackSelectedMutator(IMutator mutator); } public class MutatorSelectionContext(uint amountToPick) { public uint AmountToPick { get; } = amountToPick; } public class MutatorSelectionService : IMutatorSelectionService { private readonly IDictionary<ModSettings.MultiMutatorScalingType, MutatorSelectionStrategy> _selectionStrategies; private readonly NopMutator _nopMutator; private readonly IRepeatSelectionTracker _repeatSelectionTracker; private readonly IRandomProvider _randomProvider; internal MutatorSelectionService(NoneScalingSelectionStrategy noneScalingSelectionStrategy, MoonScalingSelectionStrategy moonScalingSelectionStrategy, RandomScalingSelectionStrategy randomScalingSelectionStrategy, IRepeatSelectionTracker repeatSelectionTracker, IRandomProvider randomProvider, NopMutator nopMutator) { _selectionStrategies = new Dictionary<ModSettings.MultiMutatorScalingType, MutatorSelectionStrategy> { { ModSettings.MultiMutatorScalingType.None, noneScalingSelectionStrategy }, { ModSettings.MultiMutatorScalingType.Moon, moonScalingSelectionStrategy }, { ModSettings.MultiMutatorScalingType.Random, randomScalingSelectionStrategy } }; _repeatSelectionTracker = repeatSelectionTracker; _randomProvider = randomProvider; _nopMutator = nopMutator; } public IMutator GetWeightedMutator(ICollection<IMutator> mutators) { ModSettings.MultiMutatorScalingType mutatorScalingType = RepoMutators.Settings.MutatorScalingType; if (ShouldUseNopMutator(_nopMutator)) { return _repeatSelectionTracker.TrackSelectedMutator(_nopMutator); } IList<IMutator> mutators2 = mutators.Where((IMutator mutator) => !(mutator is NopMutator) && mutator.Settings.Weight > 0).ToList(); return _selectionStrategies[mutatorScalingType].Execute(mutators2); } private bool ShouldUseNopMutator(NopMutator nopMutator) { int weight = nopMutator.Settings.Weight; if (weight <= 0) { return false; } float probability = (float)weight / 100f; if (_repeatSelectionTracker.ShouldBlockRepeat(nopMutator, probability)) { RepoMutators.Logger.LogDebug("Cannot pick " + nopMutator.Name + ", threshold reached"); return false; } if (weight < 100) { return _randomProvider.Range(0f, 100f) <= (float)weight; } return true; } } internal class RepeatSelectionTracker : IRepeatSelectionTracker { private int _repeatCount; public IMutator? PreviousMutator { get; private set; } public bool ShouldBlockRepeat(IMutator mutator, float probability) { if (mutator == PreviousMutator) { return MathF.Pow(probability, _repeatCount + 1) < 0.1f; } return false; } public IMutator TrackSelectedMutator(IMutator mutator) { if (mutator == PreviousMutator) { _repeatCount++; } else { PreviousMutator = mutator; _repeatCount = 1; } return mutator; } } } namespace Mutators.Services.Selection.Strategies { internal class MoonScalingSelectionStrategy : MutatorSelectionStrategy { private readonly ModSettings.MoonSettings _moonSettings; private readonly ISemiFuncProvider _semiFuncProvider; public MoonScalingSelectionStrategy(GeneratedMultiMutatorSelectionRulesRegistry multiRegistry, SingleMutatorSelectionRulesRegistry singleRegistry, ModSettings.MoonSettings moonSettings, ISemiFuncProvider semiFuncProvider, IRepeatSelectionTracker repeatSelectionTracker, IRandomProvider randomProvider, NopMutator fallbackMutator) : base(multiRegistry, singleRegistry, repeatSelectionTracker, randomProvider, fallbackMutator) { _moonSettings = moonSettings; _semiFuncProvider = semiFuncProvider; } public sealed override IMutator Execute(IList<IMutator> mutators) { int moonMutatorsAmount = GetMoonMutatorsAmount(); if (moonMutatorsAmount == 1) { return GetSingleMutator(mutators); } if (!ShouldGenerateMulti(GetMoonGeneratedChance())) { return TryPickUserDefinedMultiMutatorOrElseGenerate(mutators, moonMutatorsAmount); } return PickGeneratedMultiMutator(mutators, moonMutatorsAmount); } private int GetMoonMutatorsAmount() { ModSettings.MoonSetting multiMutatorMoonRange = _moonSettings.GetMultiMutatorMoonRange(_semiFuncProvider.MoonLevel()); return randomProvider.RandomRangeInt(multiMutatorMoonRange.MinimumMutators, multiMutatorMoonRange.MaximumMutators + 1); } private int GetMoonGeneratedChance() { return _moonSettings.GetMultiMutatorMoonRange(_semiFuncProvider.MoonLevel()).GeneratedChance; } } internal abstract class MutatorSelectionStrategy { private readonly IMutator _fallbackMutator; private readonly GeneratedMultiMutatorSelectionRulesRegistry _multiRegistry; private readonly SingleMutatorSelectionRulesRegistry _singleRegistry; protected readonly IRandomProvider randomProvider; private readonly IRepeatSelectionTracker _repeatSelectionTracker; internal MutatorSelectionStrategy(GeneratedMultiMutatorSelectionRulesRegistry multiRegistry, SingleMutatorSelectionRulesRegistry singleRegistry, IRepeatSelectionTracker repeatSelectionTracker, IRandomProvider randomProvider, NopMutator nopMutator) { _multiRegistry = multiRegistry; _singleRegistry = singleRegistry; _repeatSelectionTracker = repeatSelectionTracker; this.randomProvider = randomProvider; _fallbackMutator = nopMutator; } public abstract IMutator Execute(IList<IMutator> mutators); protected IMutator PickSingleMutator(IList<IMutator> mutators, MutatorSelectionContext context) { if (context.AmountToPick == 1) { mutators = mutators.Where((IMutator m) => _singleRegistry.RunRules(m)).ToList(); } IMutator previousMutator = _repeatSelectionTracker.PreviousMutator; float num = mutators.Sum((IMutator item) => item.Settings.Weight); if (previousMutator != null && mutators.Contains(previousMutator) && num > 0f) { float num2 = previousMutator.Settings.Weight; if (num2 > 0f && _repeatSelectionTracker.ShouldBlockRepeat(previousMutator, num2 / num)) { RepoMutators.Logger.LogDebug("Cannot pick " + (previousMutator?.Name ?? "None") + ", threshold reached"); mutators = mutators.Where((IMutator m) => m != previousMutator).ToList(); num = mutators.Sum((IMutator m) => m.Settings.Weight); } } if (num <= 0f) { RepoMutators.Logger.LogWarning($"Fell back to None mutator, invalid total weight: {num}"); return _fallbackMutator; } float num3 = randomProvider.Range(0f, num); float num4 = 0f; foreach (IMutator mutator in mutators) { num4 += (float)mutator.Settings.Weight; if (num3 <= num4) { return _repeatSelectionTracker.TrackSelectedMutator(mutator); } } RepoMutators.Logger.LogWarning("Fell back to None mutator, mutator selection failed"); return _fallbackMutator; } protected IMutator PickGeneratedMultiMutator(IList<IMutator> mutators, int amountToPick) { IList<IMutator> excludeList = new List<IMutator>(1) { _fallbackMutator }; IList<IMutator> pickedMutators = new List<IMutator>(); MutatorSelectionContext context = new MutatorSelectionContext((uint)amountToPick); IList<IMutator> selection = mutators.GetSelection((IMutator mutator2) => !(mutator2 is IMultiMutator) && !excludeList.Contains(mutator2)); for (int num = 0; num < amountToPick; num++) { IList<IMutator> selection2 = selection.GetSelection((IMutator mutator2) => ApplyGenerationRules(pickedMutators, mutator2)); IMutator mutator = PickSingleMutator(selection2, context); if (mutator != _fallbackMutator) { pickedMutators.Add(mutator); } } if (pickedMutators.Count == 0) { return _fallbackMutator; } if (pickedMutators.Count == 1) { return pickedMutators.First(); } return new MultiMutator(new MultiMutatorSettings("Xepos.REPO-Mutators", string.Empty, string.Empty), ((IEnumerable<IMutator>)pickedMutators).ToDictionary((Func<IMutator, IMutator>)((IMutator m) => m), (Func<IMutator, IDictionary<string, object>>)((IMutator _) => new Dictionary<string, object>()))); } private bool ApplyGenerationRules(IList<IMutator> pickedMutators, IMutator mutator) { if (pickedMutators.Contains(mutator)) { return false; } return _multiRegistry.RunRules(new ReadOnlyCollection<IMutator>(pickedMutators), mutator); } protected bool ShouldGenerateMulti(int generatedChance) { if (generatedChance <= 0) { if (generatedChance < 0) { LogMisconfiguredMultiMutatorChance(generatedChance); RepoMutators.Logger.LogWarning("The minimum allowed value is 0."); } return false; } if (generatedChance > 100) { LogMisconfiguredMultiMutatorChance(generatedChance); RepoMutators.Logger.LogWarning("The maximum allowed value is 100."); return true; } if (generatedChance != 100) { return randomProvider.RandomRangeInt(1, 101) <= generatedChance; } return true; } protected IMutator GetSingleMutator(IList<IMutator> mutators) { return PickSingleMutator(mutators.GetSelection(delegate(IMutator mutator) { if (mutator is IMultiMutator multiMutator) { IReadOnlyDictionary<IMutator, IDictionary<string, object>> subMutators = multiMutator.SubMutators; if (subMutators != null) { int count = subMutators.Count; if (count == 1) { goto IL_001f; } } return false; } goto IL_001f; IL_001f: return true; }).ToList(), new MutatorSelectionContext(1u)); } protected IMutator TryPickUserDefinedMultiMutatorOrElseGenerate(IList<IMutator> mutators, int amountOfSubMutators) { IList<IMutator> list = mutators.GetSelection((IMutator mutator) => mutator is IMultiMutator multiMutator && multiMutator.SubMutators.Count == amountOfSubMutators).ToList(); if (list.Select((IMutator m) => m.Settings.Weight).Sum() > 0) { return PickSingleMutator(list, new MutatorSelectionContext(1u)); } RepoMutators.Logger.LogDebug($"No user defined multi-mutator found for {amountOfSubMutators} sub-mutators."); return PickGeneratedMultiMutator(mutators, amountOfSubMutators); } private static void LogMisconfiguredMultiMutatorChance(int generatedChance) { RepoMutators.Logger.LogWarning($"Generated multi-mutator chance is misconfigured: {generatedChance}."); } } internal class NoneScalingSelectionStrategy : MutatorSelectionStrategy { public NoneScalingSelectionStrategy(GeneratedMultiMutatorSelectionRulesRegistry multiRegistry, SingleMutatorSelectionRulesRegistry singleRegistry, IRepeatSelectionTracker repeatSelectionTracker, IRandomProvider randomProvider, NopMutator fallbackMutator) : base(multiRegistry, singleRegistry, repeatSelectionTracker, randomProvider, fallbackMutator) { } public sealed override IMutator Execute(IList<IMutator> mutators) { return PickSingleMutator(mutators.GetSelection(delegate(IMutator mutator) { if (mutator is IMultiMutator multiMutator) { IReadOnlyDictionary<IMutator, IDictionary<string, object>> subMutators = multiMutator.SubMutators; if (subMutators != null) { int count = subMutators.Count; if (count == 1) { goto IL_001f; } } return false; } goto IL_001f; IL_001f: return true; }), new MutatorSelectionContext(1u)); } } internal class RandomScalingSelectionStrategy : MutatorSelectionStrategy { private readonly ModSettings.RandomSettings _randomSettings; public RandomScalingSelectionStrategy(GeneratedMultiMutatorSelectionRulesRegistry multiRegistry, SingleMutatorSelectionRulesRegistry singleRegistry, ModSettings.RandomSettings randomSettings, IRepeatSelectionTracker repeatSelectionTracker, IRandomProvider randomProvider, NopMutator fallbackMutator) : base(multiRegistry, singleRegistry, repeatSelectionTracker, randomProvider, fallbackMutator) { _randomSettings = randomSettings; } public sealed override IMutator Execute(IList<IMutator> mutators) { int randomAmountOfMutatorsBetweenByWeight = GetRandomAmountOfMutatorsBetweenByWeight(); if (randomAmountOfMutatorsBetweenByWeight == 1) { return GetSingleMutator(mutators); } if (!ShouldGenerateMulti(GetRandomGeneratedChance(randomAmountOfMutatorsBetweenByWeight))) { return TryPickUserDefinedMultiMutatorOrElseGenerate(mutators, randomAmountOfMutatorsBetweenByWeight); } return PickGeneratedMultiMutator(mutators, randomAmountOfMutatorsBetweenByWeight); } private int GetRandomGeneratedChance(int amountOfMutators) { return _randomSettings.GetGeneratedChance(amountOfMutators); } private int GetRandomAmountOfMutatorsBetweenByWeight() { int minimumAmount = _randomSettings.MinimumAmount; int maximumAmount = _randomSettings.MaximumAmount; if (minimumAmount < 1 || minimumAmount > ModSettings.MaximumGeneratedActiveSubMutators) { throw new ArgumentException($"Minimum amount of Mutators should be between 1 and {ModSettings.MaximumGeneratedActiveSubMutators}", "minimumAmount"); } if (maximumAmount < 1 || maximumAmount > ModSettings.MaximumGeneratedActiveSubMutators) { throw new ArgumentException($"Maximum amount of Mutators should be between 1 and {ModSettings.MaximumGeneratedActiveSubMutators}", "maximumAmount"); } if (minimumAmount > maximumAmount) { throw new ArgumentException("Minimum amount of Mutators should not be greater than maximum amount of Mutators.", "minimumAmount"); } int num = 0; for (int i = minimumAmount; i <= maximumAmount; i++) { num += _randomSettings.GetWeight(i); } if (num <= 0) { return randomProvider.RandomRangeInt(minimumAmount, maximumAmount + 1); } int num2 = randomProvider.RandomRangeInt(0, num); int num3 = 0; for (int j = minimumAmount; j <= maximumAmount; j++) { num3 += _randomSettings.GetWeight(j); if (num2 < num3) { return j; } } return maximumAmount; } } } namespace Mutators.Rules { public static class MultiMutatorRules { public static Func<IReadOnlyCollection<string>, string, bool> RequiresAmountOfOtherMutatorsRule(string namespacedName, int amount) { return (IReadOnlyCollection<string> pickedList, string candidate) => namespacedName != candidate || pickedList.Count >= amount; } public static Func<IReadOnlyCollection<string>, string, bool> MutualExclusionRule(string firstNamespacedName, string secondNamespacedName) { return (IReadOnlyCollection<string> pickedList, string candidate) => (!pickedList.Contains(firstNamespacedName) || !string.Equals(candidate, secondNamespacedName, StringComparison.OrdinalIgnoreCase)) && (!pickedList.Contains(secondNamespacedName) || !string.Equals(candidate, firstNamespacedName, StringComparison.OrdinalIgnoreCase)); } } public static class MultiMutatorRuleType { public const string Exclusion = "Exclusion"; public const string MutualExclusion = "Mutual Exclusion"; public const string RequiresAmountOfOtherMutators = "Requires Amount Of Other Mutators"; } public static class SingleMutatorRules { public static Predicate<IMutator> ExclusionRule(string namespacedName) { return (IMutator mutator) => mutator.NamespacedName != namespacedName; } } public static class SingleMutatorRuleType { public const string Exclusion = "Exclusion"; } } namespace Mutators.Rules.Registries { public class GeneratedMultiMutatorSelectionRulesRegistry : MutatorSelectionRulesRegistry<Func<IReadOnlyCollection<string>, string, bool>, Func<IReadOnlyCollection<IMutator>, IMutator, bool>> { internal GeneratedMultiMutatorSelectionRulesRegistry() { } internal bool RunRules(ICollection<IMutator> pickedMutators, IMutator mutator) { ReadOnlyCollection<string> pickedMutatorNamesReadOnly = pickedMutators.Select((IMutator m) => m.NamespacedName).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToList() .AsReadOnly(); IReadOnlyCollection<IMutator> pickedMutatorsReadonly = new ReadOnlyCollection<IMutator>(pickedMutators.ToList()); if (mutatorSelectionRulesExtended.Values.All((Func<IReadOnlyCollection<IMutator>, IMutator, bool> rule) => rule(pickedMutatorsReadonly, mutator))) { return mutatorSelectionRules.Values.All((Func<IReadOnlyCollection<string>, string, bool> rule) => rule(pickedMutatorNamesReadOnly, mutator.NamespacedName)); } return false; } } public abstract class MutatorSelectionRulesRegistry<T, TE> { protected readonly IDictionary<string, T> mutatorSelectionRules = new Dictionary<string, T>(); protected readonly IDictionary<string, TE> mutatorSelectionRulesExtended = new Dictionary<string, TE>(); public void Register(string key, T rule) { ValidateAndThen(key, delegate { mutatorSelectionRules.Add(key, rule); }); } public void Register(string key, TE rule) { ValidateAndThen(key, delegate { mutatorSelectionRulesExtended.Add(key, rule); }); } public bool Unregister(string key) { if (!mutatorSelectionRules.Remove(key)) { return mutatorSelectionRulesExtended.Remove(key); } return true; } public bool IsRuleRegistered(string key) { if (!mutatorSelectionRules.ContainsKey(key)) { return mutatorSelectionRulesExtended.ContainsKey(key); } return true; } private void ValidateAndThen(string key, Action action) { if (IsRuleRegistered(key)) { throw new InvalidOperationException("Rule with key " + key + " already exists"); } action(); } } public class SingleMutatorSelectionRulesRegistry : MutatorSelectionRulesRegistry<Predicate<string>, Predicate<IMutator>> { internal bool RunRules(IMutator mutator) { if (mutatorSelectionRulesExtended.Values.All((Predicate<IMutator> rule) => rule(mutator))) { return mutatorSelectionRules.Values.All((Predicate<string> rule) => rule(mutator.NamespacedName)); } return false; } } } namespace Mutators.Rules.Loaders { public interface IRuleLoader<T> { void AddDefaultRule(JsonMutatorRule jsonRule); void AddDefaultRules(params JsonMutatorRule[] jsonRules); void AddRuleStrategy(IRuleLoadingStrategy<T> strategy); } internal sealed class MultiMutatorRuleLoader : RuleLoader<Func<IReadOnlyCollection<string>, string, bool>> { private static readonly string MultiMutatorRulesPath = Path.Combine(RepoMutators.ConfigPath, "multi-mutator-rules.json"); protected override JsonMutatorRule[] CreateDefaultRules() { return Array.Empty<JsonMutatorRule>(); } protected override string GetRulesPath() { return MultiMutatorRulesPath; } } public abstract class RuleLoader<T> : IRuleLoader<T> { protected readonly IDictionary<string, JsonMutatorRule> defaultRules = new Dictionary<string, JsonMutatorRule>(); protected readonly IDictionary<string, IRuleLoadingStrategy<T>> strategies = new Dictionary<string, IRuleLoadingStrategy<T>>(); public IDictionary<string, T> Load() { IDictionary<string, T> executableRules = new Dictionary<string, T>(); CreateDefaultFilesAndThen(RepoMutators.ConfigPath, delegate { JsonMutatorRule[] array = ReadRules(GetRulesPath()); JsonMutatorRule[] array2 = array; foreach (JsonMutatorRule jsonMutatorRule in array2) { defaultRules[jsonMutatorRule.Key] = jsonMutatorRule; if (!strategies.TryGetValue(jsonMutatorRule.Type, out IRuleLoadingStrategy<T> value)) { RepoMutators.Logger.LogError("Skipping invalid rule " + jsonMutatorRule.Key + ": unknown rule type " + jsonMutatorRule.Type); } else if (!jsonMutatorRule.Enabled) { RepoMutators.Logger.LogDebug("Skipping disabled rule " + jsonMutatorRule.Key); } else if (!value.Validate(jsonMutatorRule)) { RepoMutators.Logger.LogWarning("Rule " + jsonMutatorRule.Key + " has an invalid configuration, skipping..."); } else { executableRules.Add(jsonMutatorRule.Key, value.Load(jsonMutatorRule.Mutators, jsonMutatorRule.Arguments)); RepoMutators.Logger.LogDebug("Successfully loaded rule " + jsonMutatorRule.Key + " with strategy " + value.Key + "!"); } } }); WriteRules(GetRulesPath(), GetSortedDefaultRules()); return executableRules; } protected void CreateDefaultFilesAndThen(string configPath, Action then) { if (!Directory.Exists(configPath)) { Directory.CreateDirectory(configPath); } if (!File.Exists(GetRulesPath())) { WriteRules(GetRulesPath(), GetSortedDefaultRules()); } then(); } private JsonMutatorRule[] GetSortedDefaultRules() { return (from kv in defaultRules select kv.Value into rule orderby rule.Enabled, rule.Key select rule).ToArray(); } public void AddDefaultRule(JsonMutatorRule jsonRule) { defaultRules.Add(jsonRule.Key, jsonRule); } public void AddDefaultRules(params JsonMutatorRule[] jsonRules) { foreach (JsonMutatorRule jsonRule in jsonRules) { AddDefaultRule(jsonRule); } } public void AddRuleStrategy(IRuleLoadingStrategy<T> strategy) { strategies.Add(strategy.Key, strategy); } protected JsonMutatorRule[] ReadRules(string path) { string text = File.ReadAllText(path); return JsonConvert.DeserializeObject<JsonMutatorRule[]>(text) ?? CreateDefaultRules(); } protected void WriteRules(string path, JsonMutatorRule[] rules) { RepoMutators.Logger.LogDebug("Writing rules to " + path); File.WriteAllText(path, JsonConvert.SerializeObject((object)rules, (Formatting)1)); } protected abstract JsonMutatorRule[] CreateDefaultRules(); protected abstract string GetRulesPath(); } internal sealed class SingleMutatorRuleLoader : RuleLoader<Predicate<string>> { private static readonly string SingleMutatorRulesPath = Path.Combine(RepoMutators.ConfigPath, "single-mutator-rules.json"); protected override JsonMutatorRule[] CreateDefaultRules() { return Array.Empty<JsonMutatorRule>(); } protected override string GetRulesPath() { return SingleMutatorRulesPath; } private void ValidateAndRegisterExclusionRul