Decompiled source of CrewUpgrades v0.1.0

BepInEx/plugins/Serenta-CrewUpgrades/CrewUpgrades.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using Unity.Collections;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("CrewUpgrades")]
[assembly: AssemblyDescription("Configurable, team-shared terminal upgrades for Lethal Company.")]
[assembly: AssemblyCompany("Serenta")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyVersion("0.1.0.0")]
namespace CrewUpgrades;

public sealed class PurchaseResult
{
	public bool Success;

	public int Cost;

	public int NewLevel;

	public string Reason;
}
[Serializable]
public sealed class TeamProgression
{
	public int[] Levels = new int[7];

	public int Revision;

	public int QuotaMarker = -1;

	public int ExplorationsCompleted;

	public bool CompanyRolled;

	public bool CompanyActive;

	public string[] CompletedTripIds = Array.Empty<string>();

	public int Level(UpgradeKind kind)
	{
		if (!UpgradeSettings.Known(kind) || Levels == null || (int)kind >= Levels.Length)
		{
			return 0;
		}
		return Math.Min(Math.Max(0, Levels[(int)kind]), (kind == UpgradeKind.CompanyRelations) ? 1 : 5);
	}

	public TeamProgression ValidatedCopy(UpgradeSettings settings)
	{
		UpgradeSettings upgradeSettings = (settings ?? new UpgradeSettings()).ValidatedCopy();
		TeamProgression teamProgression = new TeamProgression
		{
			Revision = Math.Max(0, Revision),
			QuotaMarker = Math.Max(-1, QuotaMarker),
			ExplorationsCompleted = Math.Max(0, ExplorationsCompleted),
			CompanyRolled = CompanyRolled
		};
		for (int i = 0; i < 7; i++)
		{
			teamProgression.Levels[i] = Math.Min(Level((UpgradeKind)i), upgradeSettings.MaxLevel((UpgradeKind)i));
		}
		HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
		List<string> list = new List<string>();
		if (CompletedTripIds != null)
		{
			string[] completedTripIds = CompletedTripIds;
			for (int j = 0; j < completedTripIds.Length; j++)
			{
				string text = ValidTripId(completedTripIds[j]);
				if (text != null && hashSet.Add(text))
				{
					list.Add(text);
				}
			}
		}
		teamProgression.CompletedTripIds = list.ToArray();
		if (teamProgression.ExplorationsCompleted >= upgradeSettings.CompanyExplorationCount)
		{
			teamProgression.CompanyRolled = true;
		}
		teamProgression.CompanyActive = CompanyActive && teamProgression.CompanyRolled && teamProgression.Level(UpgradeKind.CompanyRelations) > 0;
		return teamProgression;
	}

	public PurchaseResult TryPurchase(UpgradeKind kind, int expectedLevel, ref int credits, UpgradeSettings settings)
	{
		lock (this)
		{
			UpgradeSettings upgradeSettings = (settings ?? new UpgradeSettings()).ValidatedCopy();
			int num = Level(kind);
			if (!UpgradeSettings.Known(kind))
			{
				return Failure("unknown-upgrade", num);
			}
			if (!upgradeSettings.IsEnabled(kind))
			{
				return Failure("disabled", num);
			}
			if (num != expectedLevel)
			{
				return Failure("stale-level", num);
			}
			if (num >= upgradeSettings.MaxLevel(kind))
			{
				return Failure("max-level", num);
			}
			int num2 = upgradeSettings.Price(kind, num + 1);
			if (num2 < 0)
			{
				return Failure("invalid-price", num);
			}
			if (credits < 0 || credits < num2)
			{
				return Failure("insufficient-credits", num);
			}
			int[] array = new int[7];
			for (int i = 0; i < 7; i++)
			{
				array[i] = Level((UpgradeKind)i);
			}
			array[(int)kind] = num + 1;
			credits -= num2;
			Levels = array;
			AdvanceRevision();
			return new PurchaseResult
			{
				Success = true,
				Cost = num2,
				NewLevel = num + 1,
				Reason = "ok"
			};
		}
	}

	private static PurchaseResult Failure(string reason, int level)
	{
		return new PurchaseResult
		{
			Success = false,
			Cost = 0,
			NewLevel = level,
			Reason = reason
		};
	}

	public bool CompleteExploration(string tripId, double draw, UpgradeSettings settings)
	{
		lock (this)
		{
			string text = ValidTripId(tripId);
			if (text == null || double.IsNaN(draw) || double.IsInfinity(draw) || draw < 0.0 || draw >= 1.0)
			{
				return false;
			}
			UpgradeSettings upgradeSettings = (settings ?? new UpgradeSettings()).ValidatedCopy();
			if (!upgradeSettings.Enabled)
			{
				return false;
			}
			List<string> list = new List<string>(CompletedTripIds ?? Array.Empty<string>());
			if (list.Contains(text))
			{
				return false;
			}
			list.Add(text);
			int num = Math.Max(0, ExplorationsCompleted);
			CompletedTripIds = list.ToArray();
			ExplorationsCompleted = ((num < int.MaxValue) ? (num + 1) : int.MaxValue);
			bool result = false;
			if (!CompanyRolled && ExplorationsCompleted >= upgradeSettings.CompanyExplorationCount)
			{
				CompanyRolled = true;
				result = (CompanyActive = num < upgradeSettings.CompanyExplorationCount && upgradeSettings.IsEnabled(UpgradeKind.CompanyRelations) && Level(UpgradeKind.CompanyRelations) > 0 && draw < (double)upgradeSettings.CompanyChance);
			}
			AdvanceRevision();
			return result;
		}
	}

	public void NewQuota(int marker)
	{
		if (marker < 0)
		{
			throw new ArgumentOutOfRangeException("marker");
		}
		lock (this)
		{
			if (QuotaMarker != marker)
			{
				QuotaMarker = marker;
				ExplorationsCompleted = 0;
				CompanyRolled = (CompanyActive = false);
				CompletedTripIds = Array.Empty<string>();
				AdvanceRevision();
			}
		}
	}

	public void ResetAll()
	{
		lock (this)
		{
			Levels = new int[7];
			QuotaMarker = -1;
			ExplorationsCompleted = 0;
			CompanyRolled = (CompanyActive = false);
			CompletedTripIds = Array.Empty<string>();
			AdvanceRevision();
		}
	}

	private void AdvanceRevision()
	{
		Revision = ((Revision < int.MaxValue) ? (Math.Max(0, Revision) + 1) : int.MaxValue);
	}

	private static string ValidTripId(string id)
	{
		if (string.IsNullOrWhiteSpace(id) || id.Length > 256)
		{
			return null;
		}
		return id.Trim();
	}
}
public enum UpgradeKind
{
	Stamina,
	ActionTraining,
	Strength,
	CarryWeight,
	Interaction,
	Equipment,
	CompanyRelations
}
[Serializable]
public sealed class UpgradeSettings
{
	public bool Enabled = true;

	public bool ResetOnFired = true;

	public bool RequireAllPlayers = true;

	public bool[] UpgradeEnabled = new bool[7] { true, true, true, true, true, true, true };

	public int[] StaminaPrices = new int[5] { 500, 1000, 2000, 4000, 8000 };

	public int[] ActionPrices = new int[5] { 200, 400, 800, 1600, 3200 };

	public int[] StrengthPrices = new int[5] { 500, 1000, 5000, 10000, 20000 };

	public int[] CarryPrices = new int[5] { 500, 1000, 2000, 4000, 8000 };

	public int[] InteractionPrices = new int[5] { 200, 400, 800, 1600, 3200 };

	public int[] EquipmentPrices = new int[5] { 500, 1000, 2000, 4000, 8000 };

	public int CompanyPrice = 1000;

	public float[] StaminaCapacity = new float[6] { 100f, 120f, 140f, 160f, 180f, 200f };

	public float[] SprintSpeed = new float[6] { 1f, 1.02f, 1.04f, 1.06f, 1.08f, 1.1f };

	public float[] JumpFraction = new float[6] { 0.08f, 0.07f, 0.06f, 0.05f, 0.04f, 0.03f };

	public float[] StandingRecoverySeconds = new float[6] { 15f, 13.5f, 12f, 10.5f, 9f, 7.5f };

	public float[] WalkingRecoverySeconds = new float[6] { 20f, 18.5f, 17f, 15.5f, 14f, 12.5f };

	public float BaseSprintSeconds = 11f;

	public float SprintStopFraction = 0.1f;

	public float[] FootstepRange = new float[6] { 1f, 0.9f, 0.8f, 0.7f, 0.6f, 0.5f };

	public float[] CrouchMultiplier = new float[6] { 1f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f };

	public float[] LadderMultiplier = new float[6] { 1f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f };

	public float[] UphillPenalty = new float[6] { 1f, 0.8f, 0.6f, 0.4f, 0.2f, 0f };

	public float[] CritChance = new float[6] { 0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f };

	public int[] MeleeBonus = new int[6] { 0, 0, 0, 1, 1, 1 };

	public float[] CritMultiplier = new float[6] { 2f, 2f, 2f, 2f, 2f, 2f };

	public float[] WeightFraction = new float[6] { 1f, 0.85f, 0.7f, 0.55f, 0.4f, 0.25f };

	public float[] HoldDuration = new float[6] { 1f, 0.9f, 0.8f, 0.7f, 0.6f, 0.5f };

	public float[] ReachBonus = new float[6] { 0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f };

	public float[] BatteryLifetime = new float[6] { 1f, 1.2f, 1.4f, 1.6f, 1.8f, 2f };

	public float[] JetAcceleration = new float[6] { 1f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f };

	public float[] JetCap = new float[6] { 1f, 1.05f, 1.1f, 1.15f, 1.2f, 1.25f };

	public float[] NormalTeleportWait = new float[6] { 3f, 2.5f, 2f, 1.5f, 1f, 0.5f };

	public float[] InverseTeleportWait = new float[6] { 5f, 4.5f, 4f, 3.5f, 3f, 2.5f };

	public bool[] HoldCategories = new bool[10] { true, true, true, true, true, true, true, true, true, true };

	public int CompanyExplorationCount = 3;

	public float CompanyChance = 0.1f;

	public float CompanyMultiplier = 2f;

	public bool CompanyNotify = true;

	public int MaxLevel(UpgradeKind kind)
	{
		if (Known(kind))
		{
			if (kind != UpgradeKind.CompanyRelations)
			{
				return 5;
			}
			return 1;
		}
		return 0;
	}

	public bool IsEnabled(UpgradeKind kind)
	{
		if (Enabled && Known(kind) && UpgradeEnabled != null && UpgradeEnabled.Length == 7)
		{
			return UpgradeEnabled[(int)kind];
		}
		return false;
	}

	public int Price(UpgradeKind kind, int nextLevel)
	{
		if (nextLevel < 1 || nextLevel > MaxLevel(kind))
		{
			return -1;
		}
		int[] array;
		switch (kind)
		{
		case UpgradeKind.CompanyRelations:
			return CompanyPrice;
		case UpgradeKind.Stamina:
			array = StaminaPrices;
			break;
		case UpgradeKind.ActionTraining:
			array = ActionPrices;
			break;
		case UpgradeKind.Strength:
			array = StrengthPrices;
			break;
		case UpgradeKind.CarryWeight:
			array = CarryPrices;
			break;
		case UpgradeKind.Interaction:
			array = InteractionPrices;
			break;
		case UpgradeKind.Equipment:
			array = EquipmentPrices;
			break;
		default:
			return -1;
		}
		if (array == null || array.Length != 5)
		{
			return -1;
		}
		return array[nextLevel - 1];
	}

	internal static bool Known(UpgradeKind kind)
	{
		if (kind >= UpgradeKind.Stamina)
		{
			return kind < (UpgradeKind)7;
		}
		return false;
	}

	private static bool Finite(float value)
	{
		if (!float.IsNaN(value))
		{
			return !float.IsInfinity(value);
		}
		return false;
	}

	private static float Scalar(float value, float fallback, float min, float max)
	{
		if (!Finite(value) || !(value >= min) || !(value <= max))
		{
			return fallback;
		}
		return value;
	}

	private static int Scalar(int value, int fallback, int min, int max)
	{
		if (value < min || value > max)
		{
			return fallback;
		}
		return value;
	}

	private static float[] Values(float[] values, float[] defaults, float min, float max)
	{
		float[] array = (float[])defaults.Clone();
		if (values == null || values.Length != array.Length)
		{
			return array;
		}
		for (int i = 0; i < array.Length; i++)
		{
			array[i] = Scalar(values[i], defaults[i], min, max);
		}
		return array;
	}

	private static int[] Values(int[] values, int[] defaults, int min, int max)
	{
		int[] array = (int[])defaults.Clone();
		if (values == null || values.Length != array.Length)
		{
			return array;
		}
		for (int i = 0; i < array.Length; i++)
		{
			array[i] = Scalar(values[i], defaults[i], min, max);
		}
		return array;
	}

	private static bool[] Values(bool[] values, bool[] defaults)
	{
		if (values == null || values.Length != defaults.Length)
		{
			return (bool[])defaults.Clone();
		}
		return (bool[])values.Clone();
	}

	public UpgradeSettings ValidatedCopy()
	{
		UpgradeSettings upgradeSettings = new UpgradeSettings();
		return new UpgradeSettings
		{
			Enabled = Enabled,
			ResetOnFired = ResetOnFired,
			RequireAllPlayers = RequireAllPlayers,
			UpgradeEnabled = Values(UpgradeEnabled, upgradeSettings.UpgradeEnabled),
			StaminaPrices = Values(StaminaPrices, upgradeSettings.StaminaPrices, 0, int.MaxValue),
			ActionPrices = Values(ActionPrices, upgradeSettings.ActionPrices, 0, int.MaxValue),
			StrengthPrices = Values(StrengthPrices, upgradeSettings.StrengthPrices, 0, int.MaxValue),
			CarryPrices = Values(CarryPrices, upgradeSettings.CarryPrices, 0, int.MaxValue),
			InteractionPrices = Values(InteractionPrices, upgradeSettings.InteractionPrices, 0, int.MaxValue),
			EquipmentPrices = Values(EquipmentPrices, upgradeSettings.EquipmentPrices, 0, int.MaxValue),
			CompanyPrice = Scalar(CompanyPrice, upgradeSettings.CompanyPrice, 0, int.MaxValue),
			StaminaCapacity = Values(StaminaCapacity, upgradeSettings.StaminaCapacity, 1f, 1000000f),
			SprintSpeed = Values(SprintSpeed, upgradeSettings.SprintSpeed, 0.01f, 100f),
			JumpFraction = Values(JumpFraction, upgradeSettings.JumpFraction, 0f, 1f),
			StandingRecoverySeconds = Values(StandingRecoverySeconds, upgradeSettings.StandingRecoverySeconds, 0.01f, 86400f),
			WalkingRecoverySeconds = Values(WalkingRecoverySeconds, upgradeSettings.WalkingRecoverySeconds, 0.01f, 86400f),
			BaseSprintSeconds = Scalar(BaseSprintSeconds, upgradeSettings.BaseSprintSeconds, 0.01f, 86400f),
			SprintStopFraction = Scalar(SprintStopFraction, upgradeSettings.SprintStopFraction, 0f, 0.99f),
			FootstepRange = Values(FootstepRange, upgradeSettings.FootstepRange, 0f, 10f),
			CrouchMultiplier = Values(CrouchMultiplier, upgradeSettings.CrouchMultiplier, 0.01f, 100f),
			LadderMultiplier = Values(LadderMultiplier, upgradeSettings.LadderMultiplier, 0.01f, 100f),
			UphillPenalty = Values(UphillPenalty, upgradeSettings.UphillPenalty, 0f, 1f),
			CritChance = Values(CritChance, upgradeSettings.CritChance, 0f, 1f),
			MeleeBonus = Values(MeleeBonus, upgradeSettings.MeleeBonus, 0, 1000),
			CritMultiplier = Values(CritMultiplier, upgradeSettings.CritMultiplier, 1f, 100f),
			WeightFraction = Values(WeightFraction, upgradeSettings.WeightFraction, 0f, 1f),
			HoldDuration = Values(HoldDuration, upgradeSettings.HoldDuration, 0.01f, 100f),
			ReachBonus = Values(ReachBonus, upgradeSettings.ReachBonus, 0f, 100f),
			BatteryLifetime = Values(BatteryLifetime, upgradeSettings.BatteryLifetime, 0.01f, 100f),
			JetAcceleration = Values(JetAcceleration, upgradeSettings.JetAcceleration, 0.01f, 100f),
			JetCap = Values(JetCap, upgradeSettings.JetCap, 0.01f, 100f),
			NormalTeleportWait = Values(NormalTeleportWait, upgradeSettings.NormalTeleportWait, 0f, 3600f),
			InverseTeleportWait = Values(InverseTeleportWait, upgradeSettings.InverseTeleportWait, 0f, 3600f),
			HoldCategories = Values(HoldCategories, upgradeSettings.HoldCategories),
			CompanyExplorationCount = Scalar(CompanyExplorationCount, upgradeSettings.CompanyExplorationCount, 1, 1000),
			CompanyChance = Scalar(CompanyChance, upgradeSettings.CompanyChance, 0f, 1f),
			CompanyMultiplier = Scalar(CompanyMultiplier, upgradeSettings.CompanyMultiplier, 1f, 1000f),
			CompanyNotify = CompanyNotify
		};
	}
}
[BepInPlugin("Serenta.CrewUpgrades", "Crew Upgrades", "0.1.0")]
public sealed class CrewPlugin : BaseUnityPlugin
{
	public const string Guid = "Serenta.CrewUpgrades";

	public const string Version = "0.1.0";

	private void Awake()
	{
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_0018: Expected O, but got Unknown
		//IL_0053: Unknown result type (might be due to invalid IL or missing references)
		//IL_0059: Expected O, but got Unknown
		Harmony val = null;
		try
		{
			ConfigFile val2 = new ConfigFile(Path.Combine(Paths.ConfigPath, "CrewUpgrades.cfg"), true);
			UpgradeRuntime.Initialize(((BaseUnityPlugin)this).Logger, new UpgradeConfig(val2).Read());
			UpgradeTerminal.Chinese = val2.Bind<bool>("Local UI", "Chinese", true, "Chinese terminal text. Local preference; not synchronized. Set false for English.").Value;
			val = new Harmony("Serenta.CrewUpgrades");
			val.PatchAll(typeof(CrewPlugin).Assembly);
			if (PlayerUpgrades.HasPatchFailures || EquipmentUpgrades.HasPatchFailures)
			{
				val.UnpatchSelf();
				throw new InvalidOperationException("Game IL compatibility checks failed. See the exact player/equipment patch errors above.");
			}
			StrengthUpgrades.Initialize(val);
			CrewRuntimeHost.Initialize(val);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"CrewUpgrades 0.1.0 initialized. Config: CrewUpgrades.cfg. Terminal: upgrades.");
		}
		catch (Exception ex)
		{
			if (val != null)
			{
				val.UnpatchSelf();
			}
			((BaseUnityPlugin)this).Logger.LogError((object)("CrewUpgrades initialization failed: " + ex));
			((Behaviour)this).enabled = false;
		}
	}
}
internal static class CrewRuntimeHost
{
	private static bool initialized;

	private static bool quitting;

	private static Harmony harmony;

	private static CrewDriver driver;

	private static bool optionalHooksReady;

	private static float optionalHooksAfter;

	internal static void Initialize(Harmony patches)
	{
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0046: Expected O, but got Unknown
		if (!initialized)
		{
			initialized = true;
			harmony = patches;
			optionalHooksAfter = Time.realtimeSinceStartup + 4f;
			SceneManager.sceneLoaded += SceneLoaded;
			Application.onBeforeRender += new UnityAction(Ensure);
			Application.quitting += Shutdown;
			Ensure();
		}
	}

	private static void SceneLoaded(Scene scene, LoadSceneMode mode)
	{
		Ensure();
	}

	private static void Ensure()
	{
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0025: Expected O, but got Unknown
		if (!quitting && !((Object)(object)driver != (Object)null))
		{
			GameObject val = new GameObject("CrewUpgrades.Runtime");
			Object.DontDestroyOnLoad((Object)val);
			driver = val.AddComponent<CrewDriver>();
		}
	}

	internal static void TickOptionalHooks()
	{
		if (!optionalHooksReady && !(Time.realtimeSinceStartup < optionalHooksAfter))
		{
			optionalHooksReady = true;
			StrengthUpgrades.Initialize(harmony);
		}
	}

	private static void Shutdown()
	{
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: Expected O, but got Unknown
		quitting = true;
		SceneManager.sceneLoaded -= SceneLoaded;
		Application.onBeforeRender -= new UnityAction(Ensure);
		Application.quitting -= Shutdown;
		UpgradeRuntime.Shutdown();
		Harmony obj = harmony;
		if (obj != null)
		{
			obj.UnpatchSelf();
		}
	}
}
public sealed class CrewDriver : MonoBehaviour
{
	private void Update()
	{
		CrewRuntimeHost.TickOptionalHooks();
		UpgradeRuntime.Tick();
	}
}
internal static class EquipmentUpgrades
{
	private static readonly List<string> failures = new List<string>();

	internal static string[] PatchFailures
	{
		get
		{
			lock (failures)
			{
				return failures.ToArray();
			}
		}
	}

	internal static bool HasPatchFailures
	{
		get
		{
			lock (failures)
			{
				return failures.Count > 0;
			}
		}
	}

	internal static void Failure(string patch, string reason)
	{
		string text = patch + ": " + reason + ". Original instructions retained.";
		lock (failures)
		{
			failures.Add(text);
		}
		ManualLogSource log = UpgradeRuntime.Log;
		if (log != null)
		{
			log.LogError((object)("Equipment IL compatibility failure: " + text));
		}
	}

	private static float At(float[] values, int level, float fallback)
	{
		if (values == null || level < 0 || level >= values.Length)
		{
			return fallback;
		}
		float num = values[level];
		if (!float.IsNaN(num) && !float.IsInfinity(num))
		{
			return num;
		}
		return fallback;
	}

	private static int Level()
	{
		return UpgradeRuntime.Level(UpgradeKind.Equipment);
	}

	internal static float ContinuousBatteryDuration(float original)
	{
		int num = Level();
		if (num != 0)
		{
			return original * At(UpgradeRuntime.Settings.BatteryLifetime, num, 1f);
		}
		return original;
	}

	internal static float TriggerBatteryCost(float original)
	{
		int num = Level();
		if (num != 0)
		{
			return original / Math.Max(0.01f, At(UpgradeRuntime.Settings.BatteryLifetime, num, 1f));
		}
		return original;
	}

	internal static float Acceleration(float original)
	{
		int num = Level();
		if (num != 0)
		{
			return original * At(UpgradeRuntime.Settings.JetAcceleration, num, 1f);
		}
		return original;
	}

	internal static float PropulsionCap(float original)
	{
		int num = Level();
		if (num != 0)
		{
			return original * At(UpgradeRuntime.Settings.JetCap, num, 1f);
		}
		return original;
	}

	internal static float PowerCap(float original)
	{
		int num = Level();
		if (num == 0)
		{
			return original;
		}
		return Math.Max(original, 400f * At(UpgradeRuntime.Settings.JetCap, num, 1f));
	}

	internal static float NormalTeleportWait(float original)
	{
		int num = Level();
		if (num != 0)
		{
			return At(UpgradeRuntime.Settings.NormalTeleportWait, num, original);
		}
		return original;
	}

	internal static float InverseTeleportStep(float original)
	{
		int num = Level();
		if (num != 0)
		{
			return At(UpgradeRuntime.Settings.InverseTeleportWait, num, original * 5f) / 5f;
		}
		return original;
	}

	internal static bool Field(CodeInstruction code, Type type, string name)
	{
		if (code.opcode == OpCodes.Ldfld && code.operand is FieldInfo fieldInfo && fieldInfo.DeclaringType == type)
		{
			return fieldInfo.Name == name;
		}
		return false;
	}

	internal static bool Float(CodeInstruction code, float value)
	{
		if (code.opcode == OpCodes.Ldc_R4 && code.operand is float num)
		{
			return Math.Abs(num - value) < 1E-06f;
		}
		return false;
	}

	internal static bool Method(CodeInstruction code, Type type, string name)
	{
		if ((code.opcode == OpCodes.Call || code.opcode == OpCodes.Callvirt) && code.operand is MethodInfo methodInfo && methodInfo.DeclaringType == type)
		{
			return methodInfo.Name == name;
		}
		return false;
	}

	internal static bool WaitConstructor(CodeInstruction code)
	{
		if (code.opcode == OpCodes.Newobj && code.operand is ConstructorInfo constructorInfo && constructorInfo.DeclaringType == typeof(WaitForSeconds) && constructorInfo.GetParameters().Length == 1)
		{
			return constructorInfo.GetParameters()[0].ParameterType == typeof(float);
		}
		return false;
	}

	internal static List<CodeInstruction> Copy(IEnumerable<CodeInstruction> source)
	{
		return source.Select((Func<CodeInstruction, CodeInstruction>)((CodeInstruction c) => new CodeInstruction(c))).ToList();
	}

	internal static CodeInstruction Call(string name)
	{
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Expected O, but got Unknown
		return new CodeInstruction(OpCodes.Call, (object)typeof(EquipmentUpgrades).GetMethod(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic));
	}

	internal static IEnumerable<CodeInstruction> Battery(IEnumerable<CodeInstruction> source, bool trigger)
	{
		List<CodeInstruction> list = Copy(source);
		List<int> list2 = new List<int>();
		for (int i = 1; i + 1 < list.Count; i++)
		{
			if (Field(list[i - 1], typeof(GrabbableObject), "itemProperties") && Field(list[i], typeof(Item), "batteryUsage") && list[i + 1].opcode == (trigger ? OpCodes.Sub : OpCodes.Div))
			{
				list2.Add(i);
			}
		}
		if (list2.Count != 1)
		{
			Failure(trigger ? "UseItemBatteries" : "GrabbableObject.Update", "expected one original battery usage calculation, found " + list2.Count);
			return list;
		}
		list.Insert(list2[0] + 1, Call(trigger ? "TriggerBatteryCost" : "ContinuousBatteryDuration"));
		return list;
	}

	internal static IEnumerable<CodeInstruction> Jetpack(IEnumerable<CodeInstruction> source)
	{
		List<CodeInstruction> list = Copy(source);
		List<int> list2 = new List<int>();
		List<int> list3 = new List<int>();
		List<int> list4 = new List<int>();
		for (int i = 0; i + 1 < list.Count; i++)
		{
			if (Field(list[i], typeof(JetpackItem), "jetpackAcceleration") && list[i + 1].opcode == OpCodes.Mul)
			{
				list2.Add(i);
			}
			if (Float(list[i], 500f) && Method(list[i + 1], typeof(Mathf), "Clamp"))
			{
				list3.Add(i);
			}
			if (Float(list[i], 400f) && Method(list[i + 1], typeof(Vector3), "ClampMagnitude"))
			{
				list4.Add(i);
			}
		}
		if (list2.Count != 1 || list3.Count != 1 || list4.Count != 1)
		{
			Failure("JetpackItem.Update", "expected acceleration/power/output sites 1/1/1, found " + list2.Count + "/" + list3.Count + "/" + list4.Count);
			return list;
		}
		foreach (KeyValuePair<int, string> item in new KeyValuePair<int, string>[3]
		{
			new KeyValuePair<int, string>(list2[0], "Acceleration"),
			new KeyValuePair<int, string>(list3[0], "PowerCap"),
			new KeyValuePair<int, string>(list4[0], "PropulsionCap")
		}.OrderByDescending((KeyValuePair<int, string> x) => x.Key))
		{
			list.Insert(item.Key + 1, Call(item.Value));
		}
		return list;
	}

	internal static IEnumerable<CodeInstruction> NormalTeleport(IEnumerable<CodeInstruction> source)
	{
		List<CodeInstruction> list = Copy(source);
		List<int> list2 = new List<int>();
		for (int i = 0; i + 1 < list.Count; i++)
		{
			if (!Float(list[i], 3f) || !WaitConstructor(list[i + 1]))
			{
				continue;
			}
			for (int j = Math.Max(0, i - 8); j < i; j++)
			{
				if (list[j].opcode == OpCodes.Ldstr && object.Equals(list[j].operand, "Teleport A"))
				{
					list2.Add(i);
					break;
				}
			}
		}
		if (list2.Count != 1)
		{
			Failure("ShipTeleporter.beamUpPlayer.MoveNext", "expected one Teleport A wait, found " + list2.Count);
			return list;
		}
		list.Insert(list2[0] + 1, Call("NormalTeleportWait"));
		return list;
	}

	internal static IEnumerable<CodeInstruction> InverseTeleport(IEnumerable<CodeInstruction> source)
	{
		List<CodeInstruction> list = Copy(source);
		List<int> list2 = new List<int>();
		bool flag = false;
		for (int i = 0; i + 1 < list.Count; i++)
		{
			if (Float(list[i], 1f) && WaitConstructor(list[i + 1]))
			{
				list2.Add(i);
			}
			if (list[i].opcode == OpCodes.Ldc_I4_5 && (list[i + 1].opcode == OpCodes.Blt || list[i + 1].opcode == OpCodes.Blt_S))
			{
				flag = true;
			}
		}
		if (list2.Count != 1 || !flag)
		{
			Failure("ShipTeleporter.beamOutPlayer.MoveNext", "expected one1-second wait in the unchanged five-pass loop");
			return list;
		}
		list.Insert(list2[0] + 1, Call("InverseTeleportStep"));
		return list;
	}
}
[HarmonyPatch(typeof(GrabbableObject), "Update")]
internal static class EquipmentContinuousBatteryPatch
{
	[HarmonyTranspiler]
	[HarmonyPriority(0)]
	private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
	{
		return EquipmentUpgrades.Battery(instructions, trigger: false);
	}
}
[HarmonyPatch(typeof(GrabbableObject), "UseItemBatteries")]
internal static class EquipmentTriggerBatteryPatch
{
	[HarmonyTranspiler]
	[HarmonyPriority(0)]
	private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
	{
		return EquipmentUpgrades.Battery(instructions, trigger: true);
	}
}
[HarmonyPatch(typeof(JetpackItem), "Update")]
internal static class EquipmentJetpackPatch
{
	[HarmonyTranspiler]
	[HarmonyPriority(0)]
	private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
	{
		return EquipmentUpgrades.Jetpack(instructions);
	}
}
[HarmonyPatch]
internal static class EquipmentNormalTeleporterPatch
{
	private static MethodBase TargetMethod()
	{
		return AccessTools.EnumeratorMoveNext((MethodBase)AccessTools.DeclaredMethod(typeof(ShipTeleporter), "beamUpPlayer", (Type[])null, (Type[])null));
	}

	[HarmonyTranspiler]
	[HarmonyPriority(0)]
	private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
	{
		return EquipmentUpgrades.NormalTeleport(instructions);
	}
}
[HarmonyPatch]
internal static class EquipmentInverseTeleporterPatch
{
	private static MethodBase TargetMethod()
	{
		return AccessTools.EnumeratorMoveNext((MethodBase)AccessTools.DeclaredMethod(typeof(ShipTeleporter), "beamOutPlayer", (Type[])null, (Type[])null));
	}

	[HarmonyTranspiler]
	[HarmonyPriority(0)]
	private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
	{
		return EquipmentUpgrades.InverseTeleport(instructions);
	}
}
internal static class InteractionUpgrades
{
	[HarmonyPatch]
	private static class ReachPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			yield return AccessTools.Method(typeof(PlayerControllerB), "BeginGrabObject", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(PlayerControllerB), "SetHoverTipAndCurrentInteractTrigger", (Type[])null, (Type[])null);
		}

		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, MethodBase __originalMethod)
		{
			List<CodeInstruction> list = PlayerUpgradeIl.Copy(instructions);
			Dictionary<int, List<CodeInstruction>> edits = new Dictionary<int, List<CodeInstruction>>();
			int num = 0;
			for (int i = 0; i < list.Count; i++)
			{
				if (PlayerUpgradeIl.Field(list[i], typeof(PlayerControllerB), "grabDistance"))
				{
					PlayerUpgradeIl.After(edits, i, PlayerUpgradeIl.Call(typeof(InteractionUpgrades), "Reach"));
					num++;
				}
			}
			if (num != 1)
			{
				PlayerUpgrades.PatchFailure(__originalMethod.Name, "expected one ordinary reach read, found " + num);
				return list;
			}
			return PlayerUpgradeIl.Apply(list, edits);
		}
	}

	[HarmonyPatch(typeof(PlayerControllerB), "ClickHoldInteraction")]
	private static class HoldPatch
	{
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Expected O, but got Unknown
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Expected O, but got Unknown
			List<CodeInstruction> list = PlayerUpgradeIl.Copy(instructions);
			Dictionary<int, List<CodeInstruction>> edits = new Dictionary<int, List<CodeInstruction>>();
			int num = 0;
			for (int i = 0; i < list.Count; i++)
			{
				if (PlayerUpgradeIl.Field(list[i], typeof(InteractTrigger), "timeToHold"))
				{
					PlayerUpgradeIl.After(edits, i, new CodeInstruction(OpCodes.Ldarg_0, (object)null), new CodeInstruction(OpCodes.Ldfld, (object)AccessTools.Field(typeof(PlayerControllerB), "hoveringOverTrigger")), PlayerUpgradeIl.Call(typeof(InteractionUpgrades), "HoldDuration"));
					num++;
				}
			}
			if (num != 1)
			{
				PlayerUpgrades.PatchFailure("PlayerControllerB.ClickHoldInteraction", "expected one hold duration read, found " + num);
				return list;
			}
			return PlayerUpgradeIl.Apply(list, edits);
		}
	}

	internal static float Reach(float original)
	{
		int num = PlayerUpgrades.ActiveLevel(UpgradeKind.Interaction);
		if (num != 0)
		{
			return original + Mathf.Max(0f, PlayerUpgrades.At(UpgradeRuntime.Settings.ReachBonus, num, 0f));
		}
		return original;
	}

	internal static float HoldDuration(float original, InteractTrigger trigger)
	{
		int num = PlayerUpgrades.ActiveLevel(UpgradeKind.Interaction);
		if (num == 0 || original <= 0f || (Object)(object)trigger == (Object)null)
		{
			return original;
		}
		int num2 = AllowedCategory(trigger);
		bool[] holdCategories = UpgradeRuntime.Settings.HoldCategories;
		if (num2 < 0 || holdCategories == null || num2 >= holdCategories.Length || !holdCategories[num2])
		{
			return original;
		}
		return original * Mathf.Max(0.01f, PlayerUpgrades.At(UpgradeRuntime.Settings.HoldDuration, num, 1f));
	}

	internal static int AllowedCategory(InteractTrigger trigger)
	{
		int num = EventCategory((UnityEventBase)(object)trigger.onInteract);
		if (num < 0)
		{
			return EventCategory((UnityEventBase)(object)trigger.onInteractEarly);
		}
		return num;
	}

	private static int EventCategory(UnityEventBase action)
	{
		if (action == null)
		{
			return -1;
		}
		for (int i = 0; i < action.GetPersistentEventCount(); i++)
		{
			Object persistentTarget = action.GetPersistentTarget(i);
			string persistentMethodName = action.GetPersistentMethodName(i);
			if (persistentTarget is EntranceTeleport && persistentMethodName == "TeleportPlayer")
			{
				return 0;
			}
			DoorLock val = (DoorLock)(object)((persistentTarget is DoorLock) ? persistentTarget : null);
			if (val != null && persistentMethodName == "OpenOrCloseDoor")
			{
				if (!val.isLocked)
				{
					return 1;
				}
				continue;
			}
			if (persistentTarget is SteamValveHazard && persistentMethodName == "FixValve")
			{
				return 2;
			}
			if (persistentTarget is StartMatchLever && persistentMethodName == "PullLever")
			{
				return 3;
			}
			if (persistentTarget is UnlockableSuit && persistentMethodName == "SwitchSuitToThis")
			{
				return 4;
			}
			if (persistentTarget is PlaceableObjectsSurface && persistentMethodName == "PlaceObject")
			{
				return 5;
			}
			if (persistentTarget is BeltBagItem && persistentMethodName == "TryCheckBagContents")
			{
				return 6;
			}
			if (persistentTarget is VehicleController)
			{
				switch (persistentMethodName)
				{
				case "TakeControlOfVehicle":
				case "SetPassengerInCar":
					return 7;
				case "RemoveKeyFromIgnition":
					return 8;
				case "ToggleHoodOpenLocalClient":
					return 9;
				}
			}
		}
		return -1;
	}
}
internal static class PlayerUpgrades
{
	[HarmonyPatch(typeof(PlayerControllerB), "Update")]
	private static class MovementPatch
	{
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Expected O, but got Unknown
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Expected O, but got Unknown
			//IL_034e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0354: Expected O, but got Unknown
			List<CodeInstruction> list = PlayerUpgradeIl.Copy(instructions);
			Dictionary<int, List<CodeInstruction>> edits = new Dictionary<int, List<CodeInstruction>>();
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			int num4 = 0;
			int num5 = 0;
			int num6 = 0;
			for (int i = 0; i < list.Count; i++)
			{
				if (i > 0 && PlayerUpgradeIl.Field(list[i], typeof(PlayerControllerB), "carryWeight") && list[i - 1].opcode == OpCodes.Ldarg_0)
				{
					PlayerUpgradeIl.After(edits, i, PlayerUpgradeIl.Call(typeof(PlayerUpgrades), "EffectiveWeight"));
					num++;
				}
				if (i + 1 < list.Count && PlayerUpgradeIl.Field(list[i], typeof(PlayerControllerB), "sprintMultiplier") && PlayerUpgradeIl.Method(list[i + 1], typeof(Vector3), "op_Multiply"))
				{
					PlayerUpgradeIl.After(edits, i, new CodeInstruction(OpCodes.Ldarg_0, (object)null), new CodeInstruction(OpCodes.Ldfld, (object)AccessTools.Field(typeof(PlayerControllerB), "isSprinting")), PlayerUpgradeIl.Call(typeof(PlayerUpgrades), "SprintVelocity"));
					num4++;
				}
				if (i > 0 && PlayerUpgradeIl.Float(list[i], 0.1f) && PlayerUpgradeIl.Field(list[i - 1], typeof(PlayerControllerB), "sprintMeter"))
				{
					PlayerUpgradeIl.After(edits, i, PlayerUpgradeIl.Call(typeof(PlayerUpgrades), "SprintStop"));
					num6++;
				}
				if (i >= 3 && i + 2 < list.Count && PlayerUpgradeIl.Float(list[i], 1.5f) && PlayerUpgradeIl.Field(list[i - 3], typeof(PlayerControllerB), "isCrouching") && list[i + 1].opcode == OpCodes.Div)
				{
					PlayerUpgradeIl.After(edits, i, PlayerUpgradeIl.Call(typeof(PlayerUpgrades), "CrouchDivisor"));
					num2++;
				}
				if (PlayerUpgradeIl.Field(list[i], typeof(PlayerControllerB), "climbSpeed"))
				{
					PlayerUpgradeIl.After(edits, i, PlayerUpgradeIl.Call(typeof(PlayerUpgrades), "LadderSpeed"));
					num3++;
				}
				if (i >= 10 && PlayerUpgradeIl.Method(list[i], typeof(Mathf), "Max") && PlayerUpgradeIl.Float(list[i - 9], 0.8f) && PlayerUpgradeIl.Field(list[i - 5], typeof(PlayerControllerB), "slopeIntensity") && PlayerUpgradeIl.Field(list[i - 3], typeof(PlayerControllerB), "slopeModifier") && PlayerUpgradeIl.IsLocalLoad(list[i - 10]) && list[i - 1].opcode == OpCodes.Add)
				{
					PlayerUpgradeIl.After(edits, i, new CodeInstruction(list[i - 10].opcode, list[i - 10].operand), PlayerUpgradeIl.Call(typeof(PlayerUpgrades), "UphillResult"));
					num5++;
				}
			}
			if (num != 3 || num2 != 1 || num3 != 1 || num4 != 1 || num5 != 1 || num6 != 2)
			{
				PatchFailure("PlayerControllerB.Update", $"expected weight/crouch/ladder/sprint/slope/stop=3/1/1/1/1/2, found {num}/{num2}/{num3}/{num4}/{num5}/{num6}");
				return list;
			}
			return PlayerUpgradeIl.Apply(list, edits);
		}
	}

	[HarmonyPatch(typeof(PlayerControllerB), "LateUpdate")]
	private static class StaminaPatch
	{
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			List<CodeInstruction> list = PlayerUpgradeIl.Copy(instructions);
			Dictionary<int, List<CodeInstruction>> edits = new Dictionary<int, List<CodeInstruction>>();
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			int num4 = 0;
			for (int i = 0; i < list.Count; i++)
			{
				if (PlayerUpgradeIl.Field(list[i], typeof(PlayerControllerB), "carryWeight"))
				{
					PlayerUpgradeIl.After(edits, i, PlayerUpgradeIl.Call(typeof(PlayerUpgrades), "EffectiveWeight"));
					num4++;
				}
				if (!PlayerUpgradeIl.Field(list[i], typeof(PlayerControllerB), "sprintTime") || i + 1 >= list.Count)
				{
					continue;
				}
				if (list[i + 1].opcode == OpCodes.Div)
				{
					PlayerUpgradeIl.After(edits, i, PlayerUpgradeIl.Call(typeof(PlayerUpgrades), "CapacitySprintSeconds"));
					num++;
				}
				else if (i + 3 < list.Count && list[i + 2].opcode == OpCodes.Add && list[i + 3].opcode == OpCodes.Div)
				{
					if (PlayerUpgradeIl.Float(list[i + 1], 4f))
					{
						PlayerUpgradeIl.After(edits, i + 2, PlayerUpgradeIl.Call(typeof(PlayerUpgrades), "StandingRecovery"));
						num2++;
					}
					else if (PlayerUpgradeIl.Float(list[i + 1], 9f))
					{
						PlayerUpgradeIl.After(edits, i + 2, PlayerUpgradeIl.Call(typeof(PlayerUpgrades), "WalkingRecovery"));
						num3++;
					}
				}
			}
			if (num != 2 || num2 != 1 || num3 != 1 || num4 != 1)
			{
				PatchFailure("PlayerControllerB.LateUpdate", $"expected drain/standing/walking/weight=2/1/1/1, found {num}/{num2}/{num3}/{num4}");
				return list;
			}
			return PlayerUpgradeIl.Apply(list, edits);
		}
	}

	[HarmonyPatch(typeof(PlayerControllerB), "Jump_performed")]
	private static class JumpPatch
	{
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			List<CodeInstruction> list = PlayerUpgradeIl.Copy(instructions);
			Dictionary<int, List<CodeInstruction>> edits = new Dictionary<int, List<CodeInstruction>>();
			int num = 0;
			for (int i = 1; i + 1 < list.Count; i++)
			{
				if (PlayerUpgradeIl.Float(list[i], 0.08f) && PlayerUpgradeIl.Field(list[i - 1], typeof(PlayerControllerB), "sprintMeter") && !(list[i + 1].opcode != OpCodes.Sub))
				{
					PlayerUpgradeIl.After(edits, i, PlayerUpgradeIl.Call(typeof(PlayerUpgrades), "JumpCost"));
					num++;
				}
			}
			if (num != 1)
			{
				PatchFailure("PlayerControllerB.Jump_performed", "expected one fixed jump cost, found " + num);
				return list;
			}
			return PlayerUpgradeIl.Apply(list, edits);
		}
	}

	[HarmonyPatch]
	private static class FootstepPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			yield return AccessTools.Method(typeof(PlayerControllerB), "PlayFootstepLocal", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(PlayerControllerB), "PlayFootstepServer", (Type[])null, (Type[])null);
		}

		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, MethodBase __originalMethod)
		{
			List<CodeInstruction> list = PlayerUpgradeIl.Copy(instructions);
			Dictionary<int, List<CodeInstruction>> edits = new Dictionary<int, List<CodeInstruction>>();
			int num = 0;
			for (int i = 0; i + 5 < list.Count; i++)
			{
				if ((PlayerUpgradeIl.Float(list[i], 22f) || PlayerUpgradeIl.Float(list[i], 17f)) && PlayerUpgradeIl.Method(list[i + 5], typeof(RoundManager), "PlayAudibleNoise"))
				{
					PlayerUpgradeIl.After(edits, i, PlayerUpgradeIl.Call(typeof(PlayerUpgrades), "FootstepDistance"));
					num++;
				}
			}
			if (num != 2)
			{
				PatchFailure(__originalMethod.Name, "expected two movement footstep ranges, found " + num);
				return list;
			}
			return PlayerUpgradeIl.Apply(list, edits);
		}
	}

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

	public static string[] PatchFailures
	{
		get
		{
			lock (failures)
			{
				return failures.ToArray();
			}
		}
	}

	public static bool HasPatchFailures
	{
		get
		{
			lock (failures)
			{
				return failures.Count != 0;
			}
		}
	}

	internal static void PatchFailure(string method, string detail)
	{
		string text = method + ": " + detail + ". This patch was left unchanged.";
		lock (failures)
		{
			failures.Add(text);
		}
		ManualLogSource log = UpgradeRuntime.Log;
		if (log != null)
		{
			log.LogError((object)("Player/interaction IL compatibility failure: " + text));
		}
	}

	internal static int ActiveLevel(UpgradeKind kind)
	{
		if (!UpgradeRuntime.Ready)
		{
			return 0;
		}
		return UpgradeRuntime.Level(kind);
	}

	internal static float At(float[] values, int level, float fallback)
	{
		if (values == null || level < 0 || level >= values.Length)
		{
			return fallback;
		}
		float num = values[level];
		if (!float.IsNaN(num) && !float.IsInfinity(num))
		{
			return num;
		}
		return fallback;
	}

	internal static float EffectiveWeight(float actual)
	{
		int num = ActiveLevel(UpgradeKind.CarryWeight);
		if (num == 0)
		{
			return actual;
		}
		float num2 = Mathf.Clamp01(At(UpgradeRuntime.Settings.WeightFraction, num, 1f));
		if (!(actual > 1f))
		{
			return actual;
		}
		return 1f + (actual - 1f) * num2;
	}

	internal static float CapacitySprintSeconds(float original)
	{
		int num = ActiveLevel(UpgradeKind.Stamina);
		if (num == 0)
		{
			return original;
		}
		UpgradeSettings settings = UpgradeRuntime.Settings;
		float num2 = Mathf.Max(1f, At(settings.StaminaCapacity, 0, 100f));
		float num3 = Mathf.Max(1f, At(settings.StaminaCapacity, num, num2));
		float num4 = settings.BaseSprintSeconds;
		if (float.IsNaN(num4) || float.IsInfinity(num4) || num4 <= 0f)
		{
			num4 = 11f;
		}
		return original * (num4 / 11f) * (num3 / num2);
	}

	internal static float StandingRecovery(float original)
	{
		int num = ActiveLevel(UpgradeKind.Stamina);
		if (num != 0)
		{
			return Mathf.Max(0.01f, At(UpgradeRuntime.Settings.StandingRecoverySeconds, num, original));
		}
		return original;
	}

	internal static float WalkingRecovery(float original)
	{
		int num = ActiveLevel(UpgradeKind.Stamina);
		if (num != 0)
		{
			return Mathf.Max(0.01f, At(UpgradeRuntime.Settings.WalkingRecoverySeconds, num, original));
		}
		return original;
	}

	internal static float JumpCost(float original)
	{
		int num = ActiveLevel(UpgradeKind.Stamina);
		if (num != 0)
		{
			return Mathf.Clamp01(At(UpgradeRuntime.Settings.JumpFraction, num, original));
		}
		return original;
	}

	internal static float SprintVelocity(float original, bool isSprinting)
	{
		int num = ActiveLevel(UpgradeKind.Stamina);
		if (num != 0 && isSprinting)
		{
			return original * Mathf.Max(0.01f, At(UpgradeRuntime.Settings.SprintSpeed, num, 1f));
		}
		return original;
	}

	internal static float SprintStop(float original)
	{
		if (ActiveLevel(UpgradeKind.Stamina) == 0)
		{
			return original;
		}
		float sprintStopFraction = UpgradeRuntime.Settings.SprintStopFraction;
		if (!float.IsNaN(sprintStopFraction) && !float.IsInfinity(sprintStopFraction))
		{
			return Mathf.Clamp(sprintStopFraction, 0f, 0.99f);
		}
		return original;
	}

	internal static float CrouchDivisor(float original)
	{
		int num = ActiveLevel(UpgradeKind.ActionTraining);
		if (num != 0)
		{
			return original / Mathf.Max(0.01f, At(UpgradeRuntime.Settings.CrouchMultiplier, num, 1f));
		}
		return original;
	}

	internal static float LadderSpeed(float original)
	{
		int num = ActiveLevel(UpgradeKind.ActionTraining);
		if (num != 0)
		{
			return original * Mathf.Max(0.01f, At(UpgradeRuntime.Settings.LadderMultiplier, num, 1f));
		}
		return original;
	}

	internal static float UphillResult(float result, float beforeSlope)
	{
		int num = ActiveLevel(UpgradeKind.ActionTraining);
		if (num == 0 || result >= beforeSlope)
		{
			return result;
		}
		float num2 = Mathf.Clamp01(At(UpgradeRuntime.Settings.UphillPenalty, num, 1f));
		return beforeSlope + (result - beforeSlope) * num2;
	}

	internal static float FootstepDistance(float original)
	{
		int num = ActiveLevel(UpgradeKind.ActionTraining);
		if (num != 0)
		{
			return original * Mathf.Max(0f, At(UpgradeRuntime.Settings.FootstepRange, num, 1f));
		}
		return original;
	}
}
internal static class PlayerUpgradeIl
{
	private static readonly Dictionary<OpCode, OpCode> LongBranches = new Dictionary<OpCode, OpCode>
	{
		[OpCodes.Br_S] = OpCodes.Br,
		[OpCodes.Brfalse_S] = OpCodes.Brfalse,
		[OpCodes.Brtrue_S] = OpCodes.Brtrue,
		[OpCodes.Beq_S] = OpCodes.Beq,
		[OpCodes.Bge_S] = OpCodes.Bge,
		[OpCodes.Bge_Un_S] = OpCodes.Bge_Un,
		[OpCodes.Bgt_S] = OpCodes.Bgt,
		[OpCodes.Bgt_Un_S] = OpCodes.Bgt_Un,
		[OpCodes.Ble_S] = OpCodes.Ble,
		[OpCodes.Ble_Un_S] = OpCodes.Ble_Un,
		[OpCodes.Blt_S] = OpCodes.Blt,
		[OpCodes.Blt_Un_S] = OpCodes.Blt_Un,
		[OpCodes.Bne_Un_S] = OpCodes.Bne_Un,
		[OpCodes.Leave_S] = OpCodes.Leave
	};

	internal static List<CodeInstruction> Copy(IEnumerable<CodeInstruction> instructions)
	{
		return instructions.Select((Func<CodeInstruction, CodeInstruction>)((CodeInstruction i) => new CodeInstruction(i))).ToList();
	}

	internal static bool Field(CodeInstruction i, Type owner, string name)
	{
		if (i.opcode == OpCodes.Ldfld && i.operand is FieldInfo fieldInfo && fieldInfo.DeclaringType == owner)
		{
			return fieldInfo.Name == name;
		}
		return false;
	}

	internal static bool Float(CodeInstruction i, float value)
	{
		if (i.opcode == OpCodes.Ldc_R4 && i.operand is float num)
		{
			return Math.Abs(num - value) < 1E-06f;
		}
		return false;
	}

	internal static bool Method(CodeInstruction i, Type owner, string name)
	{
		if ((i.opcode == OpCodes.Call || i.opcode == OpCodes.Callvirt) && i.operand is MethodInfo methodInfo && methodInfo.DeclaringType == owner)
		{
			return methodInfo.Name == name;
		}
		return false;
	}

	internal static bool IsLocalLoad(CodeInstruction i)
	{
		if (!(i.opcode == OpCodes.Ldloc) && !(i.opcode == OpCodes.Ldloc_S) && !(i.opcode == OpCodes.Ldloc_0) && !(i.opcode == OpCodes.Ldloc_1) && !(i.opcode == OpCodes.Ldloc_2))
		{
			return i.opcode == OpCodes.Ldloc_3;
		}
		return true;
	}

	internal static CodeInstruction Call(Type owner, string name)
	{
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0014: Expected O, but got Unknown
		return new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(owner, name, (Type[])null, (Type[])null));
	}

	internal static void After(Dictionary<int, List<CodeInstruction>> edits, int index, params CodeInstruction[] inserted)
	{
		if (!edits.TryGetValue(index, out var value))
		{
			value = (edits[index] = new List<CodeInstruction>());
		}
		value.AddRange(inserted);
	}

	internal static IEnumerable<CodeInstruction> Apply(List<CodeInstruction> code, Dictionary<int, List<CodeInstruction>> edits)
	{
		List<CodeInstruction> list = new List<CodeInstruction>(code.Count + edits.Sum((KeyValuePair<int, List<CodeInstruction>> x) => x.Value.Count));
		for (int num = 0; num < code.Count; num++)
		{
			list.Add(code[num]);
			if (edits.TryGetValue(num, out var value))
			{
				list.AddRange(value);
			}
		}
		foreach (CodeInstruction item in list)
		{
			if (LongBranches.TryGetValue(item.opcode, out var value2))
			{
				item.opcode = value2;
			}
		}
		return list;
	}
}
public static class StrengthUpgrades
{
	internal sealed class AttackScope
	{
		internal readonly AttackScope Parent;

		internal readonly GrabbableObject Weapon;

		internal readonly UpgradeSettings Settings;

		internal readonly int Level;

		internal readonly Dictionary<EnemyAI, HitEntry> Hits = new Dictionary<EnemyAI, HitEntry>(EnemyReferenceComparer.Instance);

		internal AttackScope(AttackScope parent, GrabbableObject weapon, UpgradeSettings settings, int level)
		{
			Parent = parent;
			Weapon = weapon;
			Settings = settings;
			Level = level;
		}
	}

	internal sealed class HitEntry
	{
		internal int Force;

		internal bool Accepted;
	}

	private sealed class EnemyReferenceComparer : IEqualityComparer<EnemyAI>
	{
		internal static readonly EnemyReferenceComparer Instance = new EnemyReferenceComparer();

		public bool Equals(EnemyAI first, EnemyAI second)
		{
			return first == second;
		}

		public int GetHashCode(EnemyAI value)
		{
			return RuntimeHelpers.GetHashCode(value);
		}
	}

	private static readonly MethodInfo InterfaceHit = AccessTools.Method(typeof(IHittable), "Hit", new Type[5]
	{
		typeof(int),
		typeof(Vector3),
		typeof(PlayerControllerB),
		typeof(bool),
		typeof(int)
	}, (Type[])null);

	private static readonly MethodInfo EnemyHit = AccessTools.Method(typeof(EnemyAI), "HitEnemyOnLocalClient", new Type[5]
	{
		typeof(int),
		typeof(Vector3),
		typeof(PlayerControllerB),
		typeof(bool),
		typeof(int)
	}, (Type[])null);

	private static readonly MethodInfo InterfaceWrapper = AccessTools.Method(typeof(StrengthUpgrades), "HitTarget", (Type[])null, (Type[])null);

	private static readonly MethodInfo EnemyWrapper = AccessTools.Method(typeof(StrengthUpgrades), "HitGloveEnemy", (Type[])null, (Type[])null);

	private static readonly HashSet<MethodBase> Installed = new HashSet<MethodBase>();

	private static readonly object RandomGate = new object();

	private static readonly Random Random = new Random();

	[ThreadStatic]
	private static AttackScope current;

	internal static Func<double> Draw = NextDraw;

	public static void Initialize(Harmony harmony)
	{
		if (harmony == null)
		{
			throw new ArgumentNullException("harmony");
		}
		Install(harmony, typeof(Shovel), "HitShovel", typeof(bool), glove: false, required: true);
		Install(harmony, typeof(KnifeItem), "HitKnife", typeof(bool), glove: false, required: true);
		InstallOptional(harmony, "PiggyVarietyMod", "PiggyVarietyMod.Patches.AxeItem", "HitAxe");
		InstallOptional(harmony, "PiggysVarietyMod", "PiggyVarietyMod.Patches.AxeItem", "HitAxe");
		string[] array = new string[6] { "AntiqueSwordScript", "CandyDispenserScript", "CrowbarScript", "FireAxeScript", "SizableScissorsScript", "SledgehammerScript" };
		foreach (string text in array)
		{
			InstallOptional(harmony, "UsualScrap", "UsualScrap.Behaviors." + text, "HitMeleeWeapon");
		}
		Type type = FindType("LCGoldScrapMod", "GoldenGloveScript");
		if (type != null)
		{
			Install(harmony, type, "CollideWhileExtending", typeof(RaycastHit), glove: true, required: false);
		}
	}

	public static void Reset()
	{
		current = null;
	}

	private static void InstallOptional(Harmony harmony, string assembly, string type, string method)
	{
		Type type2 = FindType(assembly, type);
		if (type2 != null)
		{
			Install(harmony, type2, method, typeof(bool), glove: false, required: false);
		}
	}

	private static Type FindType(string assemblyName, string typeName)
	{
		return (from a in AppDomain.CurrentDomain.GetAssemblies()
			where string.Equals(a.GetName().Name, assemblyName, StringComparison.Ordinal)
			select a.GetType(typeName, throwOnError: false)).FirstOrDefault((Type t) => t != null);
	}

	private static void Install(Harmony harmony, Type type, string name, Type argument, bool glove, bool required)
	{
		//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
		//IL_0109: Unknown result type (might be due to invalid IL or missing references)
		//IL_0114: Expected O, but got Unknown
		//IL_0114: Expected O, but got Unknown
		//IL_0114: Expected O, but got Unknown
		MethodInfo method = type.GetMethod(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { argument }, null);
		if (method == null || method.ReturnType != typeof(void) || !typeof(GrabbableObject).IsAssignableFrom(type))
		{
			string text = "Melee upgrade hook does not match " + type.FullName + "." + name;
			if (required)
			{
				throw new InvalidOperationException(text);
			}
			ManualLogSource log = UpgradeRuntime.Log;
			if (log != null)
			{
				log.LogWarning((object)(text + "; this optional weapon remains unmodified."));
			}
			return;
		}
		lock (Installed)
		{
			if (Installed.Contains(method))
			{
				return;
			}
			try
			{
				harmony.Patch((MethodBase)method, new HarmonyMethod(AccessTools.Method(typeof(StrengthUpgrades), "BeginAttack", (Type[])null, (Type[])null)), (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(StrengthUpgrades), glove ? "RewriteGloveContact" : "RewriteMeleeContact", (Type[])null, (Type[])null))
				{
					priority = 0
				}, new HarmonyMethod(AccessTools.Method(typeof(StrengthUpgrades), "EndAttack", (Type[])null, (Type[])null)), (HarmonyMethod)null);
				Installed.Add(method);
				ManualLogSource log2 = UpgradeRuntime.Log;
				if (log2 != null)
				{
					log2.LogInfo((object)("Strength contact hook: " + type.FullName + "." + name));
				}
			}
			catch (Exception ex)
			{
				if (required)
				{
					throw;
				}
				ManualLogSource log3 = UpgradeRuntime.Log;
				if (log3 != null)
				{
					log3.LogError((object)("Strength contact hook failed for " + type.FullName + "." + name + "; upgrade support for this weapon is unavailable: " + ex));
				}
			}
		}
	}

	internal static void BeginAttack(GrabbableObject __instance, out AttackScope __state)
	{
		__state = null;
		if (!((Object)(object)__instance == (Object)null))
		{
			int level = (UpgradeRuntime.Ready ? UpgradeRuntime.Level(UpgradeKind.Strength) : 0);
			UpgradeSettings settings = UpgradeRuntime.Settings;
			__state = new AttackScope(current, __instance, settings, level);
			current = __state;
		}
	}

	internal static Exception EndAttack(Exception __exception, AttackScope __state)
	{
		if (__state != null)
		{
			current = __state.Parent;
		}
		return __exception;
	}

	internal static IEnumerable<CodeInstruction> RewriteMeleeContact(IEnumerable<CodeInstruction> instructions)
	{
		return RewriteContacts(instructions, glove: false);
	}

	internal static IEnumerable<CodeInstruction> RewriteGloveContact(IEnumerable<CodeInstruction> instructions)
	{
		return RewriteContacts(instructions, glove: true);
	}

	private static IEnumerable<CodeInstruction> RewriteContacts(IEnumerable<CodeInstruction> instructions, bool glove)
	{
		List<CodeInstruction> list = instructions.Select((Func<CodeInstruction, CodeInstruction>)((CodeInstruction i) => new CodeInstruction(i))).ToList();
		int num = 0;
		int num2 = 0;
		foreach (CodeInstruction item in list)
		{
			if (CodeInstructionExtensions.Calls(item, InterfaceHit))
			{
				item.opcode = OpCodes.Call;
				item.operand = InterfaceWrapper;
				num++;
			}
			else if (glove && CodeInstructionExtensions.Calls(item, EnemyHit))
			{
				item.opcode = OpCodes.Call;
				item.operand = EnemyWrapper;
				num2++;
			}
		}
		if ((!glove && num == 0) || (glove && num2 != 1))
		{
			throw new InvalidOperationException("Audited melee contact call site changed; refusing a broad damage patch.");
		}
		return list;
	}

	public static bool HitTarget(IHittable target, int force, Vector3 direction, PlayerControllerB playerWhoHit, bool playHitSFX, int hitID)
	{
		//IL_0031: Unknown result type (might be due to invalid IL or missing references)
		//IL_005b: Unknown result type (might be due to invalid IL or missing references)
		EnemyAICollisionDetect val = (EnemyAICollisionDetect)(object)((target is EnemyAICollisionDetect) ? target : null);
		if ((Object)(object)val == (Object)null || (val.onlyCollideWhenGrounded && !val.alwaysAllowHitting) || !CanUpgrade(val.mainScript, force, playerWhoHit))
		{
			return target.Hit(force, direction, playerWhoHit, playHitSFX, hitID);
		}
		HitEntry hitEntry = Contact(val.mainScript, force);
		if (hitEntry.Accepted)
		{
			return true;
		}
		return hitEntry.Accepted = target.Hit(hitEntry.Force, direction, playerWhoHit, playHitSFX, hitID);
	}

	public static void HitGloveEnemy(EnemyAI target, int force, Vector3 direction, PlayerControllerB playerWhoHit, bool playHitSFX, int hitID)
	{
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		if (!CanUpgrade(target, force, playerWhoHit))
		{
			target.HitEnemyOnLocalClient(force, direction, playerWhoHit, playHitSFX, hitID);
			return;
		}
		HitEntry hitEntry = Contact(target, force);
		if (!hitEntry.Accepted)
		{
			target.HitEnemyOnLocalClient(hitEntry.Force, direction, playerWhoHit, playHitSFX, hitID);
			hitEntry.Accepted = true;
		}
	}

	private static bool CanUpgrade(EnemyAI target, int force, PlayerControllerB attacker)
	{
		if (current == null || current.Level <= 0 || current.Settings == null || (Object)(object)target == (Object)null || target.isEnemyDead || force <= 0 || (Object)(object)attacker == (Object)null || !UpgradeRuntime.Ready)
		{
			return false;
		}
		GameNetworkManager instance = GameNetworkManager.Instance;
		if ((Object)(object)instance != (Object)null && (Object)(object)instance.localPlayerController == (Object)(object)attacker)
		{
			return !attacker.isPlayerDead;
		}
		return false;
	}

	private static HitEntry Contact(EnemyAI target, int originalForce)
	{
		if (!current.Hits.TryGetValue(target, out var value))
		{
			value = new HitEntry
			{
				Force = ComputeForce(originalForce, current.Settings, current.Level, Draw)
			};
			current.Hits.Add(target, value);
		}
		return value;
	}

	internal static int ComputeForce(int force, UpgradeSettings settings, int level, Func<double> draw)
	{
		if (force <= 0 || settings == null || level <= 0)
		{
			return force;
		}
		int num = Math.Max(0, At(settings.MeleeBonus, level, 0));
		double num2 = (double)force + (double)num;
		float num3 = At(settings.CritChance, level, 0f);
		if (float.IsNaN(num3) || float.IsInfinity(num3))
		{
			num3 = 0f;
		}
		num3 = Math.Max(0f, Math.Min(1f, num3));
		if (num3 > 0f && draw() < (double)num3)
		{
			float num4 = At(settings.CritMultiplier, level, 2f);
			if (float.IsNaN(num4) || float.IsInfinity(num4) || num4 < 1f)
			{
				num4 = 2f;
			}
			num2 *= (double)num4;
		}
		if (!(num2 >= 2147483647.0))
		{
			return Math.Max(1, (int)Math.Round(num2, MidpointRounding.AwayFromZero));
		}
		return int.MaxValue;
	}

	private static T At<T>(T[] values, int level, T fallback)
	{
		if (values == null || level < 0 || level >= values.Length)
		{
			return fallback;
		}
		return values[level];
	}

	private static double NextDraw()
	{
		lock (RandomGate)
		{
			return Random.NextDouble();
		}
	}
}
public sealed class UpgradeConfig
{
	private readonly Dictionary<string, ConfigEntry<bool>> flags = new Dictionary<string, ConfigEntry<bool>>();

	private readonly Dictionary<string, ConfigEntry<int>> integers = new Dictionary<string, ConfigEntry<int>>();

	private readonly Dictionary<string, ConfigEntry<float>> numbers = new Dictionary<string, ConfigEntry<float>>();

	private readonly Dictionary<string, ConfigEntry<string>> lists = new Dictionary<string, ConfigEntry<string>>();

	private readonly UpgradeSettings defaults = new UpgradeSettings();

	private static readonly string[] Sections = new string[7] { "01 - Stamina", "02 - Action Training", "03 - Strength", "04 - Carry Weight", "05 - Interaction", "06 - Equipment", "07 - Company Relations" };

	public UpgradeConfig(ConfigFile file)
	{
		if (file == null)
		{
			throw new ArgumentNullException("file");
		}
		Flag(file, "General", "Enabled", d: true, "Enable the shared upgrade system. All gameplay settings are controlled by the host and synchronized to peers.");
		Flag(file, "General", "Reset On Fired", d: true, "Clear purchased upgrades when the team is fired. Quota success always retains purchased levels.");
		Flag(file, "General", "Require All Players", d: true, "Require every connected player to have a compatible CrewUpgrades version before allowing gameplay or purchases.");
		for (int i = 0; i < Sections.Length; i++)
		{
			Flag(file, Sections[i], "Enabled", d: true, "Allow this upgrade to be purchased and applied to the whole team.");
			if (i != 6)
			{
				List(file, Sections[i], "Purchase Prices", Prices((UpgradeKind)i), "Five comma-separated integer prices for buying levels 1, 2, 3, 4 and 5. Each is the incremental team cost; 0 allows a free level. Negative values fall back to the default.");
			}
		}
		Number(file, Sections[0], "Base Sprint Seconds", defaults.BaseSprintSeconds, "Baseline sprint stamina drain parameter in seconds. Vanilla v81 uses 11; ordinary sprint stops before zero stamina.");
		Number(file, Sections[0], "Sprint Stop Fraction", defaults.SprintStopFraction, "Remaining stamina fraction where normal sprint ends. Vanilla is 0.1 (10 percent). Valid range 0 to 0.99.");
		Effect(file, 0, "Stamina Capacity", defaults.StaminaCapacity, "Stamina capacity in points, using vanilla 100 points as the baseline.");
		Effect(file, 0, "Sprint Speed Multiplier", defaults.SprintSpeed, "Multiplier of ordinary vanilla sprint movement speed; 1.1 means 10 percent faster.");
		Effect(file, 0, "Jump Cost Fraction", defaults.JumpFraction, "Fraction of CURRENT stamina capacity spent on a normal jump; 0.08 means 8 percent.");
		Effect(file, 0, "Standing Recovery Seconds", defaults.StandingRecoverySeconds, "Seconds to recover from empty to full while standing under normal conditions. Independent of stamina capacity.");
		Effect(file, 0, "Walking Recovery Seconds", defaults.WalkingRecoverySeconds, "Seconds to recover from empty to full while normally walking. Independent of stamina capacity.");
		Effect(file, 1, "Footstep Noise Range Multiplier", defaults.FootstepRange, "Multiplier of walking/running footstep AI noise propagation range, not a universal enemy detection radius.");
		Effect(file, 1, "Crouch Speed Multiplier", defaults.CrouchMultiplier, "Multiplier of vanilla crouch speed. Default level 5 is 1.5, matching ordinary walking speed.");
		Effect(file, 1, "Ladder Speed Multiplier", defaults.LadderMultiplier, "Multiplier of the instance's original ladder climbing speed.");
		Effect(file, 1, "Remaining Uphill Penalty", defaults.UphillPenalty, "Remaining fraction of vanilla's extra uphill movement penalty. 1 keeps the penalty; 0 removes it. Does not change climbable slope limits.");
		Effect(file, 2, "Critical Chance", defaults.CritChance, "Probability from 0 to 1 of a critical eligible melee hit against a monster. Fists, ranged damage and friendly fire are excluded.");
		List(file, Sections[2], "Melee Damage Bonus", defaults.MeleeBonus, "Six comma-separated nonnegative integer melee force bonuses for levels 0 through 5. Added before the critical multiplier. Not player HP damage.");
		Effect(file, 2, "Critical Damage Multiplier", defaults.CritMultiplier, "Critical eligible melee hit multiplier, applied after the flat bonus. Must be at least 1.");
		Effect(file, 3, "Effective Weight Fraction", defaults.WeightFraction, "Fraction of actual item weight used for movement slowdown and additional sprint stamina cost. Effective carryWeight = 1 + (actual carryWeight - 1) * fraction. 0.25 means 25 percent item-weight penalty.");
		Effect(file, 4, "Hold Duration Multiplier", defaults.HoldDuration, "Duration multiplier for explicitly allowed native hold interactions only. 0.5 halves the hold duration. Does not change QTE, safe decoder, reload, lockpick or pickup animations.");
		Effect(file, 4, "Additional Reach", defaults.ReachBonus, "Extra Unity units added to the instance's original ordinary pickup/interaction reach. Vanilla v81 baseline is 3 units.");
		string[] array = new string[10] { "Facility Entrances", "Ordinary Doors", "Steam Valves", "Ship Landing Lever", "Suit Changing", "Storage Placement", "Belt Bag Inspection", "Cruiser Seat Entry", "Cruiser Key Removal", "Cruiser Hood Opening" };
		for (int j = 0; j < array.Length; j++)
		{
			Flag(file, "05 - Interaction Allowlist", array[j], d: true, "Allow hold-duration upgrades for this operation category. Unlisted interactions remain unaffected.");
		}
		Effect(file, 5, "Battery Lifetime Multiplier", defaults.BatteryLifetime, "Divide normal battery consumption by this multiplier. 2 doubles lifetime, not infinite battery. Custom item-specific logic needs separate compatibility support.");
		Effect(file, 5, "Jetpack Acceleration Multiplier", defaults.JetAcceleration, "Multiplier of original jetpack propulsion acceleration.");
		Effect(file, 5, "Jetpack Propulsion Cap Multiplier", defaults.JetCap, "Multiplier of the vanilla jetpack propulsion vector cap. This does not guarantee the same proportional increase to total flight speed.");
		Effect(file, 5, "Normal Teleport Wait Seconds", defaults.NormalTeleportWait, "Execution delay for an ordinary living-player teleport in seconds. Does not modify the teleporter cooldown.");
		Effect(file, 5, "Inverse Teleport Wait Seconds", defaults.InverseTeleportWait, "Execution delay for inverse teleport in seconds. Does not modify the inverse teleporter cooldown.");
		Integer(file, Sections[6], "Purchase Price", defaults.CompanyPrice, "Single team purchase price. The unlock remains across quota cycles until the configured fired reset.");
		Integer(file, Sections[6], "Explorations Before Draw", defaults.CompanyExplorationCount, "Draw once after this many non-company explorations return to orbit in each quota cycle. Company visits do not count. Buying after this scheduled draw waits until the next cycle. Valid range 1 to 1000.");
		Number(file, Sections[6], "Activation Chance", defaults.CompanyChance, "Chance from 0 to 1 for the cycle's single company bonus draw; 0.1 means 10 percent. It is not a per-item roll.");
		Number(file, Sections[6], "Sale Multiplier", defaults.CompanyMultiplier, "Multiplier of the normal company buying rate while this cycle's bonus is active. 2 doubles subsequent company sale proceeds. Minimum 1.");
		Flag(file, Sections[6], "Notify Players", defaults.CompanyNotify, "Show all players the activated company bonus and send the current active state to late joiners.");
	}

	private int[] Prices(UpgradeKind kind)
	{
		int[] array = new int[5];
		for (int i = 0; i < array.Length; i++)
		{
			array[i] = defaults.Price(kind, i + 1);
		}
		return array;
	}

	private static string Id(string section, string key)
	{
		return section + "\n" + key;
	}

	private void Flag(ConfigFile f, string s, string k, bool d, string help)
	{
		flags[Id(s, k)] = f.Bind<bool>(s, k, d, help);
	}

	private void Integer(ConfigFile f, string s, string k, int d, string help)
	{
		integers[Id(s, k)] = f.Bind<int>(s, k, d, help);
	}

	private void Number(ConfigFile f, string s, string k, float d, string help)
	{
		numbers[Id(s, k)] = f.Bind<float>(s, k, d, help);
	}

	private void List(ConfigFile f, string s, string k, int[] d, string help)
	{
		lists[Id(s, k)] = f.Bind<string>(s, k, string.Join(",", Array.ConvertAll(d, (int x) => x.ToString(CultureInfo.InvariantCulture))), help);
	}

	private void Effect(ConfigFile f, int section, string key, float[] d, string help)
	{
		lists[Id(Sections[section], key)] = f.Bind<string>(Sections[section], key, string.Join(",", Array.ConvertAll(d, (float x) => x.ToString("R", CultureInfo.InvariantCulture))), "Six comma-separated values for levels 0, 1, 2, 3, 4 and 5. Level 0 documents the unupgraded baseline. " + help);
	}

	private bool Flag(string s, string k)
	{
		return flags[Id(s, k)].Value;
	}

	private int Integer(string s, string k)
	{
		return integers[Id(s, k)].Value;
	}

	private float Number(string s, string k)
	{
		return numbers[Id(s, k)].Value;
	}

	private float[] Effect(int section, string key, float[] fallback)
	{
		string[] array = (lists[Id(Sections[section], key)].Value ?? "").Split(',');
		float[] array2 = (float[])fallback.Clone();
		if (array.Length != array2.Length)
		{
			return array2;
		}
		for (int i = 0; i < array2.Length; i++)
		{
			if (float.TryParse(array[i].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
			{
				array2[i] = result;
			}
		}
		return array2;
	}

	private int[] List(int section, string key, int[] fallback)
	{
		string[] array = (lists[Id(Sections[section], key)].Value ?? "").Split(',');
		int[] array2 = (int[])fallback.Clone();
		if (array.Length != array2.Length)
		{
			return array2;
		}
		for (int i = 0; i < array2.Length; i++)
		{
			if (int.TryParse(array[i].Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
			{
				array2[i] = result;
			}
		}
		return array2;
	}

	public UpgradeSettings Read()
	{
		UpgradeSettings upgradeSettings = new UpgradeSettings
		{
			Enabled = Flag("General", "Enabled"),
			ResetOnFired = Flag("General", "Reset On Fired"),
			RequireAllPlayers = Flag("General", "Require All Players"),
			StaminaPrices = List(0, "Purchase Prices", defaults.StaminaPrices),
			ActionPrices = List(1, "Purchase Prices", defaults.ActionPrices),
			StrengthPrices = List(2, "Purchase Prices", defaults.StrengthPrices),
			CarryPrices = List(3, "Purchase Prices", defaults.CarryPrices),
			InteractionPrices = List(4, "Purchase Prices", defaults.InteractionPrices),
			EquipmentPrices = List(5, "Purchase Prices", defaults.EquipmentPrices),
			BaseSprintSeconds = Number(Sections[0], "Base Sprint Seconds"),
			SprintStopFraction = Number(Sections[0], "Sprint Stop Fraction"),
			StaminaCapacity = Effect(0, "Stamina Capacity", defaults.StaminaCapacity),
			SprintSpeed = Effect(0, "Sprint Speed Multiplier", defaults.SprintSpeed),
			JumpFraction = Effect(0, "Jump Cost Fraction", defaults.JumpFraction),
			StandingRecoverySeconds = Effect(0, "Standing Recovery Seconds", defaults.StandingRecoverySeconds),
			WalkingRecoverySeconds = Effect(0, "Walking Recovery Seconds", defaults.WalkingRecoverySeconds),
			FootstepRange = Effect(1, "Footstep Noise Range Multiplier", defaults.FootstepRange),
			CrouchMultiplier = Effect(1, "Crouch Speed Multiplier", defaults.CrouchMultiplier),
			LadderMultiplier = Effect(1, "Ladder Speed Multiplier", defaults.LadderMultiplier),
			UphillPenalty = Effect(1, "Remaining Uphill Penalty", defaults.UphillPenalty),
			CritChance = Effect(2, "Critical Chance", defaults.CritChance),
			MeleeBonus = List(2, "Melee Damage Bonus", defaults.MeleeBonus),
			CritMultiplier = Effect(2, "Critical Damage Multiplier", defaults.CritMultiplier),
			WeightFraction = Effect(3, "Effective Weight Fraction", defaults.WeightFraction),
			HoldDuration = Effect(4, "Hold Duration Multiplier", defaults.HoldDuration),
			ReachBonus = Effect(4, "Additional Reach", defaults.ReachBonus),
			BatteryLifetime = Effect(5, "Battery Lifetime Multiplier", defaults.BatteryLifetime),
			JetAcceleration = Effect(5, "Jetpack Acceleration Multiplier", defaults.JetAcceleration),
			JetCap = Effect(5, "Jetpack Propulsion Cap Multiplier", defaults.JetCap),
			NormalTeleportWait = Effect(5, "Normal Teleport Wait Seconds", defaults.NormalTeleportWait),
			InverseTeleportWait = Effect(5, "Inverse Teleport Wait Seconds", defaults.InverseTeleportWait),
			CompanyPrice = Integer(Sections[6], "Purchase Price"),
			CompanyExplorationCount = Integer(Sections[6], "Explorations Before Draw"),
			CompanyChance = Number(Sections[6], "Activation Chance"),
			CompanyMultiplier = Number(Sections[6], "Sale Multiplier"),
			CompanyNotify = Flag(Sections[6], "Notify Players")
		};
		for (int i = 0; i < Sections.Length; i++)
		{
			upgradeSettings.UpgradeEnabled[i] = Flag(Sections[i], "Enabled");
		}
		string[] array = new string[10] { "Facility Entrances", "Ordinary Doors", "Steam Valves", "Ship Landing Lever", "Suit Changing", "Storage Placement", "Belt Bag Inspection", "Cruiser Seat Entry", "Cruiser Key Removal", "Cruiser Hood Opening" };
		for (int j = 0; j < array.Length; j++)
		{
			upgradeSettings.HoldCategories[j] = Flag("05 - Interaction Allowlist", array[j]);
		}
		return upgradeSettings.ValidatedCopy();
	}
}
internal static class UpgradeNetwork
{
	[Serializable]
	private sealed class Packet
	{
		public UpgradeSettings Settings;

		public TeamProgression State;

		public string Result;

		public bool Usable;
	}

	private const string Hello = "Serenta.CrewUpgrades.Hello.1";

	private const string Snapshot = "Serenta.CrewUpgrades.State.1";

	private const string Buy = "Serenta.CrewUpgrades.Buy.1";

	private const byte Protocol = 1;

	private static NetworkManager manager;

	private static CustomMessagingManager messages;

	private static ulong clientNonce;

	private static ulong serverNonce;

	private static ulong sequence;

	private static ulong pending;

	private static float nextHello;

	private static float pendingUntil;

	private static readonly Dictionary<ulong, ulong> peers = new Dictionary<ulong, ulong>();

	private static readonly Dictionary<ulong, ulong> lastRequest = new Dictionary<ulong, ulong>();

	private static readonly Dictionary<ulong, float> nextCommand = new Dictionary<ulong, float>();

	private static Action<ulong> disconnected;

	internal static bool Pending => pending != 0;

	internal static bool AllPeersReady
	{
		get
		{
			if ((Object)(object)manager == (Object)null || !manager.IsServer)
			{
				return false;
			}
			foreach (ulong connectedClientsId in manager.ConnectedClientsIds)
			{
				if (connectedClientsId != 0L && !peers.ContainsKey(connectedClientsId))
				{
					return false;
				}
			}
			return true;
		}
	}

	internal static void Tick()
	{
		NetworkManager singleton = NetworkManager.Singleton;
		if ((Object)(object)singleton == (Object)null || !singleton.IsListening)
		{
			if (manager != null)
			{
				Shutdown();
			}
			return;
		}
		if (singleton != manager)
		{
			Bind(singleton);
		}
		if (!manager.IsServer && Time.realtimeSinceStartup >= nextHello)
		{
			nextHello = Time.realtimeSinceStartup + (float)(UpgradeRuntime.Ready ? 30 : 3);
			SendHello();
		}
		if (pending != 0L && Time.realtimeSinceStartup > pendingUntil)
		{
			pending = 0uL;
			UpgradeTerminal.Feedback("timeout");
		}
	}

	private static void Bind(NetworkManager current)
	{
		//IL_007c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0086: Expected O, but got Unknown
		//IL_0097: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a1: Expected O, but got Unknown
		//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bc: Expected O, but got Unknown
		Shutdown();
		manager = current;
		messages = manager.CustomMessagingManager;
		clientNonce = Nonce();
		serverNonce = (manager.IsServer ? Nonce() : 0);
		sequence = (pending = 0uL);
		nextHello = 0f;
		UpgradeRuntime.NewConnection();
		NetworkManager captured = manager;
		messages.RegisterNamedMessageHandler("Serenta.CrewUpgrades.Hello.1", (HandleNamedMessageDelegate)delegate(ulong sender, FastBufferReader reader)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			if (manager == captured)
			{
				Guard(delegate
				{
					//IL_0007: Unknown result type (might be due to invalid IL or missing references)
					OnHello(sender, reader);
				});
			}
		});
		messages.RegisterNamedMessageHandler("Serenta.CrewUpgrades.State.1", (HandleNamedMessageDelegate)delegate(ulong sender, FastBufferReader reader)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			if (manager == captured)
			{
				Guard(delegate
				{
					//IL_0007: Unknown result type (might be due to invalid IL or missing references)
					OnSnapshot(sender, reader);
				});
			}
		});
		messages.RegisterNamedMessageHandler("Serenta.CrewUpgrades.Buy.1", (HandleNamedMessageDelegate)delegate(ulong sender, FastBufferReader reader)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			if (manager == captured)
			{
				Guard(delegate
				{
					//IL_0007: Unknown result type (might be due to invalid IL or missing references)
					OnBuy(sender, reader);
				});
			}
		});
		disconnected = delegate(ulong id)
		{
			if (manager == captured)
			{
				peers.Remove(id);
				lastRequest.Remove(id);
				nextCommand.Remove(id);
				if (id == manager.LocalClientId)
				{
					Shutdown();
				}
			}
		};
		manager.OnClientDisconnectCallback += disconnected;
	}

	private static ulong Nonce()
	{
		return BitConverter.ToUInt64(Guid.NewGuid().ToByteArray(), 0) | 1;
	}

	private unsafe static void SendHello()
	{
		//IL_0013: 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_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: Unknown result type (might be due to invalid IL or missing references)
		FastBufferWriter val = default(FastBufferWriter);
		((FastBufferWriter)(ref val))..ctor(32, (Allocator)2, -1);
		try
		{
			byte b = 1;
			((FastBufferWriter)(ref val)).WriteValueSafe<byte>(ref b, default(ForPrimitives));
			((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref clientNonce, default(ForPrimitives));
			messages.SendNamedMessage("Serenta.CrewUpgrades.Hello.1", 0uL, val, (NetworkDelivery)3);
		}
		finally
		{
			((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose();
		}
	}

	private static void OnHello(ulong sender, FastBufferReader reader)
	{
		//IL_0025: 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_003d: Unknown result type (might be due to invalid IL or missing references)
		if (!manager.IsServer || !manager.ConnectedClients.ContainsKey(sender))
		{
			return;
		}
		byte b = default(byte);
		((FastBufferReader)(ref reader)).ReadValueSafe<byte>(ref b, default(ForPrimitives));
		ulong num = default(ulong);
		((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref num, default(ForPrimitives));
		if (b == 1 && num != 0L)
		{
			if (peers.TryGetValue(sender, out var value) && value != num)
			{
				lastRequest.Remove(sender);
			}
			peers[sender] = num;
			if (UpgradeRuntime.Ready)
			{
				SendState(sender, 0uL, null);
			}
		}
	}

	internal static void Publish()
	{
		if ((Object)(object)manager == (Object)null || !manager.IsServer)
		{
			return;
		}
		foreach (ulong peer in new List<ulong>(peers.Keys))
		{
			Guard(delegate
			{
				SendState(peer, 0uL, null);
			});
		}
		UpgradeTerminal.Refresh();
	}

	private unsafe static void SendState(ulong target, ulong reply, string result)
	{
		//IL_0089: 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_009c: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b2: 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_00c5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
		if (!peers.TryGetValue(target, out var value))
		{
			return;
		}
		TeamProgression teamProgression = UpgradeRuntime.State.ValidatedCopy(UpgradeRuntime.Settings);
		teamProgression.CompletedTripIds = Array.Empty<string>();
		string text = JsonUtility.ToJson((object)new Packet
		{
			Settings = UpgradeRuntime.Settings,
			State = teamProgression,
			Result = result,
			Usable = UpgradeRuntime.Ready
		});
		if (text.Length > 24000)
		{
			throw new InvalidOperationException("Crew state exceeds protocol bound.");
		}
		FastBufferWriter val = default(FastBufferWriter);
		((FastBufferWriter)(ref val))..ctor(65536, (Allocator)2, -1);
		try
		{
			byte b = 1;
			((FastBufferWriter)(ref val)).WriteValueSafe<byte>(ref b, default(ForPrimitives));
			((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref value, default(ForPrimitives));
			((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref serverNonce, default(ForPrimitives));
			((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref reply, default(ForPrimitives));
			((FastBufferWriter)(ref val)).WriteValueSafe(text, false);
			messages.SendNamedMessage("Serenta.CrewUpgrades.State.1", target, val, (NetworkDelivery)4);
		}
		finally
		{
			((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose();
		}
	}

	private static void OnSnapshot(ulong sender, FastBufferReader reader)
	{
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_002a: 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_003d: Unknown result type (might be due to invalid IL or missing references)
		//IL_004a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0050: Unknown result type (might be due to invalid IL or missing references)
		//IL_005d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0063: Unknown result type (might be due to invalid IL or missing references)
		if (manager.IsServer || sender != 0L || ((FastBufferReader)(ref reader)).Length > 65536)
		{
			return;
		}
		byte b = default(byte);
		((FastBufferReader)(ref reader)).ReadValueSafe<byte>(ref b, default(ForPrimitives));
		ulong num = default(ulong);
		((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref num, default(ForPrimitives));
		ulong num2 = default(ulong);
		((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref num2, default(ForPrimitives));
		ulong num3 = default(ulong);
		((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref num3, default(ForPrimitives));
		if (b != 1 || num != clientNonce || num2 == 0L || (serverNonce != 0L && num2 != serverNonce))
		{
			return;
		}
		string text = default(string);
		((FastBufferReader)(ref reader)).ReadValueSafe(ref text, false);
		if (text == null || text.Length > 24000)
		{
			return;
		}
		Packet packet = JsonUtility.FromJson<Packet>(text);
		if (packet?.Settings != null && packet.State != null && (!UpgradeRuntime.Ready || packet.State.Revision >= UpgradeRuntime.State.Revision))
		{
			serverNonce = num2;
			UpgradeRuntime.AcceptState(packet.Settings, packet.State, packet.Usable);
			if (num3 != 0L && num3 == pending)
			{
				pending = 0uL;
				UpgradeTerminal.Feedback(packet.Result ?? "not-ready");
			}
		}
	}

	internal unsafe static void Request(UpgradeKind kind, int expected)
	{
		//IL_0080: Unknown result type (might be due to invalid IL or missing references)
		//IL_0086: 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_009b: Unknown result type (might be due to invalid IL or missing references)
		//IL_00aa: 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_00bf: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c5: 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_00d9: 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_00eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)manager == (Object)null || !UpgradeRuntime.Ready)
		{
			UpgradeTerminal.Feedback("not-ready");
		}
		else
		{
			if (Pending)
			{
				return;
			}
			if (manager.IsServer)
			{
				UpgradeTerminal.Feedback(UpgradeRuntime.Purchase(manager.LocalClientId, kind, expected));
				return;
			}
			pending = ++sequence;
			pendingUntil = Time.realtimeSinceStartup + 10f;
			FastBufferWriter val = default(FastBufferWriter);
			((FastBufferWriter)(ref val))..ctor(64, (Allocator)2, -1);
			try
			{
				byte b = 1;
				((FastBufferWriter)(ref val)).WriteValueSafe<byte>(ref b, default(ForPrimitives));
				((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref clientNonce, default(ForPrimitives));
				((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref serverNonce, default(ForPrimitives));
				((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref pending, default(ForPrimitives));
				int num = (int)kind;
				((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref num, default(ForPrimitives));
				((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref expected, default(ForPrimitives));
				messages.SendNamedMessage("Serenta.CrewUpgrades.Buy.1", 0uL, val, (NetworkDelivery)3);
			}
			finally
			{
				((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose();
			}
			UpgradeTerminal.Refresh();
		}
	}

	private static void OnBuy(ulong sender, FastBufferReader reader)
	{
		//IL_002c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0032: Unknown result type (might be due to invalid IL or missing references)
		//IL_003f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0045: 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_0058: Unknown result type (might be due to invalid IL or missing references)
		//IL_0065: 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_009c: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00af: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
		if (!manager.IsServer || !UpgradeRuntime.Ready || !manager.ConnectedClients.ContainsKey(sender))
		{
			return;
		}
		byte b = default(byte);
		((FastBufferReader)(ref reader)).ReadValueSafe<byte>(ref b, default(ForPrimitives));
		ulong num = default(ulong);
		((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref num, default(ForPrimitives));
		ulong num2 = default(ulong);
		((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref num2, default(ForPrimitives));
		ulong num3 = default(ulong);
		((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref num3, default(ForPrimitives));
		if (b != 1 || num2 != serverNonce || !peers.TryGetValue(sender, out var value) || num != value || num3 == 0L)
		{
			return;
		}
		int num4 = default(int);
		((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num4, default(ForPrimitives));
		int expectedLevel = default(int);
		((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref expectedLevel, default(ForPrimitives));
		if (lastRequest.TryGetValue(sender, out var value2) && num3 <= value2)
		{
			SendState(sender, num3, "stale-level");
			return;
		}
		lastRequest[sender] = num3;
		if (nextCommand.TryGetValue(sender, out var value3) && Time.realtimeSinceStartup < value3)
		{
			SendState(sender, num3, "busy");
			return;
		}
		nextCommand[sender] = Time.realtimeSinceStartup + 0.25f;
		string result = ((num4 < 0 || num4 > 6) ? "unknown-upgrade" : UpgradeRuntime.Purchase(sender, (UpgradeKind)num4, expectedLevel));
		SendState(sender, num3, result);
	}

	internal static void Shutdown()
	{
		if (manager == null)
		{
			return;
		}
		UpgradeRuntime.EndConnection();
		try
		{
			if (disconnected != null)
			{
				manager.OnClientDisconnectCallback -= disconnected;
			}
			CustomMessagingManager obj = messages;
			if (obj != null)
			{
				obj.UnregisterNamedMessageHandler("Serenta.CrewUpgrades.Hello.1");
			}
			CustomMessagingManager obj2 = messages;
			if (obj2 != null)
			{
				obj2.UnregisterNamedMessageHandler("Serenta.CrewUpgrades.State.1");
			}
			CustomMessagingManager obj3 = messages;
			if (obj3 != null)
			{
				obj3.UnregisterNamedMessageHandler("Serenta.CrewUpgrades.Buy.1");
			}
		}
		catch (Exception ex)
		{
			ManualLogSource log = UpgradeRuntime.Log;
			if (log != null)
			{
				log.LogDebug((object)ex.Message);
			}
		}
		manager = null;
		messages = null;
		disconnected = null;
		peers.Clear();
		lastRequest.Clear();
		nextCommand.Clear();
		pending = 0uL;
	}

	private static void Guard(Action action)
	{
		try
		{
			action();
		}
		catch (Exception ex)
		{
			ManualLogSource log = UpgradeRuntime.Log;
			if (log != null)
			{
				log.LogWarning((object)("Crew network message rejected: " + ex.Message));
			}
		}
	}
}
public static class UpgradeRuntime
{
	internal static TeamProgression State = new TeamProgression();

	private static UpgradeSettings localSettings;

	private static bool loaded;

	private static bool saveBlocked;

	private static StartOfRound initializedRound;

	private static string saveFile;

	private static string pendingTrip;

	private static bool pendingExploration;

	private static bool pendingBonusNotice;

	private static float nextRetry;

	private static float nextTip;

	public static UpgradeSettings Settings { get; private set; } = new UpgradeSettings().ValidatedCopy();

	public static ManualLogSource Log { get; private set; }

	public static bool Ready { get; private set; }

	internal static bool IsServer
	{
		get
		{
			if ((Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.IsListening)
			{
				return NetworkManager.Singleton.IsServer;
			}
			return false;
		}
	}

	public static int Level(UpgradeKind kind)
	{
		if (!Ready || !Settings.IsEnabled(kind))
		{
			return 0;
		}
		return State.Level(kind);
	}

	internal static void Initialize(ManualLogSource log, UpgradeSettings settings)
	{
		Log = log;
		Settings = (localSettings = settings.ValidatedCopy());
	}

	internal static void Tick()
	{
		try
		{
			UpgradeNetwork.Tick();
			TryShowBonusNotice();
			if (!IsServer || loaded || (Object)(object)StartOfRound.Instance == (Object)null || initializedRound != StartOfRound.Instance || (Object)(object)TimeOfDay.Instance == (Object)null || Time.realtimeSinceStartup < nextRetry)
			{
				return;
			}
			nextRetry = Time.realtimeSinceStartup + 2f;
			saveFile = GameNetworkManager.Instance?.currentSaveFileName;
			if (!string.IsNullOrEmpty(saveFile))
			{
				Settings = localSettings;
				State = UpgradeSave.Load(saveFile, Settings);
				pendingTrip = null;
				pendingExploration = false;
				State.NewQuota(TimeOfDay.Instance.timesFulfilledQuota);
				loaded = (Ready = true);
				Log.LogInfo((object)("Crew progression loaded: " + saveFile + ", revision " + State.Revision));
				RefreshBuyingRate();
				UpgradeNetwork.Publish();
				if (State.CompanyActive && Settings.CompanyNotify)
				{
					NotifyBonus();
				}
			}
		}
		catch (Exception ex)
		{
			ManualLogSource log = Log;
			if (log != null)
			{
				log.LogError((object)("Crew runtime: " + ex));
			}
		}
	}

	internal static void NewConnection()
	{
		Ready = (loaded = (saveBlocked = false));
		State = new TeamProgression();
		Settings = localSettings;
		pendingTrip = null;
		pendingExploration = false;
		nextRetry = 0f;
		pendingBonusNotice = false;
		UpgradeTerminal.Close();
		StrengthUpgrades.Reset();
	}

	internal static void MarkRoundStarted(StartOfRound round)
	{
		initializedRound = round;
	}

	internal static void EndConnection()
	{
		Save();
		Ready = (loaded = false);
		State = new TeamProgression();
		Settings = localSettings;
		pendingTrip = null;
		pendingExploration = false;
		saveFile = null;
		pendingBonusNotice = false;
		UpgradeTerminal.Close();
		StrengthUpgrades.Reset();
	}

	internal static void AcceptState(UpgradeSettings settings, TeamProgression state, bool usable = true)
	{
		bool flag = (!Ready || !State.CompanyActive) && state.CompanyActive;
		Settings = settings.ValidatedCopy();
		State = state.ValidatedCopy(Settings);
		Ready = usable;
		if (!State.CompanyActive)
		{
			pendingBonusNotice = false;
		}
		RefreshBuyingRate();
		if (usable && flag && Settings.CompanyNotify)
		{
			NotifyBonus();
		}
		UpgradeTerminal.Refresh();
	}

	internal static bool Save()
	{
		if (!IsServer || !loaded || saveBlocked || string.IsNullOrEmpty(saveFile))
		{
			return false;
		}
		if (UpgradeSave.Store(saveFile, State))
		{
			return true;
		}
		saveBlocked = true;
		Ready = false;
		RefreshBuyingRate();
		UpgradeNetwork.Publish();
		return false;
	}

	internal static string Purchase(ulong sender, UpgradeKind kind, int expectedLevel)
	{
		//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
		if (!IsServer || !Ready)
		{
			return "not-ready";
		}
		if (Settings.RequireAllPlayers && !UpgradeNetwork.AllPeersReady)
		{
			return "not-ready";
		}
		Terminal val = Object.FindObjectOfType<Terminal>();
		StartOfRound instance = StartOfRound.Instance;
		if ((Object)(object)val == (Object)null || (Object)(object)instance == (Object)null)
		{
			return "not-ready";
		}
		PlayerControllerB val2 = null;
		PlayerControllerB[] allPlayerScripts = instance.allPlayerScripts;
		foreach (PlayerControllerB val3 in allPlayerScripts)
		{
			if ((Object)(object)val3 != (Object)null && val3.actualClientId == sender && val3.isPlayerControlled)
			{
				val2 = val3;
				break;
			}
		}
		if ((Object)(object)val2 == (Object)null || val2.isPlayerDead || !val2.isInHangarShipRoom || Vector3.Distance(((Component)val2).transform.position, ((Component)val).transform.position) > 8f || (Object)(object)val.placeableObject == (Object)null || !val.placeableObject.inUse)
		{
			return "terminal-required";
		}
		int credits = val.groupCredits;
		TeamProgression state = State.ValidatedCopy(Settings);
		PurchaseResult purchaseResult = State.TryPurchase(kind, expectedLevel, ref credits, Settings);
		if (!purchaseResult.Success)
		{
			return purchaseResult.Reason;
		}
		switch (UpgradeSave.StorePurchase(saveFile, State, credits))
		{
		case PurchaseCommit.NotCommitted:
			State = state;
			return "save-failed";
		case PurchaseCommit.Unknown:
			saveBlocked = true;
			Ready = false;
			val.groupCredits = credits;
			try
			{
				val.SyncGroupCreditsClientRpc(credits, -1);
			}
			catch (Exception ex2)
			{
				Log.LogError((object)("Could not broadcast the provisional balance: " + ex2));
			}
			RefreshBuyingRate();
			UpgradeNetwork.Publish();
			return "save-uncertain";
		default:
			val.groupCredits = credits;
			try
			{
				val.SyncGroupCreditsClientRpc(credits, -1);
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Purchase committed, but the native credit broadcast failed: " + ex));
			}
			Log.LogInfo((object)$"Upgrade bought by {sender}: {kind} level {purchaseResult.NewLevel}, cost {purchaseResult.Cost}, credits {credits}.");
			UpgradeNetwork.Publish();
			return "ok";
		}
	}

	internal static bool CanLaunch()
	{
		if (!IsServer || !Settings.Enabled || (Ready && (!Settings.RequireAllPlayers || UpgradeNetwork.AllPeersReady)))
		{
			return true;
		}
		if (Time.realtimeSinceStartup >= nextTip)
		{
			nextTip = Time.realtimeSinceStartup + 4f;
			HUDManager instance = HUDManager.Instance;
			if (instance != null)
			{
				instance.DisplayTip("Crew Upgrades", UpgradeTerminal.Chinese ? "有玩家尚未同步 CrewUpgrades,请确认全员安装相同版本。" : "Waiting for CrewUpgrades on every player. Install the same version on all clients.", true, false, "LC_Tip1");
			}
		}
		StartMatchLever obj = Object.FindObjectOfType<StartMatchLever>();
		if (obj != null)
		{
			obj.CancelStartGameClientRpc();
		}
		return false;
	}

	internal static void BeginTrip(StartOfRound round)
	{
		if (IsServer && Ready)
		{
			pendingExploration = (Object)(object)round.currentLevel != (Object)null && round.currentLevel.planetHasTime && round.currentLevelID != 3;
			pendingTrip = (pendingExploration ? (State.QuotaMarker + ":" + round.gameStats.daysSpent + ":" + round.randomMapSeed) : null);
		}
	}

	internal static void CompleteTrip()
	{
		if (IsServer && Ready && pendingExploration && pendingTrip != null)
		{
			string tripId = pendingTrip;
			pendingTrip = null;
			pendingExploration = false;
			bool flag = State.CompleteExploration(tripId, NextDraw(), Settings);
			Save();
			RefreshBuyingRate();
			UpgradeNetwork.Publish();
			if (Ready && flag && Settings.CompanyNotify)
			{
				NotifyBonus();
			}
			Log.LogInfo((object)$"Exploration completed: {State.ExplorationsCompleted}; company drawn={State.CompanyRolled}, bonus={State.CompanyActive}.");
		}
	}

	private static double NextDraw()
	{
		return (double)(BitConverter.ToUInt64(Guid.NewGuid().ToByteArray(), 0) >> 11) * 1.1102230246251565E-16;
	}

	internal static void NewQuota()
	{
		if (IsServer && Ready && !((Object)(object)TimeOfDay.Instance == (Object)null))
		{
			State.NewQuota(TimeOfDay.Instance.timesFulfilledQuota);
			pendingBonusNotice = false;
			pendingTrip = null;
			pendingExploration = false;
			Save();
			RefreshBuyingRate();
			UpgradeNetwork.Publish();
		}
	}

	internal static void Fired()
	{
		if (IsServer && Ready)
		{
			if (Settings.ResetOnFired)
			{
				State.ResetAll();
			}
			State.QuotaMarker = -1;
			State.NewQuota(0);
			pendingBonusNotice = false;
			pendingTrip = null;
			pendingExploration = false;
			Save();
			RefreshBuyingRate();
			UpgradeNetwork.Publish();
		}
	}

	internal static void RefreshBuyingRate()
	{
		if ((Object)(object)TimeOfDay.Instance != (Object)null && (Object)(object)StartOfRound.Instance != (Object)null)
		{
			TimeOfDay.Instance.SetBuyingRateForDay();
		}
	}

	internal static void MultiplyBuyingRate()
	{
		if (Ready && Settings.IsEnabled(UpgradeKind.CompanyRelations) && State.CompanyActive && (Object)(object)StartOfRound.Instance != (Object)null)
		{
			StartOfRound instance = StartOfRound.Instance;
			instance.companyBuyingRate *= Settings.CompanyMultiplier;
		}
	}

	internal static void NotifyBonus()
	{
		pendingBonusNotice = true;
		TryShowBonusNotice();
	}

	private static void TryShowBonusNotice()
	{
		if (pendingBonusNotice && Ready && State.CompanyActive && !((Object)(object)HUDManager.Instance == (Object)null))
		{
			pendingBonusNotice = false;
			HUDManager.Instance.DisplayTip(UpgradeTerminal.Chinese ? "公司收购加价" : "Company bonus", UpgradeTerminal.Chinese ? $"本配额周期收购价 ×{Settings.CompanyMultiplier:0.##},可前往公司交货。" : $"Company buying rate x{Settings.CompanyMultiplier:0.##} for this quota. Ready for delivery.", false, false, "LC_Tip1");
		}
	}

	internal static void Shutdown()
	{
		Save();
		UpgradeNetwork.Shutdown();
	}
}
[HarmonyPatch]
internal static class ProgressionPatches
{
	[HarmonyPrefix]
	[HarmonyPatch(typeof(StartOfRound), "StartGame")]
	private static bool BeforeLaunch(StartOfRound __instance, out bool __state)
	{
		bool flag = UpgradeRuntime.CanLaunch();
		__state = flag && UpgradeRuntime.IsServer && UpgradeRuntime.Ready && __instance.inShipPhase && !__instance.beganLoadingNewLevel;
		return flag;
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(StartOfRound), "StartGame")]
	private static void AfterLaunch(StartOfRound __instance, bool __state, bool __runOriginal)
	{
		if (__state && __runOriginal && __instance.beganLoadingNewLevel)
		{
			UpgradeRuntime.BeginTrip(__instance);
		}
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(StartOfRound), "Start")]
	private static void Started(StartOfRound __instance)
	{
		UpgradeRuntime.MarkRoundStarted(__instance);
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(StartOfRound), "SetShipReadyToLand")]
	private static void Orbit()
	{
		UpgradeRuntime.CompleteTrip();
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(TimeOfDay), "SetNewProfitQuota")]
	private static void Quota()
	{
		UpgradeRuntime.NewQuota();
	}

	[HarmonyPostfix]
	[HarmonyPriority(0)]
	[HarmonyPatch(typeof(TimeOfDay), "SetBuyingRateForDay")]
	private static void Rate()
	{
		UpgradeRuntime.MultiplyBuyingRate();
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(StartOfRound), "ResetShip")]
	private static void Fired()
	{
		UpgradeRuntime.Fired();
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(GameNetworkManager), "SaveGameValues")]
	private static void Save()
	{
		UpgradeRuntime.Save();
	}
}
internal enum PurchaseCommit
{
	NotCommitted,
	Committed,
	Unknown
}
internal static class UpgradeSave
{
	private const string Key = "Serenta.CrewUpgrades.v1";

	internal static TeamProgression Load(string file, UpgradeSettings settings)
	{
		if (!ES3.KeyExists("Serenta.CrewUpgrades.v1", file))
		{
			return new TeamProgression();
		}
		string text = ES3.Load<string>("Serenta.CrewUpgrades.v1", file);
		if (string.IsNullOrEmpty(text) || text.Length > 65536)
		{
			throw new InvalidOperationException("Invalid CrewUpgrades save