Decompiled source of MimesisPlayerEnhancement v26.8.2
MimesisPlayerEnhancement.dll
Decompiled 2 days 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.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; using System.Collections.Specialized; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Net; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using Bifrost.ConstEnum; using Bifrost.Cooked; using Cysharp.Threading.Tasks; using DLAgent; using DunGen; using FishNet.Object; using FishNet.Object.Synchronizing; using FishySteamworks.Server; using HarmonyLib; using MelonLoader; using MelonLoader.InternalUtils; using MelonLoader.Logging; using MelonLoader.Pastel; using MelonLoader.Preferences; using MelonLoader.Utils; using Microsoft.CodeAnalysis; using MimesisPlayerEnhancement; using MimesisPlayerEnhancement.Config; using MimesisPlayerEnhancement.Config.HostConfigSync; using MimesisPlayerEnhancement.Config.Models; using MimesisPlayerEnhancement.Config.QuickSettings; using MimesisPlayerEnhancement.Features.DungeonRandomizer; using MimesisPlayerEnhancement.Features.DungeonRandomizer.Patches; using MimesisPlayerEnhancement.Features.DungeonTime; using MimesisPlayerEnhancement.Features.Economy; using MimesisPlayerEnhancement.Features.ExtendedSaveSlots; using MimesisPlayerEnhancement.Features.JoinAnytime; using MimesisPlayerEnhancement.Features.LootMultiplicator; using MimesisPlayerEnhancement.Features.LootMultiplicator.Patches; using MimesisPlayerEnhancement.Features.MimicTuning; using MimesisPlayerEnhancement.Features.MimicTuning.MimicEmoteProps; using MimesisPlayerEnhancement.Features.MimicTuning.MimicHorn; using MimesisPlayerEnhancement.Features.MimicTuning.MimicInventoryCopy; using MimesisPlayerEnhancement.Features.MimicTuning.MimicPossession; using MimesisPlayerEnhancement.Features.MimicTuning.MimicSocial; using MimesisPlayerEnhancement.Features.MimicTuning.MimicTrust; using MimesisPlayerEnhancement.Features.MimicTuning.MimicVoiceTuning; using MimesisPlayerEnhancement.Features.MimicTuning.Patches; using MimesisPlayerEnhancement.Features.ModVersionDisplay; using MimesisPlayerEnhancement.Features.MorePlayers; using MimesisPlayerEnhancement.Features.MoreVoices; using MimesisPlayerEnhancement.Features.Persistence; using MimesisPlayerEnhancement.Features.Persistence.Patches; using MimesisPlayerEnhancement.Features.PlayerAnnouncements; using MimesisPlayerEnhancement.Features.PlayerTuning; using MimesisPlayerEnhancement.Features.Privacy; using MimesisPlayerEnhancement.Features.SavegamePreparation; using MimesisPlayerEnhancement.Features.SpawnScaling; using MimesisPlayerEnhancement.Features.Statistics; using MimesisPlayerEnhancement.Features.Statistics.Models; using MimesisPlayerEnhancement.Features.UserInterface; using MimesisPlayerEnhancement.Features.UserInterface.CustomLoadingScreen; using MimesisPlayerEnhancement.Features.UserInterface.DiscoBallSound; using MimesisPlayerEnhancement.Features.UserInterface.FpsUi; using MimesisPlayerEnhancement.Features.UserInterface.InGameMenuPlayerList; using MimesisPlayerEnhancement.Features.UserInterface.InventoryNumberKeys; using MimesisPlayerEnhancement.Features.UserInterface.InventorySlotOptimization; using MimesisPlayerEnhancement.Features.UserInterface.LoadingWaitMovementLock; using MimesisPlayerEnhancement.Features.UserInterface.LoadingWaitPlayerList; using MimesisPlayerEnhancement.Features.UserInterface.RoundStartSound; using MimesisPlayerEnhancement.Features.UserInterface.SpectatorPlayerList; using MimesisPlayerEnhancement.Features.UserInterface.SpectatorVoiceBalance; using MimesisPlayerEnhancement.Features.UserInterface.SurvivalResultPlayerList; using MimesisPlayerEnhancement.Features.UserInterface.VoiceNoiseGate; using MimesisPlayerEnhancement.Features.UserInterface.WorldOverlays; using MimesisPlayerEnhancement.Features.Weather; using MimesisPlayerEnhancement.Features.WebDashboard; using MimesisPlayerEnhancement.Features.WebDashboard.Debug; using MimesisPlayerEnhancement.Features.WebDashboard.Models; using MimesisPlayerEnhancement.Ui; using MimesisPlayerEnhancement.Ui.MenuMirror; using MimesisPlayerEnhancement.Util; using MimesisPlayerEnhancement.Util.Patches; using MimesisPlayerEnhancement.Util.Players; using Mimic; using Mimic.Actors; using Mimic.Animation; using Mimic.Audio; using Mimic.InputSystem; using Mimic.Voice; using Mimic.Voice.SpeechSystem; using ModUtility; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Newtonsoft.Json.Serialization; using ReluProtocol; using ReluProtocol.C2S; using ReluProtocol.Enum; using ReluReplay; using ReluReplay.Data; using ReluReplay.Recorder; using ReluReplay.Serializer; using ReluReplay.Shared; using ReluServerBase.Threading; using Steamworks; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.Networking; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: MelonInfo(typeof(Mod), "MimesisPlayerEnhancement", "26.8.2", "kalle", null)] [assembly: MelonGame("ReLUGames", "MIMESIS")] [assembly: HarmonyDontPatchAll] [assembly: InternalsVisibleTo("MimesisPlayerEnhancement.Tests")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyVersion("0.0.0.0")] [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 MimesisPlayerEnhancement { internal static class GlobalConfigStore { private const string Feature = "GlobalConfig"; private static SparseTomlConfig.Document? _pendingDoc; private static bool _pendingDocLoaded; private static bool _dirty; internal static bool IsDirty => _dirty; internal static bool HasPendingDocument { get { if (_pendingDocLoaded) { return _pendingDoc != null; } return false; } } internal static SparseTomlConfig.Document Load() { if (string.IsNullOrEmpty(ModConfig.FilePath)) { return new SparseTomlConfig.Document(); } return SparseTomlConfig.Load(AtomicFileIO.ReadText(ModConfig.FilePath, "GlobalConfig")); } internal static bool TryWriteValue(string sectionId, string key, string normalized, out string? error, bool waitForCompletion = false) { error = null; if (!ModConfig.IsInitialized) { error = "Configuration is not initialized."; return false; } if (string.IsNullOrEmpty(ModConfig.FilePath)) { error = "Global config path unavailable."; return false; } if (!ModConfigRegistry.TryGetEntry(sectionId, key, out MelonPreferences_Entry entry) || entry == null) { error = "Unknown setting."; return false; } string rawB = ModConfigRegistry.FormatEntryDefaultValue(entry); EnsurePendingDoc(); SparseTomlConfig.Document pendingDoc = _pendingDoc; if (ModConfigRegistry.RawValuesEqual(sectionId, key, normalized, rawB)) { RemoveKey(pendingDoc, sectionId, key); } else { EnsureSection(pendingDoc, sectionId); pendingDoc.Sections[sectionId][key] = normalized; } _dirty = true; return true; } internal static void RebuildPendingFromMemory() { if (!ModConfig.IsInitialized || SaveSlotConfigStore.IsApplyingOverrides || HostConfigMirror.BlocksGlobalConfigPersistence || _dirty) { return; } SparseTomlConfig.Document document = new SparseTomlConfig.Document(); foreach (string item in ModConfigRegistry.GetSectionOrder()) { foreach (string item2 in ModConfigRegistry.GetEntryOrder(item)) { if (ModConfigRegistry.TryGetEntry(item, item2, out MelonPreferences_Entry entry) && entry != null) { string text = ModConfigRegistry.FormatEntryValue(entry); string rawB = ModConfigRegistry.FormatEntryDefaultValue(entry); if (!ModConfigRegistry.RawValuesEqual(item, item2, text, rawB)) { EnsureSection(document, item); document.Sections[item][item2] = text; } } } } _pendingDoc = document; _pendingDocLoaded = true; _dirty = true; } internal static void FlushIfDirty() { if (_dirty) { ModLog.Debug("GlobalConfig", "Flushing pending global config before reload."); FlushToDisk(); } } internal static void RestorePendingToDisk() { if (HasPendingDocument) { ModLog.Debug("GlobalConfig", "Restoring pending global config after MelonLoader save."); FlushToDisk(waitForCompletion: true, force: true); } } internal static void FlushToDisk(bool waitForCompletion = true, bool force = false) { if (!ModConfig.IsInitialized || string.IsNullOrEmpty(ModConfig.FilePath) || (!force && !_dirty)) { return; } if (!_pendingDocLoaded || _pendingDoc == null) { if (!force) { _dirty = false; } return; } try { string directoryName = Path.GetDirectoryName(ModConfig.FilePath); if (!string.IsNullOrEmpty(directoryName)) { Directory.CreateDirectory(directoryName); } if (SparseTomlConfig.IsEmpty(_pendingDoc) && File.Exists(ModConfig.FilePath)) { AtomicFileIO.Delete(ModConfig.FilePath, "GlobalConfig"); } else if (!SparseTomlConfig.IsEmpty(_pendingDoc)) { AtomicFileIO.WriteText(ModConfig.FilePath, SparseTomlConfig.Serialize(_pendingDoc), "GlobalConfig"); } _dirty = false; } catch (Exception ex) { ModLog.Error("GlobalConfig", "FlushToDisk: " + ex.Message); } } private static void EnsurePendingDoc() { if (!_pendingDocLoaded) { _pendingDoc = Load(); _pendingDocLoaded = true; } } private static void EnsureSection(SparseTomlConfig.Document doc, string sectionId) { if (!doc.Sections.ContainsKey(sectionId)) { doc.SectionOrder.Add(sectionId); doc.Sections[sectionId] = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); } } private static void RemoveKey(SparseTomlConfig.Document doc, string sectionId, string key) { if (doc.Sections.TryGetValue(sectionId, out Dictionary<string, string> value)) { value.Remove(key); if (value.Count == 0) { doc.Sections.Remove(sectionId); doc.SectionOrder.Remove(sectionId); } } } } public static class ModConfig { private const string MainCategoryId = "MimesisPlayerEnhancement"; private static readonly List<MelonPreferences_Entry<float>> FloatEntries = new List<MelonPreferences_Entry<float>>(); public static int Version => ModConfigRegistry.Version; public static bool IsInitialized { get; private set; } public static string FilePath { get; private set; } = ""; public static MelonPreferences_Category MainCategory { get; private set; } = null; public static MelonPreferences_Entry<bool> EnableMorePlayers { get; internal set; } = null; public static MelonPreferences_Entry<int> MaxPlayers { get; internal set; } = null; public static MelonPreferences_Entry<bool> EnableScalingRoundGoals { get; internal set; } = null; public static MelonPreferences_Entry<bool> OverrideMaxStageCount { get; internal set; } = null; public static MelonPreferences_Entry<float> RoundGoalBasePerZone { get; internal set; } = null; public static MelonPreferences_Entry<float> RoundGoalMoneyMultiplier { get; internal set; } = null; public static MelonPreferences_Entry<int> RoundGoalRandomSpreadPercent { get; internal set; } = null; public static MelonPreferences_Entry<float> RoundGoalCurveExponent { get; internal set; } = null; public static MelonPreferences_Entry<bool> EnableMoreVoices { get; internal set; } = null; public static MelonPreferences_Entry<bool> UnifyIndoorOutdoorVoices { get; internal set; } = null; public static MelonPreferences_Entry<int> MaxIndoorVoiceEvents { get; internal set; } = null; public static MelonPreferences_Entry<int> MaxDeathMatchVoiceEvents { get; internal set; } = null; public static MelonPreferences_Entry<int> MaxOutdoorVoiceEvents { get; internal set; } = null; public static MelonPreferences_Entry<bool> RecordVoiceInMaintenance { get; internal set; } = null; public static MelonPreferences_Entry<bool> RecordVoiceInTram { get; internal set; } = null; public static MelonPreferences_Entry<bool> RecordVoiceDuringMimicPossession { get; internal set; } = null; public static MelonPreferences_Entry<bool> EnableVoicePerformanceCache { get; internal set; } = null; public static MelonPreferences_Entry<int> VoiceClipCacheMaxEntries { get; internal set; } = null; public static MelonPreferences_Entry<bool> EnablePersistence { get; internal set; } = null; public static MelonPreferences_Entry<bool> EnableStatistics { get; internal set; } = null; public static MelonPreferences_Entry<int> SessionReconnectGraceMinutes { get; internal set; } = null; public static MelonPreferences_Entry<bool> ShowStatisticsToasts { get; internal set; } = null; public static MelonPreferences_Entry<bool> ShowPlayerAnnouncements { get; internal set; } = null; public static MelonPreferences_Entry<bool> EnableJoinAnytime { get; internal set; } = null; public static MelonPreferences_Entry<int> JoinConnectionGraceSeconds { get; internal set; } = null; public static MelonPreferences_Entry<bool> EnableSpawnScaling { get; internal set; } = null; public static MelonPreferences_Entry<int> SpawnScalingBaselinePlayerCount { get; internal set; } = null; public static MelonPreferences_Entry<float> MimicSpawnMultiplier { get; internal set; } = null; public static MelonPreferences_Entry<float> MimicSpawnPerPlayerMultiplier { get; internal set; } = null; public static MelonPreferences_Entry<float> BossSpawnMultiplier { get; internal set; } = null; public static MelonPreferences_Entry<float> BossSpawnPerPlayerMultiplier { get; internal set; } = null; public static MelonPreferences_Entry<float> GruntSpawnMultiplier { get; internal set; } = null; public static MelonPreferences_Entry<float> GruntSpawnPerPlayerMultiplier { get; internal set; } = null; public static MelonPreferences_Entry<float> SpecialSpawnMultiplier { get; internal set; } = null; public static MelonPreferences_Entry<float> SpecialSpawnPerPlayerMultiplier { get; internal set; } = null; public static MelonPreferences_Entry<float> TrapSpawnMultiplier { get; internal set; } = null; public static MelonPreferences_Entry<float> TrapSpawnPerPlayerMultiplier { get; internal set; } = null; public static MelonPreferences_Entry<string> TrapRespawnMode { get; internal set; } = null; public static MelonPreferences_Entry<float> TrapRespawnDelaySeconds { get; internal set; } = null; public static MelonPreferences_Entry<float> TrapRespawnDelayMinSeconds { get; internal set; } = null; public static MelonPreferences_Entry<float> TrapRespawnDelayMaxSeconds { get; internal set; } = null; public static MelonPreferences_Entry<float> TrapRespawnMinPlayerDistanceMeters { get; internal set; } = null; public static MelonPreferences_Entry<string> MimicWaveMode { get; internal set; } = null; public static MelonPreferences_Entry<float> MimicWaveInitialDelaySeconds { get; internal set; } = null; public static MelonPreferences_Entry<float> MimicWaveInitialDelayMinSeconds { get; internal set; } = null; public static MelonPreferences_Entry<float> MimicWaveInitialDelayMaxSeconds { get; internal set; } = null; public static MelonPreferences_Entry<float> MimicWaveIntervalSeconds { get; internal set; } = null; public static MelonPreferences_Entry<float> MimicWaveIntervalMinSeconds { get; internal set; } = null; public static MelonPreferences_Entry<float> MimicWaveIntervalMaxSeconds { get; internal set; } = null; public static MelonPreferences_Entry<string> GruntWaveMode { get; internal set; } = null; public static MelonPreferences_Entry<float> GruntWaveInitialDelaySeconds { get; internal set; } = null; public static MelonPreferences_Entry<float> GruntWaveInitialDelayMinSeconds { get; internal set; } = null; public static MelonPreferences_Entry<float> GruntWaveInitialDelayMaxSeconds { get; internal set; } = null; public static MelonPreferences_Entry<float> GruntWaveIntervalSeconds { get; internal set; } = null; public static MelonPreferences_Entry<float> GruntWaveIntervalMinSeconds { get; internal set; } = null; public static MelonPreferences_Entry<float> GruntWaveIntervalMaxSeconds { get; internal set; } = null; public static MelonPreferences_Entry<float> BonusEncounterDelayMinSeconds { get; internal set; } = null; public static MelonPreferences_Entry<float> BonusEncounterDelayMaxSeconds { get; internal set; } = null; public static MelonPreferences_Entry<float> BonusEncounterMinPlayerDistanceMeters { get; internal set; } = null; public static MelonPreferences_Entry<float> OtherSpawnMultiplier { get; internal set; } = null; public static MelonPreferences_Entry<float> OtherSpawnPerPlayerMultiplier { get; internal set; } = null; public static MelonPreferences_Entry<bool> EnableLootMultiplicator { get; internal set; } = null; public static MelonPreferences_Entry<int> LootMultiplicatorBaselinePlayerCount { get; internal set; } = null; public static MelonPreferences_Entry<float> MapLootMultiplier { get; internal set; } = null; public static MelonPreferences_Entry<float> MapLootPerPlayerMultiplier { get; internal set; } = null; public static MelonPreferences_Entry<float> DropLootMultiplier { get; internal set; } = null; public static MelonPreferences_Entry<float> DropLootPerPlayerMultiplier { get; internal set; } = null; public static MelonPreferences_Entry<string> LootItemFilterMode { get; internal set; } = null; public static MelonPreferences_Entry<string> LootAllowlist { get; internal set; } = null; public static MelonPreferences_Entry<string> LootBlocklist { get; internal set; } = null; public static MelonPreferences_Entry<bool> AutoScaleMapLootBudgetForFilter { get; internal set; } = null; public static MelonPreferences_Entry<int> ConvertFakeActorDyingDropChancePercent { get; internal set; } = null; public static MelonPreferences_Entry<bool> EnableEconomy { get; internal set; } = null; public static MelonPreferences_Entry<int> EconomyBaselinePlayerCount { get; internal set; } = null; public static MelonPreferences_Entry<int> StartupMoney { get; internal set; } = null; public static MelonPreferences_Entry<int> StartingZone { get; internal set; } = null; public static MelonPreferences_Entry<bool> EnableUpgradeTramHorn { get; internal set; } = null; public static MelonPreferences_Entry<bool> EnableUpgradeScrapScanner { get; internal set; } = null; public static MelonPreferences_Entry<bool> EnableUpgradeTramBooster { get; internal set; } = null; public static MelonPreferences_Entry<bool> EnableUpgradeTramLight { get; internal set; } = null; public static MelonPreferences_Entry<float> ScrapSellValueMultiplier { get; internal set; } = null; public static MelonPreferences_Entry<float> ScrapSellValuePerPlayerMultiplier { get; internal set; } = null; public static MelonPreferences_Entry<float> ShopBuyPriceMultiplier { get; internal set; } = null; public static MelonPreferences_Entry<float> ShopBuyPricePerPlayerMultiplier { get; internal set; } = null; public static MelonPreferences_Entry<int> ShopDiscountMinPercent { get; internal set; } = null; public static MelonPreferences_Entry<int> ShopDiscountMaxPercent { get; internal set; } = null; public static MelonPreferences_Entry<int> ShopDiscountChancePercent { get; internal set; } = null; public static MelonPreferences_Entry<float> ReinforcePriceMultiplier { get; internal set; } = null; public static MelonPreferences_Entry<float> ReinforcePricePerPlayerMultiplier { get; internal set; } = null; public static MelonPreferences_Entry<bool> RetainUnspentCurrencyBetweenCycles { get; internal set; } = null; public static MelonPreferences_Entry<bool> EnableDungeonTime { get; internal set; } = null; public static MelonPreferences_Entry<int> DungeonTimeBaselinePlayerCount { get; internal set; } = null; public static MelonPreferences_Entry<float> ExtraShiftSecondsPerPlayerAboveBaseline { get; internal set; } = null; public static MelonPreferences_Entry<string> StartTimePreset { get; internal set; } = null; public static MelonPreferences_Entry<float> TimeMultiplier { get; internal set; } = null; public static MelonPreferences_Entry<bool> EnableRealtimeTramClock { get; internal set; } = null; public static MelonPreferences_Entry<bool> EnableMimicPossessionTuning { get; internal set; } = null; public static MelonPreferences_Entry<bool> RandomizeMimicPossessionDuration { get; internal set; } = null; public static MelonPreferences_Entry<float> MimicPossessionMinTimeSeconds { get; internal set; } = null; public static MelonPreferences_Entry<float> MimicPossessionMaxTimeSeconds { get; internal set; } = null; public static MelonPreferences_Entry<float> MimicPossessionCooltimeMultiplier { get; internal set; } = null; public static MelonPreferences_Entry<bool> EnableMimicTuning { get; internal set; } = null; public static MelonPreferences_Entry<string> MimicVoiceTuningMode { get; internal set; } = null; public static MelonPreferences_Entry<float> PeriodicVoiceIntervalMultiplier { get; internal set; } = null; public static MelonPreferences_Entry<int> PlayerVoiceResponseChancePercent { get; internal set; } = null; public static MelonPreferences_Entry<float> PlayerVoiceResponseCooldownSeconds { get; internal set; } = null; public static MelonPreferences_Entry<float> PlayerVoiceResponseDelayMinSeconds { get; internal set; } = null; public static MelonPreferences_Entry<float> PlayerVoiceResponseDelayMaxSeconds { get; internal set; } = null; public static MelonPreferences_Entry<float> PlayerVoiceResponseMaxDistance { get; internal set; } = null; public static MelonPreferences_Entry<string> MimicInventoryCopyMode { get; internal set; } = null; public static MelonPreferences_Entry<string> MimicInventoryCopyPickRule { get; internal set; } = null; public static MelonPreferences_Entry<float> ClipReuseCooldownSeconds { get; internal set; } = null; public static MelonPreferences_Entry<float> DeathMatchClipReuseCooldownSeconds { get; internal set; } = null; public static MelonPreferences_Entry<float> SpeakAudienceRangeMeters { get; internal set; } = null; public static MelonPreferences_Entry<string> PostReplyIntervalMode { get; internal set; } = null; public static MelonPreferences_Entry<float> PostReplyIntervalFixedSeconds { get; internal set; } = null; public static MelonPreferences_Entry<float> PostReplyIntervalMinSeconds { get; internal set; } = null; public static MelonPreferences_Entry<float> PostReplyIntervalMaxSeconds { get; internal set; } = null; public static MelonPreferences_Entry<int> MinRequiredSpeechClips { get; internal set; } = null; public static MelonPreferences_Entry<string> HearOwnVoiceFromMimic { get; internal set; } = null; public static MelonPreferences_Entry<string> VoiceInitIntervalMode { get; internal set; } = null; public static MelonPreferences_Entry<float> VoiceInitIntervalMin { get; internal set; } = null; public static MelonPreferences_Entry<float> VoiceInitIntervalMax { get; internal set; } = null; public static MelonPreferences_Entry<string> VoicePeriodicIntervalMode { get; internal set; } = null; public static MelonPreferences_Entry<float> VoicePeriodicIntervalMin { get; internal set; } = null; public static MelonPreferences_Entry<float> VoicePeriodicIntervalMax { get; internal set; } = null; public static MelonPreferences_Entry<string> VoiceDeathMatchIntervalMode { get; internal set; } = null; public static MelonPreferences_Entry<float> VoiceDeathMatchIntervalMin { get; internal set; } = null; public static MelonPreferences_Entry<float> VoiceDeathMatchIntervalMax { get; internal set; } = null; public static MelonPreferences_Entry<string> MimicTrustMode { get; internal set; } = null; public static MelonPreferences_Entry<float> TrustOutdoorMultiplier { get; internal set; } = null; public static MelonPreferences_Entry<float> TrustLookingDelta { get; internal set; } = null; public static MelonPreferences_Entry<float> TrustNotLookingDelta { get; internal set; } = null; public static MelonPreferences_Entry<float> TrustApproachDelta { get; internal set; } = null; public static MelonPreferences_Entry<float> TrustMaintainDelta { get; internal set; } = null; public static MelonPreferences_Entry<float> TrustWalkAwayDelta { get; internal set; } = null; public static MelonPreferences_Entry<float> TrustSprintAwayDelta { get; internal set; } = null; public static MelonPreferences_Entry<float> TrustHitDamageMultiplier { get; internal set; } = null; public static MelonPreferences_Entry<float> TrustFriendlyThreshold { get; internal set; } = null; public static MelonPreferences_Entry<float> TrustDistrustThreshold { get; internal set; } = null; public static MelonPreferences_Entry<string> TrustScoreValueMode { get; internal set; } = null; public static MelonPreferences_Entry<float> TrustInitialFixed { get; internal set; } = null; public static MelonPreferences_Entry<float> TrustInitialRandomMin { get; internal set; } = null; public static MelonPreferences_Entry<float> TrustInitialRandomMax { get; internal set; } = null; public static MelonPreferences_Entry<float> TrustBehaviorFixed { get; internal set; } = null; public static MelonPreferences_Entry<float> TrustBehaviorRandomMin { get; internal set; } = null; public static MelonPreferences_Entry<float> TrustBehaviorRandomMax { get; internal set; } = null; public static MelonPreferences_Entry<float> ChaseActivationDistanceMeters { get; internal set; } = null; public static MelonPreferences_Entry<float> ChaseForceRunDistanceMeters { get; internal set; } = null; public static MelonPreferences_Entry<string> MimicSocialMode { get; internal set; } = null; public static MelonPreferences_Entry<float> MimicRunawayChance { get; internal set; } = null; public static MelonPreferences_Entry<int> JumpCopyChancePercent { get; internal set; } = null; public static MelonPreferences_Entry<int> SlotFollowChangeChancePercent { get; internal set; } = null; public static MelonPreferences_Entry<float> PossessionRangeMeters { get; internal set; } = null; public static MelonPreferences_Entry<string> PossessionBtGateMode { get; internal set; } = null; public static MelonPreferences_Entry<string> MimicEmotePropsMode { get; internal set; } = null; public static MelonPreferences_Entry<int> EmoteRespondChancePercent { get; internal set; } = null; public static MelonPreferences_Entry<int> EmoteSuggestChancePercent { get; internal set; } = null; public static MelonPreferences_Entry<int> ReactToSprinklerChancePercent { get; internal set; } = null; public static MelonPreferences_Entry<int> UseTrapSwitchChancePercent { get; internal set; } = null; public static MelonPreferences_Entry<int> UseChargerChancePercent { get; internal set; } = null; public static MelonPreferences_Entry<int> UseTransmitterChancePercent { get; internal set; } = null; public static MelonPreferences_Entry<int> UseShutterSwitchChancePercent { get; internal set; } = null; public static MelonPreferences_Entry<string> HornImitationMode { get; internal set; } = null; public static MelonPreferences_Entry<bool> AllowHornImitation { get; internal set; } = null; public static MelonPreferences_Entry<float> HornMaxRecordSeconds { get; internal set; } = null; public static MelonPreferences_Entry<float> HornRecordingGapSeconds { get; internal set; } = null; public static MelonPreferences_Entry<int> HornMaxStoredRecords { get; internal set; } = null; public static MelonPreferences_Entry<bool> EnablePlayerTuning { get; internal set; } = null; public static MelonPreferences_Entry<float> MoveSpeedMultiplier { get; internal set; } = null; public static MelonPreferences_Entry<float> NoClipSpeedMultiplier { get; internal set; } = null; public static MelonPreferences_Entry<float> MaxStaminaMultiplier { get; internal set; } = null; public static MelonPreferences_Entry<float> StaminaDrainMultiplier { get; internal set; } = null; public static MelonPreferences_Entry<float> StaminaRegenMultiplier { get; internal set; } = null; public static MelonPreferences_Entry<float> StaminaRegenDelayMultiplier { get; internal set; } = null; public static MelonPreferences_Entry<float> MaxCarryWeightMultiplier { get; internal set; } = null; public static MelonPreferences_Entry<bool> DisablePlayerCollision { get; internal set; } = null; public static MelonPreferences_Entry<bool> EnableDungeonRandomizer { get; internal set; } = null; public static MelonPreferences_Entry<bool> RandomizeDungeonPick { get; internal set; } = null; public static MelonPreferences_Entry<string> DungeonPickPoolMode { get; internal set; } = null; public static MelonPreferences_Entry<string> DungeonAllowlist { get; internal set; } = null; public static MelonPreferences_Entry<string> DungeonBlocklist { get; internal set; } = null; public static MelonPreferences_Entry<bool> IgnoreDungeonExcludeList { get; internal set; } = null; public static MelonPreferences_Entry<bool> RandomizeMapVariant { get; internal set; } = null; public static MelonPreferences_Entry<string> DungeonSeedFlavor { get; internal set; } = null; public static MelonPreferences_Entry<bool> EnableWeather { get; internal set; } = null; public static MelonPreferences_Entry<string> WeatherMode { get; internal set; } = null; public static MelonPreferences_Entry<string> FixedWeatherPreset { get; internal set; } = null; public static MelonPreferences_Entry<bool> DisableRandomWeather { get; internal set; } = null; public static MelonPreferences_Entry<string> WeatherCyclePresets { get; internal set; } = null; public static MelonPreferences_Entry<float> WeatherCycleMinDelaySeconds { get; internal set; } = null; public static MelonPreferences_Entry<float> WeatherCycleMaxDelaySeconds { get; internal set; } = null; public static MelonPreferences_Entry<string> WebDashboardListenAddress { get; internal set; } = null; public static MelonPreferences_Entry<int> WebDashboardListenPort { get; internal set; } = null; public static MelonPreferences_Entry<bool> EnableDebugLogging { get; private set; } = null; public static MelonPreferences_Entry<string> LastSeenModVersion { get; private set; } = null; public static MelonPreferences_Entry<float> ModToastDurationSeconds { get; internal set; } = null; public static MelonPreferences_Entry<bool> EnableExtendedSaveSlots { get; internal set; } = null; public static MelonPreferences_Entry<bool> EnableExtendedSpectatorPlayerList { get; internal set; } = null; public static MelonPreferences_Entry<bool> EnableLoadingWaitPlayerList { get; internal set; } = null; public static MelonPreferences_Entry<bool> EnableExtendedInGameMenuPlayerList { get; internal set; } = null; public static MelonPreferences_Entry<bool> EnableDamageHealthGlow { get; internal set; } = null; public static MelonPreferences_Entry<bool> EnableFloatingDamageNumbers { get; internal set; } = null; public static MelonPreferences_Entry<float> FloatingDamageDurationSeconds { get; internal set; } = null; public static MelonPreferences_Entry<bool> EnableFpsUi { get; internal set; } = null; public static MelonPreferences_Entry<bool> EnableFpsUiInventoryNetWorth { get; internal set; } = null; public static MelonPreferences_Entry<bool> EnableInventorySlotOptimization { get; internal set; } = null; public static MelonPreferences_Entry<string> InventoryPickupSelectMode { get; internal set; } = null; public static MelonPreferences_Entry<bool> EnableInventorySelectNextOnRemove { get; internal set; } = null; public static MelonPreferences_Entry<bool> EnableInventoryNumberKeySelection { get; internal set; } = null; public static MelonPreferences_Entry<string> RoundStartSoundMode { get; internal set; } = null; public static MelonPreferences_Entry<string> RoundStartSoundVariant { get; internal set; } = null; public static MelonPreferences_Entry<string> RoundStartSoundRandomPool { get; internal set; } = null; public static MelonPreferences_Entry<float> RoundStartSoundVolume { get; internal set; } = null; public static MelonPreferences_Entry<string> DiscoBallSoundMode { get; internal set; } = null; public static MelonPreferences_Entry<string> DiscoBallSoundVariant { get; internal set; } = null; public static MelonPreferences_Entry<string> DiscoBallSoundRandomPool { get; internal set; } = null; public static MelonPreferences_Entry<float> DiscoBallSoundVolume { get; internal set; } = null; public static MelonPreferences_Entry<string> CustomLoadingScreenMode { get; internal set; } = null; public static MelonPreferences_Entry<string> CustomLoadingScreenVariant { get; internal set; } = null; public static MelonPreferences_Entry<string> CustomLoadingScreenRandomPool { get; internal set; } = null; public static MelonPreferences_Entry<bool> CustomLoadingScreenMotion { get; internal set; } = null; public static MelonPreferences_Entry<string> SpectatorVoiceBalanceMode { get; internal set; } = null; public static MelonPreferences_Entry<float> SpectatorVoiceAttenuation { get; internal set; } = null; public static MelonPreferences_Entry<float> SpectatorVoiceDuckLevel { get; internal set; } = null; public static MelonPreferences_Entry<bool> EnableVoiceNoiseGate { get; internal set; } = null; public static MelonPreferences_Entry<float> VoiceNoiseGateStrength { get; internal set; } = null; public static MelonPreferences_Entry<bool> EnablePrivacy { get; internal set; } = null; public static MelonPreferences_Entry<bool> BlockReluTelemetry { get; internal set; } = null; public static MelonPreferences_Entry<bool> BlockReplayUpload { get; internal set; } = null; public static MelonPreferences_Entry<bool> BlockReplayRecording { get; internal set; } = null; public static MelonPreferences_Entry<bool> BlockCrashReports { get; internal set; } = null; public static MelonPreferences_Entry<bool> StripCrashReportMetadata { get; internal set; } = null; public static MelonPreferences_Entry<bool> BlockKraftonGppSdk { get; internal set; } = null; public static event Action<ModConfigChangeInfo>? Changed { add { ModConfigChangeTracker.Changed += value; } remove { ModConfigChangeTracker.Changed -= value; } } internal static void TrackFloatEntry(MelonPreferences_Entry<float> entry) { FloatEntries.Add(entry); } public static void Initialize(Instance logger) { ModConfigRegistry.ClearRegistrationOrder(); FilePath = Path.Combine(MelonEnvironment.UserDataDirectory, "MimesisPlayerEnhancement.cfg"); SparseTomlConfig.RepairTomletCompatibility(FilePath); MainCategory = CreateCategory("MimesisPlayerEnhancement"); PrivacyConfig.CreateCategory(); UiConfig.CreateCategory(); SavegamePreparationConfig.CreateCategory(); JoinAnytimeConfig.CreateCategory(); MorePlayersConfig.CreateCategory(); MoreVoicesConfig.CreateCategory(); PersistenceConfig.CreateCategory(); PlayerAnnouncementsConfig.CreateCategory(); StatisticsConfig.CreateCategory(); DungeonTimeConfig.CreateCategory(); EconomyConfig.CreateCategory(); LootMultiplicatorConfig.CreateCategory(); SpawnScalingConfig.CreateCategory(); DungeonRandomizerConfig.CreateCategory(); MimicTuningConfig.CreateCategory(); PlayerTuningConfig.CreateCategory(); WeatherConfig.CreateCategory(); WebDashboardConfig.CreateCategory(); EnableDebugLogging = CreateTrackedEntry(MainCategory, "EnableDebugLogging", defaultValue: false); LastSeenModVersion = CreateHiddenTrackedEntry(MainCategory, "LastSeenModVersion", string.Empty); PrivacyConfig.CreateEntries(); UiConfig.CreateEntries(); SavegamePreparationConfig.CreateEntries(); JoinAnytimeConfig.CreateEntries(); MorePlayersConfig.CreateEntries(); MoreVoicesConfig.CreateEntries(); PersistenceConfig.CreateEntries(); PlayerAnnouncementsConfig.CreateEntries(); StatisticsConfig.CreateEntries(); DungeonTimeConfig.CreateEntries(); EconomyConfig.CreateEntries(); LootMultiplicatorConfig.CreateEntries(); SpawnScalingConfig.CreateEntries(); DungeonRandomizerConfig.CreateEntries(); MimicTuningConfig.CreateEntries(); PlayerTuningConfig.CreateEntries(); WeatherConfig.CreateEntries(); WebDashboardConfig.CreateEntries(); MorePlayersConfig.SanitizeInitialValues(logger); JoinAnytimeConfig.SanitizeInitialValues(logger); EconomyConfig.SanitizeInitialValues(logger); DungeonRandomizerConfig.SanitizeInitialValues(logger); DungeonTimeConfig.SanitizeInitialValues(logger); WeatherConfig.SanitizeInitialValues(logger); MorePlayersConfig.WireValidation(logger); MoreVoicesConfig.WireValidation(logger); PersistenceConfig.WireValidation(); StatisticsConfig.WireValidation(logger); PlayerAnnouncementsConfig.WireValidation(); JoinAnytimeConfig.WireValidation(logger); SpawnScalingConfig.WireValidation(logger); LootMultiplicatorConfig.WireValidation(logger); EconomyConfig.WireValidation(logger); SavegamePreparationConfig.WireValidation(logger); DungeonTimeConfig.WireValidation(logger); MimicTuningConfig.WireValidation(); PlayerTuningConfig.WireValidation(logger); DungeonRandomizerConfig.WireValidation(logger); WeatherConfig.WireValidation(logger); WebDashboardConfig.WireValidation(logger); UiConfig.WireValidation(logger); PrivacyConfig.WireValidation(); ((MelonEventBase<LemonAction<bool, bool>>)(object)EnableDebugLogging.OnEntryValueChanged).Subscribe((LemonAction<bool, bool>)delegate { NotifyChanged((MelonPreferences_Entry)(object)EnableDebugLogging); }, 0, false); RegisterFloatEntries(); ModConfigFloatHelper.SanitizeAll(FloatEntries); NormalizeSavedFloats(); ModConfigRegistry.Rebuild(); ModConfigRegistry.PurgeObsoleteFromFile(FilePath); MainCategory.LoadFromFile(false); SanitizeFloatEntries(); IsInitialized = true; ModConfigLocalization.Apply(); } public static void SaveToFile() { ModConfigRegistry.SaveToFile(); } internal static void FlushGlobalToDisk() { GlobalConfigStore.FlushToDisk(); } internal static void ReloadGlobalFromFile() { if (IsInitialized) { GlobalConfigStore.FlushIfDirty(); MainCategory.LoadFromFile(false); SanitizeFloatEntries(); } } public static bool TrySetEntryValue(string sectionId, string key, string value, out string? error) { return ModConfigRegistry.TrySetEntryValue(sectionId, key, value, out error); } internal static void NotifyFileReloaded() { ModConfigChangeTracker.NotifyFullReload(); } public static void NormalizeSavedFloats() { ModConfigFloatHelper.NormalizeSavedFloats(FilePath, FloatEntries); } internal static void SanitizeFloatEntries() { ModConfigFloatHelper.SanitizeAll(FloatEntries); } private static void RegisterFloatEntries() { MorePlayersConfig.RegisterFloatEntries(); SpawnScalingConfig.RegisterFloatEntries(); LootMultiplicatorConfig.RegisterFloatEntries(); EconomyConfig.RegisterFloatEntries(); DungeonTimeConfig.RegisterFloatEntries(); MimicTuningConfig.RegisterFloatEntries(); PlayerTuningConfig.RegisterFloatEntries(); WeatherConfig.RegisterFloatEntries(); UiConfig.RegisterFloatEntries(); } internal static void OnSpawnMultiplierChanged(Instance logger, float value, MelonPreferences_Entry<float> entry) { if (value < 0f) { logger.Warning(((MelonPreferences_Entry)entry).Identifier + " must be >= 0; resetting to 0."); entry.Value = 0f; } else { ModConfigFloatHelper.SanitizeEntry(entry); NotifyChanged((MelonPreferences_Entry)(object)entry); } } internal static void OnBaselinePlayerCountChanged(Instance logger, int value, MelonPreferences_Entry<int> entry) { if (value < 1) { logger.Warning(((MelonPreferences_Entry)entry).Identifier + " must be at least 1; resetting to 1."); entry.Value = 1; } else { NotifyChanged((MelonPreferences_Entry)(object)entry); } } internal static MelonPreferences_Category CreateCategory(string id) { string text = ModL10n.GetConfigSectionTitle(id) ?? id; MelonPreferences_Category obj = MelonPreferences.CreateCategory(id, text); obj.SetFilePath(FilePath); ModConfigRegistry.TrackCategory(obj); return obj; } internal static MelonPreferences_Entry<T> CreateTrackedEntry<T>(MelonPreferences_Category category, string identifier, T defaultValue) { string identifier2 = category.Identifier; string text = ModL10n.GetConfigEntryTitle(identifier2, identifier) ?? identifier; string text2 = ModL10n.GetConfigEntryDescription(identifier2, identifier) ?? string.Empty; MelonPreferences_Entry<T> obj = category.CreateEntry<T>(identifier, defaultValue, text, text2, false, false, (ValueValidator)null, (string)null); ModConfigRegistry.TrackEntry((MelonPreferences_Entry)(object)obj); return obj; } internal static MelonPreferences_Entry<T> CreateHiddenTrackedEntry<T>(MelonPreferences_Category category, string identifier, T defaultValue) { MelonPreferences_Entry<T> obj = CreateTrackedEntry(category, identifier, defaultValue); ((MelonPreferences_Entry)obj).IsHidden = true; return obj; } internal static void NotifyChanged(MelonPreferences_Entry entry) { ModConfigChangeTracker.NotifyEntryChanged(entry); } } public sealed class ModConfigKeyChange { public string SectionId { get; set; } = ""; public string Key { get; set; } = ""; } public sealed class ModConfigChangeInfo { public static ModConfigChangeInfo FullReload { get; } = new ModConfigChangeInfo { IsFullReload = true }; public bool IsFullReload { get; set; } public IReadOnlyList<ModConfigKeyChange> ChangedKeys { get; set; } = Array.Empty<ModConfigKeyChange>(); public IEnumerable<string> ChangedSections => ChangedKeys.Select((ModConfigKeyChange change) => change.SectionId).Distinct<string>(StringComparer.OrdinalIgnoreCase); public bool AffectsSection(string sectionId) { if (IsFullReload) { return true; } return ChangedKeys.Any((ModConfigKeyChange change) => string.Equals(change.SectionId, sectionId, StringComparison.OrdinalIgnoreCase)); } } internal static class ModConfigChangeTracker { private sealed class ChangeKeyComparer : IEqualityComparer<(string SectionId, string Key)> { internal static ChangeKeyComparer Instance { get; } = new ChangeKeyComparer(); public bool Equals((string SectionId, string Key) x, (string SectionId, string Key) y) { if (string.Equals(x.SectionId, y.SectionId, StringComparison.OrdinalIgnoreCase)) { return string.Equals(x.Key, y.Key, StringComparison.OrdinalIgnoreCase); } return false; } public int GetHashCode((string SectionId, string Key) obj) { return HashCode.Combine(StringComparer.OrdinalIgnoreCase.GetHashCode(obj.SectionId), StringComparer.OrdinalIgnoreCase.GetHashCode(obj.Key)); } } private static int _batchDepth; private static readonly HashSet<(string SectionId, string Key)> PendingChanges = new HashSet<(string, string)>(ChangeKeyComparer.Instance); internal static event Action<ModConfigChangeInfo>? Changed; internal static void BeginBatch() { _batchDepth++; } internal static void EndBatch() { if (_batchDepth > 0) { _batchDepth--; if (_batchDepth <= 0 && PendingChanges.Count != 0) { FlushPending(); } } } internal static void CancelBatch() { PendingChanges.Clear(); _batchDepth = 0; } internal static void NotifyChange(string sectionId, string key) { if (!string.IsNullOrWhiteSpace(sectionId) && !string.IsNullOrWhiteSpace(key)) { if (_batchDepth > 0) { PendingChanges.Add((sectionId, key)); } else { Dispatch(BuildInfo(new <>z__ReadOnlySingleElementList<(string, string)>((sectionId, key)))); } } } internal static void NotifyEntryChanged(MelonPreferences_Entry entry) { NotifyChange(entry.Category.Identifier, entry.Identifier); } internal static void NotifyFullReload() { PendingChanges.Clear(); _batchDepth = 0; Dispatch(ModConfigChangeInfo.FullReload); } private static void FlushPending() { if (PendingChanges.Count != 0) { List<(string SectionId, string Key)> pendingChanges = PendingChanges.ToList(); PendingChanges.Clear(); Dispatch(BuildInfo(pendingChanges)); } } private static ModConfigChangeInfo BuildInfo(IReadOnlyList<(string SectionId, string Key)> pendingChanges) { ModConfigKeyChange[] changedKeys = pendingChanges.Select<(string, string), ModConfigKeyChange>(delegate((string SectionId, string Key) pair) { ModConfigKeyChange modConfigKeyChange = new ModConfigKeyChange(); (modConfigKeyChange.SectionId, modConfigKeyChange.Key) = pair; return modConfigKeyChange; }).ToArray(); return new ModConfigChangeInfo { ChangedKeys = changedKeys }; } private static void Dispatch(ModConfigChangeInfo info) { ModConfigRegistry.NotifyRuntimeChange(); ModConfigChangeTracker.Changed?.Invoke(info); } } internal readonly struct ModConfigEntryBound { internal string? MinValue { get; } internal string? MaxValue { get; } internal ModConfigEntryBound(string? minValue, string? maxValue) { MinValue = minValue; MaxValue = maxValue; } } internal static class ModConfigEntryBounds { private sealed class SectionKeyComparer : IEqualityComparer<(string SectionId, string Key)> { internal static readonly SectionKeyComparer Instance = new SectionKeyComparer(); public bool Equals((string SectionId, string Key) x, (string SectionId, string Key) y) { if (string.Equals(x.SectionId, y.SectionId, StringComparison.OrdinalIgnoreCase)) { return string.Equals(x.Key, y.Key, StringComparison.OrdinalIgnoreCase); } return false; } public int GetHashCode((string SectionId, string Key) obj) { return HashCode.Combine(StringComparer.OrdinalIgnoreCase.GetHashCode(obj.SectionId), StringComparer.OrdinalIgnoreCase.GetHashCode(obj.Key)); } } private static readonly Dictionary<(string SectionId, string Key), ModConfigEntryBound> Bounds; static ModConfigEntryBounds() { Bounds = new Dictionary<(string, string), ModConfigEntryBound>(SectionKeyComparer.Instance); Float("MimesisPlayerEnhancement_Ui", "ModToastDurationSeconds", 1f); FloatRange("MimesisPlayerEnhancement_Ui", "FloatingDamageDurationSeconds", 1f, 3f); FloatRange("MimesisPlayerEnhancement_Ui", "RoundStartSoundVolume", 0f, 1f); FloatRange("MimesisPlayerEnhancement_Ui", "DiscoBallSoundVolume", 0f, 1f); FloatRange("MimesisPlayerEnhancement_Ui", "SpectatorVoiceAttenuation", 0f, 1f); FloatRange("MimesisPlayerEnhancement_Ui", "SpectatorVoiceDuckLevel", 0f, 1f); FloatRange("MimesisPlayerEnhancement_Ui", "VoiceNoiseGateStrength", 0f, 1f); Int("MimesisPlayerEnhancement_MorePlayers", "MaxPlayers", 1); MinZeroFloat("MimesisPlayerEnhancement_MorePlayers", "RoundGoalBasePerZone", "RoundGoalMoneyMultiplier"); FloatRange("MimesisPlayerEnhancement_MorePlayers", "RoundGoalCurveExponent", 0.1f, 2f); IntRange("MimesisPlayerEnhancement_MorePlayers", "RoundGoalRandomSpreadPercent", 0, 100); Int("MimesisPlayerEnhancement_MoreVoices", "MaxIndoorVoiceEvents", 1); Int("MimesisPlayerEnhancement_MoreVoices", "MaxDeathMatchVoiceEvents", 1); Int("MimesisPlayerEnhancement_MoreVoices", "MaxOutdoorVoiceEvents", 1); Int("MimesisPlayerEnhancement_MoreVoices", "VoiceClipCacheMaxEntries", 1); Int("MimesisPlayerEnhancement_Statistics", "SessionReconnectGraceMinutes", 1); FloatRange("MimesisPlayerEnhancement_PlayerTuning", "MoveSpeedMultiplier"); FloatRange("MimesisPlayerEnhancement_PlayerTuning", "NoClipSpeedMultiplier"); FloatRange("MimesisPlayerEnhancement_PlayerTuning", "MaxStaminaMultiplier"); FloatRange("MimesisPlayerEnhancement_PlayerTuning", "StaminaDrainMultiplier"); FloatRange("MimesisPlayerEnhancement_PlayerTuning", "StaminaRegenMultiplier"); FloatRange("MimesisPlayerEnhancement_PlayerTuning", "StaminaRegenDelayMultiplier"); FloatRange("MimesisPlayerEnhancement_PlayerTuning", "MaxCarryWeightMultiplier"); MinZeroFloat("MimesisPlayerEnhancement_Economy", "ScrapSellValueMultiplier", "ScrapSellValuePerPlayerMultiplier", "ShopBuyPriceMultiplier", "ShopBuyPricePerPlayerMultiplier", "ReinforcePriceMultiplier", "ReinforcePricePerPlayerMultiplier"); Int("MimesisPlayerEnhancement_Economy", "EconomyBaselinePlayerCount", 1); Int("MimesisPlayerEnhancement_SavegamePreparation", "StartupMoney", 0); IntRange("MimesisPlayerEnhancement_SavegamePreparation", "StartingZone", 1, 99); IntRange("MimesisPlayerEnhancement_Economy", "ShopDiscountMinPercent", 0, 100); IntRange("MimesisPlayerEnhancement_Economy", "ShopDiscountMaxPercent", 0, 100); IntRange("MimesisPlayerEnhancement_Economy", "ShopDiscountChancePercent", 0, 100); MinZeroFloat("MimesisPlayerEnhancement_LootMultiplicator", "MapLootMultiplier", "MapLootPerPlayerMultiplier", "DropLootMultiplier", "DropLootPerPlayerMultiplier"); Int("MimesisPlayerEnhancement_LootMultiplicator", "LootMultiplicatorBaselinePlayerCount", 1); IntRange("MimesisPlayerEnhancement_LootMultiplicator", "ConvertFakeActorDyingDropChancePercent", 0, 100); MinZeroFloat("MimesisPlayerEnhancement_SpawnScaling", "MimicSpawnMultiplier", "MimicSpawnPerPlayerMultiplier", "MimicWaveInitialDelaySeconds", "MimicWaveInitialDelayMinSeconds", "MimicWaveInitialDelayMaxSeconds", "MimicWaveIntervalSeconds", "MimicWaveIntervalMinSeconds", "MimicWaveIntervalMaxSeconds", "BossSpawnMultiplier", "BossSpawnPerPlayerMultiplier", "GruntSpawnMultiplier", "GruntSpawnPerPlayerMultiplier", "GruntWaveInitialDelaySeconds", "GruntWaveInitialDelayMinSeconds", "GruntWaveInitialDelayMaxSeconds", "GruntWaveIntervalSeconds", "GruntWaveIntervalMinSeconds", "GruntWaveIntervalMaxSeconds", "SpecialSpawnMultiplier", "SpecialSpawnPerPlayerMultiplier", "TrapSpawnMultiplier", "TrapSpawnPerPlayerMultiplier", "TrapRespawnDelaySeconds", "TrapRespawnDelayMinSeconds", "TrapRespawnDelayMaxSeconds", "TrapRespawnMinPlayerDistanceMeters", "OtherSpawnMultiplier", "OtherSpawnPerPlayerMultiplier", "BonusEncounterDelayMinSeconds", "BonusEncounterDelayMaxSeconds", "BonusEncounterMinPlayerDistanceMeters"); Int("MimesisPlayerEnhancement_SpawnScaling", "SpawnScalingBaselinePlayerCount", 1); Int("MimesisPlayerEnhancement_JoinAnytime", "JoinConnectionGraceSeconds", 1); Int("MimesisPlayerEnhancement_DungeonTime", "DungeonTimeBaselinePlayerCount", 1); Float("MimesisPlayerEnhancement_DungeonTime", "ExtraShiftSecondsPerPlayerAboveBaseline", 0f); FloatRange("MimesisPlayerEnhancement_DungeonTime", "TimeMultiplier", -5f, 5f); Float("MimesisPlayerEnhancement_Weather", "WeatherCycleMinDelaySeconds", 0f); Float("MimesisPlayerEnhancement_Weather", "WeatherCycleMaxDelaySeconds", 0f); FloatRange("MimesisPlayerEnhancement_MimicTuning", "MimicPossessionMinTimeSeconds", 0.1f, 120f); FloatRange("MimesisPlayerEnhancement_MimicTuning", "MimicPossessionMaxTimeSeconds", 0.1f, 120f); FloatRange("MimesisPlayerEnhancement_MimicTuning", "MimicPossessionCooltimeMultiplier", 0.1f, 10f); MinZeroFloat("MimesisPlayerEnhancement_MimicTuning", "PeriodicVoiceIntervalMultiplier", "PlayerVoiceResponseCooldownSeconds", "PlayerVoiceResponseDelayMinSeconds", "PlayerVoiceResponseDelayMaxSeconds", "ClipReuseCooldownSeconds", "DeathMatchClipReuseCooldownSeconds", "SpeakAudienceRangeMeters", "PostReplyIntervalFixedSeconds", "PostReplyIntervalMinSeconds", "PostReplyIntervalMaxSeconds", "VoiceInitIntervalMin", "VoiceInitIntervalMax", "VoicePeriodicIntervalMin", "VoicePeriodicIntervalMax", "VoiceDeathMatchIntervalMin", "VoiceDeathMatchIntervalMax"); Float("MimesisPlayerEnhancement_MimicTuning", "PlayerVoiceResponseMaxDistance", 1f); IntRange("MimesisPlayerEnhancement_MimicTuning", "PlayerVoiceResponseChancePercent", 0, 100); IntRange("MimesisPlayerEnhancement_MimicTuning", "MinRequiredSpeechClips", 0, 50); FloatRange("MimesisPlayerEnhancement_MimicTuning", "TrustOutdoorMultiplier", 0f, 10f); FloatRange("MimesisPlayerEnhancement_MimicTuning", "TrustLookingDelta", -100f, 100f); FloatRange("MimesisPlayerEnhancement_MimicTuning", "TrustNotLookingDelta", -100f, 100f); FloatRange("MimesisPlayerEnhancement_MimicTuning", "TrustApproachDelta", -100f, 100f); FloatRange("MimesisPlayerEnhancement_MimicTuning", "TrustMaintainDelta", -100f, 100f); FloatRange("MimesisPlayerEnhancement_MimicTuning", "TrustWalkAwayDelta", -100f, 100f); FloatRange("MimesisPlayerEnhancement_MimicTuning", "TrustSprintAwayDelta", -100f, 100f); FloatRange("MimesisPlayerEnhancement_MimicTuning", "TrustHitDamageMultiplier", -100f, 100f); FloatRange("MimesisPlayerEnhancement_MimicTuning", "TrustFriendlyThreshold", 0f, 100f); FloatRange("MimesisPlayerEnhancement_MimicTuning", "TrustDistrustThreshold", 0f, 100f); FloatRange("MimesisPlayerEnhancement_MimicTuning", "TrustInitialFixed", 0f, 100f); FloatRange("MimesisPlayerEnhancement_MimicTuning", "TrustInitialRandomMin", 0f, 100f); FloatRange("MimesisPlayerEnhancement_MimicTuning", "TrustInitialRandomMax", 0f, 100f); FloatRange("MimesisPlayerEnhancement_MimicTuning", "TrustBehaviorFixed", 0f, 100f); FloatRange("MimesisPlayerEnhancement_MimicTuning", "TrustBehaviorRandomMin", 0f, 100f); FloatRange("MimesisPlayerEnhancement_MimicTuning", "TrustBehaviorRandomMax", 0f, 100f); FloatRange("MimesisPlayerEnhancement_MimicTuning", "ChaseActivationDistanceMeters", 0.1f, 200f); FloatRange("MimesisPlayerEnhancement_MimicTuning", "ChaseForceRunDistanceMeters", 0.1f, 200f); FloatRange("MimesisPlayerEnhancement_MimicTuning", "MimicRunawayChance", 0f, 1f); IntRange("MimesisPlayerEnhancement_MimicTuning", "JumpCopyChancePercent", 0, 100); IntRange("MimesisPlayerEnhancement_MimicTuning", "SlotFollowChangeChancePercent", 0, 100); FloatRange("MimesisPlayerEnhancement_MimicTuning", "PossessionRangeMeters", 1f, 200f); IntRange("MimesisPlayerEnhancement_MimicTuning", "EmoteRespondChancePercent", 0, 100); IntRange("MimesisPlayerEnhancement_MimicTuning", "EmoteSuggestChancePercent", 0, 100); IntRange("MimesisPlayerEnhancement_MimicTuning", "ReactToSprinklerChancePercent", 0, 100); IntRange("MimesisPlayerEnhancement_MimicTuning", "UseTrapSwitchChancePercent", 0, 100); IntRange("MimesisPlayerEnhancement_MimicTuning", "UseChargerChancePercent", 0, 100); IntRange("MimesisPlayerEnhancement_MimicTuning", "UseTransmitterChancePercent", 0, 100); IntRange("MimesisPlayerEnhancement_MimicTuning", "UseShutterSwitchChancePercent", 0, 100); FloatRange("MimesisPlayerEnhancement_MimicTuning", "HornMaxRecordSeconds", 0.1f, 60f); FloatRange("MimesisPlayerEnhancement_MimicTuning", "HornRecordingGapSeconds", 0.1f, 30f); IntRange("MimesisPlayerEnhancement_MimicTuning", "HornMaxStoredRecords", 1, 100); IntRange("MimesisPlayerEnhancement_WebDashboard", "WebDashboardListenPort", 1, 65535); } internal static bool TryGet(string sectionId, string key, out ModConfigEntryBound bound) { return Bounds.TryGetValue((sectionId, key), out bound); } private static void Float(string sectionId, string key, float min, float? max = null) { Bounds[(sectionId, key)] = new ModConfigEntryBound(ModConfigFloatHelper.Format(min), max.HasValue ? ModConfigFloatHelper.Format(max.Value) : null); } private static void FloatRange(string sectionId, string key, float min, float max) { Float(sectionId, key, min, max); } private static void FloatRange(string sectionId, string key) { FloatRange(sectionId, key, 0.1f, 5f); } private static void Int(string sectionId, string key, int min, int? max = null) { Bounds[(sectionId, key)] = new ModConfigEntryBound(min.ToString(CultureInfo.InvariantCulture), max?.ToString(CultureInfo.InvariantCulture)); } private static void IntRange(string sectionId, string key, int min, int max) { Int(sectionId, key, min, max); } private static void MinZeroFloat(string sectionId, params string[] keys) { for (int i = 0; i < keys.Length; i++) { Float(sectionId, keys[i], 0f); } } } internal readonly struct ModConfigEntryDependency { internal string DependsOnKey { get; } internal string? DependsOnValue { get; } internal ModConfigEntryDependency(string dependsOnKey, string? dependsOnValue = null) { DependsOnKey = dependsOnKey; DependsOnValue = dependsOnValue; } } internal static class ModConfigEntryDependencies { internal static void ApplyToEntry(WebDashboardConfigSectionDto section, WebDashboardConfigEntryDto entry) { if (TryGetDependency(section.Id, entry.Key, out var dependency)) { entry.DependsOnKey = dependency.DependsOnKey; entry.DependsOnValue = dependency.DependsOnValue; } } private static bool TryGetDependency(string sectionId, string key, out ModConfigEntryDependency dependency) { dependency = default(ModConfigEntryDependency); if (string.IsNullOrWhiteSpace(sectionId) || string.IsNullOrWhiteSpace(key)) { return false; } return sectionId switch { "MimesisPlayerEnhancement_MimicTuning" => TryGetMimicTuningDependency(key, out dependency), "MimesisPlayerEnhancement_SpawnScaling" => TryGetSpawnScalingDependency(key, out dependency), "MimesisPlayerEnhancement_LootMultiplicator" => TryGetLootDependency(key, out dependency), "MimesisPlayerEnhancement_Economy" => TryGetEconomyDependency(key, out dependency), "MimesisPlayerEnhancement_DungeonRandomizer" => TryGetDungeonRandomizerDependency(key, out dependency), "MimesisPlayerEnhancement_Weather" => TryGetWeatherDependency(key, out dependency), "MimesisPlayerEnhancement_DungeonTime" => TryGetDungeonTimeDependency(key, out dependency), "MimesisPlayerEnhancement_Ui" => TryGetUiDependency(key, out dependency), _ => false, }; } private static bool TryGetUiDependency(string key, out ModConfigEntryDependency dependency) { dependency = default(ModConfigEntryDependency); if (string.Equals(key, "RoundStartSoundVariant", StringComparison.Ordinal)) { dependency = new ModConfigEntryDependency("RoundStartSoundMode", "Specific"); return true; } if (string.Equals(key, "RoundStartSoundRandomPool", StringComparison.Ordinal)) { dependency = new ModConfigEntryDependency("RoundStartSoundMode", "Random"); return true; } if (string.Equals(key, "DiscoBallSoundVariant", StringComparison.Ordinal)) { dependency = new ModConfigEntryDependency("DiscoBallSoundMode", "Specific"); return true; } if (string.Equals(key, "DiscoBallSoundRandomPool", StringComparison.Ordinal)) { dependency = new ModConfigEntryDependency("DiscoBallSoundMode", "Random"); return true; } if (string.Equals(key, "CustomLoadingScreenVariant", StringComparison.Ordinal)) { dependency = new ModConfigEntryDependency("CustomLoadingScreenMode", "Specific"); return true; } if (string.Equals(key, "CustomLoadingScreenRandomPool", StringComparison.Ordinal)) { dependency = new ModConfigEntryDependency("CustomLoadingScreenMode", "Random"); return true; } if (string.Equals(key, "SpectatorVoiceAttenuation", StringComparison.Ordinal)) { dependency = new ModConfigEntryDependency("SpectatorVoiceBalanceMode", "StaticAttenuation"); return true; } if (string.Equals(key, "SpectatorVoiceDuckLevel", StringComparison.Ordinal)) { dependency = new ModConfigEntryDependency("SpectatorVoiceBalanceMode", "SpeechDucking"); return true; } if (string.Equals(key, "VoiceNoiseGateStrength", StringComparison.Ordinal)) { dependency = new ModConfigEntryDependency("EnableVoiceNoiseGate"); return true; } return false; } private static bool TryGetMimicTuningDependency(string key, out ModConfigEntryDependency dependency) { dependency = default(ModConfigEntryDependency); switch (key) { case "PlayerVoiceResponseChancePercent": case "PlayerVoiceResponseDelayMaxSeconds": case "PlayerVoiceResponseDelayMinSeconds": case "PlayerVoiceResponseCooldownSeconds": case "DeathMatchClipReuseCooldownSeconds": case "ClipReuseCooldownSeconds": case "SpeakAudienceRangeMeters": case "HearOwnVoiceFromMimic": case "PostReplyIntervalMode": case "VoiceInitIntervalMode": case "MinRequiredSpeechClips": case "VoicePeriodicIntervalMode": case "VoiceDeathMatchIntervalMode": case "PeriodicVoiceIntervalMultiplier": case "PlayerVoiceResponseMaxDistance": dependency = new ModConfigEntryDependency("MimicVoiceTuningMode", "Custom"); return true; case "PostReplyIntervalFixedSeconds": dependency = new ModConfigEntryDependency("PostReplyIntervalMode", "Fixed"); return true; case "PostReplyIntervalMinSeconds": case "PostReplyIntervalMaxSeconds": dependency = new ModConfigEntryDependency("PostReplyIntervalMode", "Random"); return true; case "VoiceInitIntervalMin": case "VoiceInitIntervalMax": dependency = new ModConfigEntryDependency("VoiceInitIntervalMode", "Random"); return true; case "VoicePeriodicIntervalMin": case "VoicePeriodicIntervalMax": dependency = new ModConfigEntryDependency("VoicePeriodicIntervalMode", "Random"); return true; case "VoiceDeathMatchIntervalMax": case "VoiceDeathMatchIntervalMin": dependency = new ModConfigEntryDependency("VoiceDeathMatchIntervalMode", "Random"); return true; case "TrustHitDamageMultiplier": case "TrustOutdoorMultiplier": case "TrustFriendlyThreshold": case "TrustDistrustThreshold": case "ChaseForceRunDistanceMeters": case "ChaseActivationDistanceMeters": case "TrustSprintAwayDelta": case "TrustNotLookingDelta": case "TrustLookingDelta": case "TrustMaintainDelta": case "TrustWalkAwayDelta": case "TrustApproachDelta": case "TrustScoreValueMode": dependency = new ModConfigEntryDependency("MimicTrustMode", "Custom"); return true; case "TrustInitialFixed": dependency = new ModConfigEntryDependency("TrustScoreValueMode", "Fixed"); return true; case "TrustInitialRandomMin": case "TrustInitialRandomMax": dependency = new ModConfigEntryDependency("TrustScoreValueMode", "Random"); return true; case "TrustBehaviorFixed": dependency = new ModConfigEntryDependency("TrustScoreValueMode", "Fixed"); return true; case "TrustBehaviorRandomMin": case "TrustBehaviorRandomMax": dependency = new ModConfigEntryDependency("TrustScoreValueMode", "Random"); return true; case "MimicInventoryCopyPickRule": dependency = new ModConfigEntryDependency("MimicInventoryCopyMode", "Custom"); return true; case "RandomizeMimicPossessionDuration": dependency = new ModConfigEntryDependency("EnableMimicPossessionTuning"); return true; case "MimicPossessionMinTimeSeconds": case "MimicPossessionMaxTimeSeconds": dependency = new ModConfigEntryDependency("RandomizeMimicPossessionDuration"); return true; case "PossessionRangeMeters": case "PossessionBtGateMode": case "MimicPossessionCooltimeMultiplier": dependency = new ModConfigEntryDependency("EnableMimicPossessionTuning"); return true; case "EmoteRespondChancePercent": case "EmoteSuggestChancePercent": case "UseTransmitterChancePercent": case "ReactToSprinklerChancePercent": case "UseShutterSwitchChancePercent": case "UseTrapSwitchChancePercent": case "UseChargerChancePercent": dependency = new ModConfigEntryDependency("MimicEmotePropsMode", "Custom"); return true; case "HornMaxRecordSeconds": case "HornMaxStoredRecords": case "AllowHornImitation": case "HornRecordingGapSeconds": dependency = new ModConfigEntryDependency("HornImitationMode", "Custom"); return true; default: return false; } } private static bool TryGetSpawnScalingDependency(string key, out ModConfigEntryDependency dependency) { dependency = default(ModConfigEntryDependency); switch (key) { case "MimicWaveInitialDelaySeconds": case "MimicWaveIntervalSeconds": dependency = new ModConfigEntryDependency("MimicWaveMode", "Fixed"); return true; case "MimicWaveInitialDelayMinSeconds": case "MimicWaveInitialDelayMaxSeconds": case "MimicWaveIntervalMinSeconds": case "MimicWaveIntervalMaxSeconds": dependency = new ModConfigEntryDependency("MimicWaveMode", "Random"); return true; case "GruntWaveInitialDelaySeconds": case "GruntWaveIntervalSeconds": dependency = new ModConfigEntryDependency("GruntWaveMode", "Fixed"); return true; case "GruntWaveInitialDelayMinSeconds": case "GruntWaveInitialDelayMaxSeconds": case "GruntWaveIntervalMinSeconds": case "GruntWaveIntervalMaxSeconds": dependency = new ModConfigEntryDependency("GruntWaveMode", "Random"); return true; case "TrapRespawnDelaySeconds": dependency = new ModConfigEntryDependency("TrapRespawnMode", "Fixed"); return true; case "TrapRespawnDelayMaxSeconds": case "TrapRespawnDelayMinSeconds": dependency = new ModConfigEntryDependency("TrapRespawnMode", "Random"); return true; case "TrapRespawnMinPlayerDistanceMeters": dependency = new ModConfigEntryDependency("TrapRespawnMode", "!=Vanilla"); return true; default: return false; } } private static bool TryGetLootDependency(string key, out ModConfigEntryDependency dependency) { dependency = default(ModConfigEntryDependency); if (!(key == "LootAllowlist")) { if (key == "LootBlocklist") { dependency = new ModConfigEntryDependency("LootItemFilterMode", "BlocklistOnly"); return true; } return false; } dependency = new ModConfigEntryDependency("LootItemFilterMode", "AllowlistOnly"); return true; } private static bool TryGetEconomyDependency(string key, out ModConfigEntryDependency dependency) { dependency = default(ModConfigEntryDependency); if (key == "ShopDiscountMinPercent" || key == "ShopDiscountMaxPercent") { dependency = new ModConfigEntryDependency("ShopDiscountChancePercent", ">0"); return true; } return false; } private static bool TryGetDungeonRandomizerDependency(string key, out ModConfigEntryDependency dependency) { dependency = default(ModConfigEntryDependency); switch (key) { case "DungeonPickPoolMode": case "DungeonAllowlist": case "DungeonBlocklist": case "IgnoreDungeonExcludeList": dependency = new ModConfigEntryDependency("RandomizeDungeonPick"); return true; case "DungeonSeedFlavor": dependency = new ModConfigEntryDependency("EnableDungeonRandomizer"); return true; default: return false; } } private static bool TryGetWeatherDependency(string key, out ModConfigEntryDependency dependency) { dependency = default(ModConfigEntryDependency); switch (key) { case "FixedWeatherPreset": dependency = new ModConfigEntryDependency("WeatherMode", "Fixed"); return true; case "DisableRandomWeather": dependency = new ModConfigEntryDependency("WeatherMode", "Vanilla"); return true; case "WeatherCyclePresets": case "WeatherCycleMinDelaySeconds": case "WeatherCycleMaxDelaySeconds": dependency = new ModConfigEntryDependency("WeatherMode", "Cycle"); return true; default: return false; } } private static bool TryGetDungeonTimeDependency(string key, out ModConfigEntryDependency dependency) { dependency = default(ModConfigEntryDependency); if (key == "EnableRealtimeTramClock" || key == "TimeMultiplier") { dependency = new ModConfigEntryDependency("EnableDungeonTime"); return true; } return false; } } internal static class ModConfigEntryLocalEffect { private sealed class EntryKeyComparer : IEqualityComparer<(string SectionId, string Key)> { internal static readonly EntryKeyComparer Instance = new EntryKeyComparer(); public bool Equals((string SectionId, string Key) x, (string SectionId, string Key) y) { if (string.Equals(x.SectionId, y.SectionId, StringComparison.OrdinalIgnoreCase)) { return string.Equals(x.Key, y.Key, StringComparison.OrdinalIgnoreCase); } return false; } public int GetHashCode((string SectionId, string Key) obj) { return HashCode.Combine(StringComparer.OrdinalIgnoreCase.GetHashCode(obj.SectionId), StringComparer.OrdinalIgnoreCase.GetHashCode(obj.Key)); } } private const string MainSectionId = "MimesisPlayerEnhancement"; private const string PlayerTuningSectionId = "MimesisPlayerEnhancement_PlayerTuning"; private static readonly HashSet<(string SectionId, string Key)> LocalEntries = new HashSet<(string, string)>(EntryKeyComparer.Instance) { ("MimesisPlayerEnhancement", "EnableDebugLogging"), ("MimesisPlayerEnhancement", "LastSeenModVersion"), ("MimesisPlayerEnhancement_Ui", "ModToastDurationSeconds"), ("MimesisPlayerEnhancement_Ui", "EnableExtendedSaveSlots"), ("MimesisPlayerEnhancement_Ui", "EnableExtendedSpectatorPlayerList"), ("MimesisPlayerEnhancement_Ui", "EnableLoadingWaitPlayerList"), ("MimesisPlayerEnhancement_Ui", "EnableExtendedInGameMenuPlayerList"), ("MimesisPlayerEnhancement_Ui", "EnableDamageHealthGlow"), ("MimesisPlayerEnhancement_Ui", "EnableFloatingDamageNumbers"), ("MimesisPlayerEnhancement_Ui", "FloatingDamageDurationSeconds"), ("MimesisPlayerEnhancement_Ui", "EnableFpsUi"), ("MimesisPlayerEnhancement_Ui", "EnableFpsUiInventoryNetWorth"), ("MimesisPlayerEnhancement_Ui", "EnableInventorySlotOptimization"), ("MimesisPlayerEnhancement_Ui", "InventoryPickupSelectMode"), ("MimesisPlayerEnhancement_Ui", "EnableInventorySelectNextOnRemove"), ("MimesisPlayerEnhancement_Ui", "EnableInventoryNumberKeySelection"), ("MimesisPlayerEnhancement_Ui", "RoundStartSoundMode"), ("MimesisPlayerEnhancement_Ui", "RoundStartSoundVariant"), ("MimesisPlayerEnhancement_Ui", "RoundStartSoundRandomPool"), ("MimesisPlayerEnhancement_Ui", "RoundStartSoundVolume"), ("MimesisPlayerEnhancement_Ui", "DiscoBallSoundMode"), ("MimesisPlayerEnhancement_Ui", "DiscoBallSoundVariant"), ("MimesisPlayerEnhancement_Ui", "DiscoBallSoundRandomPool"), ("MimesisPlayerEnhancement_Ui", "DiscoBallSoundVolume"), ("MimesisPlayerEnhancement_Ui", "CustomLoadingScreenMode"), ("MimesisPlayerEnhancement_Ui", "CustomLoadingScreenVariant"), ("MimesisPlayerEnhancement_Ui", "CustomLoadingScreenRandomPool"), ("MimesisPlayerEnhancement_Ui", "CustomLoadingScreenMotion"), ("MimesisPlayerEnhancement_Ui", "SpectatorVoiceBalanceMode"), ("MimesisPlayerEnhancement_Ui", "SpectatorVoiceAttenuation"), ("MimesisPlayerEnhancement_Ui", "SpectatorVoiceDuckLevel"), ("MimesisPlayerEnhancement_Ui", "EnableVoiceNoiseGate"), ("MimesisPlayerEnhancement_Ui", "VoiceNoiseGateStrength"), ("MimesisPlayerEnhancement_Privacy", "EnablePrivacy"), ("MimesisPlayerEnhancement_Privacy", "BlockReluTelemetry"), ("MimesisPlayerEnhancement_Privacy", "BlockReplayUpload"), ("MimesisPlayerEnhancement_Privacy", "BlockReplayRecording"), ("MimesisPlayerEnhancement_Privacy", "BlockCrashReports"), ("MimesisPlayerEnhancement_Privacy", "StripCrashReportMetadata"), ("MimesisPlayerEnhancement_Privacy", "BlockKraftonGppSdk"), ("MimesisPlayerEnhancement_PlayerTuning", "DisablePlayerCollision") }; internal static bool HasLocalEffect(string sectionId, string key) { return LocalEntries.Contains((sectionId, key)); } } internal static class ModConfigEntryUiHints { private sealed class EntryKeyComparer : IEqualityComparer<(string SectionId, string Key)> { internal static readonly EntryKeyComparer Instance = new EntryKeyComparer(); public bool Equals((string SectionId, string Key) x, (string SectionId, string Key) y) { if (string.Equals(x.SectionId, y.SectionId, StringComparison.OrdinalIgnoreCase)) { return string.Equals(x.Key, y.Key, StringComparison.OrdinalIgnoreCase); } return false; } public int GetHashCode((string SectionId, string Key) obj) { return HashCode.Combine(StringComparer.OrdinalIgnoreCase.GetHashCode(obj.SectionId), StringComparer.OrdinalIgnoreCase.GetHashCode(obj.Key)); } } private const string LootSectionId = "MimesisPlayerEnhancement_LootMultiplicator"; private const string DungeonSectionId = "MimesisPlayerEnhancement_DungeonRandomizer"; private const string WeatherSectionId = "MimesisPlayerEnhancement_Weather"; private const string DungeonTimeSectionId = "MimesisPlayerEnhancement_DungeonTime"; private const string SpawnScalingSectionId = "MimesisPlayerEnhancement_SpawnScaling"; private const string EconomySectionId = "MimesisPlayerEnhancement_Economy"; private const string MimicTuningSectionId = "MimesisPlayerEnhancement_MimicTuning"; private const string SavegamePreparationSectionId = "MimesisPlayerEnhancement_SavegamePreparation"; private const string MoreVoicesSectionId = "MimesisPlayerEnhancement_MoreVoices"; private const string UiSectionId = "MimesisPlayerEnhancement_Ui"; private static readonly Dictionary<(string SectionId, string Key), string[]> SelectValuesByEntry = new Dictionary<(string, string), string[]>(EntryKeyComparer.Instance) { [("MimesisPlayerEnhancement_LootMultiplicator", "LootItemFilterMode")] = new string[3] { "All", "AllowlistOnly", "BlocklistOnly" }, [("MimesisPlayerEnhancement_DungeonRandomizer", "DungeonPickPoolMode")] = new string[2] { "WidenVanilla", "AllActiveUniform" }, [("MimesisPlayerEnhancement_DungeonRandomizer", "DungeonSeedFlavor")] = DungeonSeedFlavorUtil.AllNames, [("MimesisPlayerEnhancement_Weather", "WeatherMode")] = new string[3] { "Vanilla", "Fixed", "Cycle" }, [("MimesisPlayerEnhancement_Weather", "FixedWeatherPreset")] = new string[4] { "Sunny", "Rain", "HeavyRain", "Squall" }, [("MimesisPlayerEnhancement_DungeonTime", "StartTimePreset")] = new string[6] { "Vanilla", "Morning", "Noon", "Dusk", "Night", "Midnight" }, [("MimesisPlayerEnhancement_SpawnScaling", "MimicWaveMode")] = new string[3] { "Vanilla", "Fixed", "Random" }, [("MimesisPlayerEnhancement_SpawnScaling", "GruntWaveMode")] = new string[3] { "Vanilla", "Fixed", "Random" }, [("MimesisPlayerEnhancement_SpawnScaling", "TrapRespawnMode")] = new string[3] { "Vanilla", "Fixed", "Random" }, [("MimesisPlayerEnhancement_MimicTuning", "MimicVoiceTuningMode")] = new string[2] { "Vanilla", "Custom" }, [("MimesisPlayerEnhancement_MimicTuning", "PostReplyIntervalMode")] = new string[3] { "Vanilla", "Fixed", "Random" }, [("MimesisPlayerEnhancement_MimicTuning", "HearOwnVoiceFromMimic")] = new string[3] { "Vanilla", "AlwaysHear", "OnlyWhenSingleplayer" }, [("MimesisPlayerEnhancement_MimicTuning", "VoiceInitIntervalMode")] = new string[2] { "Vanilla", "Random" }, [("MimesisPlayerEnhancement_MimicTuning", "VoicePeriodicIntervalMode")] = new string[2] { "Vanilla", "Random" }, [("MimesisPlayerEnhancement_MimicTuning", "VoiceDeathMatchIntervalMode")] = new string[2] { "Vanilla", "Random" }, [("MimesisPlayerEnhancement_MimicTuning", "MimicTrustMode")] = new string[2] { "Vanilla", "Custom" }, [("MimesisPlayerEnhancement_MimicTuning", "TrustScoreValueMode")] = new string[3] { "Vanilla", "Fixed", "Random" }, [("MimesisPlayerEnhancement_MimicTuning", "MimicSocialMode")] = new string[2] { "Vanilla", "Custom" }, [("MimesisPlayerEnhancement_MimicTuning", "MimicInventoryCopyMode")] = new string[2] { "Vanilla", "Custom" }, [("MimesisPlayerEnhancement_MimicTuning", "MimicInventoryCopyPickRule")] = new string[3] { "MinDistance", "MaxDistance", "Random" }, [("MimesisPlayerEnhancement_MimicTuning", "PossessionBtGateMode")] = new string[2] { "Vanilla", "Always" }, [("MimesisPlayerEnhancement_MimicTuning", "MimicEmotePropsMode")] = new string[2] { "Vanilla", "Custom" }, [("MimesisPlayerEnhancement_MimicTuning", "HornImitationMode")] = new string[2] { "Vanilla", "Custom" }, [("MimesisPlayerEnhancement_Ui", "RoundStartSoundMode")] = new string[3] { "Vanilla", "Random", "Specific" }, [("MimesisPlayerEnhancement_Ui", "DiscoBallSoundMode")] = new string[3] { "Vanilla", "Random", "Specific" }, [("MimesisPlayerEnhancement_Ui", "CustomLoadingScreenMode")] = new string[3] { "Vanilla", "Random", "Specific" }, [("MimesisPlayerEnhancement_Ui", "InventoryPickupSelectMode")] = new string[3] { "Vanilla", "WeaponsOnly", "Always" }, [("MimesisPlayerEnhancement_Ui", "SpectatorVoiceBalanceMode")] = new string[3] { "Vanilla", "SpeechDucking", "StaticAttenuation" } }; internal static string ResolveInputKind(string sectionId, string key) { if (sectionId == "MimesisPlayerEnhancement_LootMultiplicator" && (string.Equals(key, "LootAllowlist", StringComparison.Ordinal) || string.Equals(key, "LootBlocklist", StringComparison.Ordinal))) { return "ItemIdList"; } if (sectionId == "MimesisPlayerEnhancement_DungeonRandomizer" && (string.Equals(key, "DungeonAllowlist", StringComparison.Ordinal) || string.Equals(key, "DungeonBlocklist", StringComparison.Ordinal))) { return "DungeonIdList"; } if (sectionId == "MimesisPlayerEnhancement_Weather" && string.Equals(key, "WeatherCyclePresets", StringComparison.Ordinal)) { return "WeatherPresetList"; } if (sectionId == "MimesisPlayerEnhancement_Ui" && (string.Equals(key, "RoundStartSoundRandomPool", StringComparison.Ordinal) || string.Equals(key, "DiscoBallSoundRandomPool", StringComparison.Ordinal) || string.Equals(key, "CustomLoadingScreenRandomPool", StringComparison.Ordinal))) { return "VariantIdList"; } if (sectionId == "MimesisPlayerEnhancement_Ui" && string.Equals(key, "RoundStartSoundVariant", StringComparison.Ordinal)) { return "Select"; } if (sectionId == "MimesisPlayerEnhancement_Ui" && string.Equals(key, "DiscoBallSoundVariant", StringComparison.Ordinal)) { return "Select"; } if (sectionId == "MimesisPlayerEnhancement_Ui" && string.Equals(key, "CustomLoadingScreenVariant", StringComparison.Ordinal)) { return "Select"; } if (SelectValuesByEntry.ContainsKey((sectionId, key))) { return "Select"; } return "Default"; } internal static void ApplyToEntry(WebDashboardConfigEntryDto entry, string sectionId, string key) { entry.InputKind = ResolveInputKind(sectionId, key); if (string.Equals(entry.InputKind, "VariantIdList", StringComparison.Ordinal)) { if (sectionId == "MimesisPlayerEnhancement_Ui" && string.Equals(key, "RoundStartSoundRandomPool", StringComparison.Ordinal)) { entry.SelectOptions = BuildRoundStartSoundVariantOptions(); } else if (sectionId == "MimesisPlayerEnhancement_Ui" && string.Equals(key, "DiscoBallSoundRandomPool", StringComparison.Ordinal)) { entry.SelectOptions = BuildDiscoBallSoundVariantOptions(); } else if (sectionId == "MimesisPlayerEnhancement_Ui" && string.Equals(key, "CustomLoadingScreenRandomPool", StringComparison.Ordinal)) { entry.SelectOptions = BuildCustomLoadingScreenVariantOptions(); } } else { if (!string.Equals(entry.InputKind, "Select", StringComparison.Ordinal)) { return; } string[] value; if (sectionId == "MimesisPlayerEnhancement_Ui" && string.Equals(key, "RoundStartSoundVariant", StringComparison.Ordinal)) { entry.SelectOptions = BuildRoundStartSoundVariantOptions(); } else if (sectionId == "MimesisPlayerEnhancement_Ui" && string.Equals(key, "DiscoBallSoundVariant", StringComparison.Ordinal)) { entry.SelectOptions = BuildDiscoBallSoundVariantOptions(); } else if (sectionId == "MimesisPlayerEnhancement_Ui" && string.Equals(key, "CustomLoadingScreenVariant", StringComparison.Ordinal)) { entry.SelectOptions = BuildCustomLoadingScreenVariantOptions(); } else if (SelectValuesByEntry.TryGetValue((sectionId, key), out value)) { List<WebDashboardConfigSelectOptionDto> list = new List<WebDashboardConfigSelectOptionDto>(); string[] array = value; foreach (string text in array) { string configSelectOptionLabel = WebDashboardL10n.GetConfigSelectOptionLabel(sectionId, key, text); list.Add(new WebDashboardConfigSelectOptionDto { Value = text, Label = (string.IsNullOrWhiteSpace(configSelectOptionLabel) ? text : configSelectOptionLabel) }); } entry.SelectOptions = list; } } } internal static void AssignEntryGroups(WebDashboardConfigSectionDto section) { string id = section.Id; string b = section.FeatureToggle?.Key; List<WebDashboardConfigEntryDto> entries = section.Entries; Dictionary<string, string> explicitGroups = GetExplicitGroups(id); foreach (WebDashboardConfigEntryDto item in entries) { if (explicitGroups.TryGetValue(item.Key, out var value)) { item.EntryGroup = id + "::" + value; } } for (int i = 0; i < entries.Count; i++) { WebDashboardConfigEntryDto webDashboardConfigEntryDto = entries[i]; if (!webDashboardConfigEntryDto.Key.StartsWith("Enable", StringComparison.Ordinal) || string.Equals(webDashboardConfigEntryDto.Key, b, StringComparison.Ordinal) || !string.IsNullOrEmpty(webDashboardConfigEntryDto.EntryGroup)) { continue; } string entryGroup = (webDashboardConfigEntryDto.EntryGroup = id + "::" + webDashboardConfigEntryDto.Key); for (int j = i + 1; j < entries.Count; j++) { WebDashboardConfigEntryDto webDashboardConfigEntryDto2 = entries[j]; if (webDashboardConfigEntryDto2.Key.StartsWith("Enable", StringComparison.Ordinal) || !string.IsNullOrEmpty(webDashboardConfigEntryDto2.EntryGroup)) { break; } webDashboardConfigEntryDto2.EntryGroup = entryGroup; } } } private static Dictionary<string, string> GetExplicitGroups(string sectionId) { return sectionId switch { "MimesisPlayerEnhancement_LootMultiplicator" => new Dictionary<string, string>(StringComparer.Ordinal) { ["LootMultiplicatorBaselinePlayerCount"] = "playerScaling", ["MapLootMultiplier"] = "mapLoot", ["MapLootPerPlayerMultiplier"] = "mapLoot", ["DropLootMultiplier"] = "dropLoot", ["DropLootPerPlayerMultiplier"] = "dropLoot", ["LootItemFilterMode"] = "lootFilter", ["LootAllowlist"] = "lootFilter", ["LootBlocklist"] = "lootFilter", ["AutoScaleMapLootBudgetForFilter"] = "lootFilter" }, "MimesisPlayerEnhancement_Economy" => new Dictionary<string, string>(StringComparer.Ordinal) { ["EconomyBaselinePlayerCount"] = "playerScaling", ["ScrapSellValueMultiplier"] = "scrapSellValue", ["ScrapSellValuePerPlayerMultiplier"] = "scrapSellValue", ["ShopBuyPriceMultiplier"] = "shopBuyPrice", ["ShopBuyPricePerPlayerMultiplier"] = "shopBuyPrice", ["ReinforcePriceMultiplier"] = "reinforcePrice", ["ReinforcePricePerPlayerMultiplier"] = "reinforcePrice" }, "MimesisPlayerEnhancement_DungeonRandomizer" => new Dictionary<string, string>(StringComparer.Ordinal) { ["RandomizeDungeonPick"] = "dungeonPick", ["DungeonPickPoolMode"] = "dungeonPick", ["DungeonAllowlist"] = "dungeonPick", ["DungeonBlocklist"] = "dungeonPick", ["IgnoreDungeonExcludeList"] = "dungeonPick", ["DungeonSeedFlavor"] = "dungeonSeed" }, "MimesisPlayerEnhancement_SpawnScaling" => new Dictionary<string, string>(StringComparer.Ordinal) { ["SpawnScalingBaselinePlayerCount"] = "playerScaling", ["MimicSpawnMultiplier"] = "mimicSpawn", ["MimicSpawnPerPlayerMultiplier"] = "mimicSpawn", ["MimicWaveMode"] = "mimicWaveTiming", ["MimicWaveInitialDelaySeconds"] = "mimicWaveTiming", ["MimicWaveInitialDelayMinSeconds"] = "mimicWaveTiming", ["MimicWaveInitialDelayMaxSeconds"] = "mimicWaveTiming", ["MimicWaveIntervalSeconds"] = "mimicWaveTiming", ["MimicWaveIntervalMinSeconds"] = "mimicWaveTiming", ["MimicWaveIntervalMaxSeconds"] = "mimicWaveTiming", ["BossSpawnMultiplier"] = "bossSpawn", ["BossSpawnPerPlayerMultiplier"] = "bossSpawn", ["GruntSpawnMultiplier"] = "gruntSpawn", ["GruntSpawnPerPlayerMultiplier"] = "gruntSpawn", ["GruntWaveMode"] = "gruntWaveTiming", ["GruntWaveInitialDelaySeconds"] = "gruntWaveTiming", ["GruntWaveInitialDelayMinSeconds"] = "gruntWaveTiming", ["GruntWaveInitialDelayMaxSeconds"] = "gruntWaveTiming", ["GruntWaveIntervalSeconds"] = "gruntWaveTiming", ["GruntWaveIntervalMinSeconds"] = "gruntWaveTiming", ["GruntWaveIntervalMaxSeconds"] = "gruntWaveTiming", ["SpecialSpawnMultiplier"] = "specialSpawn", ["SpecialSpawnPerPlayerMultiplier"] = "specialSpawn", ["TrapSpawnMultiplier"] = "trapSpawn", ["TrapSpawnPerPlayerMultiplier"] = "trapSpawn", ["OtherSpawnMultiplier"] = "otherSpawn", ["OtherSpawnPerPlayerMultiplier"] = "otherSpawn", ["TrapRespawnMode"] = "trapRespawn", ["TrapRespawnDelaySeconds"] = "trapRespawn", ["TrapRespawnDelayMinSeconds"] = "trapRespawn", ["TrapRespawnDelayMaxSeconds"] = "trapRespawn", ["TrapRespawnMinPlayerDistanceMeters"] = "trapRespawn", ["BonusEncounterDelayMinSeconds"] = "bonusEncounterTiming", ["BonusEncounterDelayMaxSeconds"] = "bonusEncounterTiming", ["BonusEncounterMinPlayerDistanceMeters"] = "bonusEncounterTiming" }, "MimesisPlayerEnhancement_MimicTuning" => new Dictionary<string, string>(StringComparer.Ordinal) { ["MimicVoiceTuningMode"] = "mimicVoice", ["PeriodicVoiceIntervalMultiplier"] = "mimicVoice", ["PlayerVoiceResponseChancePercent"] = "mimicVoice", ["PlayerVoiceResponseCooldownSeconds"] = "mimicVoice", ["PlayerVoiceResponseDelayMinSeconds"] = "mimicVoice", ["PlayerVoiceResponseDelayMaxSeconds"] = "mimicVoice", ["PlayerVoiceResponseMaxDistance"] = "mimicVoice", ["ClipReuseCooldownSeconds"] = "mimicVoice", ["DeathMatchClipReuseCooldownSeconds"] = "mimicVoice", ["SpeakAudienceRangeMeters"] = "mimicVoice", ["PostReplyIntervalMode"] = "mimicVoice", ["PostReplyIntervalFixedSeconds"] = "mimicVoice", ["PostReplyIntervalMinSeconds"] = "mimicVoice", ["PostReplyIntervalMaxSeconds"] = "mimicVoice", ["MinRequiredSpeechClips"] = "mimicVoice", ["HearOwnVoiceFromMimic"] = "mimicVoice", ["VoiceInitIntervalMode"] = "mimicVoice", ["VoiceInitIntervalMin"] = "mimicVoice", ["VoiceInitIntervalMax"] = "mimicVoice", ["VoicePeriodicIntervalMode"] = "mimicVoice", ["VoicePeriodicIntervalMin"] = "mimicVoice", ["VoicePeriodicIntervalMax"] = "mimicVoice", ["VoiceDeathMatchIntervalMode"] = "mimicVoice", ["VoiceDeathMatchIntervalMin"] = "mimicVoice", ["VoiceDeathMatchIntervalMax"] = "mimicVoice", ["MimicTrustMode"] = "mimicTrust", ["TrustOutdoorMultiplier"] = "mimicTrust", ["TrustLookingDelta"] = "mimicTrust", ["TrustNotLookingDelta"] = "mimicTrust", ["TrustApproachDelta"] = "mimicTrust", ["TrustMaintainDelta"] = "mimicTrust", ["TrustWalkAwayDelta"] = "mimicTrust", ["TrustSprintAwayDelta"] = "mimicTrust", ["TrustHitDamageMultiplier"] = "mimicTrust", ["TrustFriendlyThreshold"] = "mimicTrust", ["TrustDistrustThreshold"] = "mimicTrust", ["TrustScoreValueMode"] = "mimicTrust", ["TrustInitialFixed"] = "mimicTrust", ["TrustInitialRandomMin"] = "mimicTrust", ["TrustInitialRandomMax"] = "mimicTrust", ["TrustBehaviorFixed"] = "mimicTrust", ["TrustBehaviorRandomMin"] = "mimicTrust", ["TrustBehaviorRandomMax"] = "mimicTrust", ["ChaseActivationDistanceMeters"] = "mimicTrust", ["ChaseForceRunDistanceMeters"] = "mimicTrust", ["MimicSocialMode"] = "mimicSocial", ["MimicRunawayChance"] = "mimicSocial", ["JumpCopyChancePercent"] = "mimicSocial", ["SlotFollowChangeChancePercent"] = "mimicSocial", ["EnableMimicPossessionTuning"] = "mimicPossession", ["RandomizeMimicPossessionDuration"] = "mimicPossession", ["MimicPossessionMinTimeSeconds"] = "mimicPossession", ["MimicPossessionMaxTimeSeconds"] = "mimicPossession", ["MimicPossessionCooltimeMultiplier"] = "mimicPossession", ["PossessionRangeMeters"] = "mimicPossession", ["PossessionBtGateMode"] = "mimicPossession", ["MimicEmotePropsMode"] = "mimicEmoteProps", ["EmoteRespondChancePercent"] = "mimicEmoteProps", ["EmoteSuggestChancePercent"] = "mimicEmoteProps", ["ReactToSprinklerChancePercent"] = "mimicEmoteProps", ["UseTrapSwitchChancePercent"] = "mimicEmoteProps", ["UseChargerChancePercent"] = "mimicEmoteProps", ["UseTransmitterChancePercent"] = "mimicEmoteProps", ["UseShutterSwitchChancePercent"] = "mimicEmoteProps", ["HornImitationMode"] = "mimicHorn", ["AllowHornImitation"] = "mimicHorn", ["HornMaxRecordSeconds"] = "mimicHorn", ["HornRecordingGapSeconds"] = "mimicHorn", ["HornMaxStoredRecords"] = "mimicHorn" }, "MimesisPlayerEnhancement_DungeonTime" => new Dictionary<string, string>(StringComparer.Ordinal) { ["StartTimePreset"] = "startTime", ["TimeMultiplier"] = "startTime", ["EnableRealtimeTramClock"] = "startTime" }, "MimesisPlayerEnhancement_Weather" => new Dictionary<string, string>(StringComparer.Ordinal) { ["WeatherMode"] = "weatherMode", ["FixedWeatherPreset"] = "weatherMode", ["DisableRandomWeather"] = "weatherMode", ["WeatherCyclePresets"] = "weatherCycle", ["WeatherCycleMinDelaySeconds"] = "weatherCycle", ["WeatherCycleMaxDelaySeconds"] = "weatherCycle" }, "MimesisPlayerEnhancement_Ui" => new Dictionary<string, string>(StringComparer.Ordinal) { ["ModToastDurationSeconds"] = "modToasts", ["EnableExtendedSaveSlots"] = "extendedSaveSlots", ["EnableExtendedSpectatorPlayerList"] = "extendedSpectatorList", ["EnableLoadingWaitPlayerList"] = "customLoadingScreen", ["EnableExtendedInGameMenuPlayerList"] = "extendedInGameMenuPlayerList", ["EnableDamageHealthGlow"] = "damageHealthGlow", ["EnableFloatingDamageNumbers"] = "floatingDamage", ["FloatingDamageDurationSeconds"] = "floatingDamage", ["EnableFpsUi"] = "fpsUi", ["EnableFpsUiInventoryNetWorth"] = "fpsUi", ["EnableInventorySlotOptimization"] = "inventorySlotOptimization", ["InventoryPickupSelectMode"] = "inventorySlotOptimization", ["EnableInventorySelectNextOnRemove"] = "inventorySlotOptimization", ["EnableInventoryNumberKeySelection"] = "inventoryNumberKeys", ["RoundStartSoundMode"] = "roundStartSound", ["RoundStartSoundRandomPool"] = "roundStartSound", ["RoundStartSoundVariant"] = "roundStartSound", ["RoundStartSoundVolume"] = "roundStartSound", ["DiscoBallSoundMode"] = "discoBallSound", ["DiscoBallSoundRandomPool"] = "discoBallSound", ["DiscoBallSoundVariant"] = "discoBallSound", ["DiscoBallSoundVolume"] = "discoBallSound", ["CustomLoadingScreenMode"] = "customLoadingScreen", ["CustomLoadingScreenRandomPool"] = "customLoadingScreen", ["CustomLoadingScreenVariant"] = "customLoadingScreen", ["CustomLoadingScreenMotion"] = "customLoadingScreen", ["SpectatorVoiceBalanceMode"] = "spectatorVoiceBalance", ["SpectatorVoiceAttenuation"] = "spectatorVoiceBalance", ["SpectatorVoiceDuckLevel"] = "spectatorVoiceBalance", ["EnableVoiceNoiseGate"] = "voiceNoiseGate", ["VoiceNoiseGateStrength"] = "voiceNoiseGate" }, "MimesisPlayerEnhancement_Privacy" => new Dictionary<string, string>(StringComparer.Ordinal) { ["BlockReluTelemetry"] = "telemetry", ["BlockReplayUpload"] = "replay", ["BlockReplayRecording"] = "replay", ["BlockCrashReports"] = "crashReports", ["StripCrashReportMetadata"] = "crashReports", ["BlockKraftonGppSdk"] = "thirdParty" }, "MimesisPlayerEnhancement_SavegamePreparation" => new Dictionary<string, string>(StringComparer.Ordinal) { ["StartupMoney"] = "startupMoney", ["StartingZone"] = "startingZone", ["EnableUpgradeTramHorn"] = "upgrades", ["EnableUpgradeScrapScanner"] = "upgrades", ["EnableUpgradeTramBooster"] = "upgrades", ["EnableUpgradeTramLight"] = "upgrades" }, "MimesisPlayerEnhancement_MoreVoices" => new Dictionary<string, string>(StringComparer.Ordinal) { ["UnifyIndoorOutdoorVoices"] = "voiceLimits", ["MaxIndoorVoiceEvents"] = "voiceLimits", ["MaxDeathMatchVoiceEvents"] = "voiceLimits", ["MaxOutdoorVoiceEvents"] = "voiceLimits", ["RecordVoiceInMaintenance"] = "voiceRecording", ["RecordVoiceInTram"] = "voiceRecording", ["RecordVoiceDuringMimicPossession"] = "voiceRecording" }, _ => new Dictionary<string, string>(StringComparer.Ordinal), }; } private static List<WebDashboardConfigSelectOptionDto> BuildRoundStartSoundVariantOptions() { List<WebDashboardConfigSelectOptionDto> list = new List<WebDashboardConfigSelectOptionDto>(); foreach (string item in RoundStartSoundResolver.ListVariantOptionValues()) { string configSelectOptionLabel = WebDashboardL10n.GetConfigSelectOptionLabel("MimesisPlayerEnhancement_Ui", "RoundStartSoundVariant", item); list.Add(new WebDashboardConfigSelectOptionDto { Value = item, Label = (string.IsNullOrWhiteSpace(configSelectOptionLabel) ? RoundStartSoundResolver.FormatVariantDisplayName(item) : configSelectOptionLabel) }); } return list; } private static List<WebDashboardConfigSelectOptionDto> BuildDiscoBallSoundVariantOptions() { List<WebDashboardConfigSelectOptionDto> list = new List<WebDashboardConfigSelectOptionDto>(); foreach (string item in DiscoBallSoundResolver.ListVariantOptionValues()) { string configSelectOptionLabel = WebDashboardL10n.GetConfigSelectOptionLabel("MimesisPlayerEnhancement_Ui", "DiscoBallSoundVariant", item); list.Add(new WebDashboardConfigSelectOptionDto { Value = item, Label = (string.IsNullOrWhiteSpace(configSelectOptionLabel) ? DiscoBallSoundResolver.FormatVariantDisplayName(item) : configSelectOptionLabel) }); } return list; } private static List<WebDashboardConfigSelectOptionDto> BuildCustomLoadingScreenVariantOptions() { List<WebDashboardConfigSelectOptionDto> list = new List<WebDashboardConfigSelectOptionDto>(); foreach (string item in CustomLoadingScreenResolver.ListVariantOpti