Decompiled source of MoreSettings v0.2.5

plugins/MoreSettings/MoreSettings.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Mirror;
using MoreSettings.Configuration;
using MoreSettings.Models;
using MoreSettings.Network;
using MoreSettings.Runtime;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyVersion("0.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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]
	[Microsoft.CodeAnalysis.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]
	[Microsoft.CodeAnalysis.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 MoreSettings
{
	[BepInPlugin("com.lncinteractive", "MoreSettings", "0.2.5")]
	public sealed class PluginMain : BaseUnityPlugin
	{
		public const string PluginGuid = "com.lncinteractive";

		public const string PluginName = "MoreSettings";

		public const string PluginVersion = "0.2.5";

		private Harmony? _harmony;

		internal static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			TimingCoordinator.Initialize(ActiveSettings.Bind(((BaseUnityPlugin)this).Config), Log);
			TimingCoordinator.TryApplyFromResources("PluginMain.Awake");
			LobbyVisibility.Initialize(Log);
			LobbyVisibility.RegisterMessageDelegates();
			_harmony = new Harmony("com.lncinteractive");
			_harmony.PatchAll();
			Log.LogInfo((object)"MoreSettings 0.2.5 initialized.");
		}

		private void OnDestroy()
		{
			Harmony? harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
}
namespace MoreSettings.Runtime
{
	public static class NativeLobbySettingsMenu
	{
		private sealed class SliderRuntimeBinding
		{
			public string Key { get; }

			public Transform Root { get; }

			public SliderRuntimeBinding(string key, Transform root)
			{
				Key = key;
				Root = root;
			}
		}

		private const int MaxEditableQuotaMultipliers = 6;

		private const float DefaultCurrencySliderMax = 100000f;

		private const float CurrencySliderStep = 5000f;

		private const float SliderValueFieldWidth = 120f;

		private static readonly List<string> QuotaScalingModeOptions = new List<string> { "Vanilla scaling", "Custom pattern" };

		private static readonly List<SliderRuntimeBinding> SliderRuntimeBindings = new List<SliderRuntimeBinding>();

		public const string SectionKey = "moresettings.section";

		public const string TimeSectionKey = "moresettings.time.section";

		public const string QuotaSectionKey = "moresettings.quota.section";

		public const string DayDurationMinutesKey = "moresettings.day-duration-minutes";

		public const string StartingMoneyKey = "moresettings.starting-money";

		public const string StartingQuotaKey = "moresettings.starting-quota";

		public const string CatchUpFactorKey = "moresettings.catch-up-factor";

		public const string QuotaScalingModeKey = "moresettings.quota-scaling-mode";

		public const string QuotaPatternLengthKey = "moresettings.quota-pattern-length";

		private static SettingsLayout? _lobbySettingsLayout;

		private static SettingsLayout? _runtimeLobbySettingsLayout;

		private static bool _isSynchronizing;

		public static void RegisterLobbyLayout(SettingsLayout layout, string source)
		{
			if (!((Object)(object)layout == (Object)null))
			{
				_lobbySettingsLayout = layout;
				PluginMain.Log.LogDebug((object)("[NativeLobbySettingsMenu] Registered lobby settings layout from " + source + "."));
			}
		}

		public static void RegisterRuntimeLobbyLayout(SettingsLayout layout, string source)
		{
			if (!((Object)(object)layout == (Object)null))
			{
				_runtimeLobbySettingsLayout = layout;
				PluginMain.Log.LogDebug((object)("[NativeLobbySettingsMenu] Registered runtime lobby settings layout from " + source + "."));
			}
		}

		public static bool EnsureInjected(SettingsLayout layout, string source)
		{
			if ((Object)(object)layout == (Object)null)
			{
				return false;
			}
			if (!IsLobbySettingsLayout(layout))
			{
				return false;
			}
			if (layout.tabs == null || layout.tabs.Count == 0)
			{
				PluginMain.Log.LogDebug((object)("[NativeLobbySettingsMenu] Skipped injection from " + source + " because the layout had no tabs."));
				return false;
			}
			Tab val = FindTargetTab(layout);
			if (val == null)
			{
				PluginMain.Log.LogDebug((object)("[NativeLobbySettingsMenu] Skipped injection from " + source + " because no lobby settings tab was found."));
				return false;
			}
			Tab val2 = val;
			if (val2.entries == null)
			{
				val2.entries = new List<SettingItemBase>();
			}
			int num = 0;
			num += EnsureTitleEntry(val.entries, "moresettings.section", "MoreSettings");
			num += EnsureTitleEntry(val.entries, "moresettings.time.section", "Time");
			num += EnsureSliderEntry(val.entries, "moresettings.day-duration-minutes", "Day duration (minutes)", 1f, 1440f, wholeNumbers: true);
			num += EnsureTitleEntry(val.entries, "moresettings.quota.section", "Quota");
			num += EnsureSliderEntry(val.entries, "moresettings.starting-money", "Starting money", 0f, 100000f, wholeNumbers: true);
			num += EnsureSliderEntry(val.entries, "moresettings.starting-quota", "Starting quota", 0f, 100000f, wholeNumbers: true);
			num += EnsureSliderEntry(val.entries, "moresettings.catch-up-factor", "Catch-up factor", 0f, 5f, wholeNumbers: false);
			num += EnsureDropdownEntry(val.entries, "moresettings.quota-scaling-mode", "Quota scaling", QuotaScalingModeOptions, 0);
			num += EnsureSliderEntry(val.entries, "moresettings.quota-pattern-length", "Custom pattern length", 1f, 6f, wholeNumbers: true);
			for (int i = 0; i < 6; i++)
			{
				num += EnsureSliderEntry(val.entries, GetQuotaMultiplierKey(i), $"Pattern multiplier {i + 1}", 0.01f, 100f, wholeNumbers: false);
			}
			SyncFromCurrentState();
			PluginMain.Log.LogInfo((object)string.Format("[NativeLobbySettingsMenu] {0} native lobby settings entries from {1}. Tab='{2}', added={3}.", (num > 0) ? "Injected" : "Reused", source, val.tabName, num));
			return true;
		}

		public static void RefreshFromLobbyButton(SettingsLayout layout)
		{
			EnsureInjected(layout, "LobbyModeDropdownButton.OnClick");
			SyncFromCurrentState();
			RefreshRuntimeSliderBindings();
		}

		public static void HandleSettingChanged(SettingItemBase entry)
		{
			SettingsLayout activeLobbySettingsLayout = GetActiveLobbySettingsLayout();
			if (_isSynchronizing || (Object)(object)entry == (Object)null || !IsMoreSettingsKey(entry.key) || (Object)(object)activeLobbySettingsLayout == (Object)null)
			{
				return;
			}
			TimingProfile profile;
			SliderSettingItem dayDurationEntry;
			SliderSettingItem startingMoneyEntry;
			SliderSettingItem startingQuotaEntry;
			SliderSettingItem catchUpFactorEntry;
			DropdownSettingItem quotaScalingModeEntry;
			SliderSettingItem quotaPatternLengthEntry;
			SliderSettingItem[] quotaMultiplierEntries;
			if (string.Equals(entry.key, "moresettings.starting-money", StringComparison.OrdinalIgnoreCase) && !IsStartingMoneyEditable())
			{
				SyncFromCurrentState();
			}
			else if (TryGetCurrentProfileForEditing(out profile) && profile != null && TryGetEditableEntries(activeLobbySettingsLayout, out dayDurationEntry, out startingMoneyEntry, out startingQuotaEntry, out catchUpFactorEntry, out quotaScalingModeEntry, out quotaPatternLengthEntry, out quotaMultiplierEntries))
			{
				QuotaScalingMode quotaScalingMode = ResolveQuotaScalingMode(quotaScalingModeEntry);
				float[] array = QuotaPatternEditor.BuildPattern(quotaMultiplierEntries.Select((SliderSettingItem multiplierEntry) => multiplierEntry.value).ToArray(), Mathf.RoundToInt(quotaPatternLengthEntry.value));
				long startingMoney = (long)Mathf.Round(startingMoneyEntry.value);
				long startingQuota = EnsureLinkedStartingQuota(startingMoney, (long)Mathf.Round(startingQuotaEntry.value));
				float dayDurationSeconds = dayDurationEntry.value * 60f;
				int daysBeforeQuota = profile.DaysBeforeQuota;
				float value = catchUpFactorEntry.value;
				IReadOnlyList<float> quotaMultipliers;
				if (quotaScalingMode != QuotaScalingMode.CustomPattern)
				{
					quotaMultipliers = profile.QuotaMultipliers;
				}
				else
				{
					IReadOnlyList<float> readOnlyList = array;
					quotaMultipliers = readOnlyList;
				}
				TimingProfile updatedProfile = new TimingProfile("ActiveConfig", isVanillaProfile: false, dayDurationSeconds, daysBeforeQuota, startingQuota, startingMoney, value, quotaScalingMode, quotaMultipliers);
				if (!TimingCoordinator.TryApplyManualOverrides(profile, updatedProfile, "NativeLobbySettingsMenu.NotifyChanged", out IReadOnlyList<ValidationOutcome> _))
				{
					SyncFromCurrentState();
					return;
				}
				LobbyVisibility.BroadcastCurrentState();
				SyncFromCurrentState();
			}
		}

		private static void SyncFromCurrentState()
		{
			SettingsLayout activeLobbySettingsLayout = GetActiveLobbySettingsLayout();
			if ((Object)(object)activeLobbySettingsLayout == (Object)null || !TryGetCurrentProfileForEditing(out TimingProfile profile) || profile == null || !TryGetEditableEntries(activeLobbySettingsLayout, out SliderSettingItem dayDurationEntry, out SliderSettingItem startingMoneyEntry, out SliderSettingItem startingQuotaEntry, out SliderSettingItem catchUpFactorEntry, out DropdownSettingItem quotaScalingModeEntry, out SliderSettingItem quotaPatternLengthEntry, out SliderSettingItem[] quotaMultiplierEntries))
			{
				return;
			}
			float[] array = QuotaPatternEditor.BuildEditableValues(profile.QuotaMultipliers, 6);
			_isSynchronizing = true;
			try
			{
				dayDurationEntry.value = Mathf.Clamp(profile.DayDurationSeconds / 60f, dayDurationEntry.min, dayDurationEntry.max);
				dayDurationEntry.defaultValue = dayDurationEntry.value;
				long startingMoney = profile.StartingMoney;
				long num = EnsureLinkedStartingQuota(profile.StartingMoney, profile.StartingQuota);
				ApplyCurrencyBounds(startingMoneyEntry, startingQuotaEntry, startingMoney, num);
				startingMoneyEntry.value = Mathf.Clamp((float)startingMoney, startingMoneyEntry.min, startingMoneyEntry.max);
				startingMoneyEntry.defaultValue = startingMoneyEntry.value;
				startingQuotaEntry.value = Mathf.Clamp((float)num, startingQuotaEntry.min, startingQuotaEntry.max);
				startingQuotaEntry.defaultValue = startingQuotaEntry.value;
				catchUpFactorEntry.value = Mathf.Clamp(profile.CatchUpFactor, catchUpFactorEntry.min, catchUpFactorEntry.max);
				catchUpFactorEntry.defaultValue = catchUpFactorEntry.value;
				quotaScalingModeEntry.index = Mathf.Clamp((int)profile.QuotaScalingMode, 0, QuotaScalingModeOptions.Count - 1);
				quotaPatternLengthEntry.value = Mathf.Clamp((float)Mathf.Max(1, Mathf.Min(profile.QuotaMultipliers.Count, 6)), quotaPatternLengthEntry.min, quotaPatternLengthEntry.max);
				quotaPatternLengthEntry.defaultValue = quotaPatternLengthEntry.value;
				for (int i = 0; i < quotaMultiplierEntries.Length; i++)
				{
					SliderSettingItem val = quotaMultiplierEntries[i];
					val.max = Mathf.Max(100f, Mathf.Ceil(array[i]));
					val.value = Mathf.Clamp(array[i], val.min, val.max);
					val.defaultValue = val.value;
				}
			}
			finally
			{
				_isSynchronizing = false;
			}
			RefreshRuntimeSliderBindings();
		}

		private static bool TryGetCurrentProfileForEditing(out TimingProfile? profile)
		{
			if (!TimingCoordinator.TryGetResolvedProfile("NativeLobbySettingsMenu.Sync", out profile) || profile == null)
			{
				return false;
			}
			return true;
		}

		private static bool IsMoreSettingsKey(string? key)
		{
			switch (key)
			{
			default:
				return IsQuotaMultiplierKey(key);
			case "moresettings.day-duration-minutes":
			case "moresettings.starting-money":
			case "moresettings.starting-quota":
			case "moresettings.catch-up-factor":
			case "moresettings.quota-scaling-mode":
			case "moresettings.quota-pattern-length":
				return true;
			}
		}

		public static bool IsManagedSliderKey(string? key)
		{
			switch (key)
			{
			default:
				return IsQuotaMultiplierKey(key);
			case "moresettings.day-duration-minutes":
			case "moresettings.starting-money":
			case "moresettings.starting-quota":
			case "moresettings.catch-up-factor":
			case "moresettings.quota-pattern-length":
				return true;
			}
		}

		public static bool IsStartingMoneyEditable()
		{
			if (!NetworkServer.active)
			{
				return !NetworkClient.active;
			}
			return false;
		}

		public static void RegisterSliderRuntimeBinding(string? key, Transform? root)
		{
			Transform root2 = root;
			if (!string.IsNullOrWhiteSpace(key) && !((Object)(object)root2 == (Object)null) && IsManagedSliderKey(key))
			{
				SliderRuntimeBindings.RemoveAll((SliderRuntimeBinding binding) => (Object)(object)binding.Root == (Object)null);
				if (!SliderRuntimeBindings.Any((SliderRuntimeBinding binding) => (Object)(object)binding.Root == (Object)(object)root2))
				{
					SliderRuntimeBindings.Add(new SliderRuntimeBinding(key, root2));
				}
				ApplyRuntimeSliderPresentation(key, root2);
			}
		}

		public static void RefreshRuntimeSliderBindings()
		{
			SliderRuntimeBindings.RemoveAll((SliderRuntimeBinding binding) => (Object)(object)binding.Root == (Object)null);
			foreach (SliderRuntimeBinding sliderRuntimeBinding in SliderRuntimeBindings)
			{
				ApplyRuntimeSliderPresentation(sliderRuntimeBinding.Key, sliderRuntimeBinding.Root);
			}
		}

		private static SettingsLayout? GetActiveLobbySettingsLayout()
		{
			return _runtimeLobbySettingsLayout ?? _lobbySettingsLayout;
		}

		private static bool IsLobbySettingsLayout(SettingsLayout layout)
		{
			return FindTargetTab(layout) != null;
		}

		private static Tab? FindTargetTab(SettingsLayout layout)
		{
			foreach (Tab tab in layout.tabs)
			{
				if (tab.entries != null && tab.entries.Count != 0)
				{
					if (tab.entries.Any(IsLobbyModeEntry))
					{
						return tab;
					}
					if (!string.IsNullOrWhiteSpace(tab.tabName) && string.Equals(tab.tabName.Trim(), "Settings", StringComparison.OrdinalIgnoreCase))
					{
						return tab;
					}
				}
			}
			return null;
		}

		private static bool IsLobbyModeEntry(SettingItemBase entry)
		{
			if (!(entry is DropdownSettingItem))
			{
				return false;
			}
			if (string.Equals(entry.key, "moresettings.section", StringComparison.OrdinalIgnoreCase))
			{
				return false;
			}
			return string.Equals(entry.label?.Trim(), "Lobby Mode", StringComparison.OrdinalIgnoreCase);
		}

		private static int EnsureTitleEntry(ICollection<SettingItemBase> entries, string key, string label)
		{
			string key2 = key;
			if (entries.Any((SettingItemBase entry) => entry.key == key2))
			{
				return 0;
			}
			TitleSettingItem val = ScriptableObject.CreateInstance<TitleSettingItem>();
			((Object)val).hideFlags = (HideFlags)61;
			((SettingItemBase)val).key = key2;
			((SettingItemBase)val).label = label;
			entries.Add((SettingItemBase)(object)val);
			return 1;
		}

		private static int EnsureDropdownEntry(ICollection<SettingItemBase> entries, string key, string label, IReadOnlyCollection<string> options, int defaultIndex)
		{
			string key2 = key;
			if (entries.Any((SettingItemBase entry) => entry.key == key2))
			{
				return 0;
			}
			DropdownSettingItem val = ScriptableObject.CreateInstance<DropdownSettingItem>();
			((Object)val).hideFlags = (HideFlags)61;
			((SettingItemBase)val).key = key2;
			((SettingItemBase)val).label = label;
			val.options = options.ToList();
			val.index = Mathf.Clamp(defaultIndex, 0, val.options.Count - 1);
			val.loadOnSceneStart = false;
			entries.Add((SettingItemBase)(object)val);
			return 1;
		}

		private static int EnsureSliderEntry(ICollection<SettingItemBase> entries, string key, string label, float min, float max, bool wholeNumbers)
		{
			string key2 = key;
			if (entries.Any((SettingItemBase entry) => entry.key == key2))
			{
				return 0;
			}
			SliderSettingItem val = ScriptableObject.CreateInstance<SliderSettingItem>();
			((Object)val).hideFlags = (HideFlags)61;
			((SettingItemBase)val).key = key2;
			((SettingItemBase)val).label = label;
			val.min = min;
			val.max = max;
			val.wholeNumbers = wholeNumbers;
			val.value = min;
			val.defaultValue = min;
			val.loadOnSceneStart = false;
			entries.Add((SettingItemBase)(object)val);
			return 1;
		}

		private static T? FindEntry<T>(SettingsLayout layout, string key) where T : SettingItemBase
		{
			string key2 = key;
			foreach (Tab tab in layout.tabs)
			{
				if (tab.entries != null)
				{
					T val = tab.entries.OfType<T>().FirstOrDefault((T entry) => ((SettingItemBase)entry).key == key2);
					if ((Object)(object)val != (Object)null)
					{
						return val;
					}
				}
			}
			return default(T);
		}

		private static bool TryGetEditableEntries(SettingsLayout layout, out SliderSettingItem? dayDurationEntry, out SliderSettingItem? startingMoneyEntry, out SliderSettingItem? startingQuotaEntry, out SliderSettingItem? catchUpFactorEntry, out DropdownSettingItem? quotaScalingModeEntry, out SliderSettingItem? quotaPatternLengthEntry, out SliderSettingItem[]? quotaMultiplierEntries)
		{
			dayDurationEntry = FindEntry<SliderSettingItem>(layout, "moresettings.day-duration-minutes");
			startingMoneyEntry = FindEntry<SliderSettingItem>(layout, "moresettings.starting-money");
			startingQuotaEntry = FindEntry<SliderSettingItem>(layout, "moresettings.starting-quota");
			catchUpFactorEntry = FindEntry<SliderSettingItem>(layout, "moresettings.catch-up-factor");
			quotaScalingModeEntry = FindEntry<DropdownSettingItem>(layout, "moresettings.quota-scaling-mode");
			quotaPatternLengthEntry = FindEntry<SliderSettingItem>(layout, "moresettings.quota-pattern-length");
			quotaMultiplierEntries = GetQuotaMultiplierEntries(layout);
			if ((Object)(object)dayDurationEntry != (Object)null && (Object)(object)startingMoneyEntry != (Object)null && (Object)(object)startingQuotaEntry != (Object)null && (Object)(object)catchUpFactorEntry != (Object)null && (Object)(object)quotaScalingModeEntry != (Object)null && (Object)(object)quotaPatternLengthEntry != (Object)null)
			{
				return quotaMultiplierEntries.Length == 6;
			}
			return false;
		}

		private static SliderSettingItem[] GetQuotaMultiplierEntries(SettingsLayout layout)
		{
			SliderSettingItem[] array = (SliderSettingItem[])(object)new SliderSettingItem[6];
			for (int i = 0; i < 6; i++)
			{
				SliderSettingItem val = FindEntry<SliderSettingItem>(layout, GetQuotaMultiplierKey(i));
				if ((Object)(object)val == (Object)null)
				{
					return Array.Empty<SliderSettingItem>();
				}
				array[i] = val;
			}
			return array;
		}

		private static QuotaScalingMode ResolveQuotaScalingMode(DropdownSettingItem entry)
		{
			if (entry.index != 1)
			{
				return QuotaScalingMode.Vanilla;
			}
			return QuotaScalingMode.CustomPattern;
		}

		private static float ComputeLinkedCurrencySliderMax(long startingMoney, long startingQuota)
		{
			float num = Mathf.Max(new float[3] { 100000f, startingMoney, startingQuota });
			return Mathf.Max(100000f, Mathf.Ceil(num / 5000f) * 5000f);
		}

		private static long EnsureLinkedStartingQuota(long startingMoney, long startingQuota)
		{
			return Math.Max(startingMoney, startingQuota);
		}

		private static void ApplyCurrencyBounds(SliderSettingItem startingMoneyEntry, SliderSettingItem startingQuotaEntry, long startingMoney, long startingQuota)
		{
			float max = ComputeLinkedCurrencySliderMax(startingMoney, startingQuota);
			startingMoneyEntry.min = 0f;
			startingMoneyEntry.max = max;
			startingQuotaEntry.min = startingMoney;
			startingQuotaEntry.max = max;
		}

		private static void ApplyRuntimeSliderPresentation(string key, Transform root)
		{
			SettingsLayout activeLobbySettingsLayout = GetActiveLobbySettingsLayout();
			SliderSettingItem val = (((Object)(object)activeLobbySettingsLayout != (Object)null) ? FindEntry<SliderSettingItem>(activeLobbySettingsLayout, key) : null);
			if ((Object)(object)val != (Object)null)
			{
				SyncRuntimeSlider(root, val);
			}
			TMP_InputField val2 = FindRuntimeValueInput(root);
			if ((Object)(object)val2 != (Object)null)
			{
				ApplyFixedWidth(((Component)val2).gameObject);
			}
			Transform val3 = root.Find("ValueText");
			if ((Object)(object)val3 != (Object)null)
			{
				ApplyFixedWidth(((Component)val3).gameObject);
			}
			if (string.Equals(key, "moresettings.starting-money", StringComparison.OrdinalIgnoreCase))
			{
				SetInteractable(root, IsStartingMoneyEditable());
			}
		}

		private static void SyncRuntimeSlider(Transform root, SliderSettingItem entry)
		{
			Slider componentInChildren = ((Component)root).GetComponentInChildren<Slider>(true);
			if ((Object)(object)componentInChildren != (Object)null)
			{
				componentInChildren.minValue = entry.min;
				componentInChildren.maxValue = entry.max;
				componentInChildren.wholeNumbers = entry.wholeNumbers;
				componentInChildren.SetValueWithoutNotify(Mathf.Clamp(entry.value, componentInChildren.minValue, componentInChildren.maxValue));
			}
			string textWithoutNotify = (entry.wholeNumbers ? Mathf.Round(entry.value).ToString() : entry.value.ToString("F1"));
			TMP_InputField val = FindRuntimeValueInput(root);
			if ((Object)(object)val != (Object)null)
			{
				val.SetTextWithoutNotify(textWithoutNotify);
			}
		}

		private static TMP_InputField? FindRuntimeValueInput(Transform root)
		{
			TMP_InputField componentInChildren = ((Component)root).GetComponentInChildren<TMP_InputField>(true);
			if ((Object)(object)componentInChildren != (Object)null)
			{
				return componentInChildren;
			}
			Transform val = root.Find("ValueText");
			if (!((Object)(object)val != (Object)null))
			{
				return null;
			}
			return ((Component)val).GetComponent<TMP_InputField>();
		}

		private static void ApplyFixedWidth(GameObject gameObject)
		{
			LayoutElement obj = gameObject.GetComponent<LayoutElement>() ?? gameObject.AddComponent<LayoutElement>();
			obj.minWidth = 120f;
			obj.preferredWidth = 120f;
			obj.flexibleWidth = 0f;
			RectTransform component = gameObject.GetComponent<RectTransform>();
			if ((Object)(object)component != (Object)null)
			{
				component.SetSizeWithCurrentAnchors((Axis)0, 120f);
			}
		}

		private static void SetInteractable(Transform root, bool isInteractable)
		{
			Selectable[] componentsInChildren = ((Component)root).GetComponentsInChildren<Selectable>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				componentsInChildren[i].interactable = isInteractable;
			}
		}

		private static string GetQuotaMultiplierKey(int index)
		{
			return $"moresettings.quota-multiplier-{index + 1}";
		}

		private static bool IsQuotaMultiplierKey(string? key)
		{
			if (!string.IsNullOrWhiteSpace(key))
			{
				return key.StartsWith("moresettings.quota-multiplier-", StringComparison.OrdinalIgnoreCase);
			}
			return false;
		}
	}
	public static class QuotaPatternEditor
	{
		public static float[] BuildEditableValues(IReadOnlyList<float> sourceValues, int slotCount)
		{
			if (slotCount <= 0)
			{
				throw new ArgumentOutOfRangeException("slotCount");
			}
			float[] array = new float[slotCount];
			float num = ((sourceValues != null && sourceValues.Count > 0) ? sourceValues[sourceValues.Count - 1] : 1f);
			for (int i = 0; i < slotCount; i++)
			{
				array[i] = ((i < sourceValues.Count) ? sourceValues[i] : num);
			}
			return array;
		}

		public static float[] BuildPattern(IReadOnlyList<float> editableValues, int desiredLength)
		{
			if (editableValues == null)
			{
				throw new ArgumentNullException("editableValues");
			}
			if (editableValues.Count == 0)
			{
				return Array.Empty<float>();
			}
			int num = Math.Max(1, Math.Min(desiredLength, editableValues.Count));
			float[] array = new float[num];
			for (int i = 0; i < num; i++)
			{
				array[i] = editableValues[i];
			}
			return array;
		}
	}
	public static class QuotaRuntimeStatePlanner
	{
		public static int ComputeRemainingDays(int daysBeforeQuota, int daysPassed)
		{
			return Math.Max(0, daysBeforeQuota - daysPassed);
		}

		public static bool ShouldResetInitialQuota(long previousStartingQuota, long currentQuota, long requiredQuota, int daysPassed, int successfulQuota)
		{
			if (successfulQuota == 0 && daysPassed == 0 && currentQuota == previousStartingQuota)
			{
				return requiredQuota == previousStartingQuota;
			}
			return false;
		}

		public static bool ShouldResetInitialMoney(long previousStartingMoney, long currentMoney, int daysPassed, int successfulQuota)
		{
			if (successfulQuota == 0 && daysPassed == 0)
			{
				return currentMoney == previousStartingMoney;
			}
			return false;
		}
	}
	public static class TimingCoordinator
	{
		private static readonly FieldRef<GameManager, GameSettings> GameSettingsRef = AccessTools.FieldRefAccess<GameManager, GameSettings>("_gs");

		private static readonly FieldRef<SaveManager, SaveData> CurrentSaveDataRef = AccessTools.FieldRefAccess<SaveManager, SaveData>("currentSaveData");

		private static readonly PropertyInfo? HasDayStartedProperty = AccessTools.Property(typeof(GameManager), "HasDayStarted");

		private static readonly PropertyInfo? NetworkTimerProperty = AccessTools.Property(typeof(GameManager), "Network_timer");

		private static readonly PropertyInfo? NetworkCurrentQuotaProperty = AccessTools.Property(typeof(GameManager), "NetworkcurrentQuota");

		private static readonly PropertyInfo? NetworkRequiredQuotaProperty = AccessTools.Property(typeof(GameManager), "NetworkrequiredQuotaToNextFloor");

		private static readonly PropertyInfo? NetworkDaysLeftProperty = AccessTools.Property(typeof(GameManager), "NetworkdaysLeft");

		private static readonly PropertyInfo? NetworkDaysPassedProperty = AccessTools.Property(typeof(GameManager), "NetworkdaysPassed");

		private static readonly PropertyInfo? NetworkSuccessfulQuotaProperty = AccessTools.Property(typeof(GameManager), "NetworksuccessfulQuota");

		private static ActiveSettings? _settings;

		private static ManualLogSource? _log;

		private static TimingProfile? _vanillaProfile;

		public static SessionTimingState? CurrentState { get; private set; }

		public static void Initialize(ActiveSettings settings, ManualLogSource log)
		{
			_settings = settings;
			_log = log;
			_vanillaProfile = null;
			CurrentState = null;
		}

		public static bool TryApplyFromResources(string context)
		{
			EnsureInitialized();
			if (!TryGetActiveGameSettings(context, out GameSettings gameSettings, includeSceneInstance: false))
			{
				return false;
			}
			return TryApplyToGameSettings(gameSettings, context);
		}

		public static bool TryApplyToGameSettings(GameSettings gameSettings, string context)
		{
			EnsureInitialized();
			CaptureVanillaProfile(gameSettings);
			if (!_settings.IsEnabled)
			{
				TimingProfile baseProfile = GetBaseProfile(gameSettings);
				CurrentState = BuildState(baseProfile.Name, baseProfile.IsVanillaProfile, baseProfile.DayDurationSeconds, baseProfile.DaysBeforeQuota, baseProfile.StartingQuota, baseProfile.StartingMoney, baseProfile.CatchUpFactor, baseProfile.QuotaScalingMode, baseProfile.QuotaMultipliers.Count, context);
				return false;
			}
			if (!TryResolveProfile(gameSettings, context, out TimingProfile profile))
			{
				return false;
			}
			TimingProfile profile2 = profile;
			ApplyResolvedProfileToGameSettings(gameSettings, profile2, context);
			return true;
		}

		public static bool TryApplyInitialQuotaToSaveData(SaveData saveData, string context)
		{
			EnsureInitialized();
			if (saveData == null || !_settings.IsEnabled)
			{
				return false;
			}
			if (!TryGetActiveGameSettings(context, out GameSettings gameSettings))
			{
				return false;
			}
			if (!TryResolveProfile(gameSettings, context, out TimingProfile profile))
			{
				return false;
			}
			ApplyResolvedProfileToSaveData(saveData, profile, context, resetQuotaState: true);
			return true;
		}

		public static bool TryApplyToActiveSaveData(string context)
		{
			EnsureInitialized();
			if (!_settings.IsEnabled)
			{
				return false;
			}
			SaveManager val = Object.FindFirstObjectByType<SaveManager>();
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			SaveData val2 = CurrentSaveDataRef.Invoke(val);
			if (val2 == null)
			{
				return false;
			}
			GameManager val3 = Object.FindFirstObjectByType<GameManager>();
			if ((Object)(object)val3 != (Object)null && !CanUpdatePreDayRuntimeState(val3))
			{
				return false;
			}
			if (!TryGetActiveGameSettings(context, out GameSettings gameSettings))
			{
				return false;
			}
			if (!TryResolveProfile(gameSettings, context, out TimingProfile profile))
			{
				return false;
			}
			ApplyResolvedProfileToSaveData(val2, profile, context, resetQuotaState: true);
			return true;
		}

		public static bool TryGetResolvedProfile(string context, out TimingProfile? profile)
		{
			EnsureInitialized();
			if (!TryGetActiveGameSettings(context, out GameSettings gameSettings))
			{
				profile = null;
				return false;
			}
			if (!_settings.IsEnabled)
			{
				profile = GetBaseProfile(gameSettings);
				return true;
			}
			return TryResolveProfile(gameSettings, context, out profile);
		}

		public static bool TryApplyManualOverrides(TimingProfile previousProfile, TimingProfile updatedProfile, string context, out IReadOnlyList<ValidationOutcome> outcomes)
		{
			EnsureInitialized();
			if (!TryGetActiveGameSettings(context, out GameSettings gameSettings))
			{
				outcomes = new ValidationOutcome[1] { ValidationOutcome.Error("GameSettings", "No active GameSettings were available to apply manual overrides.") };
				return false;
			}
			ValidationOutcome[] array = (outcomes = TimingProfileValidator.Validate(updatedProfile)).Where((ValidationOutcome outcome) => outcome.Status == ValidationStatus.Error).ToArray();
			if (array.Length != 0)
			{
				ValidationOutcome[] array2 = array;
				foreach (ValidationOutcome validationOutcome in array2)
				{
					_log.LogError((object)("[" + context + "] " + validationOutcome.TargetField + ": " + validationOutcome.Message));
				}
				return false;
			}
			CaptureVanillaProfile(gameSettings);
			_settings.SetManualOverrides(updatedProfile);
			if (!_settings.TryCreateResolvedProfile(GetBaseProfile(gameSettings), out TimingProfile profile, out IReadOnlyList<ValidationOutcome> outcomes2))
			{
				outcomes = outcomes2;
				foreach (ValidationOutcome item in outcomes2.Where((ValidationOutcome outcome) => outcome.Status == ValidationStatus.Error))
				{
					_log.LogError((object)("[" + context + "] " + item.TargetField + ": " + item.Message));
				}
				return false;
			}
			outcomes = outcomes2;
			ApplyResolvedProfileToGameSettings(gameSettings, profile, context);
			GameManager val = Object.FindFirstObjectByType<GameManager>();
			if ((Object)(object)val != (Object)null)
			{
				ApplyManualProfileToGameManager(val, previousProfile, profile, context);
			}
			SaveManager val2 = Object.FindFirstObjectByType<SaveManager>();
			if ((Object)(object)val2 != (Object)null)
			{
				SaveData val3 = CurrentSaveDataRef.Invoke(val2);
				if (val3 != null)
				{
					ApplyManualProfileToSaveData(val3, previousProfile, profile, context);
				}
			}
			return true;
		}

		public static bool TryApplyToGameManagerRuntime(GameManager gameManager, string context)
		{
			EnsureInitialized();
			if ((Object)(object)gameManager == (Object)null || !_settings.IsEnabled)
			{
				return false;
			}
			GameSettings val = GameSettingsRef.Invoke(gameManager);
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			if (!TryResolveProfile(val, context, out TimingProfile profile))
			{
				return false;
			}
			ApplyResolvedProfileToGameManager(gameManager, profile, context);
			return true;
		}

		private static bool TryResolveProfile(GameSettings gameSettings, string context, out TimingProfile? profile)
		{
			CaptureVanillaProfile(gameSettings);
			if (!_settings.TryCreateResolvedProfile(GetBaseProfile(gameSettings), out TimingProfile profile2, out IReadOnlyList<ValidationOutcome> outcomes))
			{
				foreach (ValidationOutcome item in outcomes.Where((ValidationOutcome o) => o.Status == ValidationStatus.Error))
				{
					_log.LogError((object)("[" + context + "] " + item.TargetField + ": " + item.Message));
				}
				profile = null;
				return false;
			}
			profile = profile2;
			return true;
		}

		private static void CaptureVanillaProfile(GameSettings gameSettings)
		{
			if (_vanillaProfile == null && !((Object)(object)gameSettings == (Object)null))
			{
				_vanillaProfile = new TimingProfile("Vanilla", isVanillaProfile: true, gameSettings.dayDuration, gameSettings.daysBeforeQuota, gameSettings.startingQuota, gameSettings.startingMoney, gameSettings.catchUpFactor, QuotaScalingMode.Vanilla, (gameSettings.quotas ?? Array.Empty<float>()).ToArray());
			}
		}

		private static TimingProfile GetBaseProfile(GameSettings gameSettings)
		{
			CaptureVanillaProfile(gameSettings);
			return _vanillaProfile ?? new TimingProfile("Vanilla", isVanillaProfile: true, gameSettings.dayDuration, gameSettings.daysBeforeQuota, gameSettings.startingQuota, gameSettings.startingMoney, gameSettings.catchUpFactor, QuotaScalingMode.Vanilla, (gameSettings.quotas ?? Array.Empty<float>()).ToArray());
		}

		private static bool TryGetActiveGameSettings(string context, out GameSettings? gameSettings, bool includeSceneInstance = true)
		{
			if (includeSceneInstance)
			{
				GameManager val = Object.FindFirstObjectByType<GameManager>();
				if ((Object)(object)val != (Object)null)
				{
					GameSettings val2 = GameSettingsRef.Invoke(val);
					if ((Object)(object)val2 != (Object)null)
					{
						gameSettings = val2;
						return true;
					}
				}
			}
			gameSettings = Resources.Load<GameSettings>("GameSettings");
			if ((Object)(object)gameSettings == (Object)null)
			{
				_log.LogDebug((object)("No GameSettings resource was available during " + context + "."));
				return false;
			}
			return true;
		}

		private static void ApplyResolvedProfileToGameSettings(GameSettings gameSettings, TimingProfile profile, string context)
		{
			gameSettings.dayDuration = profile.DayDurationSeconds;
			gameSettings.startingQuota = profile.StartingQuota;
			gameSettings.startingMoney = profile.StartingMoney;
			gameSettings.catchUpFactor = profile.CatchUpFactor;
			gameSettings.quotas = profile.QuotaMultipliers.ToArray();
			CurrentState = BuildState(profile.Name, profile.IsVanillaProfile, profile.DayDurationSeconds, profile.DaysBeforeQuota, profile.StartingQuota, profile.StartingMoney, profile.CatchUpFactor, profile.QuotaScalingMode, profile.QuotaMultipliers.Count, context);
			_log.LogInfo((object)("[" + context + "] Applied timing profile '" + profile.Name + "' " + $"(dayDuration={profile.DayDurationSeconds}, daysBeforeQuota={profile.DaysBeforeQuota}, " + $"startingQuota={profile.StartingQuota}, startingMoney={profile.StartingMoney}, catchUpFactor={profile.CatchUpFactor}, " + $"quotaScalingMode={profile.QuotaScalingMode}, " + $"quotaMultipliers={profile.QuotaMultipliers.Count})."));
		}

		private static void ApplyResolvedProfileToGameManager(GameManager gameManager, TimingProfile profile, string context, bool resetQuotaState = true)
		{
			if (CanUpdatePreDayRuntimeState(gameManager))
			{
				NetworkTimerProperty?.SetValue(gameManager, profile.DayDurationSeconds);
				if (resetQuotaState)
				{
					NetworkCurrentQuotaProperty?.SetValue(gameManager, profile.StartingQuota);
					NetworkRequiredQuotaProperty?.SetValue(gameManager, profile.StartingQuota);
				}
				_log.LogInfo((object)("[" + context + "] Applied pre-day runtime state to GameManager " + $"(timer={profile.DayDurationSeconds}, quotaReset={resetQuotaState}, " + $"currentQuota={profile.StartingQuota}, requiredQuota={profile.StartingQuota})."));
			}
		}

		private static void ApplyManualProfileToGameManager(GameManager gameManager, TimingProfile previousProfile, TimingProfile updatedProfile, string context)
		{
			if (CanUpdatePreDayRuntimeState(gameManager))
			{
				NetworkTimerProperty?.SetValue(gameManager, updatedProfile.DayDurationSeconds);
				int daysPassed = ReadIntProperty(NetworkDaysPassedProperty, gameManager);
				int successfulQuota = ReadIntProperty(NetworkSuccessfulQuotaProperty, gameManager);
				bool flag = QuotaRuntimeStatePlanner.ShouldResetInitialQuota(previousProfile.StartingQuota, ReadLongProperty(NetworkCurrentQuotaProperty, gameManager), ReadLongProperty(NetworkRequiredQuotaProperty, gameManager), daysPassed, successfulQuota);
				if (flag)
				{
					NetworkCurrentQuotaProperty?.SetValue(gameManager, updatedProfile.StartingQuota);
					NetworkRequiredQuotaProperty?.SetValue(gameManager, updatedProfile.StartingQuota);
				}
				_log.LogInfo((object)("[" + context + "] Applied manual pre-day runtime state to GameManager " + $"(timer={updatedProfile.DayDurationSeconds}, preservedQuota={!flag})."));
			}
		}

		private static void ApplyResolvedProfileToSaveData(SaveData saveData, TimingProfile profile, string context, bool resetQuotaState)
		{
			if (resetQuotaState)
			{
				saveData.currentQuota = profile.StartingQuota;
				saveData.requiredQuotaToNextFloor = profile.StartingQuota;
				saveData.money = profile.StartingMoney;
				TrySyncMoneyManagerBalance(profile.StartingMoney);
			}
			_log.LogInfo((object)("[" + context + "] Applied save timing state " + $"(quotaReset={resetQuotaState}, currentQuota={saveData.currentQuota}, " + $"requiredQuota={saveData.requiredQuotaToNextFloor}, money={saveData.money})."));
		}

		private static void ApplyManualProfileToSaveData(SaveData saveData, TimingProfile previousProfile, TimingProfile updatedProfile, string context)
		{
			bool flag = QuotaRuntimeStatePlanner.ShouldResetInitialQuota(previousProfile.StartingQuota, saveData.currentQuota, saveData.requiredQuotaToNextFloor, saveData.daysPassed, saveData.successfulQuota);
			bool flag2 = QuotaRuntimeStatePlanner.ShouldResetInitialMoney(previousProfile.StartingMoney, saveData.money, saveData.daysPassed, saveData.successfulQuota);
			if (flag)
			{
				saveData.currentQuota = updatedProfile.StartingQuota;
				saveData.requiredQuotaToNextFloor = updatedProfile.StartingQuota;
			}
			if (flag2)
			{
				saveData.money = updatedProfile.StartingMoney;
			}
			if (flag2)
			{
				TrySyncMoneyManagerBalance(updatedProfile.StartingMoney);
			}
			_log.LogInfo((object)("[" + context + "] Updated active save state " + $"(preservedQuota={!flag}, preservedMoney={!flag2})."));
		}

		private static bool CanUpdatePreDayRuntimeState(GameManager gameManager)
		{
			return !(HasDayStartedProperty?.GetValue(gameManager) as bool?).GetValueOrDefault();
		}

		private static void TrySyncMoneyManagerBalance(long balance)
		{
			MoneyManager val = Object.FindFirstObjectByType<MoneyManager>();
			if (!((Object)(object)val == (Object)null))
			{
				val.SetBalance(balance, (PlayerProfile)null, (ChangeType)3);
			}
		}

		private static int ReadIntProperty(PropertyInfo? property, object instance)
		{
			object obj = property?.GetValue(instance);
			if (!(obj is int result))
			{
				if (obj is long num)
				{
					return checked((int)num);
				}
				return 0;
			}
			return result;
		}

		private static long ReadLongProperty(PropertyInfo? property, object instance)
		{
			object obj = property?.GetValue(instance);
			if (!(obj is long result))
			{
				if (obj is int num)
				{
					return num;
				}
				return 0L;
			}
			return result;
		}

		private static SessionTimingState BuildState(string profileName, bool isVanilla, float dayDurationSeconds, int daysBeforeQuota, long startingQuota, long startingMoney, float catchUpFactor, QuotaScalingMode quotaScalingMode, int quotaMultiplierCount, string context)
		{
			return new SessionTimingState(context, profileName, isVanilla, dayDurationSeconds, daysBeforeQuota, startingQuota, startingMoney, catchUpFactor, quotaScalingMode, quotaMultiplierCount, DateTimeOffset.UtcNow);
		}

		private static void EnsureInitialized()
		{
			if (_settings == null || _log == null)
			{
				throw new InvalidOperationException("TimingCoordinator.Initialize must run before timing overrides are applied.");
			}
		}
	}
}
namespace MoreSettings.Patches
{
	[HarmonyPatch(typeof(GameManager), "OnAwake")]
	internal static class DayTimerPatches
	{
		private static readonly FieldRef<GameManager, GameSettings> GameSettingsRef = AccessTools.FieldRefAccess<GameManager, GameSettings>("_gs");

		private static void Postfix(GameManager __instance)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return;
			}
			GameSettings val = GameSettingsRef.Invoke(__instance);
			if (!((Object)(object)val == (Object)null))
			{
				if (!NetworkServer.active)
				{
					LobbyVisibility.ClearClientState();
					return;
				}
				TimingCoordinator.TryApplyToGameSettings(val, "GameManager.OnAwake");
				TimingCoordinator.TryApplyToGameManagerRuntime(__instance, "GameManager.OnAwake");
				LobbyVisibility.BroadcastCurrentState();
			}
		}
	}
	[HarmonyPatch(typeof(GameManager))]
	internal static class LobbyPatches
	{
		[HarmonyPostfix]
		[HarmonyPatch("OnStartServer")]
		private static void OnStartServer_Postfix()
		{
			LobbyVisibility.BroadcastCurrentState();
		}

		[HarmonyPostfix]
		[HarmonyPatch("ServerOnClientScenePlayReady")]
		private static void ServerOnClientScenePlayReady_Postfix(NetworkConnectionToClient conn)
		{
			LobbyVisibility.SendToClient(conn);
		}

		[HarmonyPostfix]
		[HarmonyPatch("RpcSetInLobbyPresence")]
		private static void RpcSetInLobbyPresence_Postfix(GameManager __instance)
		{
			if (!((Object)(object)__instance == (Object)null) && NetworkServer.active)
			{
				TimingCoordinator.TryApplyToGameManagerRuntime(__instance, "GameManager.RpcSetInLobbyPresence");
				TimingCoordinator.TryApplyToActiveSaveData("GameManager.RpcSetInLobbyPresence");
				NativeLobbySettingsMenu.RefreshRuntimeSliderBindings();
				LobbyVisibility.BroadcastCurrentState();
			}
		}
	}
	[HarmonyPatch]
	internal static class NativeLobbySettingsRuntimeUIPatches
	{
		private static readonly FieldRef<SettingsLayoutRuntimeUI, SettingsLayout> LayoutRef = AccessTools.FieldRefAccess<SettingsLayoutRuntimeUI, SettingsLayout>("layout");

		[HarmonyPrefix]
		[HarmonyPatch(typeof(SettingsLayoutRuntimeUI), "Awake")]
		private static void SettingsLayoutRuntimeUI_Awake_Prefix(SettingsLayoutRuntimeUI __instance)
		{
			if (!((Object)(object)__instance == (Object)null))
			{
				SettingsLayout val = LayoutRef.Invoke(__instance);
				if (!((Object)(object)val == (Object)null))
				{
					NativeLobbySettingsMenu.RegisterRuntimeLobbyLayout(val, "SettingsLayoutRuntimeUI.Awake");
					NativeLobbySettingsMenu.EnsureInjected(val, "SettingsLayoutRuntimeUI.Awake");
				}
			}
		}
	}
	[HarmonyPatch]
	internal static class NativeLobbySettingsButtonPatches
	{
		private static readonly FieldRef<LobbyModeDropdownButton, SettingsLayout> LayoutRef = AccessTools.FieldRefAccess<LobbyModeDropdownButton, SettingsLayout>("settingsLayout");

		[HarmonyPostfix]
		[HarmonyPatch(typeof(LobbyModeDropdownButton), "Awake")]
		private static void LobbyModeDropdownButton_Awake_Postfix(LobbyModeDropdownButton __instance)
		{
			if (!((Object)(object)__instance == (Object)null))
			{
				SettingsLayout val = LayoutRef.Invoke(__instance);
				if (!((Object)(object)val == (Object)null))
				{
					NativeLobbySettingsMenu.RegisterLobbyLayout(val, "LobbyModeDropdownButton.Awake");
					NativeLobbySettingsMenu.EnsureInjected(val, "LobbyModeDropdownButton.Awake");
				}
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(LobbyModeDropdownButton), "OnClick")]
		private static void LobbyModeDropdownButton_OnClick_Prefix(LobbyModeDropdownButton __instance)
		{
			if (!((Object)(object)__instance == (Object)null))
			{
				SettingsLayout val = LayoutRef.Invoke(__instance);
				if (!((Object)(object)val == (Object)null))
				{
					NativeLobbySettingsMenu.RefreshFromLobbyButton(val);
				}
			}
		}
	}
	[HarmonyPatch(typeof(SettingItemBase), "NotifyChanged")]
	internal static class NativeLobbySettingChangePatches
	{
		private static void Postfix(SettingItemBase __instance)
		{
			NativeLobbySettingsMenu.HandleSettingChanged(__instance);
		}
	}
	[HarmonyPatch]
	internal static class NativeLobbySliderPresentationPatches
	{
		[HarmonyPrefix]
		[HarmonyPatch(typeof(SettingsLayoutRuntimeUI), "CreateSliderEntry")]
		private static void CreateSliderEntry_Prefix(RectTransform parent, out int __state)
		{
			__state = (((Object)(object)parent != (Object)null) ? ((Transform)parent).childCount : 0);
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(SettingsLayoutRuntimeUI), "CreateSliderEntry")]
		private static void CreateSliderEntry_Postfix(RectTransform parent, SliderSettingItem entry, int __state)
		{
			if (!((Object)(object)parent == (Object)null) && !((Object)(object)entry == (Object)null) && NativeLobbySettingsMenu.IsManagedSliderKey(((SettingItemBase)entry).key) && ((Transform)parent).childCount > __state)
			{
				Transform child = ((Transform)parent).GetChild(((Transform)parent).childCount - 1);
				Transform root = ((child.childCount > 0) ? child.GetChild(0) : child);
				NativeLobbySettingsMenu.RegisterSliderRuntimeBinding(((SettingItemBase)entry).key, root);
			}
		}
	}
	[HarmonyPatch(typeof(LocalSaveManager), "CreateNewSave")]
	internal static class QuotaSaveInitializationPatches
	{
		private static readonly FieldRef<SaveManager, SaveData> CurrentSaveDataRef = AccessTools.FieldRefAccess<SaveManager, SaveData>("currentSaveData");

		private static void Prefix()
		{
			if (NetworkServer.active)
			{
				TimingCoordinator.TryApplyFromResources("LocalSaveManager.CreateNewSave");
			}
		}

		private static void Postfix()
		{
			if (!NetworkServer.active)
			{
				return;
			}
			SaveManager val = Object.FindFirstObjectByType<SaveManager>();
			if (!((Object)(object)val == (Object)null))
			{
				SaveData val2 = CurrentSaveDataRef.Invoke(val);
				if (val2 != null)
				{
					TimingCoordinator.TryApplyInitialQuotaToSaveData(val2, "LocalSaveManager.CreateNewSave");
				}
			}
		}
	}
	[HarmonyPatch(typeof(SaveManager), "ResetCurrentSaveToDefaults")]
	internal static class QuotaSaveResetPatches
	{
		private static readonly FieldRef<SaveManager, SaveData> CurrentSaveDataRef = AccessTools.FieldRefAccess<SaveManager, SaveData>("currentSaveData");

		private static void Prefix()
		{
			if (NetworkServer.active)
			{
				TimingCoordinator.TryApplyFromResources("SaveManager.ResetCurrentSaveToDefaults");
			}
		}

		private static void Postfix(SaveManager __instance)
		{
			if (NetworkServer.active && !((Object)(object)__instance == (Object)null))
			{
				SaveData val = CurrentSaveDataRef.Invoke(__instance);
				if (val != null)
				{
					TimingCoordinator.TryApplyInitialQuotaToSaveData(val, "SaveManager.ResetCurrentSaveToDefaults");
				}
			}
		}
	}
}
namespace MoreSettings.Network
{
	public static class LobbyVisibility
	{
		private static ManualLogSource? _log;

		private static SessionTimingState? _clientState;

		public static void Initialize(ManualLogSource log)
		{
			_log = log;
		}

		public static SessionTimingState? GetVisibleState()
		{
			if (NetworkServer.active)
			{
				return TimingCoordinator.CurrentState;
			}
			if (NetworkClient.active)
			{
				return _clientState;
			}
			return TimingCoordinator.CurrentState;
		}

		public static void ClearClientState()
		{
			_clientState = null;
		}

		public static void RegisterMessageDelegates()
		{
			Writer<TimingConfigMessage>.write = delegate(NetworkWriter writer, TimingConfigMessage msg)
			{
				NetworkWriterExtensions.WriteBool(writer, msg.IsVanilla);
				NetworkWriterExtensions.WriteString(writer, msg.ProfileName ?? string.Empty);
				NetworkWriterExtensions.WriteFloat(writer, msg.DayDurationSeconds);
				NetworkWriterExtensions.WriteInt(writer, msg.DaysBeforeQuota);
				NetworkWriterExtensions.WriteLong(writer, msg.StartingQuota);
				NetworkWriterExtensions.WriteLong(writer, msg.StartingMoney);
				NetworkWriterExtensions.WriteFloat(writer, msg.CatchUpFactor);
				NetworkWriterExtensions.WriteInt(writer, msg.QuotaScalingModeValue);
				NetworkWriterExtensions.WriteInt(writer, msg.QuotaMultiplierCount);
			};
			Reader<TimingConfigMessage>.read = delegate(NetworkReader reader)
			{
				TimingConfigMessage result = default(TimingConfigMessage);
				result.IsVanilla = NetworkReaderExtensions.ReadBool(reader);
				result.ProfileName = NetworkReaderExtensions.ReadString(reader);
				result.DayDurationSeconds = NetworkReaderExtensions.ReadFloat(reader);
				result.DaysBeforeQuota = NetworkReaderExtensions.ReadInt(reader);
				result.StartingQuota = NetworkReaderExtensions.ReadLong(reader);
				result.StartingMoney = NetworkReaderExtensions.ReadLong(reader);
				result.CatchUpFactor = NetworkReaderExtensions.ReadFloat(reader);
				result.QuotaScalingModeValue = NetworkReaderExtensions.ReadInt(reader);
				result.QuotaMultiplierCount = NetworkReaderExtensions.ReadInt(reader);
				return result;
			};
			NetworkClient.RegisterHandler<TimingConfigMessage>((Action<TimingConfigMessage>)OnTimingConfigReceived, true);
		}

		public static void BroadcastCurrentState()
		{
			if (!NetworkServer.active)
			{
				return;
			}
			SessionTimingState currentState = TimingCoordinator.CurrentState;
			if (currentState != null)
			{
				NetworkServer.SendToAll<TimingConfigMessage>(BuildMessage(currentState), 0, false);
				ManualLogSource? log = _log;
				if (log != null)
				{
					log.LogDebug((object)("[LobbyVisibility] Broadcast timing profile '" + currentState.ProfileName + "' to all clients."));
				}
			}
		}

		public static void SendToClient(NetworkConnectionToClient conn)
		{
			if (NetworkServer.active)
			{
				SessionTimingState currentState = TimingCoordinator.CurrentState;
				if (currentState != null)
				{
					((NetworkConnection)conn).Send<TimingConfigMessage>(BuildMessage(currentState), 0);
				}
			}
		}

		private static TimingConfigMessage BuildMessage(SessionTimingState state)
		{
			TimingConfigMessage result = default(TimingConfigMessage);
			result.IsVanilla = state.IsVanilla;
			result.ProfileName = state.ProfileName;
			result.DayDurationSeconds = state.DayDurationSeconds;
			result.DaysBeforeQuota = state.DaysBeforeQuota;
			result.StartingQuota = state.StartingQuota;
			result.StartingMoney = state.StartingMoney;
			result.CatchUpFactor = state.CatchUpFactor;
			result.QuotaScalingModeValue = (int)state.QuotaScalingMode;
			result.QuotaMultiplierCount = state.QuotaMultiplierCount;
			return result;
		}

		private static void OnTimingConfigReceived(TimingConfigMessage msg)
		{
			if (NetworkServer.activeHost)
			{
				return;
			}
			_clientState = new SessionTimingState("TimingConfigMessage", string.IsNullOrWhiteSpace(msg.ProfileName) ? "Vanilla" : msg.ProfileName, msg.IsVanilla, msg.DayDurationSeconds, msg.DaysBeforeQuota, msg.StartingQuota, msg.StartingMoney, msg.CatchUpFactor, ResolveQuotaScalingMode(msg.QuotaScalingModeValue), msg.QuotaMultiplierCount, DateTimeOffset.UtcNow);
			if (msg.IsVanilla)
			{
				ManualLogSource? log = _log;
				if (log != null)
				{
					log.LogInfo((object)"[LobbyVisibility] Host is using vanilla timing — no overrides active.");
				}
				return;
			}
			ManualLogSource? log2 = _log;
			if (log2 != null)
			{
				log2.LogInfo((object)("[LobbyVisibility] Host timing profile '" + msg.ProfileName + "': " + $"dayDuration={msg.DayDurationSeconds}s, daysBeforeQuota={msg.DaysBeforeQuota}, " + $"startingQuota={msg.StartingQuota}, startingMoney={msg.StartingMoney}, catchUpFactor={msg.CatchUpFactor}, " + $"quotaScalingMode={ResolveQuotaScalingMode(msg.QuotaScalingModeValue)}, " + $"quotaMultiplierCount={msg.QuotaMultiplierCount}"));
			}
		}

		private static QuotaScalingMode ResolveQuotaScalingMode(int rawValue)
		{
			if (!Enum.IsDefined(typeof(QuotaScalingMode), rawValue))
			{
				return QuotaScalingMode.Vanilla;
			}
			return (QuotaScalingMode)rawValue;
		}
	}
	public struct TimingConfigMessage : NetworkMessage
	{
		public bool IsVanilla;

		public string ProfileName;

		public float DayDurationSeconds;

		public int DaysBeforeQuota;

		public long StartingQuota;

		public long StartingMoney;

		public float CatchUpFactor;

		public int QuotaScalingModeValue;

		public int QuotaMultiplierCount;
	}
}
namespace MoreSettings.Models
{
	public enum QuotaScalingMode
	{
		Vanilla,
		CustomPattern
	}
	public static class QuotaScalingModeParser
	{
		public static bool TryParse(string? rawValue, out QuotaScalingMode mode)
		{
			return Enum.TryParse<QuotaScalingMode>(rawValue?.Trim(), ignoreCase: true, out mode);
		}

		public static QuotaScalingMode ParseOrDefault(string? rawValue, QuotaScalingMode fallback = QuotaScalingMode.Vanilla)
		{
			if (!TryParse(rawValue, out var mode))
			{
				return fallback;
			}
			return mode;
		}
	}
	public sealed class SessionTimingState
	{
		public string Source { get; }

		public string ProfileName { get; }

		public bool IsVanilla { get; }

		public float DayDurationSeconds { get; }

		public int DaysBeforeQuota { get; }

		public long StartingQuota { get; }

		public long StartingMoney { get; }

		public float CatchUpFactor { get; }

		public QuotaScalingMode QuotaScalingMode { get; }

		public int QuotaMultiplierCount { get; }

		public DateTimeOffset AppliedAtUtc { get; }

		public SessionTimingState(string source, string profileName, bool isVanilla, float dayDurationSeconds, int daysBeforeQuota, long startingQuota, long startingMoney, float catchUpFactor, QuotaScalingMode quotaScalingMode, int quotaMultiplierCount, DateTimeOffset appliedAtUtc)
		{
			Source = source;
			ProfileName = profileName;
			IsVanilla = isVanilla;
			DayDurationSeconds = dayDurationSeconds;
			DaysBeforeQuota = daysBeforeQuota;
			StartingQuota = startingQuota;
			StartingMoney = startingMoney;
			CatchUpFactor = catchUpFactor;
			QuotaScalingMode = quotaScalingMode;
			QuotaMultiplierCount = quotaMultiplierCount;
			AppliedAtUtc = appliedAtUtc;
		}
	}
	public sealed class TimingProfile
	{
		public string Name { get; }

		public bool IsVanillaProfile { get; }

		public float DayDurationSeconds { get; }

		public int DaysBeforeQuota { get; }

		public long StartingQuota { get; }

		public long StartingMoney { get; }

		public float CatchUpFactor { get; }

		public QuotaScalingMode QuotaScalingMode { get; }

		public IReadOnlyList<float> QuotaMultipliers { get; }

		public TimingProfile(string name, bool isVanillaProfile, float dayDurationSeconds, int daysBeforeQuota, long startingQuota, long startingMoney, float catchUpFactor, QuotaScalingMode quotaScalingMode, IReadOnlyList<float> quotaMultipliers)
		{
			Name = name ?? throw new ArgumentNullException("name");
			IsVanillaProfile = isVanillaProfile;
			DayDurationSeconds = dayDurationSeconds;
			DaysBeforeQuota = daysBeforeQuota;
			StartingQuota = startingQuota;
			StartingMoney = startingMoney;
			CatchUpFactor = catchUpFactor;
			QuotaScalingMode = quotaScalingMode;
			QuotaMultipliers = quotaMultipliers ?? throw new ArgumentNullException("quotaMultipliers");
		}
	}
	public enum ValidationStatus
	{
		Valid,
		Warning,
		Error
	}
	public sealed class ValidationOutcome
	{
		public string TargetField { get; }

		public ValidationStatus Status { get; }

		public string Message { get; }

		public ValidationOutcome(string targetField, ValidationStatus status, string message)
		{
			TargetField = targetField;
			Status = status;
			Message = message;
		}

		public static ValidationOutcome Valid(string targetField, string message)
		{
			return new ValidationOutcome(targetField, ValidationStatus.Valid, message);
		}

		public static ValidationOutcome Warning(string targetField, string message)
		{
			return new ValidationOutcome(targetField, ValidationStatus.Warning, message);
		}

		public static ValidationOutcome Error(string targetField, string message)
		{
			return new ValidationOutcome(targetField, ValidationStatus.Error, message);
		}
	}
}
namespace MoreSettings.Configuration
{
	public sealed class ActiveSettings
	{
		private readonly ConfigFile _config;

		public const float PreserveFloat = -1f;

		public const int PreserveInt = -1;

		public const long PreserveLong = -1L;

		private readonly ConfigEntry<bool> _enableCustomTiming;

		private readonly ConfigEntry<float> _dayDurationSeconds;

		private readonly ConfigEntry<int> _daysBeforeQuota;

		private readonly ConfigEntry<long> _startingQuota;

		private readonly ConfigEntry<long> _startingMoney;

		private readonly ConfigEntry<float> _catchUpFactor;

		private readonly ConfigEntry<string> _quotaScalingMode;

		private readonly ConfigEntry<string> _quotaMultipliersCsv;

		private readonly ConfigEntry<string> _activeProfileName;

		private readonly ProfileStore _profileStore;

		public bool IsEnabled => _enableCustomTiming.Value;

		public ProfileStore Profiles => _profileStore;

		public string ActiveProfileName => _activeProfileName.Value?.Trim() ?? string.Empty;

		private ActiveSettings(ConfigFile config, ConfigEntry<bool> enableCustomTiming, ConfigEntry<float> dayDurationSeconds, ConfigEntry<int> daysBeforeQuota, ConfigEntry<long> startingQuota, ConfigEntry<long> startingMoney, ConfigEntry<float> catchUpFactor, ConfigEntry<string> quotaScalingMode, ConfigEntry<string> quotaMultipliersCsv, ConfigEntry<string> activeProfileName, ProfileStore profileStore)
		{
			_config = config;
			_enableCustomTiming = enableCustomTiming;
			_dayDurationSeconds = dayDurationSeconds;
			_daysBeforeQuota = daysBeforeQuota;
			_startingQuota = startingQuota;
			_startingMoney = startingMoney;
			_catchUpFactor = catchUpFactor;
			_quotaScalingMode = quotaScalingMode;
			_quotaMultipliersCsv = quotaMultipliersCsv;
			_activeProfileName = activeProfileName;
			_profileStore = profileStore;
		}

		public static ActiveSettings Bind(ConfigFile config)
		{
			return Bind(config, LoadVanillaDefaults());
		}

		public static ActiveSettings Bind(ConfigFile config, TimingProfile vanillaDefaults)
		{
			ConfigEntry<bool> enableCustomTiming = config.Bind<bool>("General", "EnableCustomTiming", false, "Enable host-authoritative timing overrides for new sessions.");
			ConfigEntry<float> dayDurationSeconds = config.Bind<float>("Timing", "DayDurationSeconds", vanillaDefaults.DayDurationSeconds, "Override the vanilla day duration in seconds. Defaults to the current vanilla value. Set -1 to preserve the loaded value.");
			ConfigEntry<int> daysBeforeQuota = config.Bind<int>("Timing", "DaysBeforeQuota", vanillaDefaults.DaysBeforeQuota, "Deprecated. Multi-day quota overrides are ignored. Defaults to the current vanilla value; set -1 to preserve the loaded value.");
			ConfigEntry<long> startingQuota = config.Bind<long>("Quota", "StartingQuota", vanillaDefaults.StartingQuota, "Override the starting quota for new sessions. Defaults to the current vanilla value. Set -1 to preserve the loaded value.");
			ConfigEntry<long> startingMoney = config.Bind<long>("Quota", "StartingMoney", vanillaDefaults.StartingMoney, "Override the starting money for new sessions. Defaults to the current vanilla value. Set -1 to preserve the loaded value.");
			ConfigEntry<float> catchUpFactor = config.Bind<float>("Quota", "CatchUpFactor", vanillaDefaults.CatchUpFactor, "Override the quota catch-up factor. Defaults to the current vanilla value. Set -1 to preserve the loaded value.");
			ConfigEntry<string> quotaScalingMode = config.Bind<string>("Quota", "QuotaScalingMode", QuotaScalingMode.Vanilla.ToString(), "Set the quota scaling mode to Vanilla or CustomPattern. Defaults to Vanilla.");
			ConfigEntry<string> quotaMultipliersCsv = config.Bind<string>("Quota", "QuotaMultipliersCsv", string.Empty, "Comma-separated quota multipliers. Leave blank to preserve the loaded values.");
			ConfigEntry<string> activeProfileName = config.Bind<string>("Profiles", "ActiveProfileName", string.Empty, "Name of a saved profile to load on startup. Overrides all individual Timing/Quota entries when non-empty. Use the ProfileStore API or edit profile files under BepInEx/config/MoreSettings/profiles/.");
			ProfileStore profileStore = new ProfileStore(Path.GetDirectoryName(config.ConfigFilePath) ?? AppDomain.CurrentDomain.BaseDirectory);
			return new ActiveSettings(config, enableCustomTiming, dayDurationSeconds, daysBeforeQuota, startingQuota, startingMoney, catchUpFactor, quotaScalingMode, quotaMultipliersCsv, activeProfileName, profileStore);
		}

		private static TimingProfile LoadVanillaDefaults()
		{
			GameSettings val = Resources.Load<GameSettings>("GameSettings");
			if ((Object)(object)val == (Object)null)
			{
				throw new InvalidOperationException("Could not load the GameSettings resource.");
			}
			return new TimingProfile("Vanilla", isVanillaProfile: true, val.dayDuration, val.daysBeforeQuota, val.startingQuota, val.startingMoney, val.catchUpFactor, QuotaScalingMode.Vanilla, (val.quotas ?? Array.Empty<float>()).ToArray());
		}

		public void SetManualOverrides(TimingProfile profile)
		{
			_enableCustomTiming.Value = true;
			_activeProfileName.Value = string.Empty;
			_dayDurationSeconds.Value = profile.DayDurationSeconds;
			_daysBeforeQuota.Value = -1;
			_startingQuota.Value = profile.StartingQuota;
			_startingMoney.Value = profile.StartingMoney;
			_catchUpFactor.Value = profile.CatchUpFactor;
			_quotaScalingMode.Value = profile.QuotaScalingMode.ToString();
			_quotaMultipliersCsv.Value = ((profile.QuotaScalingMode == QuotaScalingMode.CustomPattern) ? string.Join(",", profile.QuotaMultipliers.Select((float multiplier) => multiplier.ToString(CultureInfo.InvariantCulture))) : string.Empty);
			_config.Save();
		}

		public bool TryCreateResolvedProfile(TimingProfile baseProfile, out TimingProfile profile, out IReadOnlyList<ValidationOutcome> outcomes)
		{
			List<ValidationOutcome> list = new List<ValidationOutcome>();
			StoredProfile storedProfile = null;
			string text = _activeProfileName.Value?.Trim() ?? string.Empty;
			if (!string.IsNullOrEmpty(text) && _profileStore.TryLoad(text, out StoredProfile profile2))
			{
				storedProfile = profile2;
			}
			float dayDurationSeconds = ResolveFloat(storedProfile?.DayDurationSeconds ?? _dayDurationSeconds.Value, baseProfile.DayDurationSeconds);
			int daysBeforeQuota = baseProfile.DaysBeforeQuota;
			long startingQuota = ResolveLong(storedProfile?.StartingQuota ?? _startingQuota.Value, baseProfile.StartingQuota);
			long startingMoney = ResolveLong(storedProfile?.StartingMoney ?? _startingMoney.Value, baseProfile.StartingMoney);
			float catchUpFactor = ResolveFloat(storedProfile?.CatchUpFactor ?? _catchUpFactor.Value, baseProfile.CatchUpFactor);
			string text2 = _quotaMultipliersCsv.Value?.Trim() ?? string.Empty;
			QuotaScalingMode quotaScalingMode = ResolveQuotaScalingMode(storedProfile?.QuotaScalingMode, _quotaScalingMode.Value, storedProfile?.QuotaMultipliers, text2);
			float[] source = baseProfile.QuotaMultipliers.ToArray();
			if (quotaScalingMode == QuotaScalingMode.CustomPattern)
			{
				float[] array = storedProfile?.QuotaMultipliers;
				if (array != null && array.Length > 0)
				{
					source = array;
					goto IL_0196;
				}
			}
			if (quotaScalingMode == QuotaScalingMode.CustomPattern)
			{
				if (!string.IsNullOrWhiteSpace(text2))
				{
					if (TimingProfileValidator.TryParseQuotaMultipliers(text2, out float[] parsedValues, out ValidationOutcome error))
					{
						source = parsedValues;
					}
					else if (error != null)
					{
						list.Add(error);
					}
				}
				else
				{
					source = new float[0];
				}
			}
			goto IL_0196;
			IL_0196:
			string text3 = ((!string.IsNullOrEmpty(text)) ? text : "ActiveConfig");
			profile = new TimingProfile(IsEnabled ? text3 : "Vanilla", !IsEnabled, dayDurationSeconds, daysBeforeQuota, startingQuota, startingMoney, catchUpFactor, quotaScalingMode, source.ToArray());
			list.AddRange(TimingProfileValidator.Validate(profile));
			outcomes = list;
			return list.All((ValidationOutcome outcome) => outcome.Status != ValidationStatus.Error);
		}

		private static float ResolveFloat(float value, float vanilla)
		{
			if (!(value > -1f))
			{
				return vanilla;
			}
			return value;
		}

		private static int ResolveInt(int value, int vanilla)
		{
			if (value <= -1)
			{
				return vanilla;
			}
			return value;
		}

		private static long ResolveLong(long value, long vanilla)
		{
			if (value <= -1)
			{
				return vanilla;
			}
			return value;
		}

		private static QuotaScalingMode ResolveQuotaScalingMode(QuotaScalingMode? storedMode, string configuredMode, float[]? storedMultipliers, string rawMultipliers)
		{
			if (storedMode.HasValue)
			{
				return storedMode.Value;
			}
			if (QuotaScalingModeParser.TryParse(configuredMode, out var mode))
			{
				return mode;
			}
			if (((storedMultipliers != null && storedMultipliers.Length != 0) ? 1 : 0) <= (false ? 1 : 0) && string.IsNullOrWhiteSpace(rawMultipliers))
			{
				return QuotaScalingMode.Vanilla;
			}
			return QuotaScalingMode.CustomPattern;
		}
	}
	public sealed class ProfileStore
	{
		private readonly string _profilesDir;

		public ProfileStore(string configDir)
		{
			_profilesDir = Path.Combine(configDir, "MoreSettings", "profiles");
			string[] array = new string[3]
			{
				Path.Combine(configDir, "LobbySettings", "profiles"),
				Path.Combine(configDir, "ConfigManager", "profiles"),
				Path.Combine(configDir, "TimeConfig", "profiles")
			};
			foreach (string text in array)
			{
				if (!Directory.Exists(_profilesDir) && Directory.Exists(text))
				{
					Directory.CreateDirectory(Path.GetDirectoryName(_profilesDir));
					Directory.Move(text, _profilesDir);
					string text2 = Directory.GetParent(text)?.FullName;
					if (!string.IsNullOrWhiteSpace(text2) && Directory.Exists(text2) && !Directory.EnumerateFileSystemEntries(text2).Any())
					{
						Directory.Delete(text2);
					}
					break;
				}
			}
		}

		public IReadOnlyList<string> ListProfiles()
		{
			if (!Directory.Exists(_profilesDir))
			{
				return Array.Empty<string>();
			}
			return (from f in Directory.GetFiles(_profilesDir, "*.profile")
				select Path.GetFileNameWithoutExtension(f) ?? Path.GetFileName(f)).OrderBy<string, string>((string n) => n, StringComparer.OrdinalIgnoreCase).ToList();
		}

		public bool TryLoad(string name, out StoredProfile? profile)
		{
			profile = null;
			string path = ProfilePath(name);
			if (!File.Exists(path))
			{
				return false;
			}
			Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
			string[] array = File.ReadAllLines(path);
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length != 0 && text[0] != '#')
				{
					int num = text.IndexOf('=');
					if (num >= 1)
					{
						string key = text.Substring(0, num).Trim();
						string text2 = text;
						int num2 = num + 1;
						dictionary[key] = text2.Substring(num2, text2.Length - num2).Trim();
					}
				}
			}
			profile = new StoredProfile(ParseFloat(dictionary, "DayDurationSeconds", -1f), ParseInt(dictionary, "DaysBeforeQuota", -1), ParseLong(dictionary, "StartingQuota", -1L), ParseLong(dictionary, "StartingMoney", -1L), ParseFloat(dictionary, "CatchUpFactor", -1f), ParseQuotaScalingMode(dictionary), ParseMultipliers(dictionary));
			return true;
		}

		public void Save(string name, TimingProfile profile)
		{
			Directory.CreateDirectory(_profilesDir);
			List<string> list = new List<string>
			{
				"# MoreSettings profile: " + name,
				"DayDurationSeconds=" + profile.DayDurationSeconds.ToString(CultureInfo.InvariantCulture),
				$"DaysBeforeQuota={profile.DaysBeforeQuota}",
				$"StartingQuota={profile.StartingQuota}",
				$"StartingMoney={profile.StartingMoney}",
				"CatchUpFactor=" + profile.CatchUpFactor.ToString(CultureInfo.InvariantCulture),
				$"QuotaScalingMode={profile.QuotaScalingMode}"
			};
			if (profile.QuotaScalingMode == QuotaScalingMode.CustomPattern && profile.QuotaMultipliers.Count > 0)
			{
				list.Add("QuotaMultipliers=" + string.Join(",", profile.QuotaMultipliers.Select((float m) => m.ToString(CultureInfo.InvariantCulture))));
			}
			File.WriteAllLines(ProfilePath(name), list);
		}

		public bool Delete(string name)
		{
			string path = ProfilePath(name);
			if (!File.Exists(path))
			{
				return false;
			}
			File.Delete(path);
			return true;
		}

		private string ProfilePath(string name)
		{
			return Path.Combine(_profilesDir, name + ".profile");
		}

		private static float ParseFloat(Dictionary<string, string> props, string key, float fallback)
		{
			if (!props.TryGetValue(key, out string value) || !float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
			{
				return fallback;
			}
			return result;
		}

		private static int ParseInt(Dictionary<string, string> props, string key, int fallback)
		{
			if (!props.TryGetValue(key, out string value) || !int.TryParse(value, out var result))
			{
				return fallback;
			}
			return result;
		}

		private static long ParseLong(Dictionary<string, string> props, string key, long fallback)
		{
			if (!props.TryGetValue(key, out string value) || !long.TryParse(value, out var result))
			{
				return fallback;
			}
			return result;
		}

		private static QuotaScalingMode? ParseQuotaScalingMode(Dictionary<string, string> props)
		{
			if (!props.TryGetValue("QuotaScalingMode", out string value) || string.IsNullOrWhiteSpace(value))
			{
				return null;
			}
			if (!QuotaScalingModeParser.TryParse(value, out var mode))
			{
				return null;
			}
			return mode;
		}

		private static float[] ParseMultipliers(Dictionary<string, string> props)
		{
			if (!props.TryGetValue("QuotaMultipliers", out string value))
			{
				return Array.Empty<float>();
			}
			string[] array = value.Split(',');
			float[] array2 = new float[array.Length];
			for (int i = 0; i < array.Length; i++)
			{
				if (!float.TryParse(array[i].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out array2[i]))
				{
					return Array.Empty<float>();
				}
			}
			return array2;
		}
	}
	public sealed class StoredProfile
	{
		public float DayDurationSeconds { get; }

		public int DaysBeforeQuota { get; }

		public long StartingQuota { get; }

		public long StartingMoney { get; }

		public float CatchUpFactor { get; }

		public QuotaScalingMode? QuotaScalingMode { get; }

		public float[] QuotaMultipliers { get; }

		public StoredProfile(float dayDurationSeconds, int daysBeforeQuota, long startingQuota, long startingMoney, float catchUpFactor, QuotaScalingMode? quotaScalingMode, float[] quotaMultipliers)
		{
			DayDurationSeconds = dayDurationSeconds;
			DaysBeforeQuota = daysBeforeQuota;
			StartingQuota = startingQuota;
			StartingMoney = startingMoney;
			CatchUpFactor = catchUpFactor;
			QuotaScalingMode = quotaScalingMode;
			QuotaMultipliers = quotaMultipliers;
		}
	}
	public static class TimingProfileValidator
	{
		public const float MinDayDurationSeconds = 10f;

		public const float MaxDayDurationSeconds = 86400f;

		public const int MinDaysBeforeQuota = 1;

		public const int MaxDaysBeforeQuota = 30;

		public const long MaxQuotaValue = 1000000000000000000L;

		public const float MinCatchUpFactor = 0f;

		public const float MaxCatchUpFactor = 5f;

		public const float MinQuotaMultiplier = 0.01f;

		public const float MaxQuotaMultiplier = 100f;

		public static IReadOnlyList<ValidationOutcome> Validate(TimingProfile profile)
		{
			List<ValidationOutcome> list = new List<ValidationOutcome>();
			if (profile.DayDurationSeconds < 10f || profile.DayDurationSeconds > 86400f)
			{
				list.Add(ValidationOutcome.Error("DayDurationSeconds", $"Day duration must be between {10f} and {86400f} seconds."));
			}
			if (profile.DaysBeforeQuota < 1 || profile.DaysBeforeQuota > 30)
			{
				list.Add(ValidationOutcome.Error("DaysBeforeQuota", $"Days before quota must be between {1} and {30}."));
			}
			if (profile.StartingQuota < 0 || profile.StartingQuota > 1000000000000000000L)
			{
				list.Add(ValidationOutcome.Error("StartingQuota", $"Starting quota must be between 0 and {1000000000000000000L}."));
			}
			if (profile.StartingMoney < 0 || profile.StartingMoney > 1000000000000000000L)
			{
				list.Add(ValidationOutcome.Error("StartingMoney", $"Starting money must be between 0 and {1000000000000000000L}."));
			}
			if (profile.StartingQuota < profile.StartingMoney)
			{
				list.Add(ValidationOutcome.Error("StartingQuota", "Starting quota must be greater than or equal to starting money."));
			}
			if (profile.CatchUpFactor < 0f || profile.CatchUpFactor > 5f)
			{
				list.Add(ValidationOutcome.Error("CatchUpFactor", $"Catch-up factor must be between {0f} and {5f}."));
			}
			if (profile.QuotaScalingMode == QuotaScalingMode.CustomPattern)
			{
				if (profile.QuotaMultipliers.Count == 0)
				{
					list.Add(ValidationOutcome.Error("QuotaMultipliers", "At least one quota multiplier is required when custom quota scaling is enabled."));
				}
				else if (profile.QuotaMultipliers.Any((float multiplier) => multiplier < 0.01f || multiplier > 100f))
				{
					list.Add(ValidationOutcome.Error("QuotaMultipliers", $"Each quota multiplier must be between {0.01f} and {100f}."));
				}
			}
			if (list.Count == 0)
			{
				list.Add(ValidationOutcome.Valid("Profile", "Timing profile values are valid."));
			}
			return list;
		}

		public static bool TryParseQuotaMultipliers(string rawValue, out float[] parsedValues, out ValidationOutcome? error)
		{
			string[] array = (from token in rawValue.Split(new char[3] { ',', ';', ' ' }, StringSplitOptions.RemoveEmptyEntries)
				select token.Trim()).ToArray();
			if (array.Length == 0)
			{
				parsedValues = new float[0];
				error = ValidationOutcome.Error("rawValue", "Quota multipliers cannot be empty when an override string is provided.");
				return false;
			}
			List<float> list = new List<float>(array.Length);
			string[] array2 = array;
			foreach (string text in array2)
			{
				if (!float.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
				{
					parsedValues = new float[0];
					error = ValidationOutcome.Error("rawValue", "'" + text + "' is not a valid floating-point quota multiplier.");
					return false;
				}
				list.Add(result);
			}
			parsedValues = list.ToArray();
			error = null;
			return true;
		}
	}
}