Decompiled source of WorldRates v1.1.0

Mods/WorldRates_Il2Cpp.dll

Decompiled 22 minutes ago
using System;
using System.Collections;
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 System.Text;
using HarmonyLib;
using Il2CppFishNet;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppScheduleOne;
using Il2CppScheduleOne.DevUtilities;
using Il2CppScheduleOne.Economy;
using Il2CppScheduleOne.GameTime;
using Il2CppScheduleOne.Graffiti;
using Il2CppScheduleOne.ItemFramework;
using Il2CppScheduleOne.Levelling;
using Il2CppScheduleOne.Money;
using Il2CppScheduleOne.NPCs;
using Il2CppScheduleOne.NPCs.Behaviour;
using Il2CppScheduleOne.ObjectScripts;
using Il2CppScheduleOne.Persistence;
using Il2CppScheduleOne.Persistence.Datas;
using Il2CppScheduleOne.PlayerScripts;
using Il2CppScheduleOne.Product;
using Il2CppScheduleOne.Property;
using Il2CppScheduleOne.Quests;
using Il2CppScheduleOne.Storage;
using Il2CppScheduleOne.UI;
using Il2CppScheduleOne.UI.MainMenu;
using Il2CppScheduleOne.UI.Tooltips;
using Il2CppScheduleOne.Vehicles;
using Il2CppSystem;
using Il2CppSystem.Collections.Generic;
using Il2CppSystem.Reflection;
using Il2CppTMPro;
using MelonLoader;
using MelonLoader.Preferences;
using MelonLoader.Utils;
using Microsoft.CodeAnalysis;
using S1Shared;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using WorldRates;
using WorldRates.Patches;
using WorldRates.Rates;
using WorldRates.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(WorldRatesMod), "WorldRates", "1.1.0", "BbIJABNPOBATEJb", "https://github.com/BbIJABNPOBATEJb/ScheduleI-Mods")]
[assembly: MelonGame("TVGS", "Schedule I")]
[assembly: HarmonyDontPatchAll]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("WorldRates_Il2Cpp")]
[assembly: AssemblyConfiguration("Il2Cpp")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+6d02579b95b27e67a5127536582ba67848828bf5")]
[assembly: AssemblyProduct("WorldRates_Il2Cpp")]
[assembly: AssemblyTitle("WorldRates_Il2Cpp")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
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 S1Shared
{
	internal sealed class CoRunner
	{
		private sealed class Routine
		{
			public readonly Stack<IEnumerator> Stack = new Stack<IEnumerator>();

			public float WaitUntil;
		}

		private readonly List<Routine> _routines = new List<Routine>();

		private readonly Action<Exception> _onError;

		public CoRunner(Action<Exception> onError)
		{
			_onError = onError;
		}

		public void Start(IEnumerator routine)
		{
			Routine routine2 = new Routine();
			routine2.Stack.Push(routine);
			_routines.Add(routine2);
		}

		public void StopAll()
		{
			_routines.Clear();
		}

		public void Tick()
		{
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			for (int num = _routines.Count - 1; num >= 0; num--)
			{
				Routine routine = _routines[num];
				if (!(realtimeSinceStartup < routine.WaitUntil))
				{
					try
					{
						if (!Step(routine, realtimeSinceStartup))
						{
							_routines.RemoveAt(num);
						}
					}
					catch (Exception obj)
					{
						_routines.RemoveAt(num);
						_onError(obj);
					}
				}
			}
		}

		private static bool Step(Routine r, float now)
		{
			while (r.Stack.Count > 0)
			{
				IEnumerator enumerator = r.Stack.Peek();
				if (!enumerator.MoveNext())
				{
					r.Stack.Pop();
					continue;
				}
				object current = enumerator.Current;
				if (!(current is IEnumerator item))
				{
					if (!(current is float num))
					{
						if (current is int num2)
						{
							r.WaitUntil = Time.realtimeSinceStartup + (float)num2;
							return true;
						}
						return true;
					}
					r.WaitUntil = Time.realtimeSinceStartup + num;
					return true;
				}
				r.Stack.Push(item);
			}
			return false;
		}
	}
	internal static class SafePatcher
	{
		public static int Apply(Harmony harmony, Instance log)
		{
			int num = 0;
			Type[] typesFromAssembly = AccessTools.GetTypesFromAssembly(Assembly.GetExecutingAssembly());
			foreach (Type type in typesFromAssembly)
			{
				if (type.GetCustomAttributes(typeof(HarmonyPatch), inherit: false).Any())
				{
					try
					{
						harmony.CreateClassProcessor(type).Patch();
					}
					catch (Exception ex)
					{
						num++;
						log.Warning("Patch " + type.FullName + " not applied: " + (ex.InnerException ?? ex).Message);
					}
				}
			}
			return num;
		}
	}
	internal static class UiKit
	{
		public static void OnClick(Button button, Action action)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			button.onClick = new ButtonClickedEvent();
			((UnityEvent)button.onClick).AddListener(DelegateSupport.ConvertDelegate<UnityAction>((Delegate)action));
		}

		public static void OnValueChanged(Slider slider, Action<float> action)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			slider.onValueChanged = new SliderEvent();
			((UnityEvent<float>)(object)slider.onValueChanged).AddListener(DelegateSupport.ConvertDelegate<UnityAction<float>>((Delegate)action));
		}

		public static void AddListener(Toggle toggle, Action<bool> action)
		{
			((UnityEvent<bool>)(object)toggle.onValueChanged).AddListener(DelegateSupport.ConvertDelegate<UnityAction<bool>>((Delegate)action));
		}

		public static T? Get<T>(Component c) where T : Component
		{
			Component component = c.GetComponent(Il2CppType.Of<T>());
			if (component == null)
			{
				return default(T);
			}
			return ((Il2CppObjectBase)component).TryCast<T>();
		}

		public static T? Get<T>(GameObject go) where T : Component
		{
			return Get<T>((Component)(object)go.transform);
		}

		public static T? InChildren<T>(Component c) where T : Component
		{
			return UnityQuery.GetComponentsInChildren<T>(c, includeInactive: true).FirstOrDefault();
		}

		public static Transform? Find(Transform root, string name)
		{
			for (int i = 0; i < root.childCount; i++)
			{
				Transform child = root.GetChild(i);
				if (((Object)child).name == name)
				{
					return child;
				}
				Transform val = Find(child, name);
				if ((Object)(object)val != (Object)null)
				{
					return val;
				}
			}
			return null;
		}

		public static List<Transform> Children(Transform t)
		{
			List<Transform> list = new List<Transform>();
			for (int i = 0; i < t.childCount; i++)
			{
				list.Add(t.GetChild(i));
			}
			return list;
		}

		public static string Namespace(Component c)
		{
			return ((Object)c).GetIl2CppType().Namespace ?? "";
		}

		public static void SetMember(object target, string name, object? value)
		{
			Type type = target.GetType();
			while (type != null)
			{
				PropertyInfo property = type.GetProperty(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (property != null && property.CanWrite)
				{
					property.SetValue(target, value);
					break;
				}
				FieldInfo field = type.GetField(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null)
				{
					field.SetValue(target, value);
					break;
				}
				type = type.BaseType;
			}
		}
	}
	internal static class UnityQuery
	{
		public static List<T> ToManaged<T>(List<T>? list)
		{
			List<T> list2 = new List<T>();
			if (list == null)
			{
				return list2;
			}
			for (int i = 0; i < list.Count; i++)
			{
				list2.Add(list[i]);
			}
			return list2;
		}

		public static List<T> FindAllIncludingAssets<T>() where T : Object
		{
			List<T> list = new List<T>();
			foreach (Object item in (Il2CppArrayBase<Object>)(object)Resources.FindObjectsOfTypeAll(Il2CppType.Of<T>()))
			{
				T val = ((item != null) ? ((Il2CppObjectBase)item).TryCast<T>() : default(T));
				if ((Object)(object)val != (Object)null)
				{
					list.Add(val);
				}
			}
			return list;
		}

		public static List<T> FindInScenes<T>() where T : Component
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			List<T> list = new List<T>();
			foreach (T item in UnityQuery.FindAllIncludingAssets<T>())
			{
				Scene scene = ((Component)item).gameObject.scene;
				if (((Scene)(ref scene)).IsValid())
				{
					list.Add(item);
				}
			}
			return list;
		}

		public static T? GetComponentInParent<T>(Component component) where T : Component
		{
			Component componentInParent = component.GetComponentInParent(Il2CppType.Of<T>());
			if (componentInParent == null)
			{
				return default(T);
			}
			return ((Il2CppObjectBase)componentInParent).TryCast<T>();
		}

		public static List<T> GetComponentsInChildren<T>(Component root, bool includeInactive) where T : Component
		{
			List<T> list = new List<T>();
			foreach (Component item in (Il2CppArrayBase<Component>)(object)root.GetComponentsInChildren(Il2CppType.Of<T>(), includeInactive))
			{
				T val = ((item != null) ? ((Il2CppObjectBase)item).TryCast<T>() : default(T));
				if ((Object)(object)val != (Object)null)
				{
					list.Add(val);
				}
			}
			return list;
		}

		public static bool TryCastTo<T>(Object? obj, out T result) where T : Object
		{
			result = ((obj != null) ? ((Il2CppObjectBase)obj).TryCast<T>() : default(T));
			return (Object)(object)result != (Object)null;
		}

		public static string TypeName(Object obj)
		{
			return ((MemberInfo)((Object)obj).GetIl2CppType()).Name;
		}

		public static string DumpHierarchy(Transform root, int maxDepth = 12)
		{
			StringBuilder sb = new StringBuilder();
			Walk(root, 0);
			return sb.ToString();
			void Walk(Transform t, int depth)
			{
				sb.Append(' ', depth * 2).Append(((Object)t).name).Append(((Component)t).gameObject.activeSelf ? "" : " [inactive]")
					.Append("  {");
				foreach (Component component in ((Component)t).GetComponents<Component>())
				{
					if (!((Object)(object)component == (Object)null))
					{
						sb.Append(TypeName((Object)(object)component)).Append(' ');
						if (UnityQuery.TryCastTo<TMP_Text>((Object?)(object)component, out TMP_Text result))
						{
							sb.Append("\"").Append(result.text?.Replace("\n", "\\n")).Append("\" ");
						}
					}
				}
				sb.Append("}\n");
				if (depth < maxDepth)
				{
					for (int i = 0; i < t.childCount; i++)
					{
						Walk(t.GetChild(i), depth + 1);
					}
				}
			}
		}

		public static string PathOf(Transform t)
		{
			List<string> list = new List<string>();
			Transform val = t;
			while ((Object)(object)val != (Object)null)
			{
				list.Add(((Object)val).name);
				val = val.parent;
			}
			list.Reverse();
			return string.Join("/", list);
		}
	}
}
namespace WorldRates
{
	internal static class ModInfo
	{
		public const string Name = "WorldRates";

		public const string Version = "1.1.0";

		public const string Author = "BbIJABNPOBATEJb";

		public const string DownloadLink = "https://github.com/BbIJABNPOBATEJb/ScheduleI-Mods";
	}
	public sealed class WorldRatesMod : MelonMod
	{
		private CoRunner _runner;

		private MelonPreferences_Entry<KeyCode> _hotkey;

		private bool _inputBroken;

		private float _nextStorageCheck;

		public override void OnInitializeMelon()
		{
			SafePatcher.Apply(((MelonBase)this).HarmonyInstance, ((MelonBase)this).LoggerInstance);
			MelonPreferences_Category val = MelonPreferences.CreateCategory("WorldRates");
			_hotkey = val.CreateEntry<KeyCode>("WindowHotkey", (KeyCode)291, (string)null, "Opens the World Rates window (also in the pause menu). None disables the key.", false, false, (ValueValidator)null, (string)null);
			XpPatches.DebugLog = val.CreateEntry<bool>("DebugLog", false, (string)null, "Log every scaled XP gain.", false, false, (ValueValidator)null, (string)null).Value;
			RatesState.Init();
			RatesWindow.Init();
			RatesState.WorldChanged += StoragePatches.ApplyAll;
			RatesState.Current.Changed += delegate(string id)
			{
				if (id.StartsWith("storage.", StringComparison.Ordinal))
				{
					StoragePatches.ApplyAll();
				}
			};
			_runner = new CoRunner(delegate(Exception ex)
			{
				((MelonBase)this).LoggerInstance.Error((object)ex);
			});
			((MelonBase)this).LoggerInstance.Msg("WorldRates 1.1.0 loaded");
		}

		public override void OnSceneWasInitialized(int buildIndex, string sceneName)
		{
			if (sceneName == "Main")
			{
				StoragePatches.ApplyAll();
			}
		}

		public override void OnUpdate()
		{
			_runner.Tick();
			RatesState.Tick();
			RatesWindow.Tick();
			if (HotkeyPressed())
			{
				RatesWindow.Toggle();
			}
			if (RatesState.InWorld && Time.realtimeSinceStartup >= _nextStorageCheck)
			{
				_nextStorageCheck = Time.realtimeSinceStartup + 15f;
				StoragePatches.ApplyAll();
			}
		}

		public override void OnApplicationQuit()
		{
			RatesState.SaveIfChanged();
		}

		private bool HotkeyPressed()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			KeyCode value = _hotkey.Value;
			if ((int)value == 0 || _inputBroken)
			{
				return false;
			}
			try
			{
				return Input.GetKeyDown(value);
			}
			catch (Exception ex)
			{
				_inputBroken = true;
				((MelonBase)this).LoggerInstance.Warning("Hotkey disabled, legacy input unavailable: " + ex.Message);
				return false;
			}
		}
	}
}
namespace WorldRates.UI
{
	internal static class RatesWindow
	{
		private sealed class Row
		{
			public RateDef Def;

			public Slider Slider;

			public TMP_Text Value;

			public TMP_Text Label;

			public RectTransform? Hint;
		}

		private const string ScreenName = "WorldRatesScreen";

		private const string ButtonName = "WorldRatesButton";

		private static SettingsScreen? _screen;

		private static readonly List<Row> Rows = new List<Row>();

		private static readonly List<GameObject> Blockers = new List<GameObject>();

		private static TMP_Text? _worldLabel;

		private static bool _openWhenPaused;

		private static bool _buildFailed;

		private static bool _syncing;

		private static int _placeHintsIn;

		private const float HintSpace = 30f;

		public static bool IsOpen
		{
			get
			{
				if ((Object)(object)_screen != (Object)null)
				{
					return ((MenuScreen)_screen).IsOpen;
				}
				return false;
			}
		}

		public static void Init()
		{
			RatesState.Current.Changed += delegate
			{
				Sync();
			};
			RatesState.WorldChanged += delegate
			{
				_buildFailed = false;
				Sync();
			};
		}

		public static void Tick()
		{
			PauseMenu instance = Singleton<PauseMenu>.Instance;
			if ((Object)(object)_screen == (Object)null)
			{
				Rows.Clear();
				Blockers.Clear();
				if ((Object)(object)instance != (Object)null && RatesState.InWorld && !_buildFailed)
				{
					TryBuild(instance);
				}
				return;
			}
			if (_openWhenPaused && (Object)(object)instance != (Object)null && instance.IsPaused)
			{
				_openWhenPaused = false;
				Open();
			}
			if (_placeHintsIn > 0 && --_placeHintsIn == 0)
			{
				PlaceHints();
			}
		}

		public static void Toggle()
		{
			PauseMenu instance = Singleton<PauseMenu>.Instance;
			if (!((Object)(object)_screen == (Object)null) && !((Object)(object)instance == (Object)null))
			{
				if (((MenuScreen)_screen).IsOpen)
				{
					((MenuScreen)_screen).Close();
				}
				else if (!instance.IsPaused)
				{
					instance.Pause();
					_openWhenPaused = true;
				}
				else
				{
					Open();
				}
			}
		}

		public static void Open()
		{
			if ((Object)(object)_screen == (Object)null)
			{
				return;
			}
			Sync();
			foreach (ScrollRect componentsInChild in UnityQuery.GetComponentsInChildren<ScrollRect>((Component)(object)_screen, includeInactive: true))
			{
				componentsInChild.verticalNormalizedPosition = 1f;
			}
			_placeHintsIn = 2;
			((MenuScreen)_screen).Open();
		}

		private static void TryBuild(PauseMenu pause)
		{
			try
			{
				Build(pause);
			}
			catch (Exception value)
			{
				_buildFailed = true;
				_screen = null;
				MelonLogger.Error($"World Rates window could not be built: {value}");
			}
		}

		private static void Build(PauseMenu pause)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Expected O, but got Unknown
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Expected O, but got Unknown
			SettingsScreen val = ((IEnumerable<SettingsScreen>)UnityQuery.GetComponentsInChildren<SettingsScreen>((Component)(object)pause, includeInactive: true)).FirstOrDefault((Func<SettingsScreen, bool>)((SettingsScreen s) => ((Object)s).name != "WorldRatesScreen" && !((Object)s).name.EndsWith("ModScreen", StringComparison.Ordinal))) ?? throw new InvalidOperationException("in-game Settings screen not found");
			GameObject val2 = new GameObject("WorldRates Holder");
			val2.SetActive(false);
			GameObject val3 = Object.Instantiate<GameObject>(((Component)val).gameObject, val2.transform);
			((Object)val3).name = "WorldRatesScreen";
			SettingsScreen val4 = UiKit.Get<SettingsScreen>(val3);
			StripSettingsBindings(val3.transform);
			GameObject val5 = new GameObject("Unused");
			val5.transform.SetParent(val3.transform, false);
			val5.SetActive(false);
			Transform val6 = FindRowTemplate(val3.transform, "Master") ?? throw new InvalidOperationException("slider template not found");
			Transform val7 = UiKit.Find(val3.transform, "Hint");
			Transform val8 = UiKit.Find(val3.transform, "ViewCommands");
			Transform val9 = UiKit.Find(val3.transform, "Blocker");
			Transform[] array = (Transform[])(object)new Transform[4] { val6, val7, val8, val9 };
			foreach (Transform obj in array)
			{
				if (obj != null)
				{
					obj.SetParent(val5.transform, false);
				}
			}
			Il2CppReferenceArray<SettingsCategory> categories = val4.Categories;
			for (int num2 = 0; num2 < ((Il2CppArrayBase<SettingsCategory>)(object)categories).Length; num2++)
			{
				Transform val10 = UiKit.Find(((Il2CppArrayBase<SettingsCategory>)(object)categories)[num2].Panel.transform, "Vertical Layout");
				if ((Object)(object)val10 == (Object)null)
				{
					continue;
				}
				foreach (Transform item in UiKit.Children(val10))
				{
					item.SetParent(val5.transform, false);
				}
			}
			val4.HostOnlyGameObjects = Il2CppReferenceArray<GameObject>.op_Implicit((GameObject[])(object)new GameObject[0]);
			SetText(val3.transform, "Title", "World Rates");
			Transform val11 = UiKit.Find(val3.transform, "Version");
			_worldLabel = ((val11 != null) ? UiKit.Get<TMP_Text>((Component)(object)val11) : null);
			RateGroup[] array2 = new RateGroup[4]
			{
				RateGroup.Experience,
				RateGroup.Income,
				RateGroup.Deals,
				RateGroup.Storage
			};
			string[] array3 = new string[5] { "Experience", "Income", "Deals", "Storage", "Presets" };
			for (int num3 = 0; num3 < ((Il2CppArrayBase<SettingsCategory>)(object)categories).Length; num3++)
			{
				Toggle toggle = ((Il2CppArrayBase<SettingsCategory>)(object)categories)[num3].Toggle;
				if (num3 >= array3.Length)
				{
					((Component)toggle).gameObject.SetActive(false);
					continue;
				}
				TMP_Text val12 = UiKit.InChildren<TMP_Text>((Component)(object)toggle);
				if ((Object)(object)val12 != (Object)null)
				{
					val12.text = array3[num3];
				}
				UiKit.AddListener(toggle, delegate(bool on)
				{
					if (on)
					{
						_placeHintsIn = 2;
					}
				});
				Transform layout = UiKit.Find(((Il2CppArrayBase<SettingsCategory>)(object)categories)[num3].Panel.transform, "Vertical Layout");
				if (num3 < array2.Length)
				{
					foreach (RateDef item2 in RateCatalog.InGroup(array2[num3]))
					{
						AddSlider(layout, val6, val7, item2);
					}
					if ((Object)(object)val9 != (Object)null)
					{
						AddBlocker(layout, val9);
					}
				}
				else
				{
					BuildPresets(layout, val6, val8);
				}
			}
			val3.transform.SetParent(((Component)val).transform.parent, false);
			val3.transform.SetSiblingIndex(((Component)val).transform.GetSiblingIndex() + 1);
			Object.Destroy((Object)(object)val2);
			val3.SetActive(true);
			_screen = val4;
			AddPauseButton(pause);
			Sync();
			MelonLogger.Msg("World Rates window ready");
		}

		private static void StripSettingsBindings(Transform root)
		{
			foreach (MonoBehaviour componentsInChild in UnityQuery.GetComponentsInChildren<MonoBehaviour>((Component)(object)root, includeInactive: true))
			{
				if (!((Object)(object)componentsInChild == (Object)null))
				{
					string text = UiKit.Namespace((Component)(object)componentsInChild);
					string text2 = UnityQuery.TypeName((Object)(object)componentsInChild);
					if (text.EndsWith("ScheduleOne.UI.Settings", StringComparison.Ordinal) || text2 == "GameSettingsWindow" || text2 == "VersionText")
					{
						Object.DestroyImmediate((Object)(object)componentsInChild);
					}
				}
			}
		}

		private static Transform? FindRowTemplate(Transform root, string name)
		{
			Transform val = UiKit.Find(root, name);
			if (!((Object)(object)val != (Object)null) || !((Object)(object)val.parent != (Object)null) || !(((Object)val.parent).name == "Vertical Layout"))
			{
				return null;
			}
			return val;
		}

		private static void AddSlider(Transform layout, Transform template, Transform? hintTemplate, RateDef def)
		{
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
			GameObject obj = Object.Instantiate<GameObject>(((Component)template).gameObject, layout, false);
			((Object)obj).name = def.Id;
			obj.SetActive(true);
			UISlider val = UiKit.Get<UISlider>(obj);
			if ((Object)(object)val != (Object)null)
			{
				UiKit.SetMember(val, "optionName", def.Label);
			}
			TMP_Text val2 = UiKit.Get<TMP_Text>((Component)(object)obj.transform.Find("Label"));
			val2.text = def.Label;
			if (def.IsGroupTotal)
			{
				val2.fontStyle = (FontStyles)(val2.fontStyle | 1);
			}
			Slider val3 = UiKit.InChildren<Slider>((Component)(object)obj.transform);
			FitLabelColumn(obj.transform, val2, val3);
			TMP_Text value = UiKit.Get<TMP_Text>((Component)(object)((Component)val3).transform.Find("Value"));
			val3.wholeNumbers = true;
			val3.minValue = Mathf.Round(def.Min / def.Step);
			val3.maxValue = Mathf.Round(def.Max / def.Step);
			UiKit.OnValueChanged(val3, delegate(float v)
			{
				if (!_syncing)
				{
					RatesState.Current.Set(def.Id, v * def.Step);
				}
			});
			RectTransform hint = null;
			if ((Object)(object)hintTemplate != (Object)null)
			{
				GameObject obj2 = Object.Instantiate<GameObject>(((Component)hintTemplate).gameObject, val2.transform, false);
				obj2.SetActive(true);
				Tooltip val4 = UiKit.Get<Tooltip>(obj2);
				if ((Object)(object)val4 != (Object)null)
				{
					val4.text = def.Hint;
				}
				RectTransform? obj3 = UiKit.Get<RectTransform>(obj2);
				Vector2 val5 = default(Vector2);
				((Vector2)(ref val5))..ctor(0f, 0.5f);
				obj3.anchorMax = val5;
				obj3.anchorMin = val5;
				obj3.pivot = new Vector2(0f, 0.5f);
				obj3.anchoredPosition = new Vector2(val2.preferredWidth + 8f, 0f);
				hint = obj3;
			}
			Rows.Add(new Row
			{
				Def = def,
				Slider = val3,
				Value = value,
				Label = val2,
				Hint = hint
			});
		}

		private static void FitLabelColumn(Transform row, TMP_Text label, Slider slider)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			RectTransform val = UiKit.Get<RectTransform>((Component)(object)row);
			RectTransform rectTransform = label.rectTransform;
			RectTransform val2 = UiKit.Get<RectTransform>((Component)(object)((Component)slider).transform);
			float x = ((Transform)rectTransform).localPosition.x;
			Rect rect = rectTransform.rect;
			float num = x + ((Rect)(ref rect)).xMin;
			float x2 = ((Transform)val2).localPosition.x;
			rect = val2.rect;
			float num2 = x2 + ((Rect)(ref rect)).xMin - 30f;
			rect = rectTransform.rect;
			float height = ((Rect)(ref rect)).height;
			float y = ((Transform)rectTransform).localPosition.y;
			rect = rectTransform.rect;
			float num3 = y + ((Rect)(ref rect)).center.y;
			Vector2 val3 = default(Vector2);
			((Vector2)(ref val3))..ctor(0f, 0.5f);
			rectTransform.anchorMax = val3;
			rectTransform.anchorMin = val3;
			rectTransform.pivot = new Vector2(0f, 0.5f);
			rectTransform.sizeDelta = new Vector2(Mathf.Max(60f, num2 - num), height);
			rect = val.rect;
			float num4 = num - ((Rect)(ref rect)).xMin;
			rect = val.rect;
			rectTransform.anchoredPosition = new Vector2(num4, num3 - ((Rect)(ref rect)).center.y);
			label.textWrappingMode = (TextWrappingModes)0;
			label.fontSizeMax = label.fontSize;
			label.fontSizeMin = label.fontSize * 0.6f;
			label.enableAutoSizing = true;
		}

		private static void PlaceHints()
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			foreach (Row row in Rows)
			{
				if (!((Object)(object)row.Hint == (Object)null) && !((Object)(object)row.Label == (Object)null) && ((Behaviour)row.Label).isActiveAndEnabled)
				{
					row.Label.ForceMeshUpdate(false, false);
					Bounds textBounds = row.Label.textBounds;
					float x = ((Bounds)(ref textBounds)).size.x;
					Rect rect = row.Label.rectTransform.rect;
					float num = Mathf.Min(x, ((Rect)(ref rect)).width);
					row.Hint.anchoredPosition = new Vector2(num + 8f, 0f);
				}
			}
		}

		private static void AddBlocker(Transform layout, Transform template)
		{
			Transform parent = layout.parent;
			object obj;
			if (parent == null)
			{
				obj = null;
			}
			else
			{
				Transform parent2 = parent.parent;
				obj = ((parent2 != null) ? parent2.parent : null);
			}
			Transform val = (Transform)obj;
			if (!((Object)(object)val == (Object)null))
			{
				GameObject val2 = Object.Instantiate<GameObject>(((Component)template).gameObject, val, false);
				((Object)val2).name = "WorldRates Blocker";
				TMP_Text val3 = UiKit.InChildren<TMP_Text>((Component)(object)val2.transform);
				if ((Object)(object)val3 != (Object)null)
				{
					val3.text = "World rates are set by the host";
				}
				Blockers.Add(val2);
			}
		}

		private static void BuildPresets(Transform layout, Transform sliderTemplate, Transform? buttonTemplate)
		{
			AddText(layout, sliderTemplate, "Quick presets set every experience and income rate (per-source rates back to ×1).");
			if ((Object)(object)buttonTemplate == (Object)null)
			{
				return;
			}
			float[] array = new float[5] { 1f, 2f, 3f, 5f, 10f };
			foreach (float n in array)
			{
				string title = ((n == 1f) ? "Vanilla ×1" : ("All rates " + Format(n)));
				AddButton(layout, buttonTemplate, title, delegate
				{
					RatesState.Current.ApplyPreset(n);
				});
			}
			AddText(layout, sliderTemplate, "Rates are saved per world and apply instantly.");
			AddButton(layout, buttonTemplate, "Use these rates for new worlds", delegate
			{
				RatesState.SaveAsDefaults();
				Notify("Saved as default for new worlds");
			});
		}

		private static void AddText(Transform layout, Transform template, string text)
		{
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			GameObject obj = Object.Instantiate<GameObject>(((Component)template).gameObject, layout, false);
			((Object)obj).name = "Text";
			obj.SetActive(true);
			UISlider val = UiKit.Get<UISlider>(obj);
			if ((Object)(object)val != (Object)null)
			{
				Object.DestroyImmediate((Object)(object)val);
			}
			Slider val2 = UiKit.InChildren<Slider>((Component)(object)obj.transform);
			if ((Object)(object)val2 != (Object)null)
			{
				Object.DestroyImmediate((Object)(object)((Component)val2).gameObject);
			}
			TMP_Text? obj2 = UiKit.Get<TMP_Text>((Component)(object)obj.transform.Find("Label"));
			RectTransform rectTransform = obj2.rectTransform;
			rectTransform.anchorMin = new Vector2(0f, rectTransform.anchorMin.y);
			rectTransform.anchorMax = new Vector2(1f, rectTransform.anchorMax.y);
			rectTransform.offsetMax = new Vector2(-10f, rectTransform.offsetMax.y);
			obj2.textWrappingMode = (TextWrappingModes)1;
			obj2.fontStyle = (FontStyles)(obj2.fontStyle | 2);
			obj2.text = text;
		}

		private static void AddButton(Transform layout, Transform template, string title, Action onClick)
		{
			GameObject obj = Object.Instantiate<GameObject>(((Component)template).gameObject, layout, false);
			((Object)obj).name = "Button " + title;
			obj.SetActive(true);
			TMP_Text val = UiKit.InChildren<TMP_Text>((Component)(object)obj.transform);
			if ((Object)(object)val != (Object)null)
			{
				val.text = title;
			}
			UiKit.OnClick(UiKit.Get<Button>(obj), onClick);
		}

		private static void AddPauseButton(PauseMenu pause)
		{
			Transform val = UiKit.Find((Transform)(object)pause.Container, "Panel");
			Transform val2 = ((val != null) ? val.Find("Settings") : null);
			if (!((Object)(object)val == (Object)null) && !((Object)(object)val2 == (Object)null) && !((Object)(object)val.Find("WorldRatesButton") != (Object)null))
			{
				GameObject obj = Object.Instantiate<GameObject>(((Component)val2).gameObject, val, false);
				((Object)obj).name = "WorldRatesButton";
				obj.transform.SetSiblingIndex(val2.GetSiblingIndex() + 1);
				TMP_Text val3 = UiKit.Get<TMP_Text>((Component)(object)obj.transform.Find("ButtonText"));
				if ((Object)(object)val3 != (Object)null)
				{
					val3.text = "World Rates";
				}
				UiKit.OnClick(UiKit.Get<Button>(obj), Open);
			}
		}

		private static void Sync()
		{
			if ((Object)(object)_screen == (Object)null)
			{
				return;
			}
			_syncing = true;
			try
			{
				Rows.RemoveAll((Row r) => (Object)(object)r.Slider == (Object)null);
				foreach (Row row in Rows)
				{
					float num = RatesState.Current.Get(row.Def.Id);
					row.Slider.value = Mathf.Round(num / row.Def.Step);
					row.Value.text = Format(num) + EffectiveSuffix(row.Def, num);
				}
			}
			finally
			{
				_syncing = false;
			}
			bool active = !RatesState.IsAuthority;
			foreach (GameObject blocker in Blockers)
			{
				if ((Object)(object)blocker != (Object)null)
				{
					blocker.SetActive(active);
				}
			}
			if ((Object)(object)_worldLabel != (Object)null)
			{
				_worldLabel.text = RatesState.WorldName ?? "";
			}
		}

		private static string EffectiveSuffix(RateDef def, float v)
		{
			if (def.IsGroupTotal || (def.Group != RateGroup.Experience && def.Group != RateGroup.Income))
			{
				return "";
			}
			float num = RatesState.Current.Get((def.Group == RateGroup.Experience) ? "xp.all" : "income.all");
			if (!Mathf.Approximately(num, 1f))
			{
				return " <color=#9A9A9A>(" + Format(v * num) + ")</color>";
			}
			return "";
		}

		private static string Format(float v)
		{
			if (!(v <= 0f))
			{
				return "×" + v.ToString("0.##", CultureInfo.InvariantCulture);
			}
			return "Off";
		}

		private static void SetText(Transform root, string name, string text)
		{
			Transform val = UiKit.Find(root, name);
			TMP_Text val2 = (((Object)(object)val != (Object)null) ? UiKit.Get<TMP_Text>((Component)(object)val) : null);
			if ((Object)(object)val2 != (Object)null)
			{
				val2.text = text;
			}
		}

		private static void Notify(string text)
		{
			NotificationsManager instance = Singleton<NotificationsManager>.Instance;
			if ((Object)(object)instance != (Object)null)
			{
				instance.SendNotification("World Rates", text, (Sprite)null, 3f, true);
			}
		}
	}
}
namespace WorldRates.Rates
{
	internal enum RateGroup
	{
		Experience,
		Income,
		Deals,
		Storage
	}
	internal sealed class RateDef
	{
		public string Id { get; }

		public RateGroup Group { get; }

		public string Label { get; }

		public string Hint { get; }

		public float Min { get; }

		public float Max { get; }

		public float Step { get; }

		public bool IsGroupTotal { get; }

		public RateDef(string id, RateGroup group, string label, string hint, float min = 0f, float max = 10f, float step = 0.1f, bool isGroupTotal = false)
		{
			Id = id;
			Group = group;
			Label = label;
			Hint = hint;
			Min = min;
			Max = max;
			Step = step;
			IsGroupTotal = isGroupTotal;
		}
	}
	internal static class RateCatalog
	{
		public const string XpAll = "xp.all";

		public const string XpDealPlayer = "xp.deal_player";

		public const string XpDealDealer = "xp.deal_dealer";

		public const string XpSample = "xp.sample";

		public const string XpCounterOffer = "xp.counter_offer";

		public const string XpHarvest = "xp.harvest";

		public const string XpMix = "xp.mix";

		public const string XpQuest = "xp.quest";

		public const string XpPolice = "xp.police";

		public const string XpGraffiti = "xp.graffiti";

		public const string XpPickpocket = "xp.pickpocket";

		public const string XpOther = "xp.other";

		public const string IncomeAll = "income.all";

		public const string IncomeDealPlayer = "income.deal_player";

		public const string IncomeDealDealer = "income.deal_dealer";

		public const string IncomeLaundering = "income.laundering";

		public const string IncomePawnShop = "income.pawn_shop";

		public const string IncomeRecycling = "income.recycling";

		public const string DealsRequests = "deals.requests";

		public const string DealsDealers = "deals.dealers";

		public const string StorageFurniture = "storage.furniture";

		public const string StorageVehicles = "storage.vehicles";

		public static readonly IReadOnlyList<RateDef> All = new RateDef[22]
		{
			new RateDef("xp.all", RateGroup.Experience, "All experience", "Multiplies every XP gain (on top of the per-source rates).", 0f, 10f, 0.1f, isGroupTotal: true),
			new RateDef("xp.deal_player", RateGroup.Experience, "Your deals", "Completing a deal in person (20 XP)."),
			new RateDef("xp.deal_dealer", RateGroup.Experience, "Dealer deals", "A hired dealer completes a deal (10 XP)."),
			new RateDef("xp.sample", RateGroup.Experience, "Free samples", "A customer likes your free sample (50 XP)."),
			new RateDef("xp.counter_offer", RateGroup.Experience, "Counter-offers", "A customer accepts your counter-offer (5 XP)."),
			new RateDef("xp.harvest", RateGroup.Experience, "Harvesting", "Fully harvesting a plant (5 XP)."),
			new RateDef("xp.mix", RateGroup.Experience, "New mixes", "Discovering and naming a new mix (80 XP)."),
			new RateDef("xp.quest", RateGroup.Experience, "Quests", "Completing a quest."),
			new RateDef("xp.police", RateGroup.Experience, "Escaping police", "Losing the police (20-60 XP)."),
			new RateDef("xp.graffiti", RateGroup.Experience, "Graffiti", "Spraying or cleaning graffiti (25-50 XP)."),
			new RateDef("xp.pickpocket", RateGroup.Experience, "Pickpocketing", "Successful pickpocket (2 XP)."),
			new RateDef("xp.other", RateGroup.Experience, "Other", "Any other XP source."),
			new RateDef("income.all", RateGroup.Income, "All income", "Multiplies every income below (on top of its own rate).", 0f, 10f, 0.1f, isGroupTotal: true),
			new RateDef("income.deal_player", RateGroup.Income, "Your deals", "Cash a customer pays you in person, bonuses included."),
			new RateDef("income.deal_dealer", RateGroup.Income, "Dealer sales", "Cash your dealers collect from customers."),
			new RateDef("income.laundering", RateGroup.Income, "Laundering", "Money returned by business laundering."),
			new RateDef("income.pawn_shop", RateGroup.Income, "Pawn shop", "Selling items at the pawn shop."),
			new RateDef("income.recycling", RateGroup.Income, "Recycling", "Cash from the recycler."),
			new RateDef("deals.requests", RateGroup.Deals, "Deal requests", "How often customers text you asking for a deal. Order size stays the same."),
			new RateDef("deals.dealers", RateGroup.Deals, "Dealer orders", "How often customers order from your dealers - how fast a dealer sells the product you give him."),
			new RateDef("storage.furniture", RateGroup.Storage, "Placed storage", "Racks, shelves, safes and other placeable storage. Max 20 slots.", 1f, 4f, 0.25f),
			new RateDef("storage.vehicles", RateGroup.Storage, "Vehicle trunks", "Trunk capacity of vehicles. Max 20 slots.", 1f, 4f, 0.25f)
		};

		private static readonly Dictionary<string, RateDef> ById = All.ToDictionary((RateDef d) => d.Id);

		public static RateDef? Find(string id)
		{
			if (!ById.TryGetValue(id, out RateDef value))
			{
				return null;
			}
			return value;
		}

		public static IEnumerable<RateDef> InGroup(RateGroup group)
		{
			return All.Where((RateDef d) => d.Group == group);
		}
	}
	internal sealed class RateSettings
	{
		private readonly Dictionary<string, float> _values = new Dictionary<string, float>(StringComparer.Ordinal);

		public bool IsVanilla => RateCatalog.All.All((RateDef d) => Mathf.Approximately(Get(d.Id), 1f));

		public event Action<string>? Changed;

		public float Get(string id)
		{
			if (!_values.TryGetValue(id, out var value))
			{
				return 1f;
			}
			return value;
		}

		public void Set(string id, float value)
		{
			RateDef rateDef = RateCatalog.Find(id);
			if (rateDef != null)
			{
				value = Mathf.Clamp((float)Math.Round(value / rateDef.Step) * rateDef.Step, rateDef.Min, rateDef.Max);
				if (!_values.TryGetValue(id, out var value2) || !Mathf.Approximately(value2, value))
				{
					_values[id] = value;
					this.Changed?.Invoke(id);
				}
			}
		}

		public void SetAll(RateGroup group, float value)
		{
			foreach (RateDef item in RateCatalog.InGroup(group))
			{
				Set(item.Id, item.IsGroupTotal ? value : 1f);
			}
		}

		public void Reset()
		{
			foreach (RateDef item in RateCatalog.All)
			{
				Set(item.Id, 1f);
			}
		}

		public void ApplyPreset(float n)
		{
			foreach (RateDef item in RateCatalog.All)
			{
				if (item.Group == RateGroup.Storage)
				{
					Set(item.Id, Mathf.Clamp(n, item.Min, item.Max));
				}
				else if (item.Group != RateGroup.Deals)
				{
					Set(item.Id, item.IsGroupTotal ? n : 1f);
				}
				else if (Mathf.Approximately(n, 1f))
				{
					Set(item.Id, 1f);
				}
			}
		}

		public void CopyFrom(RateSettings other)
		{
			foreach (RateDef item in RateCatalog.All)
			{
				Set(item.Id, other.Get(item.Id));
			}
		}

		public string Serialize()
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("# WorldRates multipliers (1 = vanilla). Edit in game: pause menu -> World Rates, or F10.\n");
			foreach (RateDef item in RateCatalog.All)
			{
				stringBuilder.Append(item.Id).Append('=').Append(Get(item.Id).ToString("0.###", CultureInfo.InvariantCulture))
					.Append('\n');
			}
			return stringBuilder.ToString();
		}

		public void Deserialize(string text)
		{
			string[] array = text.Split('\n');
			for (int i = 0; i < array.Length; i++)
			{
				string text2 = array[i].Trim();
				if (text2.Length != 0 && text2[0] != '#')
				{
					int num = text2.IndexOf('=');
					if (num > 0 && float.TryParse(text2.Substring(num + 1), NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
					{
						Set(text2.Substring(0, num).Trim(), result);
					}
				}
			}
		}

		public void SaveTo(string path)
		{
			Directory.CreateDirectory(Path.GetDirectoryName(path));
			File.WriteAllText(path, Serialize(), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
		}

		public bool LoadFrom(string path)
		{
			if (!File.Exists(path))
			{
				return false;
			}
			Deserialize(File.ReadAllText(path, Encoding.UTF8));
			return true;
		}
	}
	internal static class RatesState
	{
		private static string? _settingsFile;

		private static bool _dirty;

		private static float _saveAt;

		public static string Root => Path.Combine(MelonEnvironment.UserDataDirectory, "WorldRates");

		private static string DefaultsPath => Path.Combine(Root, "defaults.cfg");

		public static RateSettings Current { get; } = new RateSettings();

		public static string? WorldPath { get; private set; }

		public static string? WorldName { get; private set; }

		public static bool InWorld => WorldPath != null;

		public static bool IsAuthority
		{
			get
			{
				if (!InstanceFinder.IsServer)
				{
					return !InstanceFinder.IsClient;
				}
				return true;
			}
		}

		public static event Action? WorldChanged;

		public static void Init()
		{
			Directory.CreateDirectory(Path.Combine(Root, "worlds"));
			Current.Changed += delegate
			{
				_dirty = true;
				_saveAt = Time.realtimeSinceStartup + 1f;
			};
			LoadDefaults();
		}

		public static float Multiplier(string id, string groupTotalId)
		{
			if (!InWorld)
			{
				return 1f;
			}
			return Current.Get(groupTotalId) * Current.Get(id);
		}

		public static void Tick()
		{
			LoadManager instance = Singleton<LoadManager>.Instance;
			string text = (((Object)(object)instance != (Object)null && (instance.IsGameLoaded || instance.IsLoading)) ? instance.LoadedGameFolderPath : null);
			if (string.IsNullOrEmpty(text))
			{
				text = null;
			}
			if (text != WorldPath)
			{
				SwitchWorld(text);
			}
			if (_dirty && Time.realtimeSinceStartup >= _saveAt)
			{
				Save();
			}
		}

		public static void Save()
		{
			_dirty = false;
			if (_settingsFile != null)
			{
				Current.SaveTo(_settingsFile);
			}
		}

		public static void SaveIfChanged()
		{
			if (_dirty)
			{
				Save();
			}
		}

		public static void SaveAsDefaults()
		{
			Current.SaveTo(DefaultsPath);
		}

		private static void SwitchWorld(string? path)
		{
			if (_dirty)
			{
				Save();
			}
			WorldPath = path;
			if (path == null)
			{
				WorldName = null;
				_settingsFile = null;
				LoadDefaults();
				MelonLogger.Msg("Left world, rates inactive");
			}
			else
			{
				WorldName = Path.GetFileName(path.TrimEnd('\\', '/'));
				_settingsFile = Path.Combine(Root, "worlds", WorldKey(path) + ".cfg");
				LoadDefaults();
				if (Current.LoadFrom(_settingsFile))
				{
					MelonLogger.Msg("World '" + WorldName + "': rates loaded from " + Path.GetFileName(_settingsFile));
				}
				else
				{
					MelonLogger.Msg("World '" + WorldName + "': new world, using defaults");
				}
				_dirty = false;
			}
			RatesState.WorldChanged?.Invoke();
		}

		private static void LoadDefaults()
		{
			Current.Reset();
			Current.LoadFrom(DefaultsPath);
			_dirty = false;
		}

		private static string WorldKey(string path)
		{
			string[] array = path.Replace('\\', '/').TrimEnd('/').Split('/');
			string text = string.Join("_", array.Skip(Math.Max(0, array.Length - 2)));
			char[] invalidFileNameChars = Path.GetInvalidFileNameChars();
			foreach (char oldChar in invalidFileNameChars)
			{
				text = text.Replace(oldChar, '_');
			}
			return text;
		}
	}
}
namespace WorldRates.Patches
{
	internal static class DemandPatches
	{
		private sealed class Swap
		{
			public CustomerData Data;

			public int OrderTime;

			public int MinOrders;

			public int MaxOrders;

			public float MinSpend;

			public float MaxSpend;

			public bool CountersRaised;

			public int Completed;

			public int Offered;
		}

		[HarmonyPatch(typeof(Customer), "OnMinPass")]
		private static class MinPass
		{
			private static void Prefix(Customer __instance, out Swap? __state)
			{
				__state = null;
				try
				{
					__state = Apply(__instance);
				}
				catch (Exception ex)
				{
					Warn(ex);
				}
			}

			private static void Finalizer(Customer __instance, Swap? __state)
			{
				if (__state == null)
				{
					return;
				}
				try
				{
					Restore(__instance, __state);
				}
				catch (Exception ex)
				{
					Warn(ex);
				}
			}
		}

		private const int WindowMinutes = 120;

		private const int MinutesPerDay = 1440;

		private const int MaxWindowsPerDay = 12;

		private const int CooldownPassed = 800;

		private static readonly MethodInfo? SetCompleted = AccessTools.PropertySetter(typeof(Customer), "TimeSinceLastDealCompleted");

		private static readonly MethodInfo? SetOffered = AccessTools.PropertySetter(typeof(Customer), "TimeSinceLastDealOffered");

		private static bool _warned;

		internal static bool Failed => _warned;

		internal static int VanillaOrdersPerWeek(int minOrders, int maxOrders, float dependence, float relationship)
		{
			int num = Mathf.RoundToInt(Mathf.Lerp((float)minOrders, (float)maxOrders, Mathf.Max(dependence, relationship)));
			int num2 = Mathf.Max(Mathf.RoundToInt(7f / (float)num), 1);
			return 6 / num2 + 1;
		}

		internal static int WindowsOn(int ordersPerWeek, float rate, int day)
		{
			double num = (double)ordersPerWeek * Math.Round(rate, 2) / 7.0;
			if (num <= 0.0)
			{
				return 0;
			}
			return Mathf.Clamp((int)(Math.Ceiling((double)(day + 1) * num - 1E-06) - Math.Ceiling((double)day * num - 1E-06)), 0, 12);
		}

		internal static bool IsOrderSlot(int ordersPerWeek, float rate, int day, int minuteOfDay, int orderMinuteOfDay)
		{
			int num = WindowsOn(ordersPerWeek, rate, day);
			if (num == 0)
			{
				return false;
			}
			int num2 = 1440 / num;
			int num3 = (minuteOfDay - orderMinuteOfDay + 1440) % 1440;
			if (num3 % num2 <= 120)
			{
				return num3 / num2 < num;
			}
			return false;
		}

		internal static int Cooldown(char firstLetter, float rate)
		{
			return Mathf.Max(Mathf.RoundToInt((float)(600 + firstLetter % 10 * 20) / rate), 125);
		}

		private static Swap? Apply(Customer customer)
		{
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Expected I4, but got Unknown
			if (!RatesState.InWorld || !RatesState.IsAuthority)
			{
				return null;
			}
			float num = RatesState.Current.Get(((Object)(object)customer.AssignedDealer != (Object)null) ? "deals.dealers" : "deals.requests");
			if (Mathf.Approximately(num, 1f))
			{
				return null;
			}
			NPC nPC = customer.NPC;
			CustomerData customerData = customer.CustomerData;
			if ((Object)(object)nPC == (Object)null || (Object)(object)customerData == (Object)null || !nPC.RelationData.Unlocked || (Object)(object)customer.CurrentContract != (Object)null || customer.OfferedContractInfo != null)
			{
				return null;
			}
			TimeManager instance = NetworkSingleton<TimeManager>.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				return null;
			}
			bool flag = false;
			int num2 = 1;
			if (num > 0f)
			{
				num2 = VanillaOrdersPerWeek(customerData.MinOrdersPerWeek, customerData.MaxOrdersPerWeek, customer.CurrentAddiction, nPC.RelationData.RelationDelta / 5f);
				int day = instance.ElapsedDays + (7 - customerData.PreferredOrderDay) % 7;
				int num3 = Cooldown(nPC.FirstName[0], num);
				flag = customer.TimeSinceLastDealCompleted >= num3 && customer.TimeSinceLastDealOffered >= num3 && IsOrderSlot(num2, num, day, TimeManager.GetMinSumFrom24HourTime(instance.CurrentTime), TimeManager.GetMinSumFrom24HourTime(customerData.OrderTime));
			}
			Swap swap = new Swap
			{
				Data = customerData,
				OrderTime = customerData.OrderTime,
				MinOrders = customerData.MinOrdersPerWeek,
				MaxOrders = customerData.MaxOrdersPerWeek,
				MinSpend = customerData.MinWeeklySpend,
				MaxSpend = customerData.MaxWeeklySpend
			};
			if (!flag)
			{
				customerData.OrderTime = TimeManager.AddMinutesTo24HourTime(instance.CurrentTime, 720);
				return swap;
			}
			customerData.OrderTime = TimeManager.AddMinutesTo24HourTime(instance.CurrentTime, -1);
			customerData.MinOrdersPerWeek = 7;
			customerData.MaxOrdersPerWeek = 7;
			customerData.MinWeeklySpend = swap.MinSpend * 7f / (float)num2;
			customerData.MaxWeeklySpend = swap.MaxSpend * 7f / (float)num2;
			swap.Completed = customer.TimeSinceLastDealCompleted;
			swap.Offered = customer.TimeSinceLastDealOffered;
			if (swap.Completed < 800 || swap.Offered < 800)
			{
				swap.CountersRaised = true;
				SetCounter(SetCompleted, customer, Mathf.Max(swap.Completed, 800));
				SetCounter(SetOffered, customer, Mathf.Max(swap.Offered, 800));
			}
			return swap;
		}

		private static void Restore(Customer customer, Swap swap)
		{
			CustomerData data = swap.Data;
			data.OrderTime = swap.OrderTime;
			data.MinOrdersPerWeek = swap.MinOrders;
			data.MaxOrdersPerWeek = swap.MaxOrders;
			data.MinWeeklySpend = swap.MinSpend;
			data.MaxWeeklySpend = swap.MaxSpend;
			if (swap.CountersRaised)
			{
				if (customer.TimeSinceLastDealCompleted == Mathf.Max(swap.Completed, 800) + 1)
				{
					SetCounter(SetCompleted, customer, swap.Completed + 1);
				}
				if (customer.TimeSinceLastDealOffered == Mathf.Max(swap.Offered, 800) + 1)
				{
					SetCounter(SetOffered, customer, swap.Offered + 1);
				}
			}
		}

		private static void SetCounter(MethodInfo? setter, Customer customer, int value)
		{
			setter?.Invoke(customer, new object[1] { value });
		}

		private static void Warn(Exception ex)
		{
			if (!_warned)
			{
				_warned = true;
				MelonLogger.Warning($"Deal frequency rate could not be applied: {ex}");
			}
		}
	}
	internal static class IncomePatches
	{
		[HarmonyPatch(typeof(Customer), "ProcessHandover")]
		private static class DealPayment
		{
			private static readonly PropertyInfo PaymentProperty = AccessTools.Property(typeof(Contract), "Payment");

			private static void Prefix(Contract contract, bool handoverByPlayer)
			{
				if (!((Object)(object)contract == (Object)null) && ScaledContracts.Add(((Object)contract).GetInstanceID()))
				{
					PaymentProperty.SetValue(contract, Scale(contract.Payment, handoverByPlayer ? "income.deal_player" : "income.deal_dealer"));
				}
			}
		}

		[HarmonyPatch(typeof(Business), "CompleteOperation")]
		private static class Laundering
		{
			private static void Prefix(LaunderingOperation op)
			{
				if (op != null)
				{
					op.amount = Scale(op.amount, "income.laundering");
				}
			}
		}

		[HarmonyPatch(typeof(PawnShopInterface), "FinalizeDeal")]
		private static class PawnShop
		{
			private static void Prefix(ref float amount)
			{
				amount = Scale(amount, "income.pawn_shop");
			}
		}

		[HarmonyPatch(typeof(Recycler), "CashInteracted")]
		private static class Recycling
		{
			private static void Prefix(out string? __state)
			{
				__state = _cashSource;
				_cashSource = "income.recycling";
			}

			private static void Finalizer(string? __state)
			{
				_cashSource = __state;
			}
		}

		[HarmonyPatch(typeof(MoneyManager), "ChangeCashBalance")]
		private static class CashChange
		{
			private static void Prefix(ref float change)
			{
				if (_cashSource != null)
				{
					change = Scale(change, _cashSource);
				}
			}
		}

		private static readonly HashSet<int> ScaledContracts = new HashSet<int>();

		private static string? _cashSource;

		private static float Multiplier(string id)
		{
			if (!RatesState.InWorld || !RatesState.IsAuthority)
			{
				return 1f;
			}
			return RatesState.Multiplier(id, "income.all");
		}

		private static float Scale(float amount, string id)
		{
			float num = Multiplier(id);
			if (amount <= 0f || num == 1f)
			{
				return amount;
			}
			float num2 = amount * num;
			MelonLogger.Msg($"Income {amount:0.##} -> {num2:0.##} ({id} x{num:0.##})");
			return num2;
		}
	}
	internal static class StoragePatches
	{
		private sealed class Info
		{
			public int BaseSlots;

			public int BaseRows;

			public string RateId = "";

			public LandVehicle? Vehicle;
		}

		[HarmonyPatch(typeof(StorageEntity), "Awake")]
		private static class AwakePatch
		{
			private static void Prefix(StorageEntity __instance)
			{
				try
				{
					string text = RateIdFor(__instance);
					if (text != null)
					{
						Info info = new Info
						{
							BaseSlots = __instance.SlotCount,
							BaseRows = __instance.DisplayRowCount,
							RateId = text,
							Vehicle = UnityQuery.GetComponentInParent<LandVehicle>((Component)(object)__instance)
						};
						Known[((Object)__instance).GetInstanceID()] = info;
						int slots = (__instance.SlotCount = TargetSlots(info));
						__instance.DisplayRowCount = RowsFor(info, slots);
					}
				}
				catch (Exception value)
				{
					MelonLogger.Error($"Storage setup failed: {value}");
				}
			}
		}

		[HarmonyPatch(typeof(ItemSet), "LoadTo", new Type[] { typeof(List<ItemSlot>) })]
		private static class LoadItemSet
		{
			private static void Prefix(ItemSet __instance, List<ItemSlot> slots)
			{
				if (__instance.Items != null && ((Il2CppArrayBase<string>)(object)__instance.Items).Length > slots.Count && slots.Count > 0)
				{
					GrowToFit(((Il2CppArrayBase<string>)(object)__instance.Items).Length, slots[0]);
				}
			}
		}

		[HarmonyPatch(typeof(DeserializedItemSet), "LoadTo", new Type[] { typeof(List<ItemSlot>) })]
		private static class LoadDeserializedItemSet
		{
			private static void Prefix(DeserializedItemSet __instance, List<ItemSlot> slots)
			{
				if (__instance.Items != null && ((Il2CppArrayBase<ItemInstance>)(object)__instance.Items).Length > slots.Count && slots.Count > 0)
				{
					GrowToFit(((Il2CppArrayBase<ItemInstance>)(object)__instance.Items).Length, slots[0]);
				}
			}
		}

		private const int MaxSlots = 20;

		private const int MaxColumns = 10;

		private static readonly Dictionary<int, Info> Known = new Dictionary<int, Info>();

		private static int _pendingLog;

		private static float _lastLog = -10f;

		public static void ApplyAll()
		{
			int num = 0;
			foreach (StorageEntity item in UnityQuery.FindInScenes<StorageEntity>())
			{
				if (Known.TryGetValue(((Object)item).GetInstanceID(), out Info value) && Resize(item, TargetSlots(value), value))
				{
					num++;
				}
			}
			if (num != 0)
			{
				_pendingLog += num;
				if (!(Time.realtimeSinceStartup - _lastLog < 2f))
				{
					MelonLogger.Msg($"Storage capacity updated on {_pendingLog} containers");
					_pendingLog = 0;
					_lastLog = Time.realtimeSinceStartup;
				}
			}
		}

		public static string Describe()
		{
			StringBuilder stringBuilder = new StringBuilder();
			foreach (StorageEntity item in UnityQuery.FindInScenes<StorageEntity>())
			{
				Known.TryGetValue(((Object)item).GetInstanceID(), out Info value);
				stringBuilder.Append(UnityQuery.PathOf(((Component)item).transform)).Append(" | ").Append(item.StorageEntityName)
					.Append(" slots=")
					.Append(item.ItemSlots.Count)
					.Append(" rows=")
					.Append(item.DisplayRowCount)
					.Append(" rate=")
					.Append(value?.RateId ?? "-")
					.Append(" base=")
					.Append(value?.BaseSlots ?? 0)
					.Append('\n');
			}
			return stringBuilder.ToString();
		}

		private static string? RateIdFor(StorageEntity entity)
		{
			if ((Object)(object)UnityQuery.GetComponentInParent<LandVehicle>((Component)(object)entity) != (Object)null)
			{
				return "storage.vehicles";
			}
			if ((Object)(object)UnityQuery.GetComponentInParent<PlaceableStorageEntity>((Component)(object)entity) != (Object)null || (Object)(object)UnityQuery.GetComponentInParent<SurfaceStorageEntity>((Component)(object)entity) != (Object)null)
			{
				return "storage.furniture";
			}
			return null;
		}

		private static int TargetSlots(Info info)
		{
			if ((Object)(object)info.Vehicle != (Object)null && !info.Vehicle.IsPlayerOwned)
			{
				return info.BaseSlots;
			}
			float num = (RatesState.InWorld ? RatesState.Current.Get(info.RateId) : 1f);
			return Mathf.Clamp(Mathf.RoundToInt((float)info.BaseSlots * num), 1, Math.Max(20, info.BaseSlots));
		}

		private static int RowsFor(Info info, int slots)
		{
			int num = Mathf.Clamp(Mathf.CeilToInt((float)info.BaseSlots / (float)Math.Max(1, info.BaseRows)), 1, 10);
			return Mathf.Clamp(Mathf.CeilToInt((float)slots / (float)num), info.BaseRows, 5);
		}

		private static bool Resize(StorageEntity entity, int target, Info info)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Expected O, but got Unknown
			List<ItemSlot> itemSlots = entity.ItemSlots;
			int count = itemSlots.Count;
			if (target > count)
			{
				if (entity.IsOpened || count == 0)
				{
					return false;
				}
				ItemSlot val = itemSlots[0];
				List<ItemSlot> list = new List<ItemSlot>();
				for (int i = count; i < target; i++)
				{
					ItemSlot val2 = new ItemSlot(entity.SlotsAreFilterable);
					val2.onItemDataChanged = val.onItemDataChanged;
					val2.SetSlotOwner(entity.AsOwner());
					ItemSlotSiblingSet siblingSet = val.SiblingSet;
					if (siblingSet != null)
					{
						siblingSet.AddSlot(val2);
					}
					list.Add(val2);
				}
				SyncTransitSlots(entity, list, null);
			}
			else
			{
				if (target >= count)
				{
					return false;
				}
				List<ItemSlot> list2 = new List<ItemSlot>();
				for (int num = count - 1; num >= target; num--)
				{
					ItemSlot val3 = itemSlots[num];
					if (val3.ItemInstance != null || entity.IsOpened)
					{
						break;
					}
					itemSlots.RemoveAt(num);
					ItemSlotSiblingSet siblingSet2 = val3.SiblingSet;
					if (siblingSet2 != null)
					{
						siblingSet2.Slots.Remove(val3);
					}
					list2.Add(val3);
				}
				if (list2.Count == 0)
				{
					return false;
				}
				SyncTransitSlots(entity, null, list2);
			}
			entity.SlotCount = itemSlots.Count;
			entity.DisplayRowCount = RowsFor(info, itemSlots.Count);
			return true;
		}

		private static void SyncTransitSlots(StorageEntity entity, List<ItemSlot>? added, List<ItemSlot>? removed)
		{
			PlaceableStorageEntity componentInParent = UnityQuery.GetComponentInParent<PlaceableStorageEntity>((Component)(object)entity);
			if ((Object)(object)componentInParent == (Object)null)
			{
				return;
			}
			List<ItemSlot>[] array = new List<ItemSlot>[2] { componentInParent.InputSlots, componentInParent.OutputSlots };
			foreach (List<ItemSlot> val in array)
			{
				if (val == null)
				{
					continue;
				}
				if (added != null)
				{
					foreach (ItemSlot item in added)
					{
						val.Add(item);
					}
				}
				if (removed == null)
				{
					continue;
				}
				foreach (ItemSlot item2 in removed)
				{
					val.Remove(item2);
				}
			}
		}

		private static IItemSlotOwner AsOwner(this StorageEntity entity)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			return new IItemSlotOwner(((Il2CppObjectBase)entity).Pointer);
		}

		private static void GrowToFit(int savedSlots, ItemSlot? firstSlot)
		{
			if (firstSlot == null)
			{
				return;
			}
			IItemSlotOwner slotOwner = firstSlot.SlotOwner;
			if (slotOwner != null && UnityQuery.TryCastTo<StorageEntity>(OwnerObject(slotOwner), out StorageEntity result))
			{
				if (!Known.TryGetValue(((Object)result).GetInstanceID(), out Info value))
				{
					value = new Info
					{
						BaseSlots = result.SlotCount,
						BaseRows = result.DisplayRowCount
					};
				}
				if (Resize(result, Math.Min(savedSlots, 20), value))
				{
					MelonLogger.Msg($"'{result.StorageEntityName}' grown to {result.ItemSlots.Count} slots to keep saved items");
				}
			}
		}

		private static Object? OwnerObject(IItemSlotOwner owner)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			return new Object(((Il2CppObjectBase)owner).Pointer);
		}
	}
	internal static class XpPatches
	{
		[HarmonyPatch]
		private static class AddXpWriter
		{
			private static MethodBase TargetMethod()
			{
				return AccessTools.GetDeclaredMethods(typeof(LevelManager)).First((MethodInfo m) => m.Name.StartsWith("RpcWriter___Server_AddXP", StringComparison.Ordinal));
			}

			private static void Prefix(ref int __0)
			{
				__0 = Scale(__0);
			}
		}

		[HarmonyPatch]
		private static class HandoverServerSide
		{
			private static MethodBase TargetMethod()
			{
				return AccessTools.GetDeclaredMethods(typeof(Customer)).First((MethodInfo m) => m.Name.StartsWith("RpcLogic___ProcessHandoverServerSide", StringComparison.Ordinal));
			}

			private static void Prefix(bool handoverByPlayer, out string? __state)
			{
				Begin(handoverByPlayer ? "xp.deal_player" : "xp.deal_dealer", out __state);
			}

			private static void Finalizer(string? __state)
			{
				End(__state);
			}
		}

		[HarmonyPatch]
		private static class CounterOffer
		{
			private static MethodBase TargetMethod()
			{
				return AccessTools.GetDeclaredMethods(typeof(Customer)).First((MethodInfo m) => m.Name.StartsWith("RpcLogic___ProcessCounterOfferServerSide", StringComparison.Ordinal));
			}

			private static void Prefix(out string? __state)
			{
				Begin("xp.counter_offer", out __state);
			}

			private static void Finalizer(string? __state)
			{
				End(__state);
			}
		}

		[HarmonyPatch(typeof(Customer), "SampleConsumed")]
		private static class Sample
		{
			private static void Prefix(out string? __state)
			{
				Begin("xp.sample", out __state);
			}

			private static void Finalizer(string? __state)
			{
				End(__state);
			}
		}

		[HarmonyPatch(typeof(Pot), "OnPlantFullyHarvested")]
		private static class Harvest
		{
			private static void Prefix(out string? __state)
			{
				Begin("xp.harvest", out __state);
			}

			private static void Finalizer(string? __state)
			{
				End(__state);
			}
		}

		[HarmonyPatch(typeof(ProductManager), "FinishAndNameMix", new Type[]
		{
			typeof(string),
			typeof(string),
			typeof(string)
		})]
		private static class NewMix
		{
			private static void Prefix(out string? __state)
			{
				Begin("xp.mix", out __state);
			}

			private static void Finalizer(string? __state)
			{
				End(__state);
			}
		}

		[HarmonyPatch(typeof(Quest), "Complete")]
		private static class QuestComplete
		{
			private static void Prefix(out string? __state)
			{
				Begin("xp.quest", out __state);
			}

			private static void Finalizer(string? __state)
			{
				End(__state);
			}
		}

		[HarmonyPatch(typeof(PlayerCrimeData), "TimeoutPursuit")]
		private static class PoliceEscape
		{
			private static void Prefix(out string? __state)
			{
				Begin("xp.police", out __state);
			}

			private static void Finalizer(string? __state)
			{
				End(__state);
			}
		}

		[HarmonyPatch(typeof(WorldSpraySurface), "CleanGraffiti")]
		private static class GraffitiClean
		{
			private static void Prefix(out string? __state)
			{
				Begin("xp.graffiti", out __state);
			}

			private static void Finalizer(string? __state)
			{
				End(__state);
			}
		}

		[HarmonyPatch(typeof(WorldSpraySurface), "Reward")]
		private static class GraffitiReward
		{
			private static void Prefix(out string? __state)
			{
				Begin("xp.graffiti", out __state);
			}

			private static void Finalizer(string? __state)
			{
				End(__state);
			}
		}

		[HarmonyPatch(typeof(GraffitiBehaviour), "Disable")]
		private static class GraffitiStopped
		{
			private static void Prefix(out string? __state)
			{
				Begin("xp.graffiti", out __state);
			}

			private static void Finalizer(string? __state)
			{
				End(__state);
			}
		}

		[HarmonyPatch(typeof(PickpocketScreen), "StopArrow")]
		private static class Pickpocket
		{
			private static void Prefix(out string? __state)
			{
				Begin("xp.pickpocket", out __state);
			}

			private static void Finalizer(string? __state)
			{
				End(__state);
			}
		}

		private static string? _source;

		public static bool DebugLog { get; set; }

		private static void Begin(string source, out string? previous)
		{
			previous = _source;
			_source = source;
		}

		private static void End(string? previous)
		{
			_source = previous;
		}

		private static int Scale(int xp)
		{
			if (xp <= 0 || !RatesState.InWorld || !RatesState.IsAuthority)
			{
				return xp;
			}
			string text = _source ?? "xp.other";
			float num = RatesState.Multiplier(text, "xp.all");
			int num2 = ((!(num <= 0f)) ? Math.Max(1, (int)Math.Round((float)xp * num)) : 0);
			if (DebugLog || num2 != xp)
			{
				MelonLogger.Msg($"XP {xp} -> {num2} ({text} x{num:0.##})");
			}
			return num2;
		}
	}
}