Decompiled source of Improve v1.2.1

Improve.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using MenuLib;
using MenuLib.MonoBehaviors;
using Microsoft.CodeAnalysis;
using Relay;
using TMPro;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: IgnoresAccessChecksTo("")]
[assembly: AssemblyCompany("headclef")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.2.1.0")]
[assembly: AssemblyInformationalVersion("1.2.1+6ded5c259ec819e21803a09181137ac7adda4ae5")]
[assembly: AssemblyProduct("Improve")]
[assembly: AssemblyTitle("Improve")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.2.1.0")]
[module: UnverifiableCode]
[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 Improve
{
	[BepInPlugin("headclef.Improve", "Improve", "1.2.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Improve : BaseUnityPlugin
	{
		private const string PluginGuid = "headclef.Improve";

		private const string PluginName = "Improve";

		private const string PluginVersion = "1.2.1";

		internal static ConfigEntry<float> DifficultyMultiplier;

		internal static ConfigEntry<int> BaseCost;

		internal static Improve Instance { get; private set; }

		internal static ManualLogSource Logger => Instance._logger;

		private ManualLogSource _logger => ((BaseUnityPlugin)this).Logger;

		internal Harmony? Harmony { get; set; }

		private void Awake()
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Expected O, but got Unknown
			//IL_0059: Expected O, but got Unknown
			Instance = this;
			((Component)this).gameObject.transform.parent = null;
			((Object)((Component)this).gameObject).hideFlags = (HideFlags)61;
			BindConfiguration();
			SaveData.Initialize();
			ImproveMenu.Initialize();
			if (Harmony == null)
			{
				Harmony val = new Harmony(((BaseUnityPlugin)this).Info.Metadata.GUID);
				Harmony val2 = val;
				Harmony = val;
			}
			Harmony.PatchAll();
			Logger.LogInfo((object)$"{((BaseUnityPlugin)this).Info.Metadata.GUID} v{((BaseUnityPlugin)this).Info.Metadata.Version} has loaded!");
		}

		private void OnDestroy()
		{
			Harmony? harmony = Harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		private void BindConfiguration()
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Expected O, but got Unknown
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Expected O, but got Unknown
			DifficultyMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Leveling", "Difficulty Multiplier", 0.5f, new ConfigDescription("Difficulty multiplier for level-up cost. Easy=0.25, Standard=0.5, Hard=0.75, Hardest=1.0", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 2f), Array.Empty<object>()));
			BaseCost = ((BaseUnityPlugin)this).Config.Bind<int>("Leveling", "Base Cost", 1000000, new ConfigDescription("Base haul cost for level 1. Haul to reach level N is baseCost x difficulty x N^2.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(100000, 10000000), Array.Empty<object>()));
		}
	}
	public static class ImproveMenu
	{
		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static BuilderDelegate <>9__5_0;

			public static BuilderDelegate <>9__5_1;

			public static BuilderDelegate <>9__5_2;

			public static Action <>9__9_3;

			public static Action <>9__9_0;

			public static Action <>9__9_4;

			public static Action <>9__9_1;

			public static Action <>9__9_2;

			internal void <Initialize>b__5_0(Transform parent)
			{
				AddImproveButton(parent);
			}

			internal void <Initialize>b__5_1(Transform parent)
			{
				AddImproveButton(parent);
			}

			internal void <Initialize>b__5_2(Transform parent)
			{
				AddImproveButton(parent);
			}

			internal void <CreateStatsPage>b__9_0()
			{
				//IL_0005: Unknown result type (might be due to invalid IL or missing references)
				MenuAPI.OpenPopup("Reset Stats", Color.yellow, "Reclaim all spent stat points? Your haul and level will be preserved.", (Action)delegate
				{
					SaveData.ResetStats();
					_statsPage.ClosePage(true);
				}, (Action)null);
			}

			internal void <CreateStatsPage>b__9_3()
			{
				SaveData.ResetStats();
				_statsPage.ClosePage(true);
			}

			internal void <CreateStatsPage>b__9_1()
			{
				//IL_0005: Unknown result type (might be due to invalid IL or missing references)
				MenuAPI.OpenPopup("Reset All", Color.red, "Wipe ALL progress including haul, level, and stat allocations? This cannot be undone!", (Action)delegate
				{
					SaveData.ResetAll();
					_statsPage.ClosePage(true);
				}, (Action)null);
			}

			internal void <CreateStatsPage>b__9_4()
			{
				SaveData.ResetAll();
				_statsPage.ClosePage(true);
			}

			internal void <CreateStatsPage>b__9_2()
			{
				_statsPage.ClosePage(true);
			}
		}

		private static REPOPopupPage _statsPage;

		private static REPOPopupPage _skillsPage;

		private static REPOLabel _availablePointsLabel;

		private static REPOLabel _spentPointsLabel;

		private static REPOSlider[] _skillSliders;

		internal static void Initialize()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Expected O, but got Unknown
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Expected O, but got Unknown
			object obj = <>c.<>9__5_0;
			if (obj == null)
			{
				BuilderDelegate val = delegate(Transform parent)
				{
					AddImproveButton(parent);
				};
				<>c.<>9__5_0 = val;
				obj = (object)val;
			}
			MenuAPI.AddElementToMainMenu((BuilderDelegate)obj);
			object obj2 = <>c.<>9__5_1;
			if (obj2 == null)
			{
				BuilderDelegate val2 = delegate(Transform parent)
				{
					AddImproveButton(parent);
				};
				<>c.<>9__5_1 = val2;
				obj2 = (object)val2;
			}
			MenuAPI.AddElementToEscapeMenu((BuilderDelegate)obj2);
			object obj3 = <>c.<>9__5_2;
			if (obj3 == null)
			{
				BuilderDelegate val3 = delegate(Transform parent)
				{
					AddImproveButton(parent);
				};
				<>c.<>9__5_2 = val3;
				obj3 = (object)val3;
			}
			MenuAPI.AddElementToLobbyMenu((BuilderDelegate)obj3);
		}

		private static void AddImproveButton(Transform parent)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			REPOButton btn = MenuAPI.CreateREPOButton("Improve", (Action)OpenImproveMenu, parent, new Vector2(0f, 300f));
			((MonoBehaviour)Improve.Instance).StartCoroutine(FixRepoButton(btn, parent));
		}

		private static IEnumerator FixRepoButton(REPOButton btn, Transform parent)
		{
			yield return null;
			Canvas.ForceUpdateCanvases();
			TextMeshProUGUI componentInChildren = ((Component)btn).GetComponentInChildren<TextMeshProUGUI>(true);
			if ((Object)(object)componentInChildren != (Object)null)
			{
				RectTransform rectTransform = ((REPOElement)btn).rectTransform;
				RectTransform val = (RectTransform)((TMP_Text)componentInChildren).transform;
				((Transform)val).SetParent((Transform)(object)rectTransform, false);
				val.anchorMin = Vector2.zero;
				val.anchorMax = Vector2.one;
				val.pivot = new Vector2(0.5f, 0.5f);
				val.offsetMin = new Vector2(14f, 6f);
				val.offsetMax = new Vector2(-14f, -6f);
				((TMP_Text)componentInChildren).alignment = (TextAlignmentOptions)4614;
				((TMP_Text)componentInChildren).enableWordWrapping = false;
			}
			RectTransform val2 = (RectTransform)(object)((parent is RectTransform) ? parent : null);
			if ((Object)(object)val2 != (Object)null)
			{
				float num = 40f;
				float num2 = 20f;
				Rect rect = val2.rect;
				float width = ((Rect)(ref rect)).width;
				rect = ((REPOElement)btn).rectTransform.rect;
				float num3 = width - ((Rect)(ref rect)).width - num;
				float num4 = num2;
				((Transform)((REPOElement)btn).rectTransform).localPosition = new Vector3(num3, num4, 0f);
			}
		}

		private static void OpenImproveMenu()
		{
			((BaseUnityPlugin)Improve.Instance).Config.Reload();
			CreateStatsPage();
			CreateSkillsPage();
		}

		private static void CreateStatsPage()
		{
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0204: Unknown result type (might be due to invalid IL or missing references)
			//IL_0232: Unknown result type (might be due to invalid IL or missing references)
			//IL_0256: Unknown result type (might be due to invalid IL or missing references)
			//IL_029c: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0328: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_03eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_03bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c9: Unknown result type (might be due to invalid IL or missing references)
			_statsPage = MenuAPI.CreateREPOPopupPage("Improve - Progress", (PresetSide)0, false, true, 0f);
			int num = SaveData.CurrentLevel();
			int num2 = SaveData.AvailablePoints();
			int num3 = SaveData.TotalSpent();
			int value = SaveData.HaulNeededForNextLevel();
			string difficultyLabel = GetDifficultyLabel();
			string text = (SaveData.IsOverspent() ? $"{num2} (OVER!)" : $"{num2}");
			float num4 = 0.75f;
			REPOElement[] array = (REPOElement[])(object)new REPOElement[15]
			{
				(REPOElement)MenuAPI.CreateREPOLabel("Lifetime Haul:", ((Component)_statsPage).transform, new Vector2(40f, 280f)),
				(REPOElement)MenuAPI.CreateREPOLabel("Current Level:", ((Component)_statsPage).transform, new Vector2(40f, 255f)),
				(REPOElement)MenuAPI.CreateREPOLabel("Available Points:", ((Component)_statsPage).transform, new Vector2(40f, 230f)),
				(REPOElement)MenuAPI.CreateREPOLabel("Spent Points:", ((Component)_statsPage).transform, new Vector2(40f, 205f)),
				(REPOElement)MenuAPI.CreateREPOLabel("Next Level In:", ((Component)_statsPage).transform, new Vector2(40f, 180f)),
				(REPOElement)MenuAPI.CreateREPOLabel("Difficulty:", ((Component)_statsPage).transform, new Vector2(40f, 155f)),
				(REPOElement)MenuAPI.CreateREPOLabel(FormatHaul(SaveData.LifetimeHaul.Value), ((Component)_statsPage).transform, new Vector2(240f, 280f)),
				(REPOElement)MenuAPI.CreateREPOLabel($"{num}", ((Component)_statsPage).transform, new Vector2(240f, 255f)),
				(REPOElement)(_availablePointsLabel = MenuAPI.CreateREPOLabel(text, ((Component)_statsPage).transform, new Vector2(240f, 230f))),
				(REPOElement)(_spentPointsLabel = MenuAPI.CreateREPOLabel($"{num3}", ((Component)_statsPage).transform, new Vector2(240f, 205f))),
				(REPOElement)MenuAPI.CreateREPOLabel(FormatHaul(value), ((Component)_statsPage).transform, new Vector2(240f, 180f)),
				(REPOElement)MenuAPI.CreateREPOLabel(difficultyLabel, ((Component)_statsPage).transform, new Vector2(240f, 155f)),
				(REPOElement)MenuAPI.CreateREPOButton("Reset Stats", (Action)delegate
				{
					//IL_0005: Unknown result type (might be due to invalid IL or missing references)
					MenuAPI.OpenPopup("Reset Stats", Color.yellow, "Reclaim all spent stat points? Your haul and level will be preserved.", (Action)delegate
					{
						SaveData.ResetStats();
						_statsPage.ClosePage(true);
					}, (Action)null);
				}, ((Component)_statsPage).transform, new Vector2(40f, 80f)),
				(REPOElement)MenuAPI.CreateREPOButton("Reset All", (Action)delegate
				{
					//IL_0005: Unknown result type (might be due to invalid IL or missing references)
					MenuAPI.OpenPopup("Reset All", Color.red, "Wipe ALL progress including haul, level, and stat allocations? This cannot be undone!", (Action)delegate
					{
						SaveData.ResetAll();
						_statsPage.ClosePage(true);
					}, (Action)null);
				}, ((Component)_statsPage).transform, new Vector2(220f, 80f)),
				(REPOElement)MenuAPI.CreateREPOButton("Close", (Action)delegate
				{
					_statsPage.ClosePage(true);
				}, ((Component)_statsPage).transform, new Vector2(130f, 20f))
			};
			REPOElement[] array2 = array;
			foreach (REPOElement val in array2)
			{
				REPOLabel val2 = (REPOLabel)(object)((val is REPOLabel) ? val : null);
				if (val2 != null && (Object)(object)val2.labelTMP != (Object)null)
				{
					TextMeshProUGUI labelTMP = val2.labelTMP;
					((TMP_Text)labelTMP).fontSize = ((TMP_Text)labelTMP).fontSize * num4;
				}
				else
				{
					REPOButton val3 = (REPOButton)(object)((val is REPOButton) ? val : null);
					if (val3 != null && (Object)(object)val3.labelTMP != (Object)null)
					{
						TextMeshProUGUI labelTMP2 = val3.labelTMP;
						((TMP_Text)labelTMP2).fontSize = ((TMP_Text)labelTMP2).fontSize * num4;
						((REPOElement)val3).rectTransform.sizeDelta = new Vector2(160f, ((REPOElement)val3).rectTransform.sizeDelta.y);
					}
				}
				_statsPage.AddElement(val.rectTransform, Vector2.op_Implicit(((Transform)val.rectTransform).localPosition));
			}
			_statsPage.OpenPage(false);
		}

		private static void CreateSkillsPage()
		{
			//IL_0053: 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_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_023f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0245: Unknown result type (might be due to invalid IL or missing references)
			int num = SaveData.AvailablePoints();
			int available = Math.Max(num, 0);
			_skillsPage = MenuAPI.CreateREPOPopupPage("Improve - Skills", (PresetSide)1, false, false, 0f);
			float num2 = 0.75f;
			if (SaveData.IsOverspent())
			{
				REPOLabel val = MenuAPI.CreateREPOLabel($"Over budget by {Math.Abs(num)} pts! Reset stats or earn more haul.", ((Component)_skillsPage).transform, default(Vector2));
				if ((Object)(object)val.labelTMP != (Object)null)
				{
					TextMeshProUGUI labelTMP = val.labelTMP;
					((TMP_Text)labelTMP).fontSize = ((TMP_Text)labelTMP).fontSize * num2;
				}
				_skillsPage.AddElementToScrollView(((REPOElement)val).rectTransform, default(Vector2), 0f, 0f);
			}
			_skillSliders = (REPOSlider[])(object)new REPOSlider[13]
			{
				CreateSlider("Health", SaveData.AllocHealth, available, 0),
				CreateSlider("Sprint Speed", SaveData.AllocSpeed, available, 1),
				CreateSlider("Stamina", SaveData.AllocStamina, available, 2),
				CreateSlider("Extra Jump", SaveData.AllocExtraJump, available, 3),
				CreateSlider("Grab Range", SaveData.AllocGrabRange, available, 4),
				CreateSlider("Grab Strength", SaveData.AllocGrabStrength, available, 5),
				CreateSlider("Throw", SaveData.AllocGrabThrow, available, 6),
				CreateSlider("Tumble Launch", SaveData.AllocTumbleLaunch, available, 7),
				CreateSlider("Tumble Climb", SaveData.AllocTumbleClimb, available, 8),
				CreateSlider("Tumble Wings", SaveData.AllocTumbleWings, available, 9),
				CreateSlider("Crouch Rest", SaveData.AllocCrouchRest, available, 10),
				CreateSlider("Map Count", SaveData.AllocMapPlayerCount, available, 11, 1),
				CreateSlider("Death Head", SaveData.AllocDeathHeadBattery, available, 12)
			};
			REPOSlider[] skillSliders = _skillSliders;
			foreach (REPOSlider val2 in skillSliders)
			{
				if ((Object)(object)val2.labelTMP != (Object)null)
				{
					TextMeshProUGUI labelTMP2 = val2.labelTMP;
					((TMP_Text)labelTMP2).fontSize = ((TMP_Text)labelTMP2).fontSize * num2;
				}
				_skillsPage.AddElementToScrollView(((REPOElement)val2).rectTransform, default(Vector2), 0f, 0f);
			}
			((REPOElement)_skillSliders[0]).repoScrollViewElement.topPadding = 5f;
			_skillsPage.scrollView.spacing = 5f;
			_skillsPage.OpenPage(true);
		}

		private static REPOSlider CreateSlider(string name, ConfigEntry<int> entry, int available, int index, int maxCap = int.MaxValue)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			return MenuAPI.CreateREPOSlider(name, string.Empty, (Action<int>)delegate(int value)
			{
				OnPointChanged(index, entry, value);
			}, ((Component)_skillsPage).transform, default(Vector2), 0, Math.Clamp(entry.Value + available, 0, maxCap), entry.Value, "", "", (BarBehavior)0);
		}

		private static void OnPointChanged(int index, ConfigEntry<int> entry, int value)
		{
			entry.Value = value;
			int num = SaveData.AvailablePoints();
			int num2 = Math.Max(num, 0);
			string text = (SaveData.IsOverspent() ? $"{num} (OVER!)" : $"{num}");
			((TMP_Text)_availablePointsLabel.labelTMP).text = text;
			((TMP_Text)_spentPointsLabel.labelTMP).text = $"{SaveData.TotalSpent()}";
			for (int i = 0; i < _skillSliders.Length; i++)
			{
				REPOSlider val = _skillSliders[i];
				int num3 = ((((TMP_Text)val.labelTMP).text == "Map Count") ? 1 : int.MaxValue);
				if (i == index)
				{
					val.max = Mathf.Min(value + num2, num3);
				}
				else
				{
					val.max = Mathf.Min(val.value + (float)num2, (float)num3);
				}
				val.SetValue(val.value, false);
			}
		}

		private static string GetDifficultyLabel()
		{
			float value = Improve.DifficultyMultiplier.Value;
			if (value <= 0.25f)
			{
				return "Easy";
			}
			if (value <= 0.5f)
			{
				return "Standard";
			}
			if (value <= 0.75f)
			{
				return "Hard";
			}
			return "Hardest";
		}

		private static string FormatHaul(int value)
		{
			return value / 1000 + "K";
		}
	}
	internal static class RelayReporter
	{
		private const string SourceId = "headclef.Improve";

		internal static void Report()
		{
			Relay.Report("headclef.Improve", "playerUpgradeStrength", SaveData.GetAllocationForStat("playerUpgradeStrength"));
			Relay.Report("headclef.Improve", "playerUpgradeLaunch", SaveData.GetAllocationForStat("playerUpgradeLaunch"));
			Relay.Report("headclef.Improve", "playerUpgradeThrow", SaveData.GetAllocationForStat("playerUpgradeThrow"));
			Relay.Report("headclef.Improve", "playerUpgradeTumbleWings", SaveData.GetAllocationForStat("playerUpgradeTumbleWings"));
		}

		internal static void Clear()
		{
			Relay.Clear("headclef.Improve");
		}
	}
	public static class SaveData
	{
		public static ConfigEntry<int> LifetimeHaul;

		public static ConfigEntry<int> AllocHealth;

		public static ConfigEntry<int> AllocSpeed;

		public static ConfigEntry<int> AllocStamina;

		public static ConfigEntry<int> AllocExtraJump;

		public static ConfigEntry<int> AllocGrabRange;

		public static ConfigEntry<int> AllocGrabStrength;

		public static ConfigEntry<int> AllocGrabThrow;

		public static ConfigEntry<int> AllocTumbleLaunch;

		public static ConfigEntry<int> AllocTumbleClimb;

		public static ConfigEntry<int> AllocTumbleWings;

		public static ConfigEntry<int> AllocCrouchRest;

		public static ConfigEntry<int> AllocMapPlayerCount;

		public static ConfigEntry<int> AllocDeathHeadBattery;

		private static int _lastSeenRunHaul;

		private static bool _haulBaselined;

		internal static readonly string[] AllStatNames = new string[13]
		{
			"playerUpgradeHealth", "playerUpgradeSpeed", "playerUpgradeStamina", "playerUpgradeExtraJump", "playerUpgradeRange", "playerUpgradeStrength", "playerUpgradeThrow", "playerUpgradeLaunch", "playerUpgradeTumbleClimb", "playerUpgradeTumbleWings",
			"playerUpgradeCrouchRest", "playerUpgradeMapPlayerCount", "playerUpgradeDeathHeadBattery"
		};

		private static readonly Dictionary<string, FieldInfo> _dictFields = new Dictionary<string, FieldInfo>();

		internal static readonly Dictionary<string, int> _appliedBase = new Dictionary<string, int>();

		internal static readonly Dictionary<string, int> _appliedDelta = new Dictionary<string, int>();

		private static string? _localSteamId;

		private static readonly List<string> _strippedForSave = new List<string>();

		internal static string? LocalSteamId
		{
			get
			{
				if (!string.IsNullOrEmpty(_localSteamId))
				{
					return _localSteamId;
				}
				if ((Object)(object)PlayerController.instance != (Object)null && !string.IsNullOrEmpty(PlayerController.instance.playerSteamID))
				{
					_localSteamId = PlayerController.instance.playerSteamID;
				}
				else if ((Object)(object)PlayerAvatar.instance != (Object)null && !string.IsNullOrEmpty(PlayerAvatar.instance.steamID))
				{
					_localSteamId = PlayerAvatar.instance.steamID;
				}
				return _localSteamId;
			}
		}

		internal static void Initialize()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected O, but got Unknown
			ConfigFile val = new ConfigFile(Path.Combine(Application.persistentDataPath, "REPOModData/Improve/save.cfg"), false);
			LifetimeHaul = val.Bind<int>("Progress", "LifetimeHaul", 0, "Total haul accumulated across all sessions.");
			AllocHealth = val.Bind<int>("Skills", "Health", 0, "Points spent on Health.");
			AllocSpeed = val.Bind<int>("Skills", "SprintSpeed", 0, "Points spent on Sprint Speed.");
			AllocStamina = val.Bind<int>("Skills", "Stamina", 0, "Points spent on Stamina.");
			AllocExtraJump = val.Bind<int>("Skills", "ExtraJump", 0, "Points spent on Extra Jump.");
			AllocGrabRange = val.Bind<int>("Skills", "GrabRange", 0, "Points spent on Grab Range.");
			AllocGrabStrength = val.Bind<int>("Skills", "GrabStrength", 0, "Points spent on Grab Strength.");
			AllocGrabThrow = val.Bind<int>("Skills", "GrabThrow", 0, "Points spent on Throw.");
			AllocTumbleLaunch = val.Bind<int>("Skills", "TumbleLaunch", 0, "Points spent on Tumble Launch.");
			AllocTumbleClimb = val.Bind<int>("Skills", "TumbleClimb", 0, "Points spent on Tumble Climb.");
			AllocTumbleWings = val.Bind<int>("Skills", "TumbleWings", 0, "Points spent on Tumble Wings.");
			AllocCrouchRest = val.Bind<int>("Skills", "CrouchRest", 0, "Points spent on Crouch Rest.");
			AllocMapPlayerCount = val.Bind<int>("Skills", "MapPlayerCount", 0, "Points spent on Map Player Count.");
			AllocDeathHeadBattery = val.Bind<int>("Skills", "DeathHeadBattery", 0, "Points spent on Death Head Battery.");
		}

		public static void BankRunHaul()
		{
			if (!((Object)(object)StatsManager.instance == (Object)null))
			{
				int runStatTotalHaul = StatsManager.instance.GetRunStatTotalHaul();
				if (!_haulBaselined)
				{
					_lastSeenRunHaul = runStatTotalHaul;
					_haulBaselined = true;
				}
				else if (runStatTotalHaul > _lastSeenRunHaul)
				{
					int num = (runStatTotalHaul - _lastSeenRunHaul) * 1000;
					ConfigEntry<int> lifetimeHaul = LifetimeHaul;
					lifetimeHaul.Value += num;
					_lastSeenRunHaul = runStatTotalHaul;
					Improve.Logger.LogInfo((object)$"Haul banked: +{num} (lifetime: {LifetimeHaul.Value}, level {CurrentLevel()})");
				}
				else if (runStatTotalHaul < _lastSeenRunHaul)
				{
					_lastSeenRunHaul = runStatTotalHaul;
				}
			}
		}

		public static void MarkHaulBaselineStale()
		{
			_haulBaselined = false;
		}

		public static int CurrentLevel()
		{
			float num = Improve.BaseCost.Value;
			float value = Improve.DifficultyMultiplier.Value;
			float num2 = num * value;
			if (num2 <= 0f || LifetimeHaul.Value <= 0)
			{
				return 0;
			}
			int val = (int)Math.Floor(Math.Sqrt((double)LifetimeHaul.Value / (double)num2));
			return Math.Max(val, 0);
		}

		public static int TotalHaulForLevel(int level)
		{
			if (level <= 0)
			{
				return 0;
			}
			float num = Improve.BaseCost.Value;
			float value = Improve.DifficultyMultiplier.Value;
			double val = (double)num * (double)value * (double)level * (double)level;
			return (int)Math.Min(val, 2147483647.0);
		}

		public static int HaulNeededForNextLevel()
		{
			int num = TotalHaulForLevel(CurrentLevel() + 1);
			return Math.Max(num - LifetimeHaul.Value, 0);
		}

		public static int TotalSpent()
		{
			return AllocHealth.Value + AllocSpeed.Value + AllocStamina.Value + AllocExtraJump.Value + AllocGrabRange.Value + AllocGrabStrength.Value + AllocGrabThrow.Value + AllocTumbleLaunch.Value + AllocTumbleClimb.Value + AllocTumbleWings.Value + AllocCrouchRest.Value + AllocMapPlayerCount.Value + AllocDeathHeadBattery.Value;
		}

		public static int AvailablePoints()
		{
			return CurrentLevel() - TotalSpent();
		}

		public static bool IsOverspent()
		{
			return AvailablePoints() < 0;
		}

		public static void ResetStats()
		{
			AllocHealth.Value = 0;
			AllocSpeed.Value = 0;
			AllocStamina.Value = 0;
			AllocExtraJump.Value = 0;
			AllocGrabRange.Value = 0;
			AllocGrabStrength.Value = 0;
			AllocGrabThrow.Value = 0;
			AllocTumbleLaunch.Value = 0;
			AllocTumbleClimb.Value = 0;
			AllocTumbleWings.Value = 0;
			AllocCrouchRest.Value = 0;
			AllocMapPlayerCount.Value = 0;
			AllocDeathHeadBattery.Value = 0;
			Improve.Logger.LogInfo((object)"Stat allocations reset. Points reclaimed.");
		}

		public static void ResetAll()
		{
			LifetimeHaul.Value = 0;
			ResetStats();
			Improve.Logger.LogInfo((object)"Full reset — haul and stats wiped.");
		}

		internal static int GetAllocationForStat(string statName)
		{
			return statName switch
			{
				"playerUpgradeHealth" => AllocHealth.Value, 
				"playerUpgradeSpeed" => AllocSpeed.Value, 
				"playerUpgradeStamina" => AllocStamina.Value, 
				"playerUpgradeExtraJump" => AllocExtraJump.Value, 
				"playerUpgradeRange" => AllocGrabRange.Value, 
				"playerUpgradeStrength" => AllocGrabStrength.Value, 
				"playerUpgradeThrow" => AllocGrabThrow.Value, 
				"playerUpgradeLaunch" => AllocTumbleLaunch.Value, 
				"playerUpgradeTumbleClimb" => AllocTumbleClimb.Value, 
				"playerUpgradeTumbleWings" => AllocTumbleWings.Value, 
				"playerUpgradeCrouchRest" => AllocCrouchRest.Value, 
				"playerUpgradeMapPlayerCount" => AllocMapPlayerCount.Value, 
				"playerUpgradeDeathHeadBattery" => AllocDeathHeadBattery.Value, 
				_ => 0, 
			};
		}

		private static Dictionary<string, int>? GetStatDict(string statName)
		{
			if (!_dictFields.TryGetValue(statName, out FieldInfo value))
			{
				value = typeof(StatsManager).GetField(statName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				_dictFields[statName] = value;
			}
			return value?.GetValue(StatsManager.instance) as Dictionary<string, int>;
		}

		private static int ReadStatLocal(string statName, string steamId)
		{
			Dictionary<string, int> statDict = GetStatDict(statName);
			if (statDict != null && statDict.TryGetValue(steamId, out var value))
			{
				return value;
			}
			return 0;
		}

		private static void WriteStatLocal(string statName, string steamId, int value)
		{
			Dictionary<string, int> statDict = GetStatDict(statName);
			if (statDict != null)
			{
				statDict[steamId] = value;
			}
		}

		public static void ApplyStats()
		{
			if ((Object)(object)PlayerController.instance == (Object)null || (Object)(object)StatsManager.instance == (Object)null)
			{
				return;
			}
			string playerSteamID = PlayerController.instance.playerSteamID;
			if (string.IsNullOrEmpty(playerSteamID))
			{
				return;
			}
			_localSteamId = playerSteamID;
			string[] allStatNames = AllStatNames;
			foreach (string text in allStatNames)
			{
				int allocationForStat = GetAllocationForStat(text);
				int num = ReadStatLocal(text, playerSteamID);
				int num3;
				if (_appliedBase.TryGetValue(text, out var value) && _appliedDelta.TryGetValue(text, out var value2))
				{
					int num2 = value + value2;
					num3 = ((num <= num2) ? ((num >= num2) ? value : num) : (value + (num - num2)));
				}
				else
				{
					num3 = num;
					if (allocationForStat > 0 && num3 >= allocationForStat && SemiFunc.IsMasterClientOrSingleplayer())
					{
						Improve.Logger.LogWarning((object)($"Possible double: '{text}' first-seen base={num3} already >= bonus={allocationForStat} " + $"→ about to write {num3 + allocationForStat}. The loaded dict may contain a leaked bonus."));
					}
				}
				_appliedBase[text] = num3;
				_appliedDelta[text] = allocationForStat;
				WriteStatLocal(text, playerSteamID, num3 + allocationForStat);
			}
			Improve.Logger.LogDebug((object)$"Improve stats reconciled (Level {CurrentLevel()}, {TotalSpent()} spent, {AvailablePoints()} available).");
		}

		public static void EnforceStats()
		{
			ApplyStats();
		}

		public static void StripBonusForSave()
		{
			_strippedForSave.Clear();
			if ((Object)(object)StatsManager.instance == (Object)null)
			{
				return;
			}
			string localSteamId = LocalSteamId;
			if (string.IsNullOrEmpty(localSteamId))
			{
				return;
			}
			string[] allStatNames = AllStatNames;
			foreach (string text in allStatNames)
			{
				if (_appliedDelta.TryGetValue(text, out var value) && value != 0 && _appliedBase.TryGetValue(text, out var value2))
				{
					if (ReadStatLocal(text, localSteamId) == value2 + value)
					{
						WriteStatLocal(text, localSteamId, value2);
						_strippedForSave.Add(text);
					}
					else
					{
						Improve.Logger.LogWarning((object)($"Strip skipped '{text}': dict={ReadStatLocal(text, localSteamId)} != base+delta={value2 + value}. " + "Save may keep the bonus."));
					}
				}
			}
		}

		public static void RestoreBonusAfterSave()
		{
			if (_strippedForSave.Count == 0)
			{
				return;
			}
			string localSteamId = LocalSteamId;
			if ((Object)(object)StatsManager.instance != (Object)null && !string.IsNullOrEmpty(localSteamId))
			{
				foreach (string item in _strippedForSave)
				{
					if (_appliedBase.TryGetValue(item, out var value) && _appliedDelta.TryGetValue(item, out var value2))
					{
						WriteStatLocal(item, localSteamId, value + value2);
					}
				}
			}
			_strippedForSave.Clear();
		}

		public static void ClearTracking()
		{
			_appliedBase.Clear();
			_appliedDelta.Clear();
		}
	}
	internal static class StatEffectApplier
	{
		private static PlayerController? _pc;

		private static PlayerAvatar? _avatar;

		private static int _appliedSpeed;

		private static int _appliedStamina;

		private static int _appliedRange;

		private static float _lastSpeed;

		private static float _lastStamina;

		private static float _lastRange;

		private const float DropEpsilon = 0.001f;

		internal static void Invalidate()
		{
			_pc = null;
			_avatar = null;
			_appliedSpeed = 0;
			_appliedStamina = 0;
			_appliedRange = 0;
			_lastSpeed = 0f;
			_lastStamina = 0f;
			_lastRange = 0f;
		}

		internal static void Apply()
		{
			try
			{
				if (SemiFunc.IsMasterClientOrSingleplayer() || !SemiFunc.RunIsLevel())
				{
					return;
				}
				PlayerController pc = PlayerController.instance;
				if ((Object)(object)pc == (Object)null)
				{
					return;
				}
				PlayerAvatar avatar = pc.playerAvatarScript;
				if ((Object)(object)avatar == (Object)null || avatar.deadSet)
				{
					return;
				}
				StatsManager instance = StatsManager.instance;
				if ((Object)(object)instance == (Object)null)
				{
					return;
				}
				string playerSteamID = pc.playerSteamID;
				if (string.IsNullOrEmpty(playerSteamID))
				{
					return;
				}
				bool flag = (Object)(object)_pc != (Object)(object)pc || (Object)(object)_avatar != (Object)(object)avatar;
				if (flag)
				{
					_pc = pc;
					_avatar = avatar;
					_appliedSpeed = 0;
					_appliedStamina = 0;
					_appliedRange = 0;
				}
				if ((Object)(object)avatar.playerHealth != (Object)null)
				{
					int num = 100 + instance.GetPlayerMaxHealth(playerSteamID);
					int maxHealth = avatar.playerHealth.maxHealth;
					if (num > maxHealth)
					{
						avatar.playerHealth.maxHealth = num;
						avatar.playerHealth.Heal(num - maxHealth, false);
					}
				}
				pc.JumpExtra = DictValue(instance.playerUpgradeExtraJump, playerSteamID);
				avatar.upgradeTumbleClimb = DictValue(instance.playerUpgradeTumbleClimb, playerSteamID);
				avatar.upgradeCrouchRest = DictValue(instance.playerUpgradeCrouchRest, playerSteamID);
				ReconcileRelative(flag, SaveData.GetAllocationForStat("playerUpgradeSpeed"), () => pc.playerOriginalSprintSpeed, ref _appliedSpeed, ref _lastSpeed, delegate(int diff)
				{
					PlayerController obj = pc;
					obj.SprintSpeed += (float)diff;
					PlayerController obj2 = pc;
					obj2.SprintSpeedUpgrades += (float)diff;
					PlayerController obj3 = pc;
					obj3.playerOriginalSprintSpeed += (float)diff;
				});
				ReconcileRelative(flag, SaveData.GetAllocationForStat("playerUpgradeStamina"), () => pc.EnergyStart, ref _appliedStamina, ref _lastStamina, delegate(int diff)
				{
					PlayerController obj = pc;
					obj.EnergyStart += 10f * (float)diff;
					pc.EnergyCurrent = Mathf.Min(pc.EnergyCurrent + 10f * (float)diff, pc.EnergyStart);
				});
				ReconcileRelative(flag, SaveData.GetAllocationForStat("playerUpgradeRange"), () => (!((Object)(object)avatar.physGrabber != (Object)null)) ? _lastRange : avatar.physGrabber.grabRange, ref _appliedRange, ref _lastRange, delegate(int diff)
				{
					if ((Object)(object)avatar.physGrabber != (Object)null)
					{
						PhysGrabber physGrabber = avatar.physGrabber;
						physGrabber.grabRange += (float)diff;
					}
				});
			}
			catch (Exception ex)
			{
				Improve.Logger.LogWarning((object)("Client stat apply skipped: " + ex.Message));
			}
		}

		private static void ReconcileRelative(bool fresh, int alloc, Func<float> readStable, ref int applied, ref float lastStable, Action<int> addLevels)
		{
			float num = readStable();
			if (fresh)
			{
				applied = 0;
			}
			else if (num + 0.001f < lastStable)
			{
				applied = 0;
			}
			int num2 = Mathf.Max(0, alloc);
			int num3 = num2 - applied;
			if (num3 != 0)
			{
				addLevels(num3);
			}
			applied = num2;
			lastStable = readStable();
		}

		private static int DictValue(Dictionary<string, int> dict, string id)
		{
			if (dict == null || !dict.TryGetValue(id, out var value))
			{
				return 0;
			}
			return value;
		}
	}
}
namespace Improve.Patches
{
	[HarmonyPatch]
	internal static class HaulTrackerPatch
	{
		[HarmonyPatch(typeof(SemiFunc), "OnSceneSwitch")]
		[HarmonyPrefix]
		private static void OnSceneSwitch_Prefix()
		{
			SaveData.BankRunHaul();
		}

		[HarmonyPatch(typeof(StatsManager), "LoadGame")]
		[HarmonyPostfix]
		private static void LoadGame_Postfix()
		{
			SaveData.MarkHaulBaselineStale();
		}
	}
	[HarmonyPatch]
	internal static class StatApplyPatch
	{
		private static Coroutine? _watchdog;

		private static Coroutine? _deferredApply;

		[HarmonyPatch(typeof(StatsManager), "PlayerAdd")]
		[HarmonyPostfix]
		private static void PlayerAdd_Postfix(string _steamID)
		{
			try
			{
				if (SemiFunc.RunIsLevel() && !((Object)(object)PlayerAvatar.instance == (Object)null) && !(_steamID != PlayerAvatar.instance.steamID))
				{
					Improve.Logger.LogDebug((object)"Player data added — scheduling Improve stat application...");
					ScheduleApply();
					StartWatchdog();
				}
			}
			catch (Exception ex)
			{
				Improve.Logger.LogWarning((object)("PlayerAdd hook skipped: " + ex.Message));
			}
		}

		[HarmonyPatch(typeof(PunManager), "ReceiveSyncData")]
		[HarmonyPostfix]
		private static void ReceiveSyncData_Postfix(bool finalChunk)
		{
			try
			{
				if (finalChunk && SemiFunc.RunIsLevel())
				{
					Improve.Logger.LogDebug((object)"Sync complete — reconciling Improve allocations...");
					SaveData.ApplyStats();
				}
			}
			catch (Exception ex)
			{
				Improve.Logger.LogWarning((object)("ReceiveSyncData hook skipped: " + ex.Message));
			}
		}

		[HarmonyPatch(typeof(SemiFunc), "OnSceneSwitch")]
		[HarmonyPrefix]
		private static void OnSceneSwitch_Prefix()
		{
			if (SemiFunc.RunIsLevel())
			{
				StopWatchdog();
				StopDeferredApply();
			}
		}

		[HarmonyPatch(typeof(RunManager), "ResetProgress")]
		[HarmonyPostfix]
		private static void ResetProgress_Postfix()
		{
			StopWatchdog();
			StopDeferredApply();
			StatEffectApplier.Invalidate();
			RelayReporter.Clear();
			SaveData.ClearTracking();
			SaveData.MarkHaulBaselineStale();
		}

		private static void ScheduleApply()
		{
			StopDeferredApply();
			_deferredApply = ((MonoBehaviour)Improve.Instance).StartCoroutine(DeferredApply());
		}

		private static void StopDeferredApply()
		{
			if (_deferredApply != null)
			{
				((MonoBehaviour)Improve.Instance).StopCoroutine(_deferredApply);
				_deferredApply = null;
			}
		}

		private static IEnumerator DeferredApply()
		{
			yield return null;
			yield return null;
			yield return null;
			_deferredApply = null;
			if (!SemiFunc.RunIsLevel())
			{
				yield break;
			}
			try
			{
				SaveData.ApplyStats();
				StatEffectApplier.Apply();
			}
			catch (Exception ex)
			{
				Improve.Logger.LogWarning((object)("Deferred apply skipped: " + ex.Message));
			}
		}

		private static void StartWatchdog()
		{
			StopWatchdog();
			_watchdog = ((MonoBehaviour)Improve.Instance).StartCoroutine(WatchdogLoop());
		}

		private static void StopWatchdog()
		{
			if (_watchdog != null)
			{
				((MonoBehaviour)Improve.Instance).StopCoroutine(_watchdog);
				_watchdog = null;
			}
		}

		private static IEnumerator WatchdogLoop()
		{
			yield return (object)new WaitForSeconds(1f);
			while (SemiFunc.RunIsLevel())
			{
				try
				{
					SaveData.EnforceStats();
					StatEffectApplier.Apply();
					RelayReporter.Report();
					SaveData.BankRunHaul();
				}
				catch (Exception ex)
				{
					Improve.Logger.LogWarning((object)("Watchdog tick skipped: " + ex.Message));
				}
				yield return (object)new WaitForSeconds(0.5f);
			}
			_watchdog = null;
		}
	}
	[HarmonyPatch]
	internal static class SaveLeakGuardPatch
	{
		[HarmonyPatch(typeof(StatsManager), "SaveGame")]
		[HarmonyPrefix]
		private static void SaveGame_Prefix()
		{
			SaveData.StripBonusForSave();
		}

		[HarmonyPatch(typeof(StatsManager), "SaveGame")]
		[HarmonyFinalizer]
		private static void SaveGame_Finalizer()
		{
			SaveData.RestoreBonusAfterSave();
		}
	}
}