Decompiled source of LethalGearLeveling v0.1.0

LethalGearLeveling.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using LethalGearLeveling.Core;
using LethalGearLeveling.Patches;
using Microsoft.CodeAnalysis;
using TMPro;
using Unity.Collections;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("LethalGearLeveling")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Gear that levels up the more you use it")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0")]
[assembly: AssemblyProduct("LethalGearLeveling")]
[assembly: AssemblyTitle("LethalGearLeveling")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace LethalGearLeveling
{
	[BepInPlugin("com.shronkstones.lethalgearleveling", "LethalGearLeveling", "0.1.0")]
	public class Plugin : BaseUnityPlugin
	{
		public const string modGUID = "com.shronkstones.lethalgearleveling";

		public const string modName = "LethalGearLeveling";

		public const string modVersion = "0.1.0";

		public static Plugin Instance;

		public static ManualLogSource logger;

		private void Awake()
		{
			Instance = this;
			logger = ((BaseUnityPlugin)this).Logger;
			GearConfig.Init(((BaseUnityPlugin)this).Config);
			Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), "com.shronkstones.lethalgearleveling");
			logger.LogInfo((object)"Plugin com.shronkstones.lethalgearleveling is loaded!");
		}
	}
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "LethalGearLeveling";

		public const string PLUGIN_NAME = "LethalGearLeveling";

		public const string PLUGIN_VERSION = "0.1.0";
	}
}
namespace LethalGearLeveling.Patches
{
	[HarmonyPatch(typeof(GrabbableObject), "Update")]
	internal static class BatteryPatches
	{
		private static void Prefix(GrabbableObject __instance, out float __state)
		{
			__state = ((((NetworkBehaviour)__instance).IsOwner && __instance.isBeingUsed && __instance.insertedBattery != null) ? __instance.insertedBattery.charge : (-1f));
		}

		private static void Postfix(GrabbableObject __instance, float __state)
		{
			if (__state <= 0f || __instance.insertedBattery == null)
			{
				return;
			}
			float num = __state - __instance.insertedBattery.charge;
			if (!(num <= 0f))
			{
				float num2 = GearStats.BatteryDrain(__instance);
				if (num2 < 1f)
				{
					__instance.insertedBattery.charge = __state - num * num2;
				}
			}
		}
	}
	[HarmonyPatch]
	internal static class EndOfDayPatches
	{
		public static void TrackGearOutsideShip()
		{
			if (!GearProgress.IsOnMoon())
			{
				return;
			}
			GrabbableObject[] array = Object.FindObjectsByType<GrabbableObject>((FindObjectsSortMode)0);
			foreach (GrabbableObject val in array)
			{
				if (((NetworkBehaviour)val).IsSpawned && GearTypes.Get(val) != GearType.None && !GearProgress.IsInShip(val))
				{
					GearItem.Get(val).leftShipToday = true;
				}
			}
		}

		[HarmonyPatch(typeof(RoundManager), "DespawnPropsAtEndOfRound")]
		[HarmonyPostfix]
		private static void AwardEndOfDayXp(RoundManager __instance)
		{
			if (!((NetworkBehaviour)__instance).IsServer)
			{
				return;
			}
			float num = 0f;
			StartOfRound instance = StartOfRound.Instance;
			if (GearConfig.EndOfDayXpEnabled.Value && (Object)(object)instance != (Object)null && (Object)(object)instance.currentLevel != (Object)null && instance.currentLevel.planetHasTime)
			{
				num = GearConfig.EndOfDayXp.Value * GearConfig.XpMultiplier.Value;
				if (GearConfig.EndOfDayXpScales.Value && __instance.totalScrapValueInLevel > 0f)
				{
					num *= Mathf.Clamp01((float)__instance.scrapCollectedInLevel / __instance.totalScrapValueInLevel);
				}
			}
			GrabbableObject[] array = Object.FindObjectsByType<GrabbableObject>((FindObjectsSortMode)0);
			foreach (GrabbableObject val in array)
			{
				if (GearTypes.Get(val) != GearType.None)
				{
					GearItem component = ((Component)val).GetComponent<GearItem>();
					bool flag = (Object)(object)component != (Object)null && component.leftShipToday;
					if ((Object)(object)component != (Object)null)
					{
						component.leftShipToday = false;
					}
					if (!(num <= 0f) && ((NetworkBehaviour)val).IsSpawned && !val.deactivated && (!GearConfig.EndOfDayXpRequiresTrip.Value || flag))
					{
						GearProgress.ServerAddXp(((NetworkBehaviour)val).NetworkObjectId, num);
					}
				}
			}
		}
	}
	[HarmonyPatch(typeof(FlashlightItem))]
	internal static class FlashlightPatches
	{
		[HarmonyPatch("Update")]
		[HarmonyPostfix]
		private static void ApplyStats(FlashlightItem __instance)
		{
			if (GearTypes.Get((GrabbableObject)(object)__instance) == GearType.Flashlight)
			{
				float brightness = GearStats.FlashlightBrightness(__instance);
				float size = GearStats.FlashlightLightSize(__instance);
				if ((Object)(object)__instance.flashlightBulb != (Object)null)
				{
					FlashlightLights.Apply(__instance.flashlightBulb, brightness, size);
				}
				PlayerControllerB playerHeldBy = ((GrabbableObject)__instance).playerHeldBy;
				if ((Object)(object)playerHeldBy != (Object)null && (Object)(object)playerHeldBy.helmetLight != (Object)null && ((GrabbableObject)__instance).isBeingUsed && (__instance.usingPlayerHelmetLight || !((NetworkBehaviour)__instance).IsOwner))
				{
					FlashlightLights.Apply(playerHeldBy.helmetLight, brightness, size);
				}
				if (((NetworkBehaviour)__instance).IsOwner && ((GrabbableObject)__instance).isBeingUsed && (Object)(object)playerHeldBy != (Object)null && playerHeldBy.isInsideFactory)
				{
					GearProgress.AddXp((GrabbableObject)(object)__instance, Time.deltaTime * GearConfig.FlashlightXpPerSecond.Value);
				}
			}
		}
	}
	[HarmonyPatch(typeof(JetpackItem))]
	internal static class JetpackPatches
	{
		private static readonly FieldRef<JetpackItem, bool> jetpackActivated = AccessTools.FieldRefAccess<JetpackItem, bool>("jetpackActivated");

		[HarmonyPatch("Update")]
		[HarmonyPrefix]
		private static void ApplySpeed(JetpackItem __instance)
		{
			GearItem gearItem = GearItem.Get((GrabbableObject)(object)__instance);
			GearItem gearItem2 = gearItem;
			float valueOrDefault = gearItem2.baseJetpackAcceleration.GetValueOrDefault();
			if (!gearItem2.baseJetpackAcceleration.HasValue)
			{
				valueOrDefault = __instance.jetpackAcceleration;
				gearItem2.baseJetpackAcceleration = valueOrDefault;
			}
			__instance.jetpackAcceleration = gearItem.baseJetpackAcceleration.Value * GearStats.JetpackSpeed(__instance);
		}

		[HarmonyPatch("Update")]
		[HarmonyPostfix]
		private static void CreditFlight(JetpackItem __instance)
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			GearItem gearItem = GearItem.Get((GrabbableObject)(object)__instance);
			PlayerControllerB playerHeldBy = ((GrabbableObject)__instance).playerHeldBy;
			if (!((NetworkBehaviour)__instance).IsOwner || (Object)(object)playerHeldBy == (Object)null || playerHeldBy.isPlayerDead || !jetpackActivated.Invoke(__instance) || playerHeldBy.thisController.isGrounded)
			{
				gearItem.lastJetpackPosition = null;
				return;
			}
			Vector3 position = ((Component)playerHeldBy).transform.position;
			if (gearItem.lastJetpackPosition.HasValue)
			{
				float num = Vector3.Distance(position, gearItem.lastJetpackPosition.Value);
				if (num < 10f)
				{
					GearProgress.AddXp((GrabbableObject)(object)__instance, num * GearConfig.JetpackXpPerMetre.Value);
				}
			}
			gearItem.lastJetpackPosition = position;
		}

		[HarmonyPatch("Update")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> ScaleCrashThreshold(IEnumerable<CodeInstruction> instructions)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Expected O, but got Unknown
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Expected O, but got Unknown
			Label? label = default(Label?);
			CodeMatcher val = new CodeMatcher(instructions, (ILGenerator)null).MatchForward(false, (CodeMatch[])(object)new CodeMatch[2]
			{
				new CodeMatch((OpCode?)OpCodes.Ldc_R4, (object)50f, (string)null),
				new CodeMatch((Func<CodeInstruction, bool>)((CodeInstruction i) => CodeInstructionExtensions.Branches(i, ref label)), (string)null)
			});
			if (val.IsInvalid)
			{
				Plugin.logger.LogError((object)"Could not find jetpack crash threshold; safety bonus disabled");
				return instructions;
			}
			val.Opcode = OpCodes.Ldarg_0;
			val.Operand = null;
			return val.Advance(1).Insert((CodeInstruction[])(object)new CodeInstruction[1]
			{
				new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(GearStats), "JetpackCrashThreshold", (Type[])null, (Type[])null))
			}).InstructionEnumeration();
		}
	}
	[HarmonyPatch(typeof(KnifeItem))]
	internal static class KnifePatches
	{
		private const float vanillaDamageInterval = 0.43f;

		private static KnifeItem stabbingKnife;

		private static int stabCrits;

		private static bool stabCritEffectsPlayed;

		[HarmonyPatch("HitKnife")]
		[HarmonyPrefix]
		private static void BeginStab(KnifeItem __instance, bool cancel)
		{
			if (!cancel)
			{
				stabbingKnife = __instance;
				WeaponHitPatches.attackingWeapon = (GrabbableObject)(object)__instance;
				stabCrits = WeaponCrits.Roll(GearStats.KnifeCritChance(__instance));
				stabCritEffectsPlayed = false;
			}
		}

		[HarmonyPatch("HitKnife")]
		[HarmonyFinalizer]
		private static void EndStab()
		{
			stabbingKnife = null;
			WeaponHitPatches.attackingWeapon = null;
			stabCrits = 0;
		}

		[HarmonyPatch("HitKnife")]
		[HarmonyTranspiler]
		[HarmonyPriority(0)]
		private static IEnumerable<CodeInstruction> CritAfterDamageMods(IEnumerable<CodeInstruction> instructions)
		{
			return WeaponCrits.InsertCritCall(instructions, AccessTools.Field(typeof(KnifeItem), "knifeHitForce"), AccessTools.Method(typeof(KnifePatches), "ApplyCrit", (Type[])null, (Type[])null), "knife");
		}

		public static int ApplyCrit(int force, KnifeItem knife)
		{
			if ((Object)(object)knife != (Object)(object)stabbingKnife || stabCrits <= 0)
			{
				return force;
			}
			force = WeaponCrits.Apply(force, stabCrits, GearConfig.KnifeCritMultiplier.Value);
			if (!stabCritEffectsPlayed)
			{
				stabCritEffectsPlayed = true;
				if (GearConfig.KnifeCritEffects.Value)
				{
					WeaponCrits.PlayEffects((GrabbableObject)(object)knife, knife.knifeAudio, knife.hitSFX, stabCrits);
				}
			}
			return force;
		}

		[HarmonyPatch("HitKnife")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> ScaleReachAndSpeed(IEnumerable<CodeInstruction> instructions)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Expected O, but got Unknown
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Expected O, but got Unknown
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Expected O, but got Unknown
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Expected O, but got Unknown
			CodeMatcher val = new CodeMatcher(instructions, (ILGenerator)null).MatchForward(false, (CodeMatch[])(object)new CodeMatch[1]
			{
				new CodeMatch((OpCode?)OpCodes.Ldc_R4, (object)0.75f, (string)null)
			});
			if (val.IsInvalid)
			{
				Plugin.logger.LogError((object)"Could not find knife reach; reach bonus disabled");
			}
			else
			{
				val.Opcode = OpCodes.Ldarg_0;
				val.Operand = null;
				val.Advance(1).Insert((CodeInstruction[])(object)new CodeInstruction[1]
				{
					new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(GearStats), "KnifeReach", (Type[])null, (Type[])null))
				});
			}
			val.Start().MatchForward(false, (CodeMatch[])(object)new CodeMatch[1]
			{
				new CodeMatch((OpCode?)OpCodes.Ldc_R4, (object)0.43f, (string)null)
			});
			if (val.IsInvalid)
			{
				Plugin.logger.LogError((object)"Could not find knife damage interval; stab speed bonus only affects the swing cooldown");
				return val.Start().InstructionEnumeration();
			}
			return val.Advance(1).Insert((CodeInstruction[])(object)new CodeInstruction[2]
			{
				new CodeInstruction(OpCodes.Ldarg_0, (object)null),
				new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(KnifePatches), "ScaleInterval", (Type[])null, (Type[])null))
			}).InstructionEnumeration();
		}

		public static float ScaleInterval(float seconds, KnifeItem knife)
		{
			return seconds / GearStats.KnifeAttackSpeed(knife);
		}

		[HarmonyPatch(typeof(GrabbableObject), "RequireCooldown")]
		[HarmonyPostfix]
		private static void ScaleCooldown(GrabbableObject __instance, bool __result)
		{
			if (!__result)
			{
				KnifeItem val = (KnifeItem)(object)((__instance is KnifeItem) ? __instance : null);
				if (val != null && __instance.useCooldown > 0f && __instance.currentUseCooldown == __instance.useCooldown)
				{
					__instance.currentUseCooldown /= GearStats.KnifeAttackSpeed(val);
				}
			}
		}
	}
	[HarmonyPatch(typeof(StartOfRound))]
	internal static class LifecyclePatches
	{
		[HarmonyPatch("Start")]
		[HarmonyPostfix]
		private static void AddRunner(StartOfRound __instance)
		{
			if ((Object)(object)((Component)__instance).GetComponent<GearRunner>() == (Object)null)
			{
				((Component)__instance).gameObject.AddComponent<GearRunner>();
			}
		}
	}
	[HarmonyPatch(typeof(LockPicker))]
	internal static class LockPickerPatches
	{
		[HarmonyPatch("PlaceOnDoor")]
		[HarmonyPostfix]
		private static void ShortenPickTime(LockPicker __instance, DoorLock doorScript)
		{
			if (!((Object)(object)doorScript == (Object)null) && !((Object)(object)__instance.currentlyPickingDoor != (Object)(object)doorScript))
			{
				float num = doorScript.maxTimeLeft / GearStats.LockPickerSpeed(__instance);
				doorScript.lockPickTimeLeft = Mathf.Min(doorScript.lockPickTimeLeft, num);
			}
		}

		[HarmonyPatch("FinishPickingLock")]
		[HarmonyPrefix]
		private static void CreditUnlock(LockPicker __instance)
		{
			if (((NetworkBehaviour)__instance).IsServer && __instance.isPickingLock)
			{
				GearProgress.AddXp((GrabbableObject)(object)__instance, GearConfig.LockPickerXpPerDoor.Value);
			}
		}
	}
	[HarmonyPatch(typeof(RadarBoosterItem))]
	internal static class RadarBoosterPatches
	{
		private const float vanillaFlashStun = 1.75f;

		private const float vanillaStunRadius = 12f;

		private const float vanillaStunNearRadius = 11f;

		private const int nameBits = 12;

		private const int nameMask = 4095;

		private static RadarBoosterItem flashingBooster;

		private static readonly Dictionary<int, float> lastCreditedEnemy = new Dictionary<int, float>();

		internal static void Clear()
		{
			lastCreditedEnemy.Clear();
		}

		[HarmonyPatch("Update")]
		[HarmonyPostfix]
		private static void GiveActiveXp(RadarBoosterItem __instance)
		{
			if (__instance.radarEnabled && ((NetworkBehaviour)__instance).IsServer)
			{
				GearProgress.AddXp((GrabbableObject)(object)__instance, Time.deltaTime * GearConfig.RadarBoosterXpPerMinute.Value / 60f);
			}
		}

		[HarmonyPatch("Flash")]
		[HarmonyPrefix]
		private static void BeginFlash(RadarBoosterItem __instance)
		{
			flashingBooster = __instance;
		}

		[HarmonyPatch("Flash")]
		[HarmonyFinalizer]
		private static void EndFlash()
		{
			flashingBooster = null;
		}

		[HarmonyPatch("Flash")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> ScaleFlashStun(IEnumerable<CodeInstruction> instructions)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Expected O, but got Unknown
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Expected O, but got Unknown
			CodeMatcher val = new CodeMatcher(instructions, (ILGenerator)null).MatchForward(false, (CodeMatch[])(object)new CodeMatch[1]
			{
				new CodeMatch((OpCode?)OpCodes.Ldc_R4, (object)1.75f, (string)null)
			});
			if (val.IsInvalid)
			{
				Plugin.logger.LogError((object)"Could not find the radar booster's flash stun time; flash stun bonus disabled");
				return instructions;
			}
			return val.Advance(1).Insert((CodeInstruction[])(object)new CodeInstruction[2]
			{
				new CodeInstruction(OpCodes.Ldarg_0, (object)null),
				new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(RadarBoosterPatches), "ScaleStun", (Type[])null, (Type[])null))
			}).InstructionEnumeration();
		}

		public static float ScaleStun(float seconds, RadarBoosterItem booster)
		{
			return seconds * GearStats.RadarFlashStun(booster);
		}

		[HarmonyPatch(typeof(StunGrenadeItem), "StunExplosion")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> ScaleStunRadius(IEnumerable<CodeInstruction> instructions)
		{
			int patched = 0;
			foreach (CodeInstruction instruction in instructions)
			{
				yield return instruction;
				if (instruction.opcode == OpCodes.Ldc_R4 && instruction.operand is float num && (num == 12f || num == 11f))
				{
					yield return new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(RadarBoosterPatches), "ScaleRadius", (Type[])null, (Type[])null));
					patched++;
				}
			}
			if (patched != 2)
			{
				Plugin.logger.LogWarning((object)$"Expected 2 stun radius values in StunExplosion, found {patched}; radar booster radius bonus may be off");
			}
		}

		public static float ScaleRadius(float radius)
		{
			if (!((Object)(object)flashingBooster != (Object)null))
			{
				return radius;
			}
			return radius * GearStats.RadarFlashRadius(flashingBooster);
		}

		[HarmonyPatch(typeof(EnemyAI), "SetEnemyStunned")]
		[HarmonyPrefix]
		private static void CreditFlashStun(EnemyAI __instance, bool setToStunned)
		{
			RadarBoosterItem val = flashingBooster;
			if (!((Object)(object)val == (Object)null) && setToStunned && ((NetworkBehaviour)val).IsServer && !__instance.isEnemyDead && !((Object)(object)__instance.enemyType == (Object)null) && __instance.enemyType.canBeStunned && !(__instance.postStunInvincibilityTimer >= 0f))
			{
				int instanceID = ((Object)__instance).GetInstanceID();
				if (!lastCreditedEnemy.TryGetValue(instanceID, out var value) || !(Time.time - value < GearConfig.RadarBoosterEnemyCooldown.Value))
				{
					lastCreditedEnemy[instanceID] = Time.time;
					GearProgress.AddXp((GrabbableObject)(object)val, GearConfig.RadarBoosterXpPerEnemyFlashed.Value);
				}
			}
		}

		[HarmonyPatch("GetItemDataToSave")]
		[HarmonyPostfix]
		private static void SaveXp(RadarBoosterItem __instance, ref int __result)
		{
			int num = Mathf.RoundToInt(GearProgress.GetXp((GrabbableObject)(object)__instance));
			__result = (__result & 0xFFF) | (num << 12);
		}

		[HarmonyPatch("LoadItemSaveData")]
		[HarmonyPostfix]
		private static void LoadXp(RadarBoosterItem __instance, int saveData)
		{
			int num = saveData & 0xFFF;
			AccessTools.Field(typeof(RadarBoosterItem), "radarBoosterNameIndex").SetValue(__instance, (num == 4095) ? (-1) : num);
			int num2 = saveData >> 12;
			if (num2 > 0)
			{
				GearItem.Get((GrabbableObject)(object)__instance).loadedXp = num2;
			}
		}
	}
	[HarmonyPatch]
	internal static class SavePatches
	{
		private static readonly HashSet<Item> flaggedItems = new HashSet<Item>();

		private static bool unflaggedForLoad;

		[HarmonyPatch(typeof(StartOfRound), "Awake")]
		[HarmonyPostfix]
		private static void FlagGearItems(StartOfRound __instance)
		{
			foreach (Item items in __instance.allItemsList.itemsList)
			{
				if ((Object)(object)items == (Object)null || (Object)(object)items.spawnPrefab == (Object)null || flaggedItems.Contains(items))
				{
					continue;
				}
				GrabbableObject component = items.spawnPrefab.GetComponent<GrabbableObject>();
				if (component is RadarBoosterItem && !items.saveItemVariable)
				{
					Plugin.logger.LogWarning((object)(items.itemName + " doesn't save item data; its level won't be saved"));
				}
				if (!((Object)(object)component == (Object)null) && !(component is ShotgunItem) && !(component is RadarBoosterItem) && GearTypes.Get(component) != GearType.None)
				{
					if (items.saveItemVariable)
					{
						Plugin.logger.LogWarning((object)(items.itemName + " already saves item data for something else; its level won't be saved"));
						continue;
					}
					items.saveItemVariable = true;
					flaggedItems.Add(items);
				}
			}
		}

		[HarmonyPatch(typeof(GrabbableObject), "GetItemDataToSave")]
		[HarmonyPostfix]
		private static void SaveXp(GrabbableObject __instance, ref int __result)
		{
			if (flaggedItems.Contains(__instance.itemProperties))
			{
				__result = Mathf.RoundToInt(GearProgress.GetXp(__instance));
			}
		}

		[HarmonyPatch(typeof(GrabbableObject), "LoadItemSaveData")]
		[HarmonyPostfix]
		private static void LoadXp(GrabbableObject __instance, int saveData)
		{
			if (saveData > 0 && flaggedItems.Contains(__instance.itemProperties))
			{
				GearItem.Get(__instance).loadedXp = saveData;
			}
		}

		[HarmonyPatch(typeof(StartOfRound), "LoadShipGrabbableItems")]
		[HarmonyPrefix]
		private static void CheckSaveDataLayout(StartOfRound __instance)
		{
			string currentSaveFileName = GameNetworkManager.Instance.currentSaveFileName;
			if (!ES3.KeyExists("shipGrabbableItemIDs", currentSaveFileName))
			{
				return;
			}
			int[] source = ES3.Load<int[]>("shipGrabbableItemIDs", currentSaveFileName);
			int num = (ES3.KeyExists("shipItemSaveData", currentSaveFileName) ? ES3.Load<int[]>("shipItemSaveData", currentSaveFileName).Length : 0);
			List<Item> itemsList = __instance.allItemsList.itemsList;
			List<Item> source2 = (from id in source
				where id >= 0 && id < itemsList.Count
				select itemsList[id]).ToList();
			int num2 = source2.Count((Item x) => x.saveItemVariable);
			int num3 = source2.Count((Item x) => x.saveItemVariable && !flaggedItems.Contains(x));
			if (num2 == num || num3 != num)
			{
				return;
			}
			foreach (Item flaggedItem in flaggedItems)
			{
				flaggedItem.saveItemVariable = false;
			}
			unflaggedForLoad = true;
		}

		[HarmonyPatch(typeof(StartOfRound), "LoadShipGrabbableItems")]
		[HarmonyPostfix]
		private static void FinishLoad()
		{
			if (unflaggedForLoad)
			{
				foreach (Item flaggedItem in flaggedItems)
				{
					flaggedItem.saveItemVariable = true;
				}
				unflaggedForLoad = false;
			}
			GearItem[] array = Object.FindObjectsByType<GearItem>((FindObjectsSortMode)0);
			foreach (GearItem gearItem in array)
			{
				if (!(gearItem.loadedXp <= 0f))
				{
					GrabbableObject component = ((Component)gearItem).GetComponent<GrabbableObject>();
					if ((Object)(object)component != (Object)null && ((NetworkBehaviour)component).IsSpawned)
					{
						GearProgress.ServerAddXp(((NetworkBehaviour)component).NetworkObjectId, gearItem.loadedXp, announce: false);
					}
					gearItem.loadedXp = 0f;
				}
			}
		}
	}
	[HarmonyPatch(typeof(ShotgunItem))]
	internal static class ShotgunPatches
	{
		private const float castStartBehind = 10f;

		private const int shellBits = 2;

		private const int shellMask = 3;

		[HarmonyPatch("ShootGun")]
		[HarmonyPrefix]
		private static void BeginShot(ShotgunItem __instance, out int __state)
		{
			__state = __instance.shellsLoaded;
			if (((NetworkBehaviour)__instance).IsOwner && (Object)(object)((GrabbableObject)__instance).playerHeldBy != (Object)null && (Object)(object)((GrabbableObject)__instance).playerHeldBy == (Object)(object)GameNetworkManager.Instance.localPlayerController)
			{
				WeaponHitPatches.attackingWeapon = (GrabbableObject)(object)__instance;
			}
		}

		[HarmonyPatch("ShootGun")]
		[HarmonyPostfix]
		private static void RefundShell(ShotgunItem __instance, Vector3 shotgunPosition, Vector3 shotgunForward, int __state)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			int shot = GearItem.Get((GrabbableObject)(object)__instance).shotsSinceReload++;
			if (__state > 0 && __instance.shellsLoaded < __state && SharedRoll(shotgunPosition, shotgunForward, shot) < GearStats.ShotgunAmmoSave(__instance))
			{
				__instance.shellsLoaded = __state;
			}
		}

		[HarmonyPatch("ReloadGunEffectsServerRpc")]
		[HarmonyPrefix]
		private static void ResetShotsOnOwner(ShotgunItem __instance, bool start)
		{
			if (!start && ((NetworkBehaviour)__instance).IsOwner)
			{
				GearItem.Get((GrabbableObject)(object)__instance).shotsSinceReload = 0;
			}
		}

		[HarmonyPatch("ReloadGunEffectsClientRpc")]
		[HarmonyPostfix]
		private static void ResetShotsOnOthers(ShotgunItem __instance, bool start)
		{
			if (!start && !((NetworkBehaviour)__instance).IsOwner)
			{
				GearItem.Get((GrabbableObject)(object)__instance).shotsSinceReload = 0;
			}
		}

		[HarmonyPatch("ShootGun")]
		[HarmonyFinalizer]
		private static void EndShot()
		{
			WeaponHitPatches.attackingWeapon = null;
		}

		private static float SharedRoll(Vector3 position, Vector3 forward, int shot)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: 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_003b: Unknown result type (might be due to invalid IL or missing references)
			uint num = 2166136261u;
			float[] array = new float[6] { position.x, position.y, position.z, forward.x, forward.y, forward.z };
			foreach (float value in array)
			{
				num ^= (uint)BitConverter.SingleToInt32Bits(value);
				num *= 16777619;
			}
			num ^= (uint)shot;
			num *= 16777619;
			num ^= num >> 15;
			num *= 739982445;
			num ^= num >> 12;
			return (float)(num & 0xFFFFFF) / 16777216f;
		}

		[HarmonyPatch("ShootGun")]
		[HarmonyTranspiler]
		[HarmonyPriority(0)]
		private static IEnumerable<CodeInstruction> ScaleShot(IEnumerable<CodeInstruction> instructions)
		{
			List<CodeInstruction> list = instructions.ToList();
			MethodInfo sphereCast = AccessTools.Method(typeof(Physics), "SphereCastNonAlloc", new Type[6]
			{
				typeof(Ray),
				typeof(float),
				typeof(RaycastHit[]),
				typeof(float),
				typeof(int),
				typeof(QueryTriggerInteraction)
			}, (Type[])null);
			int num = list.FindIndex((CodeInstruction i) => CodeInstructionExtensions.Calls(i, sphereCast));
			int num2 = ((num < 0) ? (-1) : list.FindLastIndex(num, (CodeInstruction i) => IsFloat(i, 15f)));
			int num3 = ((num < 0) ? (-1) : list.FindIndex(num, (CodeInstruction i) => IsFloat(i, 3.7f)));
			int num4 = ((num3 < 0) ? (-1) : list.FindIndex(num3, (CodeInstruction i) => IsFloat(i, 6f)));
			int num5 = ((num3 < 0) ? (-1) : list.FindIndex(num3, (CodeInstruction i) => CodeInstructionExtensions.IsStloc(i, (LocalBuilder)null)));
			int num6 = ((num4 < 0) ? (-1) : list.FindIndex(num4, (CodeInstruction i) => CodeInstructionExtensions.IsStloc(i, (LocalBuilder)null)));
			int num7 = ((num6 < 0) ? (-1) : list.FindIndex(num6 + 1, (CodeInstruction i) => CodeInstructionExtensions.IsStloc(i, (LocalBuilder)null)));
			if (new int[6] { num2, num3, num4, num5, num6, num7 }.Any((int x) => x < 0))
			{
				Plugin.logger.LogError((object)"Could not find shotgun range / damage values; shotgun damage and range bonuses disabled");
				return instructions;
			}
			InsertBeforeStore(list, num7, "ScaleDamage");
			InsertBeforeStore(list, num6, "ScaleDamage");
			InsertAfterConstant(list, num4, "ScaleBand");
			InsertBeforeStore(list, num5, "ScaleDamage");
			InsertAfterConstant(list, num3, "ScaleBand");
			InsertAfterConstant(list, num2, "ScaleReach");
			return list;
		}

		private static bool IsFloat(CodeInstruction instruction, float value)
		{
			if (instruction.opcode == OpCodes.Ldc_R4 && instruction.operand is float num)
			{
				return num == value;
			}
			return false;
		}

		private static void InsertAfterConstant(List<CodeInstruction> codes, int index, string method)
		{
			int i;
			for (i = index + 1; i < codes.Count && codes[i].opcode == OpCodes.Call && codes[i].operand is MethodInfo { IsStatic: not false } methodInfo; i++)
			{
				if (!(methodInfo.ReturnType == typeof(float)))
				{
					break;
				}
				if (methodInfo.GetParameters().Length != 1)
				{
					break;
				}
			}
			codes.InsertRange(i, Call(method));
		}

		private static void InsertBeforeStore(List<CodeInstruction> codes, int index, string method)
		{
			CodeInstruction[] array = Call(method);
			array[0].labels.AddRange(codes[index].labels);
			codes[index].labels.Clear();
			codes.InsertRange(index, array);
		}

		private static CodeInstruction[] Call(string method)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Expected O, but got Unknown
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			return (CodeInstruction[])(object)new CodeInstruction[2]
			{
				new CodeInstruction(OpCodes.Ldarg_0, (object)null),
				new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(ShotgunPatches), method, (Type[])null, (Type[])null))
			};
		}

		public static float ScaleReach(float castLength, ShotgunItem shotgun)
		{
			return 10f + (castLength - 10f) * GearStats.ShotgunRange(shotgun);
		}

		public static float ScaleBand(float distance, ShotgunItem shotgun)
		{
			return distance * GearStats.ShotgunRange(shotgun);
		}

		public static int ScaleDamage(int force, ShotgunItem shotgun)
		{
			float num = (float)force * GearStats.ShotgunDamage(shotgun);
			int num2 = Mathf.FloorToInt(num);
			if (!(Random.value < num - (float)num2))
			{
				return num2;
			}
			return num2 + 1;
		}

		[HarmonyPatch(typeof(PlayerControllerB), "DestroyItemInSlotAndSync")]
		[HarmonyPrefix]
		private static void CreditReload(PlayerControllerB __instance, int itemSlot)
		{
			if (((NetworkBehaviour)__instance).IsOwner)
			{
				GrabbableObject currentlyHeldObjectServer = __instance.currentlyHeldObjectServer;
				ShotgunItem val = (ShotgunItem)(object)((currentlyHeldObjectServer is ShotgunItem) ? currentlyHeldObjectServer : null);
				if (val != null && val.isReloading && ((itemSlot == 50) ? __instance.ItemOnlySlot : ((itemSlot >= 0 && itemSlot < __instance.ItemSlots.Length) ? __instance.ItemSlots[itemSlot] : null)) is GunAmmo)
				{
					GearProgress.AddXp((GrabbableObject)(object)val, GearConfig.ShotgunXpPerReload.Value);
				}
			}
		}

		[HarmonyPatch("GetItemDataToSave")]
		[HarmonyPostfix]
		private static void SaveXp(ShotgunItem __instance, ref int __result)
		{
			int num = Mathf.RoundToInt(GearProgress.GetXp((GrabbableObject)(object)__instance));
			__result = (__result & 3) | (num << 2);
		}

		[HarmonyPatch("LoadItemSaveData")]
		[HarmonyPostfix]
		private static void LoadXp(ShotgunItem __instance, int saveData)
		{
			__instance.shellsLoaded = Mathf.Min(saveData & 3, 2);
			int num = saveData >> 2;
			if (num > 0)
			{
				GearItem.Get((GrabbableObject)(object)__instance).loadedXp = num;
			}
		}
	}
	[HarmonyPatch]
	internal static class ShotgunReloadPatches
	{
		private static MethodBase TargetMethod()
		{
			return AccessTools.EnumeratorMoveNext((MethodBase)AccessTools.Method(typeof(ShotgunItem), "reloadGunAnimation", (Type[])null, (Type[])null));
		}

		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, MethodBase original)
		{
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Expected O, but got Unknown
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Expected O, but got Unknown
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Expected O, but got Unknown
			List<CodeInstruction> list = instructions.ToList();
			ConstructorInfo objB = AccessTools.Constructor(typeof(WaitForSeconds), new Type[1] { typeof(float) }, false);
			FieldInfo fieldInfo = AccessTools.Field(original.DeclaringType, "<>4__this");
			MethodInfo methodInfo = AccessTools.Method(typeof(ShotgunReloadPatches), "ScaleWait", (Type[])null, (Type[])null);
			if (fieldInfo == null)
			{
				Plugin.logger.LogError((object)"Could not find the shotgun in its reload coroutine; reload speed bonus disabled");
				return list;
			}
			int num = 0;
			for (int num2 = list.Count - 1; num2 >= 0; num2--)
			{
				if (!(list[num2].opcode != OpCodes.Newobj) && object.Equals(list[num2].operand, objB))
				{
					CodeInstruction[] array = (CodeInstruction[])(object)new CodeInstruction[3]
					{
						new CodeInstruction(OpCodes.Ldarg_0, (object)null),
						new CodeInstruction(OpCodes.Ldfld, (object)fieldInfo),
						new CodeInstruction(OpCodes.Call, (object)methodInfo)
					};
					array[0].labels.AddRange(list[num2].labels);
					list[num2].labels.Clear();
					list.InsertRange(num2, array);
					num++;
				}
			}
			if (num == 0)
			{
				Plugin.logger.LogError((object)"Could not find the shotgun reload waits; reload speed bonus disabled");
			}
			return list;
		}

		public static float ScaleWait(float seconds, ShotgunItem shotgun)
		{
			return seconds / GearStats.ShotgunReloadSpeed(shotgun);
		}
	}
	[HarmonyPatch(typeof(ShotgunItem))]
	internal static class ShotgunReloadAnimationPatches
	{
		[HarmonyPatch("Update")]
		[HarmonyPostfix]
		private static void SyncReloadAnimation(ShotgunItem __instance)
		{
			if (!((Object)(object)__instance.gunAnimator == (Object)null))
			{
				GearItem gearItem = GearItem.Get((GrabbableObject)(object)__instance);
				float num = (__instance.isReloading ? GearStats.ShotgunReloadSpeed(__instance) : 1f);
				if (!Mathf.Approximately(num, gearItem.appliedReloadAnimSpeed))
				{
					Animator gunAnimator = __instance.gunAnimator;
					gunAnimator.speed *= num / gearItem.appliedReloadAnimSpeed;
					gearItem.appliedReloadAnimSpeed = num;
				}
			}
		}
	}
	[HarmonyPatch(typeof(Shovel))]
	internal static class ShovelPatches
	{
		private static readonly FieldRef<Shovel, Coroutine> reelingUpCoroutine = AccessTools.FieldRefAccess<Shovel, Coroutine>("reelingUpCoroutine");

		private static Shovel swingingShovel;

		private static int swingCrits;

		private static bool swingCritEffectsPlayed;

		[HarmonyPatch("reelUpShovel")]
		[HarmonyPrefix]
		private static bool ReplaceReelUp(Shovel __instance, ref IEnumerator __result)
		{
			__result = ReelUpShovel(__instance);
			return false;
		}

		private static IEnumerator ReelUpShovel(Shovel shovel)
		{
			float speed = GearStats.ShovelAttackSpeed(shovel);
			PlayerControllerB playerHeldBy = ((GrabbableObject)shovel).playerHeldBy;
			playerHeldBy.activatingItem = true;
			playerHeldBy.twoHanded = true;
			playerHeldBy.playerBodyAnimator.ResetTrigger("shovelHit");
			playerHeldBy.playerBodyAnimator.SetBool("reelingUp", true);
			shovel.shovelAudio.PlayOneShot(shovel.reelUp);
			shovel.ReelUpSFXServerRpc();
			yield return (object)new WaitForSeconds(0.35f / speed);
			yield return (object)new WaitUntil((Func<bool>)(() => !shovel.isHoldingButton || !((GrabbableObject)shovel).isHeld));
			shovel.SwingShovel(!((GrabbableObject)shovel).isHeld);
			yield return (object)new WaitForSeconds(0.13f / speed);
			yield return (object)new WaitForEndOfFrame();
			shovel.HitShovel(!((GrabbableObject)shovel).isHeld);
			yield return (object)new WaitForSeconds(0.3f / speed);
			shovel.reelingUp = false;
			reelingUpCoroutine.Invoke(shovel) = null;
		}

		[HarmonyPatch("HitShovel")]
		[HarmonyPrefix]
		private static void BeginSwing(Shovel __instance, bool cancel)
		{
			if (!cancel)
			{
				swingingShovel = __instance;
				WeaponHitPatches.attackingWeapon = (GrabbableObject)(object)__instance;
				swingCrits = WeaponCrits.Roll(GearStats.ShovelCritChance(__instance));
				swingCritEffectsPlayed = false;
			}
		}

		[HarmonyPatch("HitShovel")]
		[HarmonyFinalizer]
		private static void EndSwing()
		{
			swingingShovel = null;
			WeaponHitPatches.attackingWeapon = null;
			swingCrits = 0;
		}

		[HarmonyPatch("HitShovel")]
		[HarmonyTranspiler]
		[HarmonyPriority(0)]
		private static IEnumerable<CodeInstruction> CritAfterDamageMods(IEnumerable<CodeInstruction> instructions)
		{
			return WeaponCrits.InsertCritCall(instructions, AccessTools.Field(typeof(Shovel), "shovelHitForce"), AccessTools.Method(typeof(ShovelPatches), "ApplyCrit", (Type[])null, (Type[])null), "shovel");
		}

		public static int ApplyCrit(int force, Shovel shovel)
		{
			if ((Object)(object)shovel != (Object)(object)swingingShovel || swingCrits <= 0)
			{
				return force;
			}
			force = WeaponCrits.Apply(force, swingCrits, GearConfig.ShovelCritMultiplier.Value);
			if (!swingCritEffectsPlayed)
			{
				swingCritEffectsPlayed = true;
				if (GearConfig.ShovelCritEffects.Value)
				{
					WeaponCrits.PlayEffects((GrabbableObject)(object)shovel, shovel.shovelAudio, shovel.hitSFX, swingCrits);
				}
			}
			return force;
		}

		[HarmonyPatch("HitShovel")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> ScaleReach(IEnumerable<CodeInstruction> instructions)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Expected O, but got Unknown
			CodeMatcher val = new CodeMatcher(instructions, (ILGenerator)null).MatchForward(false, (CodeMatch[])(object)new CodeMatch[1]
			{
				new CodeMatch((OpCode?)OpCodes.Ldc_R4, (object)1.5f, (string)null)
			});
			if (val.IsInvalid)
			{
				Plugin.logger.LogError((object)"Could not find shovel reach; reach bonus disabled");
				return instructions;
			}
			val.Opcode = OpCodes.Ldarg_0;
			val.Operand = null;
			return val.Advance(1).Insert((CodeInstruction[])(object)new CodeInstruction[1]
			{
				new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(GearStats), "ShovelReach", (Type[])null, (Type[])null))
			}).InstructionEnumeration();
		}
	}
	[HarmonyPatch(typeof(WalkieTalkie))]
	internal static class WalkieTalkiePatches
	{
		[HarmonyPatch("Update")]
		[HarmonyPostfix]
		private static void GiveTalkXp(WalkieTalkie __instance)
		{
			if (((NetworkBehaviour)__instance).IsOwner && ((GrabbableObject)__instance).isBeingUsed && __instance.speakingIntoWalkieTalkie)
			{
				GearProgress.AddXp((GrabbableObject)(object)__instance, Time.deltaTime * GearConfig.WalkieTalkieXpPerSecond.Value);
			}
		}
	}
	[HarmonyPatch(typeof(EnemyAI), "HitEnemyOnLocalClient")]
	internal static class WeaponHitPatches
	{
		internal static GrabbableObject attackingWeapon;

		private static void Prefix(EnemyAI __instance, out bool __state)
		{
			__state = !__instance.isEnemyDead && __instance.enemyHP > 0;
		}

		private static void Postfix(EnemyAI __instance, bool __state)
		{
			if (__state && !((Object)(object)attackingWeapon == (Object)null) && !((Object)(object)__instance.enemyType == (Object)null) && __instance.enemyType.canDie)
			{
				bool flag = __instance.isEnemyDead || __instance.enemyHP <= 0;
				float amount;
				switch (GearTypes.Get(attackingWeapon))
				{
				default:
					return;
				case GearType.Shovel:
					amount = GearConfig.ShovelXpPerHit.Value + (flag ? GearConfig.ShovelXpPerKill.Value : 0f);
					break;
				case GearType.Shotgun:
					amount = GearConfig.ShotgunXpPerHit.Value + (flag ? GearConfig.ShotgunXpPerKill.Value : 0f);
					break;
				case GearType.Knife:
					amount = GearConfig.KnifeXpPerHit.Value + (flag ? GearConfig.KnifeXpPerKill.Value : 0f);
					break;
				}
				GearProgress.AddXp(attackingWeapon, amount);
			}
		}
	}
	[HarmonyPatch(typeof(PatcherTool))]
	internal static class ZapGunPatches
	{
		private const float vanillaHoldRange = 13f;

		private static readonly FieldRef<PatcherTool, float> currentDifficultyMultiplier = AccessTools.FieldRefAccess<PatcherTool, float>("currentDifficultyMultiplier");

		[HarmonyPatch("LateUpdate")]
		[HarmonyPostfix]
		private static void GiveZapXp(PatcherTool __instance)
		{
			if (((NetworkBehaviour)__instance).IsOwner && __instance.isShocking && ((GrabbableObject)__instance).isBeingUsed && __instance.shockedTargetScript is EnemyAICollisionDetect)
			{
				GearProgress.AddXp((GrabbableObject)(object)__instance, Time.deltaTime * GearConfig.ZapGunXpPerSecond.Value);
			}
		}

		[HarmonyPatch("BeginShockingAnomalyOnClient")]
		[HarmonyPostfix]
		private static void SlowDifficultyRamp(PatcherTool __instance)
		{
			currentDifficultyMultiplier.Invoke(__instance) /= GearStats.ZapGunControl(__instance);
		}

		[HarmonyPatch("StopShockingAnomalyOnClient")]
		[HarmonyPrefix]
		private static void RememberTarget(PatcherTool __instance, out EnemyAI __state)
		{
			IShockableWithGun shockedTargetScript = __instance.shockedTargetScript;
			__state = ((EnemyAICollisionDetect)(((shockedTargetScript is EnemyAICollisionDetect) ? shockedTargetScript : null)?)).mainScript;
		}

		[HarmonyPatch("StopShockingAnomalyOnClient")]
		[HarmonyPostfix]
		private static void ExtendStun(PatcherTool __instance, EnemyAI __state)
		{
			if (!((Object)(object)__state == (Object)null) && !__state.isEnemyDead && __state.stunnedIndefinitely <= 0 && !((Object)(object)__state.enemyType == (Object)null) && __state.enemyType.canBeStunned)
			{
				__state.stunNormalizedTimer = Mathf.Max(__state.stunNormalizedTimer, 0.25f * GearStats.ZapGunStun(__instance));
			}
		}

		[HarmonyPatch("GunMeetsConditionsToShock")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> ScaleHoldRange(IEnumerable<CodeInstruction> instructions)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Expected O, but got Unknown
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Expected O, but got Unknown
			CodeMatcher val = new CodeMatcher(instructions, (ILGenerator)null).MatchForward(false, (CodeMatch[])(object)new CodeMatch[1]
			{
				new CodeMatch((OpCode?)OpCodes.Ldc_R4, (object)13f, (string)null)
			});
			if (val.IsInvalid)
			{
				Plugin.logger.LogError((object)"Could not find zap gun range; range bonus disabled");
				return instructions;
			}
			return val.Advance(1).Insert((CodeInstruction[])(object)new CodeInstruction[2]
			{
				new CodeInstruction(OpCodes.Ldarg_0, (object)null),
				new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(ZapGunPatches), "ScaleRange", (Type[])null, (Type[])null))
			}).InstructionEnumeration();
		}

		public static float ScaleRange(float distance, PatcherTool zapGun)
		{
			return distance * GearStats.ZapGunRange(zapGun);
		}
	}
	[HarmonyPatch]
	internal static class ZapGunScanPatches
	{
		private static MethodBase TargetMethod()
		{
			return AccessTools.EnumeratorMoveNext((MethodBase)AccessTools.Method(typeof(PatcherTool), "ScanGun", (Type[])null, (Type[])null));
		}

		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, MethodBase original)
		{
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Expected O, but got Unknown
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Expected O, but got Unknown
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Expected O, but got Unknown
			List<CodeInstruction> list = instructions.ToList();
			FieldInfo fieldInfo = AccessTools.Field(original.DeclaringType, "<>4__this");
			int num = list.FindIndex((CodeInstruction i) => i.opcode == OpCodes.Call && i.operand is MethodInfo methodInfo && methodInfo.Name == "SphereCastNonAlloc");
			int num2 = ((num < 0) ? (-1) : list.FindLastIndex(num, (CodeInstruction i) => i.opcode == OpCodes.Ldc_R4));
			if (fieldInfo == null || num2 < 0)
			{
				Plugin.logger.LogError((object)"Could not find the zap gun's scan distance; it won't lock on from further away");
				return list;
			}
			list.InsertRange(num2 + 1, (IEnumerable<CodeInstruction>)(object)new CodeInstruction[3]
			{
				new CodeInstruction(OpCodes.Ldarg_0, (object)null),
				new CodeInstruction(OpCodes.Ldfld, (object)fieldInfo),
				new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(ZapGunPatches), "ScaleRange", (Type[])null, (Type[])null))
			});
			return list;
		}
	}
}
namespace LethalGearLeveling.Core
{
	internal static class FlashlightLights
	{
		private struct LightBase
		{
			public float intensity;

			public float spotAngle;

			public float range;
		}

		private static readonly Dictionary<Light, LightBase> bases = new Dictionary<Light, LightBase>();

		public static void Apply(Light light, float brightness, float size)
		{
			if (!bases.TryGetValue(light, out var value))
			{
				value = new LightBase
				{
					intensity = light.intensity,
					spotAngle = light.spotAngle,
					range = light.range
				};
				bases[light] = value;
			}
			if (Mathf.Approximately(light.intensity, value.intensity) || Mathf.Approximately(light.intensity, value.intensity * brightness))
			{
				light.intensity = value.intensity * brightness;
			}
			light.spotAngle = Mathf.Min(value.spotAngle * size, 170f);
			light.range = value.range * size;
		}

		public static void Clear()
		{
			bases.Clear();
		}
	}
	internal static class GearConfig
	{
		public static ConfigEntry<string> XpThresholds;

		public static ConfigEntry<float> XpMultiplier;

		public static ConfigEntry<bool> AnnounceLevelUps;

		public static ConfigEntry<bool> ShowHud;

		public static ConfigEntry<bool> ShowStats;

		public static ConfigEntry<float> TintStrength;

		public static ConfigEntry<bool> EndOfDayXpEnabled;

		public static ConfigEntry<float> EndOfDayXp;

		public static ConfigEntry<bool> EndOfDayXpScales;

		public static ConfigEntry<bool> EndOfDayXpRequiresTrip;

		public static ConfigEntry<float> GlowStrength;

		public static ConfigEntry<float> ShovelXpPerHit;

		public static ConfigEntry<float> ShovelXpPerKill;

		public static ConfigEntry<float> ShovelCritChancePerLevel;

		public static ConfigEntry<float> ShovelCritMultiplier;

		public static ConfigEntry<bool> ShovelCritEffects;

		public static ConfigEntry<float> ShovelAttackSpeedPerLevel;

		public static ConfigEntry<float> ShovelReachPerLevel;

		public static ConfigEntry<float> FlashlightXpPerSecond;

		public static ConfigEntry<float> FlashlightBrightnessPerLevel;

		public static ConfigEntry<float> FlashlightLightSizePerLevel;

		public static ConfigEntry<float> FlashlightBatteryPerLevel;

		public static ConfigEntry<float> LockPickerXpPerDoor;

		public static ConfigEntry<float> LockPickerSpeedPerLevel;

		public static ConfigEntry<float> JetpackXpPerMetre;

		public static ConfigEntry<float> JetpackSpeedPerLevel;

		public static ConfigEntry<float> JetpackSafetyPerLevel;

		public static ConfigEntry<float> JetpackBatteryPerLevel;

		public static ConfigEntry<float> ShotgunXpPerHit;

		public static ConfigEntry<float> ShotgunXpPerKill;

		public static ConfigEntry<float> ShotgunXpPerReload;

		public static ConfigEntry<float> ShotgunDamagePerLevel;

		public static ConfigEntry<float> ShotgunRangePerLevel;

		public static ConfigEntry<float> ShotgunAmmoSavePerLevel;

		public static ConfigEntry<float> ShotgunReloadSpeedPerLevel;

		public static ConfigEntry<float> KnifeXpPerHit;

		public static ConfigEntry<float> KnifeXpPerKill;

		public static ConfigEntry<float> KnifeCritChancePerLevel;

		public static ConfigEntry<float> KnifeCritMultiplier;

		public static ConfigEntry<bool> KnifeCritEffects;

		public static ConfigEntry<float> KnifeAttackSpeedPerLevel;

		public static ConfigEntry<float> KnifeReachPerLevel;

		public static ConfigEntry<float> ZapGunXpPerSecond;

		public static ConfigEntry<float> ZapGunRangePerLevel;

		public static ConfigEntry<float> ZapGunControlPerLevel;

		public static ConfigEntry<float> ZapGunStunPerLevel;

		public static ConfigEntry<float> ZapGunBatteryPerLevel;

		public static ConfigEntry<float> RadarBoosterXpPerMinute;

		public static ConfigEntry<float> RadarBoosterXpPerEnemyFlashed;

		public static ConfigEntry<float> RadarBoosterEnemyCooldown;

		public static ConfigEntry<float> RadarBoosterFlashStunPerLevel;

		public static ConfigEntry<float> RadarBoosterFlashRadiusPerLevel;

		public static ConfigEntry<float> WalkieTalkieXpPerSecond;

		public static ConfigEntry<float> WalkieTalkieBatteryPerLevel;

		public static ConfigEntry<bool> DebugXpKeys;

		public static void Init(ConfigFile config)
		{
			XpThresholds = config.Bind<string>("General", "XpThresholds", "100,250,450,750,1150,1700,2450,3500,5000", "Total XP needed to reach Lv2 through Lv10. Must be 9 ascending numbers. Tiers: Lv1-2 Normal, 3-4 Green, 5-6 Blue, 7-8 Purple, 9-10 Orange.");
			XpMultiplier = config.Bind<float>("General", "XpMultiplier", 1f, "Multiplier applied to all XP gained.");
			AnnounceLevelUps = config.Bind<bool>("General", "AnnounceLevelUps", true, "Show a tip popup when any item levels up.");
			ShowHud = config.Bind<bool>("General", "ShowHud", true, "Show the level / XP readout for the held item.");
			ShowStats = config.Bind<bool>("General", "ShowStats", true, "Show the held item's current level bonuses under its XP bar.");
			TintStrength = config.Bind<float>("General", "TintStrength", 0.55f, "How strongly levelled items are tinted with their tier colour (0 - 1).");
			GlowStrength = config.Bind<float>("General", "GlowStrength", 1f, "How strongly levelled items glow in their tier colour at Orange (Green is a quarter of this). 0 disables the glow.");
			EndOfDayXpEnabled = config.Bind<bool>("End of day XP", "Enabled", true, "Give gear a flat XP bonus when the ship leaves a moon.");
			EndOfDayXp = config.Bind<float>("End of day XP", "Xp", 50f, "End of day XP per item (before scaling).");
			EndOfDayXpScales = config.Bind<bool>("End of day XP", "ScaleWithLoot", true, "Scale the bonus by the share of the moon's scrap value brought back (all of it = full XP). Off = always the full amount.");
			EndOfDayXpRequiresTrip = config.Bind<bool>("End of day XP", "RequireLeavingShip", true, "Only gear that left the ship during the day and made it back gets the bonus, so gear can't level by sitting in the ship.");
			ShovelXpPerHit = config.Bind<float>("Shovel", "XpPerHit", 10f, "XP for hitting a killable enemy. Also applies to stop/yield signs.");
			ShovelXpPerKill = config.Bind<float>("Shovel", "XpPerKill", 25f, "Bonus XP when the hit kills the enemy.");
			ShovelCritChancePerLevel = config.Bind<float>("Shovel", "CritChancePerLevel", 0.125f, "Crit chance per level gained. Over 100% means guaranteed crits plus a chance of another (1.125 = always 1 crit, 12.5% chance of 2).");
			ShovelCritMultiplier = config.Bind<float>("Shovel", "CritMultiplier", 1.5f, "Each crit multiplies damage by this, rounded up, and always adds at least 1. Set to 1 for a flat +1 per crit.");
			ShovelCritEffects = config.Bind<bool>("Shovel", "CritEffects", true, "Play a sharper hit sound and a small camera shake on crits.");
			ShovelAttackSpeedPerLevel = config.Bind<float>("Shovel", "AttackSpeedPerLevel", 0.05f, "Swing speed increase per level gained (0.05 = 5%).");
			ShovelReachPerLevel = config.Bind<float>("Shovel", "ReachPerLevel", 0.075f, "Extra reach in metres per level gained (vanilla reach is 1.5).");
			FlashlightXpPerSecond = config.Bind<float>("Flashlight", "XpPerSecond", 1f, "XP per second the light is on inside the facility.");
			FlashlightBrightnessPerLevel = config.Bind<float>("Flashlight", "BrightnessPerLevel", 0.075f, "Brightness increase per level gained.");
			FlashlightLightSizePerLevel = config.Bind<float>("Flashlight", "LightSizePerLevel", 0.05f, "Beam width and range increase per level gained.");
			FlashlightBatteryPerLevel = config.Bind<float>("Flashlight", "BatteryPerLevel", 0.05f, "Battery drain reduction per level gained.");
			LockPickerXpPerDoor = config.Bind<float>("Lockpicker", "XpPerDoor", 50f, "XP per door unlocked.");
			LockPickerSpeedPerLevel = config.Bind<float>("Lockpicker", "SpeedPerLevel", 0.125f, "Lock picking speed increase per level gained.");
			JetpackXpPerMetre = config.Bind<float>("Jetpack", "XpPerMetre", 0.2f, "XP per metre flown (0.2 = 1 XP every 5m).");
			JetpackSpeedPerLevel = config.Bind<float>("Jetpack", "SpeedPerLevel", 0.05f, "Thrust increase per level gained.");
			JetpackSafetyPerLevel = config.Bind<float>("Jetpack", "SafetyPerLevel", 7.5f, "Extra impact speed tolerated before a fatal crash, per level gained (vanilla threshold is 50).");
			JetpackBatteryPerLevel = config.Bind<float>("Jetpack", "BatteryPerLevel", 0.05f, "Battery drain reduction per level gained.");
			ShotgunXpPerHit = config.Bind<float>("Shotgun", "XpPerHit", 20f, "XP for each killable enemy a shot hits.");
			ShotgunXpPerKill = config.Bind<float>("Shotgun", "XpPerKill", 25f, "Bonus XP when the shot kills the enemy.");
			ShotgunXpPerReload = config.Bind<float>("Shotgun", "XpPerReload", 10f, "XP for each shell loaded.");
			ShotgunDamagePerLevel = config.Bind<float>("Shotgun", "DamagePerLevel", 0.1f, "Damage increase per level gained (vanilla is 5 / 3 / 2 at close / mid / long range). Fractions round up by chance, so 2.2 is 3 one time in five.");
			ShotgunRangePerLevel = config.Bind<float>("Shotgun", "RangePerLevel", 0.05f, "Increase per level gained to the distance of each damage band and how far the shot reaches.");
			ShotgunAmmoSavePerLevel = config.Bind<float>("Shotgun", "AmmoSavePerLevel", 0.05f, "Chance per level gained for a shot not to use up a shell.");
			ShotgunReloadSpeedPerLevel = config.Bind<float>("Shotgun", "ReloadSpeedPerLevel", 0.05f, "Reload speed increase per level gained (stacks with LateGameUpgrades' Sleight of Hand).");
			KnifeXpPerHit = config.Bind<float>("Knife", "XpPerHit", 5f, "XP for hitting a killable enemy (the knife strikes about twice as often as the shovel).");
			KnifeXpPerKill = config.Bind<float>("Knife", "XpPerKill", 25f, "Bonus XP when the hit kills the enemy.");
			KnifeCritChancePerLevel = config.Bind<float>("Knife", "CritChancePerLevel", 0.125f, "Crit chance per level gained. Over 100% means guaranteed crits plus a chance of another (1.125 = always 1 crit, 12.5% chance of 2).");
			KnifeCritMultiplier = config.Bind<float>("Knife", "CritMultiplier", 1.5f, "Each crit multiplies damage by this, rounded up, and always adds at least 1. Set to 1 for a flat +1 per crit.");
			KnifeCritEffects = config.Bind<bool>("Knife", "CritEffects", true, "Play a sharper hit sound and a small camera shake on crits.");
			KnifeAttackSpeedPerLevel = config.Bind<float>("Knife", "AttackSpeedPerLevel", 0.05f, "Stab speed increase per level gained (0.05 = 5%).");
			KnifeReachPerLevel = config.Bind<float>("Knife", "ReachPerLevel", 0.05f, "Extra reach in metres per level gained (vanilla reach is 0.75).");
			ZapGunXpPerSecond = config.Bind<float>("Zap gun", "XpPerSecond", 3f, "XP per second spent zapping an enemy.");
			ZapGunRangePerLevel = config.Bind<float>("Zap gun", "RangePerLevel", 0.05f, "Increase per level gained to how far away the gun can lock on to and hold a target (vanilla holds up to 13m).");
			ZapGunControlPerLevel = config.Bind<float>("Zap gun", "ControlPerLevel", 0.1f, "Per level gained, how much longer the beam takes to become hard to hold (0.1 = 10% longer).");
			ZapGunStunPerLevel = config.Bind<float>("Zap gun", "StunPerLevel", 0.25f, "Increase per level gained to how long enemies stay stunned after the zap ends.");
			ZapGunBatteryPerLevel = config.Bind<float>("Zap gun", "BatteryPerLevel", 0.05f, "Battery drain reduction per level gained.");
			RadarBoosterXpPerMinute = config.Bind<float>("Radar booster", "XpPerMinute", 10f, "XP per minute switched on outside the ship.");
			RadarBoosterXpPerEnemyFlashed = config.Bind<float>("Radar booster", "XpPerEnemyFlashed", 15f, "XP for each enemy stunned by a flash.");
			RadarBoosterEnemyCooldown = config.Bind<float>("Radar booster", "EnemyCooldown", 10f, "Seconds before flashing the same enemy can earn XP again, so one trapped enemy can't be farmed.");
			RadarBoosterFlashStunPerLevel = config.Bind<float>("Radar booster", "FlashStunPerLevel", 0.1f, "Increase per level gained to how long a flash stuns enemies.");
			RadarBoosterFlashRadiusPerLevel = config.Bind<float>("Radar booster", "FlashRadiusPerLevel", 0.05f, "Increase per level gained to the flash's stun radius (vanilla 12m).");
			WalkieTalkieXpPerSecond = config.Bind<float>("Walkie-talkie", "XpPerSecond", 2f, "XP per second spent talking into it. Unlike other gear, this also counts inside the ship.");
			WalkieTalkieBatteryPerLevel = config.Bind<float>("Walkie-talkie", "BatteryPerLevel", 0.08f, "Battery drain reduction per level gained.");
			DebugXpKeys = config.Bind<bool>("Debug", "DebugXpKeys", false, "Host only: Page Up / Page Down / Home sets the held item's level up one / down one / to max. Ignores the moon and ship XP rules.");
		}
	}
	internal static class GearHudText
	{
		private const int statsPerLine = 2;

		public static IEnumerable<string> ExtraLines(GrabbableObject item, int level)
		{
			ShotgunItem val = (ShotgunItem)(object)((item is ShotgunItem) ? item : null);
			if (val != null)
			{
				yield return Shells(val);
			}
			if (GearConfig.ShowStats.Value && level > 1)
			{
				List<string> stats = Stats(item);
				for (int i = 0; i < stats.Count; i += 2)
				{
					yield return string.Join(", ", stats.Skip(i).Take(2));
				}
			}
		}

		private static List<string> Stats(GrabbableObject item)
		{
			Shovel val = (Shovel)(object)((item is Shovel) ? item : null);
			if (val == null)
			{
				FlashlightItem val2 = (FlashlightItem)(object)((item is FlashlightItem) ? item : null);
				if (val2 == null)
				{
					LockPicker val3 = (LockPicker)(object)((item is LockPicker) ? item : null);
					if (val3 != null)
					{
						return new List<string> { "Pick speed " + Bonus(GearStats.LockPickerSpeed(val3)) };
					}
					JetpackItem val4 = (JetpackItem)(object)((item is JetpackItem) ? item : null);
					if (val4 != null)
					{
						return new List<string>
						{
							"Thrust " + Bonus(GearStats.JetpackSpeed(val4)),
							$"Crash safety +{GearStats.JetpackCrashThreshold(val4) - 50f:0.#}",
							"Battery drain -" + Percent(1f - GearStats.BatteryDrain((GrabbableObject)(object)val4))
						};
					}
					ShotgunItem val5 = (ShotgunItem)(object)((item is ShotgunItem) ? item : null);
					if (val5 != null)
					{
						return new List<string>
						{
							"Damage " + Bonus(GearStats.ShotgunDamage(val5)),
							"Range " + Bonus(GearStats.ShotgunRange(val5)),
							"Reload " + Bonus(GearStats.ShotgunReloadSpeed(val5)),
							"Free shot " + Percent(GearStats.ShotgunAmmoSave(val5))
						};
					}
					KnifeItem val6 = (KnifeItem)(object)((item is KnifeItem) ? item : null);
					if (val6 != null)
					{
						return new List<string>
						{
							"Crit " + Percent(GearStats.KnifeCritChance(val6)),
							"Stab " + Bonus(GearStats.KnifeAttackSpeed(val6)),
							$"Reach +{GearStats.KnifeReach(val6) - 0.75f:0.##}m"
						};
					}
					PatcherTool val7 = (PatcherTool)(object)((item is PatcherTool) ? item : null);
					if (val7 != null)
					{
						return new List<string>
						{
							"Range " + Bonus(GearStats.ZapGunRange(val7)),
							"Control " + Bonus(GearStats.ZapGunControl(val7)),
							"Stun " + Bonus(GearStats.ZapGunStun(val7)),
							"Battery drain -" + Percent(1f - GearStats.BatteryDrain((GrabbableObject)(object)val7))
						};
					}
					RadarBoosterItem val8 = (RadarBoosterItem)(object)((item is RadarBoosterItem) ? item : null);
					if (val8 != null)
					{
						return new List<string>
						{
							"Flash stun " + Bonus(GearStats.RadarFlashStun(val8)),
							"Radius " + Bonus(GearStats.RadarFlashRadius(val8))
						};
					}
					WalkieTalkie val9 = (WalkieTalkie)(object)((item is WalkieTalkie) ? item : null);
					if (val9 != null)
					{
						return new List<string> { "Battery drain -" + Percent(1f - GearStats.BatteryDrain((GrabbableObject)(object)val9)) };
					}
				}
				else if (GearTypes.Get((GrabbableObject)(object)val2) == GearType.Flashlight)
				{
					return new List<string>
					{
						"Brightness " + Bonus(GearStats.FlashlightBrightness(val2)),
						"Beam " + Bonus(GearStats.FlashlightLightSize(val2)),
						"Battery drain -" + Percent(1f - GearStats.BatteryDrain((GrabbableObject)(object)val2))
					};
				}
				return new List<string>();
			}
			return new List<string>
			{
				"Crit " + Percent(GearStats.ShovelCritChance(val)),
				"Swing " + Bonus(GearStats.ShovelAttackSpeed(val)),
				$"Reach +{GearStats.ShovelReach(val) - 1.5f:0.##}m"
			};
		}

		private static string Shells(ShotgunItem shotgun)
		{
			int num = 0;
			PlayerControllerB playerHeldBy = ((GrabbableObject)shotgun).playerHeldBy;
			if ((Object)(object)playerHeldBy != (Object)null)
			{
				foreach (GrabbableObject item in playerHeldBy.ItemSlots.Append(playerHeldBy.ItemOnlySlot))
				{
					GunAmmo val = (GunAmmo)(object)((item is GunAmmo) ? item : null);
					if (val != null && val.ammoType == shotgun.gunCompatibleAmmoID)
					{
						num++;
					}
				}
			}
			return $"Shells {shotgun.shellsLoaded}/2, {num} spare";
		}

		private static string Bonus(float multiplier)
		{
			return "+" + Percent(multiplier - 1f);
		}

		private static string Percent(float fraction)
		{
			return $"{Mathf.RoundToInt(fraction * 100f)}%";
		}
	}
	internal class GearItem : MonoBehaviour
	{
		private struct TintedMaterial
		{
			public Material material;

			public Color originalBase;

			public Color originalEmissive;

			public bool hasEmissive;
		}

		private static readonly int baseColorId = Shader.PropertyToID("_BaseColor");

		private static readonly int emissiveColorId = Shader.PropertyToID("_EmissiveColor");

		private readonly List<TintedMaterial> tintedMaterials = new List<TintedMaterial>();

		private bool materialsCached;

		private int appliedLevel = 1;

		public float xp;

		public float loadedXp;

		public bool leftShipToday;

		public AudioSource critAudio;

		public float appliedReloadAnimSpeed = 1f;

		public int shotsSinceReload;

		public float? baseJetpackAcceleration;

		public Vector3? lastJetpackPosition;

		public static GearItem Get(GrabbableObject item)
		{
			GearItem component = ((Component)item).GetComponent<GearItem>();
			if ((Object)(object)component != (Object)null)
			{
				return component;
			}
			component = ((Component)item).gameObject.AddComponent<GearItem>();
			if (((NetworkBehaviour)item).IsSpawned)
			{
				component.xp = GearProgress.StoredXp(((NetworkBehaviour)item).NetworkObjectId);
			}
			return component;
		}

		public void ApplyLevel(int level)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: 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_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: 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_00c6: Unknown result type (might be due to invalid IL or missing references)
			if (level == appliedLevel)
			{
				return;
			}
			appliedLevel = level;
			CacheMaterials();
			int num = GearLevels.TierIndex(level);
			Color val = GearLevels.TierColour(level);
			float num2 = ((num == 0) ? 0f : Mathf.Clamp01(GearConfig.TintStrength.Value));
			Color val2 = ((num == 0) ? Color.black : (val * (GearConfig.GlowStrength.Value * (float)num / 4f)));
			foreach (TintedMaterial tintedMaterial in tintedMaterials)
			{
				if (!((Object)(object)tintedMaterial.material == (Object)null))
				{
					tintedMaterial.material.SetColor(baseColorId, Color.Lerp(tintedMaterial.originalBase, val, num2));
					if (tintedMaterial.hasEmissive)
					{
						tintedMaterial.material.SetColor(emissiveColorId, tintedMaterial.originalEmissive + val2);
					}
				}
			}
		}

		private void CacheMaterials()
		{
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			if (materialsCached)
			{
				return;
			}
			materialsCached = true;
			Renderer[] componentsInChildren = ((Component)this).GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val in componentsInChildren)
			{
				if ((!(val is MeshRenderer) && !(val is SkinnedMeshRenderer)) || (Object)(object)((Component)val).GetComponent<ScanNodeProperties>() != (Object)null)
				{
					continue;
				}
				Material[] materials = val.materials;
				foreach (Material val2 in materials)
				{
					if (val2.HasProperty(baseColorId))
					{
						bool flag = val2.HasProperty(emissiveColorId);
						tintedMaterials.Add(new TintedMaterial
						{
							material = val2,
							originalBase = val2.GetColor(baseColorId),
							originalEmissive = (flag ? val2.GetColor(emissiveColorId) : Color.black),
							hasEmissive = flag
						});
					}
				}
			}
		}
	}
	internal static class GearLevels
	{
		public const int MaxLevel = 10;

		private const int levelsPerTier = 2;

		private static readonly string[] tierNames = new string[5] { "Normal", "Green", "Blue", "Purple", "Orange" };

		private static readonly Color[] tierColours = (Color[])(object)new Color[5]
		{
			Color.white,
			new Color(0.25f, 0.9f, 0.3f),
			new Color(0.25f, 0.55f, 1f),
			new Color(0.7f, 0.3f, 1f),
			new Color(1f, 0.55f, 0.1f)
		};

		private static readonly float[] defaultThresholds = new float[9] { 100f, 250f, 450f, 750f, 1150f, 1700f, 2450f, 3500f, 5000f };

		private static string parsedFrom;

		private static float[] thresholds = defaultThresholds;

		private static float[] Thresholds
		{
			get
			{
				string value = GearConfig.XpThresholds.Value;
				if (value != parsedFrom)
				{
					parsedFrom = value;
					thresholds = Parse(value);
				}
				return thresholds;
			}
		}

		public static float MaxXp => Thresholds[Thresholds.Length - 1];

		public static int GetLevel(float xp)
		{
			int num = 1;
			float[] array = Thresholds;
			foreach (float num2 in array)
			{
				if (xp >= num2)
				{
					num++;
				}
			}
			return num;
		}

		public static float XpForNextLevel(int level)
		{
			return Thresholds[Mathf.Clamp(level - 1, 0, Thresholds.Length - 1)];
		}

		public static float XpForLevel(int level)
		{
			if (level > 1)
			{
				return Thresholds[Mathf.Clamp(level - 2, 0, Thresholds.Length - 1)];
			}
			return 0f;
		}

		public static string TierName(int level)
		{
			return tierNames[TierIndex(level)];
		}

		public static Color TierColour(int level)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			return tierColours[TierIndex(level)];
		}

		public static int TierIndex(int level)
		{
			return Mathf.Clamp((level - 1) / 2, 0, tierNames.Length - 1);
		}

		private static float[] Parse(string raw)
		{
			try
			{
				float[] array = (from x in raw.Split(',')
					select float.Parse(x.Trim(), CultureInfo.InvariantCulture)).ToArray();
				bool flag = array.Zip(array.Skip(1), (float a, float b) => b > a).All((bool x) => x);
				if (array.Length == 9 && array[0] > 0f && flag)
				{
					return array;
				}
			}
			catch
			{
			}
			Plugin.logger.LogWarning((object)("Invalid XpThresholds '" + raw + "', using defaults"));
			return defaultThresholds;
		}
	}
	internal static class GearNetwork
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static HandleNamedMessageDelegate <0>__OnAddXp;

			public static HandleNamedMessageDelegate <1>__OnRequestSync;

			public static HandleNamedMessageDelegate <2>__OnSync;
		}

		private const string addXpMessage = "com.shronkstones.lethalgearleveling.AddXp";

		private const string requestSyncMessage = "com.shronkstones.lethalgearleveling.RequestSync";

		private const string syncMessage = "com.shronkstones.lethalgearleveling.Sync";

		private const float maxXpPerMessage = 200f;

		private static CustomMessagingManager Messaging => NetworkManager.Singleton.CustomMessagingManager;

		public static void Register()
		{
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Expected O, but got Unknown
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected O, but got Unknown
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Expected O, but got Unknown
			if (NetworkManager.Singleton.IsServer)
			{
				CustomMessagingManager messaging = Messaging;
				object obj = <>O.<0>__OnAddXp;
				if (obj == null)
				{
					HandleNamedMessageDelegate val = OnAddXp;
					<>O.<0>__OnAddXp = val;
					obj = (object)val;
				}
				messaging.RegisterNamedMessageHandler("com.shronkstones.lethalgearleveling.AddXp", (HandleNamedMessageDelegate)obj);
				CustomMessagingManager messaging2 = Messaging;
				object obj2 = <>O.<1>__OnRequestSync;
				if (obj2 == null)
				{
					HandleNamedMessageDelegate val2 = OnRequestSync;
					<>O.<1>__OnRequestSync = val2;
					obj2 = (object)val2;
				}
				messaging2.RegisterNamedMessageHandler("com.shronkstones.lethalgearleveling.RequestSync", (HandleNamedMessageDelegate)obj2);
			}
			else
			{
				CustomMessagingManager messaging3 = Messaging;
				object obj3 = <>O.<2>__OnSync;
				if (obj3 == null)
				{
					HandleNamedMessageDelegate val3 = OnSync;
					<>O.<2>__OnSync = val3;
					obj3 = (object)val3;
				}
				messaging3.RegisterNamedMessageHandler("com.shronkstones.lethalgearleveling.Sync", (HandleNamedMessageDelegate)obj3);
			}
		}

		public static void Unregister()
		{
			if (!((Object)(object)NetworkManager.Singleton == (Object)null) && Messaging != null)
			{
				Messaging.UnregisterNamedMessageHandler("com.shronkstones.lethalgearleveling.AddXp");
				Messaging.UnregisterNamedMessageHandler("com.shronkstones.lethalgearleveling.RequestSync");
				Messaging.UnregisterNamedMessageHandler("com.shronkstones.lethalgearleveling.Sync");
			}
		}

		public unsafe static void SendAddXp(ulong id, float amount)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			FastBufferWriter val = default(FastBufferWriter);
			((FastBufferWriter)(ref val))..ctor(12, (Allocator)2, -1);
			try
			{
				((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref id, default(ForPrimitives));
				((FastBufferWriter)(ref val)).WriteValueSafe<float>(ref amount, default(ForPrimitives));
				Messaging.SendNamedMessage("com.shronkstones.lethalgearleveling.AddXp", 0uL, val, (NetworkDelivery)2);
			}
			finally
			{
				((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose();
			}
		}

		public unsafe static void RequestSync()
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			FastBufferWriter val = default(FastBufferWriter);
			((FastBufferWriter)(ref val))..ctor(0, (Allocator)2, -1);
			try
			{
				Messaging.SendNamedMessage("com.shronkstones.lethalgearleveling.RequestSync", 0uL, val, (NetworkDelivery)2);
			}
			finally
			{
				((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose();
			}
		}

		public static void BroadcastXp(ulong id, float xp, bool announce)
		{
			KeyValuePair<ulong, float>[] entries = new KeyValuePair<ulong, float>[1]
			{
				new KeyValuePair<ulong, float>(id, xp)
			};
			foreach (ulong connectedClientsId in NetworkManager.Singleton.ConnectedClientsIds)
			{
				if (connectedClientsId != 0L)
				{
					SendSync(connectedClientsId, entries, announce);
				}
			}
		}

		private unsafe static void SendSync(ulong clientId, ICollection<KeyValuePair<ulong, float>> entries, bool announce)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			int num = 5 + entries.Count * 12;
			FastBufferWriter val = default(FastBufferWriter);
			((FastBufferWriter)(ref val))..ctor(num, (Allocator)2, -1);
			try
			{
				((FastBufferWriter)(ref val)).WriteValueSafe<bool>(ref announce, default(ForPrimitives));
				int count = entries.Count;
				((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref count, default(ForPrimitives));
				foreach (KeyValuePair<ulong, float> entry in entries)
				{
					ulong key = entry.Key;
					((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref key, default(ForPrimitives));
					float value = entry.Value;
					((FastBufferWriter)(ref val)).WriteValueSafe<float>(ref value, default(ForPrimitives));
				}
				NetworkDelivery val2 = (NetworkDelivery)((num > 1000) ? 4 : 2);
				Messaging.SendNamedMessage("com.shronkstones.lethalgearleveling.Sync", clientId, val, val2);
			}
			finally
			{
				((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose();
			}
		}

		private static void OnAddXp(ulong senderClientId, FastBufferReader reader)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			ulong id = default(ulong);
			((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref id, default(ForPrimitives));
			float num = default(float);
			((FastBufferReader)(ref reader)).ReadValueSafe<float>(ref num, default(ForPrimitives));
			if (!(num <= 0f) && !float.IsNaN(num))
			{
				GearProgress.ServerAddXp(id, Math.Min(num, 200f));
			}
		}

		private static void OnRequestSync(ulong senderClientId, FastBufferReader reader)
		{
			SendSync(senderClientId, GearProgress.AllXp.ToList(), announce: false);
		}

		private static void OnSync(ulong senderClientId, FastBufferReader reader)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: 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_0034: 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)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			bool announce = default(bool);
			((FastBufferReader)(ref reader)).ReadValueSafe<bool>(ref announce, default(ForPrimitives));
			int num = default(int);
			((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num, default(ForPrimitives));
			ulong id = default(ulong);
			float xp = default(float);
			for (int i = 0; i < num; i++)
			{
				((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref id, default(ForPrimitives));
				((FastBufferReader)(ref reader)).ReadValueSafe<float>(ref xp, default(ForPrimitives));
				GearProgress.SetXp(id, xp, announce);
			}
		}
	}
	internal static class GearProgress
	{
		private static readonly Dictionary<ulong, float> xpByItem = new Dictionary<ulong, float>();

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

		public static IReadOnlyDictionary<ulong, float> AllXp => xpByItem;

		public static float GetXp(GrabbableObject item)
		{
			if ((Object)(object)item == (Object)null)
			{
				return 0f;
			}
			GearItem component = ((Component)item).GetComponent<GearItem>();
			if ((Object)(object)component != (Object)null)
			{
				return component.xp;
			}
			if (!((NetworkBehaviour)item).IsSpawned || !xpByItem.TryGetValue(((NetworkBehaviour)item).NetworkObjectId, out var value))
			{
				return 0f;
			}
			return value;
		}

		public static float StoredXp(ulong id)
		{
			if (!xpByItem.TryGetValue(id, out var value))
			{
				return 0f;
			}
			return value;
		}

		public static int GetLevel(GrabbableObject item)
		{
			return GearLevels.GetLevel(GetXp(item));
		}

		public static void AddXp(GrabbableObject item, float amount)
		{
			if (!((Object)(object)item == (Object)null) && ((NetworkBehaviour)item).IsSpawned && !(amount <= 0f) && GearTypes.Get(item) != GearType.None && IsOnMoon() && (!IsInShip(item) || GearTypes.Get(item) == GearType.WalkieTalkie) && GetLevel(item) < 10)
			{
				ulong networkObjectId = ((NetworkBehaviour)item).NetworkObjectId;
				pendingXp.TryGetValue(networkObjectId, out var value);
				pendingXp[networkObjectId] = value + amount * GearConfig.XpMultiplier.Value;
			}
		}

		internal static bool IsInShip(GrabbableObject item)
		{
			PlayerControllerB playerHeldBy = item.playerHeldBy;
			if ((Object)(object)playerHeldBy != (Object)null)
			{
				if (!playerHeldBy.isInHangarShipRoom)
				{
					return playerHeldBy.isInElevator;
				}
				return true;
			}
			if (!item.isInShipRoom)
			{
				return item.isInElevator;
			}
			return true;
		}

		internal static bool IsOnMoon()
		{
			StartOfRound instance = StartOfRound.Instance;
			if ((Object)(object)instance != (Object)null && !instance.inShipPhase && instance.shipHasLanded && (Object)(object)instance.currentLevel != (Object)null)
			{
				return instance.currentLevel.planetHasTime;
			}
			return false;
		}

		public static void FlushPending()
		{
			if (pendingXp.Count == 0 || (Object)(object)NetworkManager.Singleton == (Object)null)
			{
				return;
			}
			bool isServer = NetworkManager.Singleton.IsServer;
			foreach (KeyValuePair<ulong, float> item in pendingXp)
			{
				if (isServer)
				{
					ServerAddXp(item.Key, item.Value);
				}
				else
				{
					GearNetwork.SendAddXp(item.Key, item.Value);
				}
			}
			pendingXp.Clear();
		}

		internal static void ServerAddXp(ulong id, float amount, bool announce = true)
		{
			xpByItem.TryGetValue(id, out var value);
			float num = Mathf.Min(value + amount, GearLevels.MaxXp);
			if (!(num <= value))
			{
				SetXp(id, num, announce);
				GearNetwork.BroadcastXp(id, num, announce);
			}
		}

		internal static void ServerSetXp(ulong id, float xp, bool announce)
		{
			xp = Mathf.Clamp(xp, 0f, GearLevels.MaxXp);
			SetXp(id, xp, announce);
			GearNetwork.BroadcastXp(id, xp, announce);
		}

		internal static void SetXp(ulong id, float xp, bool announce)
		{
			xpByItem.TryGetValue(id, out var value);
			xpByItem[id] = xp;
			GrabbableObject val = FindItem(id);
			if (!((Object)(object)val == (Object)null))
			{
				int level = GearLevels.GetLevel(value);
				int level2 = GearLevels.GetLevel(xp);
				GearItem gearItem = GearItem.Get(val);
				gearItem.xp = xp;
				gearItem.ApplyLevel(level2);
				if (announce && level2 > level && GearConfig.AnnounceLevelUps.Value && (Object)(object)HUDManager.Instance != (Object)null)
				{
					HUDManager.Instance.DisplayTip(val.itemProperties.itemName + " levelled up!", $"Now Lv {level2} ({GearLevels.TierName(level2)})", false, false, "LC_Tip1");
				}
			}
		}

		public static void Clear()
		{
			xpByItem.Clear();
			pendingXp.Clear();
		}

		private static GrabbableObject FindItem(ulong id)
		{
			NetworkManager singleton = NetworkManager.Singleton;
			if ((Object)(object)singleton == (Object)null || singleton.SpawnManager == null)
			{
				return null;
			}
			if (!singleton.SpawnManager.SpawnedObjects.TryGetValue(id, out var value))
			{
				return null;
			}
			return ((Component)value).GetComponent<GrabbableObject>();
		}
	}
	internal class GearRunner : MonoBehaviour
	{
		private const float flushInterval = 0.5f;

		private const int barLength = 10;

		private float flushTimer;

		private TextMeshProUGUI hudText;

		private void Start()
		{
			GearNetwork.Register();
			if (!NetworkManager.Singleton.IsServer)
			{
				GearNetwork.RequestSync();
			}
		}

		private void OnDestroy()
		{
			GearNetwork.Unregister();
			GearProgress.Clear();
			FlashlightLights.Clear();
			RadarBoosterPatches.Clear();
			if ((Object)(object)hudText != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)hudText).gameObject);
			}
		}

		private void Update()
		{
			flushTimer += Time.deltaTime;
			if (flushTimer >= 0.5f)
			{
				flushTimer = 0f;
				GearProgress.FlushPending();
				if (NetworkManager.Singleton.IsServer)
				{
					EndOfDayPatches.TrackGearOutsideShip();
				}
			}
			if (GearConfig.DebugXpKeys.Value && NetworkManager.Singleton.IsServer)
			{
				HandleDebugKeys();
			}
			UpdateHud();
		}

		private static void HandleDebugKeys()
		{
			Keyboard current = Keyboard.current;
			PlayerControllerB val = (((Object)(object)GameNetworkManager.Instance != (Object)null) ? GameNetworkManager.Instance.localPlayerController : null);
			GrabbableObject val2 = (((Object)(object)val != (Object)null) ? val.currentlyHeldObjectServer : null);
			if (current == null || (Object)(object)val2 == (Object)null || !((NetworkBehaviour)val2).IsSpawned || GearTypes.Get(val2) == GearType.None)
			{
				return;
			}
			int level = GearProgress.GetLevel(val2);
			int num;
			if (((ButtonControl)current.pageUpKey).wasPressedThisFrame)
			{
				num = level + 1;
			}
			else if (((ButtonControl)current.pageDownKey).wasPressedThisFrame)
			{
				num = level - 1;
			}
			else
			{
				if (!((ButtonControl)current.homeKey).wasPressedThisFrame)
				{
					return;
				}
				num = 10;
			}
			num = Mathf.Clamp(num, 1, 10);
			GearProgress.ServerSetXp(((NetworkBehaviour)val2).NetworkObjectId, GearLevels.XpForLevel(num), num > level);
		}

		private void UpdateHud()
		{
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)hudText == (Object)null && !CreateHud())
			{
				return;
			}
			PlayerControllerB val = (((Object)(object)GameNetworkManager.Instance != (Object)null) ? GameNetworkManager.Instance.localPlayerController : null);
			GrabbableObject val2 = (((Object)(object)val != (Object)null) ? val.currentlyHeldObjectServer : null);
			if (!GearConfig.ShowHud.Value || (Object)(object)val2 == (Object)null || GearTypes.Get(val2) == GearType.None)
			{
				((Behaviour)hudText).enabled = false;
				return;
			}
			float xp = GearProgress.GetXp(val2);
			int level = GearLevels.GetLevel(xp);
			string text = ColorUtility.ToHtmlStringRGB(GearLevels.TierColour(level));
			string text2;
			if (level >= 10)
			{
				text2 = "[" + new string('#', 10) + "] MAX";
			}
			else
			{
				float num = GearLevels.XpForLevel(level);
				float num2 = GearLevels.XpForNextLevel(level);
				int num3 = Mathf.Clamp(Mathf.FloorToInt((xp - num) / (num2 - num) * 10f), 0, 10);
				text2 = $"[{new string('#', num3)}{new string('-', 10 - num3)}] {Mathf.FloorToInt(xp)}/{num2:0} XP";
			}
			((Behaviour)hudText).enabled = true;
			string text3 = $"<color=#{text}>{val2.itemProperties.itemName} Lv {level}</color>\n<size=85%>{text2}</size>";
			foreach (string item in GearHudText.ExtraLines(val2, level))
			{
				text3 = text3 + "\n<size=75%>" + item + "</size>";
			}
			((TMP_Text)hudText).text = text3;
		}

		private bool CreateHud()
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: 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_00b9: Expected O, but got Unknown
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0179: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Unknown result type (might be due to invalid IL or missing references)
			//IL_0190: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			TextMeshProUGUI[] array = (((Object)(object)HUDManager.Instance != (Object)null) ? HUDManager.Instance.controlTipLines : null);
			if (array == null || array.Length == 0 || (Object)(object)array[^1] == (Object)null)
			{
				return false;
			}
			TextMeshProUGUI val = array[^1];
			RectTransform rectTransform = ((TMP_Text)val).rectTransform;
			Vector2 val2 = (Vector2)((array.Length > 1 && (Object)(object)array[^2] != (Object)null) ? (rectTransform.anchoredPosition - ((TMP_Text)array[^2]).rectTransform.anchoredPosition) : new Vector2(0f, 0f - rectTransform.sizeDelta.y));
			Vector2 val3 = rectTransform.anchoredPosition + val2;
			GameObject val4 = new GameObject("GearLevelText", new Type[1] { typeof(RectTransform) });
			val4.transform.SetParent(((TMP_Text)val).transform.parent, false);
			hudText = val4.AddComponent<TextMeshProUGUI>();
			((TMP_Text)hudText).font = ((TMP_Text)val).font;
			((TMP_Text)hudText).fontSize = ((TMP_Text)val).fontSize;
			((TMP_Text)hudText).horizontalAlignment = ((TMP_Text)val).horizontalAlignment;
			((TMP_Text)hudText).verticalAlignment = (VerticalAlignmentOptions)256;
			((TMP_Text)hudText).overflowMode = (TextOverflowModes)0;
			((TMP_Text)hudText).richText = true;
			((Graphic)hudText).raycastTarget = false;
			RectTransform rectTransform2 = ((TMP_Text)hudText).rectTransform;
			rectTransform2.anchorMin = rectTransform.anchorMin;
			rectTransform2.anchorMax = rectTransform.anchorMax;
			rectTransform2.pivot = new Vector2(rectTransform.pivot.x, 1f);
			rectTransform2.sizeDelta = new Vector2(rectTransform.sizeDelta.x, rectTransform.sizeDelta.y * 2f);
			rectTransform2.anchoredPosition = new Vector2(val3.x, val3.y + rectTransform.sizeDelta.y * (1f - rectTransform.pivot.y));
			return true;
		}
	}
	internal static class GearStats
	{
		public const float VanillaShovelReach = 1.5f;

		public const float VanillaJetpackCrashThreshold = 50f;

		public const float VanillaKnifeReach = 0.75f;

		public const float VanillaZapStun = 0.25f;

		private static int LevelsGained(GrabbableObject item)
		{
			return GearProgress.GetLevel(item) - 1;
		}

		public static float ShovelCritChance(Shovel shovel)
		{
			return (float)LevelsGained((GrabbableObject)(object)shovel) * GearConfig.ShovelCritChancePerLevel.Value;
		}

		public static float ShovelAttackSpeed(Shovel shovel)
		{
			return 1f + (float)LevelsGained((GrabbableObject)(object)shovel) * GearConfig.ShovelAttackSpeedPerLevel.Value;
		}

		public static float ShovelReach(Shovel shovel)
		{
			return 1.5f + (float)LevelsGained((GrabbableObject)(object)shovel) * GearConfig.ShovelReachPerLevel.Value;
		}

		public static float FlashlightBrightness(FlashlightItem flashlight)
		{
			return 1f + (float)LevelsGained((GrabbableObject)(object)flashlight) * GearConfig.FlashlightBrightnessPerLevel.Value;
		}

		public static float FlashlightLightSize(FlashlightItem flashlight)
		{
			return 1f + (float)LevelsGained((GrabbableObject)(object)flashlight) * GearConfig.FlashlightLightSizePerLevel.Value;
		}

		public static float LockPickerSpeed(LockPicker lockPicker)
		{
			return 1f + (float)LevelsGained((GrabbableObject)(object)lockPicker) * GearConfig.LockPickerSpeedPerLevel.Value;
		}

		public static float JetpackSpeed(JetpackItem jetpack)
		{
			return 1f + (float)LevelsGained((GrabbableObject)(object)jetpack) * GearConfig.JetpackSpeedPerLevel.Value;
		}

		public static float JetpackCrashThreshold(JetpackItem jetpack)
		{
			return 50f + (float)LevelsGained((GrabbableObject)(object)jetpack) * GearConfig.JetpackSafetyPerLevel.Value;
		}

		public static float ShotgunDamage(ShotgunItem shotgun)
		{
			return 1f + (float)LevelsGained((GrabbableObject)(object)shotgun) * GearConfig.ShotgunDamagePerLevel.Value;
		}

		public static float ShotgunRange(ShotgunItem shotgun)
		{
			return 1f + (float)LevelsGained((GrabbableObject)(object)shotgun) * GearConfig.ShotgunRangePerLevel.Value;
		}

		public static float ShotgunReloadSpeed(ShotgunItem shotgun)
		{
			return 1f + (float)LevelsGained((GrabbableObject)(object)shotgun) * GearConfig.ShotgunReloadSpeedPerLevel.Value;
		}

		public static float ShotgunAmmoSave(ShotgunItem shotgun)
		{
			return Mathf.Clamp01((float)LevelsGained((GrabbableObject)(object)shotgun) * GearConfig.ShotgunAmmoSavePerLevel.Value);
		}

		public static float KnifeCritChance(KnifeItem knife)
		{
			return (float)LevelsGained((GrabbableObject)(object)knife) * GearConfig.KnifeCritChancePerLevel.Value;
		}

		public static float KnifeAttackSpeed(KnifeItem knife)
		{
			return 1f + (float)LevelsGained((GrabbableObject)(object)knife) * GearConfig.KnifeAttackSpeedPerLevel.Value;
		}

		public static float KnifeReach(KnifeItem knife)
		{
			return 0.75f + (float)LevelsGained((GrabbableObject)(object)knife) * GearConfig.KnifeReachPerLevel.Value;
		}

		public static float ZapGunRange(PatcherTool zapGun)
		{
			return 1f + (float)LevelsGained((GrabbableObject)(object)zapGun) * GearConfig.ZapGunRangePerLevel.Value;
		}

		public static float ZapGunControl(PatcherTool zapGun)
		{
			return 1f + (float)LevelsGained((GrabbableObject)(object)zapGun) * GearConfig.ZapGunControlPerLevel.Value;
		}

		public static float ZapGunStun(PatcherTool zapGun)
		{
			return 1f + (float)LevelsGained((GrabbableObject)(object)zapGun) * GearConfig.ZapGunStunPerLevel.Value;
		}

		public static float RadarFlashStun(RadarBoosterItem booster)
		{
			return 1f + (float)LevelsGained((GrabbableObject)(object)booster) * GearConfig.RadarBoosterFlashStunPerLevel.Value;
		}

		public static float RadarFlashRadius(RadarBoosterItem booster)
		{
			return 1f + (float)LevelsGained((GrabbableObject)(object)booster) * GearConfig.RadarBoosterFlashRadiusPerLevel.Value;
		}

		public static float BatteryDrain(GrabbableObject item)
		{
			float value;
			switch (GearTypes.Get(item))
			{
			case GearType.Flashlight:
				value = GearConfig.FlashlightBatteryPerLevel.Value;
				break;
			case GearType.Jetpack:
				value = GearConfig.JetpackBatteryPerLevel.Value;
				break;
			case GearType.ZapGun:
				value = GearConfig.ZapGunBatteryPerLevel.Value;
				break;
			case GearType.WalkieTalkie:
				value = GearConfig.WalkieTalkieBatteryPerLevel.Value;
				break;
			default:
				return 1f;
			}
			return Mathf.Max(0.1f, 1f - (float)LevelsGained(item) * value);
		}
	}
	internal enum GearType
	{
		None,
		Shovel,
		Flashlight,
		LockPicker,
		Jetpack,
		Shotgun,
		Knife,
		ZapGun,
		RadarBooster,
		WalkieTalkie
	}
	internal static class GearTypes
	{
		public static GearType Get(GrabbableObject item)
		{
			if (!(item is Shovel))
			{
				FlashlightItem val = (FlashlightItem)(object)((item is FlashlightItem) ? item : null);
				if (val == null)
				{
					if (item is LockPicker)
					{
						return GearType.LockPicker;
					}
					if (item is JetpackItem)
					{
						return GearType.Jetpack;
					}
					if (item is ShotgunItem)
					{
						return GearType.Shotgun;
					}
					if (item is KnifeItem)
					{
						return GearType.Knife;
					}
					if (item is PatcherTool)
					{
						return GearType.ZapGun;
					}
					if (item is RadarBoosterItem)
					{
						return GearType.RadarBooster;
					}
					if (item is WalkieTalkie)
					{
						return GearType.WalkieTalkie;
					}
				}
				else if (!IsLaserPointer(val))
				{
					return GearType.Flashlight;
				}
				return GearType.None;
			}
			return GearType.Shovel;
		}

		private static bool IsLaserPointer(FlashlightItem flashlight)
		{
			if ((Object)(object)((GrabbableObject)flashlight).itemProperties != (Object)null)
			{
				return ((GrabbableObject)flashlight).itemProperties.itemName.ToLowerInvariant().Contains("laser");
			}
			return false;
		}
	}
	internal static class WeaponCrits
	{
		public static int Roll(float chance)
		{
			int num = Mathf.FloorToInt(chance);
			if (Random.value < chance - (float)num)
			{
				num++;
			}
			return num;
		}

		public static int Apply(int force, int crits, float multiplier)
		{
			for (int i = 0; i < crits; i++)
			{
				force = Mathf.Max(force + 1, Mathf.CeilToInt((float)force * multiplier));
			}
			return force;
		}

		public static IEnumerable<CodeInstruction> InsertCritCall(IEnumerable<CodeInstruction> instructions, FieldInfo forceField, MethodInfo applyCrit, string weaponName)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Expected O, but got Unknown
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Expected O, but got Unknown
			CodeMatcher val = new CodeMatcher(instructions, (ILGenerator)null).MatchForward(true, (CodeMatch[])(object)new CodeMatch[1]
			{
				new CodeMatch((OpCode?)OpCodes.Ldfld, (object)forceField, (string)null)
			});
			if (val.IsInvalid)
			{
				Plugin.logger.LogError((object)("Could not find " + weaponName + " hit force; crits disabled"));
				return instructions;
			}
			val.Advance(1);
			while (val.IsValid && IsForceModifier(val.Instruction))
			{
				val.Advance(1);
			}
			return val.Insert((CodeInstruction[])(object)new CodeInstruction[2]
			{
				new CodeInstruction(OpCodes.Ldarg_0, (object)null),
				new CodeInstruction(OpCodes.Call, (object)applyCrit)
			}).InstructionEnumeration();
		}

		private static bool IsForceModifier(CodeInstruction instruction)
		{
			if (instruction.opcode != OpCodes.Call || !(instruction.operand is MethodInfo methodInfo))
			{
				return false;
			}
			ParameterInfo[] parameters = methodInfo.GetParameters();
			if (methodInfo.IsStatic && methodInfo.ReturnType == typeof(int) && parameters.Length == 1)
			{
				return parameters[0].ParameterType == typeof(int);
			}
			return false;
		}

		public static void PlayEffects(GrabbableObject weapon, AudioSource template, AudioClip[] hitSFX, int crits)
		{
			GearItem gearItem = GearItem.Get(weapon);
			if ((Object)(object)gearItem.critAudio == (Object)null && (Object)(object)template != (Object)null)
			{
				gearItem.critAudio = CreateCritAudio(weapon, template);
			}
			if ((Object)(object)gearItem.critAudio != (Object)null && hitSFX != null && hitSFX.Length != 0)
			{
				gearItem.critAudio.pitch = ((crits > 1) ? 1.6f : 1.35f);
				gearItem.critAudio.PlayOneShot(hitSFX[Random.Range(0, hitSFX.Length)], 0.8f);
			}
			if ((Object)(object)weapon.playerHeldBy != (Object)null && (Object)(object)weapon.playerHeldBy == (Object)(object)GameNetworkManager.Instance.localPlayerController)
			{
				HUDManager.Instance.ShakeCamera((ScreenShakeType)0);
			}
		}

		private static AudioSource CreateCritAudio(GrabbableObject weapon, AudioSource template)
		{
			//IL_002d: 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_0056: Invalid comparison between Unknown and I4
			AudioSource val = ((Component)weapon).gameObject.AddComponent<AudioSource>();
			val.playOnAwake = false;
			val.outputAudioMixerGroup = template.outputAudioMixerGroup;
			val.spatialBlend = template.spatialBlend;
			val.rolloffMode = template.rolloffMode;
			val.minDistance = template.minDistance;
			val.maxDistance = template.maxDistance;
			if ((int)template.rolloffMode == 2)
			{
				val.SetCustomCurve((AudioSourceCurveType)0, template.GetCustomCurve((AudioSourceCurveType)0));
			}
			return val;
		}
	}
}