Decompiled source of gtfo selectable scopes v1.3.0

SelectableScopes.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using Agents;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using CellMenu;
using GTFO.API;
using GameData;
using Gear;
using HarmonyLib;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSystem;
using Il2CppSystem.Collections.Generic;
using Localization;
using Player;
using SNetwork;
using SelectableScopes.Patches;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("SelectableScopes")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+d83cc9581002173f38dcd2e549c37aed5c200581")]
[assembly: AssemblyProduct("SelectableScopes")]
[assembly: AssemblyTitle("SelectableScopes")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
namespace SelectableScopes
{
	internal enum SightEditResult
	{
		Unchanged,
		Updated,
		Failed
	}
	internal static class GearJsonEditor
	{
		internal const int SightComponentId = 21;

		internal const int CategoryComponentId = 2;

		internal const string DefaultSight = "Default";

		internal static bool TryGetComponentValue(string gearJson, int componentId, out long value)
		{
			value = 0L;
			if (!TryParse(gearJson, out var root))
			{
				return false;
			}
			JsonObject jsonObject = Components(root);
			if (jsonObject == null)
			{
				return false;
			}
			foreach (JsonObject item in ComponentObjects(jsonObject))
			{
				if (ReadInt(item["c"]) == componentId)
				{
					long? num = ReadLong(item["v"]);
					if (!num.HasValue)
					{
						return false;
					}
					value = num.Value;
					return true;
				}
			}
			return false;
		}

		internal static string GetDisplayName(string gearJson)
		{
			if (!TryParse(gearJson, out var root))
			{
				return null;
			}
			if (!(root["Name"] is JsonValue jsonValue) || !jsonValue.TryGetValue<string>(out string value))
			{
				return null;
			}
			if (!string.IsNullOrWhiteSpace(value))
			{
				return value;
			}
			return null;
		}

		internal static SightEditResult TrySetSight(string gearJson, long sightId, out string updated)
		{
			updated = gearJson;
			if (!TryParse(gearJson, out var root))
			{
				return SightEditResult.Failed;
			}
			JsonObject jsonObject = Components(root);
			if (jsonObject == null)
			{
				return SightEditResult.Failed;
			}
			foreach (JsonObject item in ComponentObjects(jsonObject))
			{
				if (ReadInt(item["c"]) == 21)
				{
					if (ReadLong(item["v"]) == sightId)
					{
						return SightEditResult.Unchanged;
					}
					item["v"] = JsonValue.Create(sightId);
					updated = root.ToJsonString();
					return SightEditResult.Updated;
				}
			}
			int num = ReadInt(jsonObject["Length"]) ?? CountComponents(jsonObject);
			if (num < 0 || num > 25)
			{
				return SightEditResult.Failed;
			}
			string propertyName = ((char)(97 + num)).ToString();
			if (jsonObject[propertyName] != null)
			{
				return SightEditResult.Failed;
			}
			jsonObject[propertyName] = new JsonObject
			{
				["c"] = JsonValue.Create(21),
				["v"] = JsonValue.Create(sightId)
			};
			jsonObject["Length"] = JsonValue.Create(num + 1);
			updated = root.ToJsonString();
			return SightEditResult.Updated;
		}

		private static bool TryParse(string gearJson, out JsonObject root)
		{
			root = null;
			if (string.IsNullOrEmpty(gearJson))
			{
				return false;
			}
			try
			{
				root = JsonNode.Parse(gearJson) as JsonObject;
				return root != null;
			}
			catch (JsonException)
			{
				return false;
			}
		}

		private static JsonObject Components(JsonObject root)
		{
			return root["Packet"]?["Comps"] as JsonObject;
		}

		private static IEnumerable<JsonObject> ComponentObjects(JsonObject comps)
		{
			foreach (KeyValuePair<string, JsonNode> comp in comps)
			{
				if (comp.Value is JsonObject jsonObject)
				{
					yield return jsonObject;
				}
			}
		}

		private static int CountComponents(JsonObject comps)
		{
			int num = 0;
			foreach (JsonObject item in ComponentObjects(comps))
			{
				_ = item;
				num++;
			}
			return num;
		}

		private static int? ReadInt(JsonNode node)
		{
			long? num = ReadLong(node);
			if (!num.HasValue || num.Value < int.MinValue || num.Value > int.MaxValue)
			{
				return null;
			}
			return (int)num.Value;
		}

		private static long? ReadLong(JsonNode node)
		{
			if (!(node is JsonValue jsonValue))
			{
				return null;
			}
			if (jsonValue.TryGetValue<long>(out var value))
			{
				return value;
			}
			if (jsonValue.TryGetValue<int>(out var value2))
			{
				return value2;
			}
			if (jsonValue.TryGetValue<bool>(out var value3))
			{
				return value3 ? 1 : 0;
			}
			return null;
		}

		internal static string ComponentSignature(string gearJson)
		{
			if (!TryParse(gearJson, out var root))
			{
				return null;
			}
			JsonObject jsonObject = Components(root);
			if (jsonObject == null)
			{
				return null;
			}
			Dictionary<int, long> dictionary = new Dictionary<int, long>();
			foreach (JsonObject item in ComponentObjects(jsonObject))
			{
				int? num = ReadInt(item["c"]);
				long? num2 = ReadLong(item["v"]);
				if (num.HasValue && num2.HasValue && num.Value != 21 && num2.Value != 0L)
				{
					dictionary[num.Value] = num2.Value;
				}
			}
			return FormatSignature(dictionary);
		}

		internal static string FormatSignature(Dictionary<int, long> values)
		{
			if (values == null || values.Count == 0)
			{
				return null;
			}
			List<int> list = new List<int>(values.Keys);
			list.Sort();
			StringBuilder stringBuilder = new StringBuilder(list.Count * 8);
			for (int i = 0; i < list.Count; i++)
			{
				if (i != 0)
				{
					stringBuilder.Append(';');
				}
				int key = list[i];
				stringBuilder.Append(key.ToString(CultureInfo.InvariantCulture));
				stringBuilder.Append(':');
				stringBuilder.Append(values[key].ToString(CultureInfo.InvariantCulture));
			}
			return stringBuilder.ToString();
		}
	}
	[BepInPlugin("SelectableScopes", "Selectable Scopes", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BasePlugin
	{
		public const string Guid = "SelectableScopes";

		public const string Version = "1.0.0";

		internal static Plugin Instance { get; private set; }

		internal static ManualLogSource LogSource { get; private set; }

		internal static ConfigEntry<bool> Enabled { get; private set; }

		internal static bool Ready { get; set; }

		public override void Load()
		{
			//IL_0036: 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)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: 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)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			LogSource = ((BasePlugin)this).Log;
			Enabled = ((BasePlugin)this).Config.Bind<bool>("General", "Enabled", true, "When true, each weapon section below can replace that weapon's sight. Default leaves the weapon unchanged.");
			Harmony val = new Harmony("SelectableScopes");
			val.PatchAll(typeof(LoadoutPagePatch));
			val.PatchAll(typeof(LobbyBarPatch));
			val.PatchAll(typeof(LobbyBarIntroPatch));
			val.PatchAll(typeof(LobbyBarSpawnPatch));
			val.PatchAll(typeof(WeaponPickedPatch));
			val.PatchAll(typeof(LobbyBarLifetimePatch));
			val.PatchAll(typeof(MenuTogglePatch));
			val.PatchAll(typeof(MenuEnterPatch));
			val.PatchAll(typeof(MenuExitPatch));
			val.PatchAll(typeof(GearSpawnPatch));
			val.PatchAll(typeof(ClipKeepPatch));
			val.PatchAll(typeof(ClipWieldPatch));
			val.PatchAll(typeof(ClipSpawnedPatch));
			val.PatchAll(typeof(ClipUpdatePatch));
			val.PatchAll(typeof(ClipStorePatch));
			val.PatchAll(typeof(GearIconPatch));
			val.PatchAll(typeof(ScopeModelAlignPatch));
			val.PatchAll(typeof(ScopeModelSpawnPatch));
			val.PatchAll(typeof(ScopeModelForgetPatch));
			val.PatchAll(typeof(ScopeCrosshairPreparePatch));
			val.PatchAll(typeof(ScopeCrosshairKeepPatch));
			val.PatchAll(typeof(ScopeCrosshairLayerPatch));
			EventAPI.OnAssetsLoaded += ScopeCatalog.BuildAndApply;
			((BasePlugin)this).Log.LogInfo((object)"Selectable Scopes loaded. Weapon entries are written after game data loads.");
		}
	}
	internal static class ScopeAlignment
	{
		private sealed class ReloadWatch
		{
			public bool SawReload;

			public float BestScore;

			public string BestName = "none";

			public Vector3 SightPosition;

			public Quaternion SightRotation;

			public readonly List<Transform> Nodes = new List<Transform>();

			public readonly List<Vector3> LocalPositions = new List<Vector3>();

			public readonly List<Quaternion> LocalRotations = new List<Quaternion>();

			public readonly List<Vector3> WorldPositions = new List<Vector3>();

			public readonly List<Quaternion> WorldRotations = new List<Quaternion>();
		}

		private struct Pair
		{
			public ConfigEntry<float> Scope;

			public ConfigEntry<float> Forward;
		}

		private struct PlaceState
		{
			public Vector3 Written;

			public float Vertical;

			public float Depth;
		}

		private struct HeldAlign
		{
			public long SightId;

			public long CarrierId;

			public bool HasCarrier;
		}

		internal const float Step = 0.005f;

		private const float Max = 0.25f;

		private const string Section = "Alignment";

		private const string ScopeSuffix = " Scope Vertical";

		private const string ForwardSuffix = " Scope Forward";

		private const string RetiredHandsSuffix = " Aim Hands Vertical";

		private const string CarrierName = "ScopeAlignCarrier";

		private const float VisualSign = -1f;

		private const float ForwardSign = 1f;

		private static readonly Dictionary<string, float> SightForwardShift = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase) { { "Sight_5", 0.1f } };

		private static readonly Dictionary<string, string> Stems = new Dictionary<string, string>(StringComparer.Ordinal);

		private static readonly Dictionary<string, string> SightsByStem = new Dictionary<string, string>(StringComparer.Ordinal);

		private static readonly Dictionary<(InventorySlot Slot, string Weapon, string Sight), Pair> Saved = new Dictionary<(InventorySlot, string, string), Pair>();

		private static readonly Dictionary<(InventorySlot Slot, string Weapon, string Sight), Vector2> Session = new Dictionary<(InventorySlot, string, string), Vector2>();

		private static readonly Dictionary<uint, string> NamesById = new Dictionary<uint, string>();

		private static readonly Dictionary<long, PlaceState> Places = new Dictionary<long, PlaceState>();

		private static readonly Dictionary<string, string> BarrelPaths = new Dictionary<string, string>(StringComparer.Ordinal);

		private static readonly Dictionary<long, ReloadWatch> Watches = new Dictionary<long, ReloadWatch>();

		private static readonly HashSet<string> BarrelLogged = new HashSet<string>(StringComparer.Ordinal);

		private static readonly Dictionary<long, Transform> FrontCarriers = new Dictionary<long, Transform>();

		private static readonly HashSet<string> LinkedIrons = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private static readonly Dictionary<IntPtr, bool> LocalCache = new Dictionary<IntPtr, bool>();

		private static readonly Dictionary<IntPtr, HeldAlign> HeldAligns = new Dictionary<IntPtr, HeldAlign>();

		private static readonly Dictionary<string, Vector2> Logged = new Dictionary<string, Vector2>(StringComparer.Ordinal);

		private static bool Bound;

		private const int BarrelNodeCap = 512;

		private const float BarrelMoveThreshold = 1.5f;

		internal static void Bind(string[] sightNames)
		{
			if (sightNames == null || Bound)
			{
				return;
			}
			Bound = true;
			HashSet<string> used = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			foreach (string text in sightNames)
			{
				if (!IsDefault(text) && !Stems.ContainsKey(text) && ScopeCatalog.Sights != null && ScopeCatalog.Sights.TryGetValue(text, out var value))
				{
					string text2 = Stem(text, value, used);
					Stems.Add(text, text2);
					SightsByStem[text2] = text;
					NamesById[value] = text;
				}
			}
			LoadSaved();
		}

		internal static bool Nudge(InventorySlot slot, string sightName, int kind, int direction)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			if (!TryTarget(slot, sightName, out (InventorySlot, string, string) key, out string _))
			{
				return false;
			}
			ReadCurrent(key, out var scope, out var forward);
			float num = ((kind == 0) ? scope : forward);
			float num2 = Mathf.Clamp(Mathf.Round((num + (float)direction * 0.005f) * 1000f) / 1000f, -0.25f, 0.25f);
			if (Mathf.Abs(num2 - num) < 1E-07f)
			{
				return false;
			}
			if (kind == 0)
			{
				scope = num2;
			}
			else
			{
				forward = num2;
			}
			Session[key] = new Vector2(scope, forward);
			Reapply();
			return true;
		}

		internal static bool Save(InventorySlot slot, string sightName)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			if (!TryTarget(slot, sightName, out (InventorySlot, string, string) key, out string weaponTitle))
			{
				return false;
			}
			ReadCurrent(key, out var scope, out var forward);
			Write(key, scope, forward);
			Session.Remove(key);
			((BasePlugin)Plugin.Instance).Config.Save();
			Plugin.LogSource.LogInfo((object)("Saved alignment for " + SlotName(slot) + " " + weaponTitle + " / " + Label(sightName) + ": scope vertical " + Signed(scope) + ", scope forward " + Signed(forward) + "."));
			Reapply();
			return true;
		}

		internal static bool Reset(InventorySlot slot, string sightName)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			if (!TryTarget(slot, sightName, out (InventorySlot, string, string) key, out string weaponTitle))
			{
				return false;
			}
			BuiltIn(key.Item2, key.Item3, out var scope, out var forward);
			bool num = Session.Remove(key);
			ReadSaved(key, out var scope2, out var forward2);
			if (!num && Mathf.Abs(scope2 - scope) < 1E-05f && Mathf.Abs(forward2 - forward) < 1E-05f)
			{
				return false;
			}
			Write(key, scope, forward);
			((BasePlugin)Plugin.Instance).Config.Save();
			Plugin.LogSource.LogInfo((object)("Reset alignment for " + SlotName(slot) + " " + weaponTitle + " / " + Label(sightName) + "."));
			Reapply();
			return true;
		}

		internal static string RowLabel(int kind, int direction, InventorySlot slot, string sightName)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			string text = ((kind != 0) ? ((direction > 0) ? "Scope forward" : "Scope backward") : ((direction > 0) ? "Scope up" : "Scope down"));
			if (!TryValues(slot, sightName, out var scope, out var forward, out var _, out var _))
			{
				return text;
			}
			return text + "    " + Signed((kind == 0) ? scope : forward);
		}

		internal static string SaveLabel(InventorySlot slot, string sightName)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			if (TryValues(slot, sightName, out var _, out var _, out var _, out var pending) && pending)
			{
				return "Save alignment    UNSAVED";
			}
			return "Save alignment";
		}

		internal static string ResetLabel()
		{
			return "Reset alignment";
		}

		internal static string Summary(InventorySlot slot, string sightName, bool selected)
		{
			//IL_0000: 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_00f3: Unknown result type (might be due to invalid IL or missing references)
			string text = SlotName(slot);
			if (IsDefault(sightName))
			{
				return "Pick a sight to align it on the equipped " + text.ToLowerInvariant() + " weapon. Each weapon keeps its own alignment.";
			}
			if (!TryValues(slot, sightName, out var scope, out var forward, out var weaponTitle, out var pending))
			{
				return "Equip a " + text.ToLowerInvariant() + " weapon, then use the rows at the top. Each click moves the sight " + 0.005f.ToString("0.000", CultureInfo.InvariantCulture) + " m.";
			}
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("Scope height ").Append(Signed(scope)).Append(" m");
			stringBuilder.Append('\n').Append("Scope forward ").Append(Signed(forward))
				.Append(" m");
			stringBuilder.Append('\n').Append(text).Append(" weapon: ")
				.Append(weaponTitle);
			stringBuilder.Append('\n').Append(Status(slot, sightName, pending));
			stringBuilder.Append('\n').Append(selected ? "Top rows nudge this sight. Save stores it for this weapon. Reset restores the default alignment." : "Top rows change the selected sight.");
			return stringBuilder.ToString();
		}

		internal static void ApplyScope(GearPartHolder holder)
		{
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.Ready || (Object)(object)holder == (Object)null || (Object)(object)holder.SightPart == (Object)null || IsIconCapture(holder) || !IsLocalHolder(holder))
			{
				return;
			}
			Transform transform = holder.SightPart.transform;
			try
			{
				if (!TryPlacement(holder, out var scope, out var forward, out var slot, out var weaponKey, out var sightName))
				{
					Transform target = TrackedIronTarget(transform);
					if (IsTracked(target))
					{
						ApplyLocal(target, 0f, 0f);
					}
					return;
				}
				Transform target2 = IronTarget(holder, transform, sightName);
				float num = -1f * scope;
				float num2 = 1f * forward;
				if (!(Mathf.Abs(num) < 1E-07f) || !(Mathf.Abs(num2) < 1E-07f) || IsTracked(target2))
				{
					ApplyLocal(target2, num, num2);
					LogIfChanged(slot, weaponKey, sightName, scope, forward);
				}
				if (ScopeCatalog.FollowsBarrel(weaponKey))
				{
					FollowBarrel(holder, target2, weaponKey);
				}
			}
			finally
			{
				RememberAlign(holder, transform);
			}
		}

		internal static void Forget(GearPartHolder holder)
		{
			if ((Object)(object)holder == (Object)null)
			{
				return;
			}
			DropBarrel(holder);
			LocalCache.Remove(((Il2CppObjectBase)holder).Pointer);
			long key = 0L;
			bool flag = false;
			long key2 = 0L;
			bool flag2 = false;
			if (HeldAligns.TryGetValue(((Il2CppObjectBase)holder).Pointer, out var value))
			{
				HeldAligns.Remove(((Il2CppObjectBase)holder).Pointer);
				key = value.SightId;
				flag = true;
				key2 = value.CarrierId;
				flag2 = value.HasCarrier;
			}
			if ((Object)(object)holder.SightPart != (Object)null)
			{
				Transform transform = holder.SightPart.transform;
				if ((Object)(object)transform != (Object)null)
				{
					key = ((Object)transform).GetInstanceID();
					flag = true;
				}
			}
			if (!flag)
			{
				return;
			}
			Places.Remove(key);
			Transform value2 = null;
			if (FrontCarriers.TryGetValue(key, out value2))
			{
				FrontCarriers.Remove(key);
			}
			if ((Object)(object)value2 != (Object)null)
			{
				Places.Remove(((Object)value2).GetInstanceID());
				if ((Object)(object)value2.parent == (Object)null)
				{
					Object.Destroy((Object)(object)((Component)value2).gameObject);
				}
			}
			else if (flag2)
			{
				Places.Remove(key2);
			}
		}

		private static void RememberAlign(GearPartHolder holder, Transform sight)
		{
			if (!((Object)(object)holder == (Object)null) && !((Object)(object)sight == (Object)null))
			{
				long num = ((Object)sight).GetInstanceID();
				HeldAlign value = new HeldAlign
				{
					SightId = num
				};
				if (FrontCarriers.TryGetValue(num, out var value2) && (Object)(object)value2 != (Object)null)
				{
					value.CarrierId = ((Object)value2).GetInstanceID();
					value.HasCarrier = true;
				}
				HeldAligns[((Il2CppObjectBase)holder).Pointer] = value;
			}
		}

		private static Transform TrackedIronTarget(Transform sight)
		{
			if ((Object)(object)sight != (Object)null && FrontCarriers.TryGetValue(((Object)sight).GetInstanceID(), out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			return sight;
		}

		private static Transform IronTarget(GearPartHolder holder, Transform sight, string sightName)
		{
			if (!TryIronMate(sightName, out var mateName, out var front))
			{
				return sight;
			}
			if (!front)
			{
				AttachMate(sight, sight, mateName);
				return sight;
			}
			return FrontCarrier(holder, sight, sightName, mateName);
		}

		private static Transform FrontCarrier(GearPartHolder holder, Transform sight, string sightName, string mateName)
		{
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: 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)
			long key = ((Object)sight).GetInstanceID();
			if (!FrontCarriers.TryGetValue(key, out var value) || (Object)(object)value == (Object)null)
			{
				value = (((Object)(object)sight.parent != (Object)null && ((Object)sight.parent).name == "ScopeAlignCarrier") ? sight.parent : null);
			}
			if ((Object)(object)value == (Object)null)
			{
				if (Places.TryGetValue(key, out var value2))
				{
					Vector3 localPosition = sight.localPosition;
					localPosition.y -= value2.Vertical;
					localPosition.z -= value2.Depth;
					sight.localPosition = localPosition;
					Places.Remove(key);
				}
				value = new GameObject("ScopeAlignCarrier").transform;
				Transform parent = sight.parent;
				int siblingIndex = sight.GetSiblingIndex();
				value.SetParent(parent, false);
				value.localPosition = sight.localPosition;
				value.localRotation = sight.localRotation;
				value.localScale = sight.localScale;
				if ((Object)(object)parent != (Object)null)
				{
					value.SetSiblingIndex(siblingIndex);
				}
				sight.SetParent(value, true);
				if (LinkedIrons.Add(sightName))
				{
					Plugin.LogSource.LogInfo((object)("Front iron " + sightName + " is moved with " + mateName + ". The front mesh was not parented to the rear sight."));
				}
			}
			else if ((Object)(object)sight.parent != (Object)(object)value)
			{
				sight.SetParent(value, true);
			}
			FrontCarriers[key] = value;
			AttachMate(value, sight, mateName);
			EnsureSightAlign(holder, value, sight);
			return value;
		}

		private static void AttachMate(Transform keepOn, Transform sight, string mateName)
		{
			if (!((Object)(object)keepOn == (Object)null) && !((Object)(object)sight == (Object)null) && !string.IsNullOrEmpty(mateName))
			{
				Transform val = FindMate(sight, mateName);
				if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)keepOn) && !((Object)(object)val == (Object)(object)sight) && !sight.IsChildOf(val) && !((Object)(object)val.parent == (Object)(object)keepOn))
				{
					val.SetParent(keepOn, true);
				}
			}
		}

		private static Transform FindMate(Transform sight, string mateName)
		{
			Transform parent = sight.parent;
			Transform val = FindDirect(parent, sight, mateName);
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
			if ((Object)(object)parent != (Object)null && ((Object)parent).name == "ScopeAlignCarrier")
			{
				return FindDirect(parent.parent, parent, mateName);
			}
			return null;
		}

		private static Transform FindDirect(Transform parent, Transform skip, string partName)
		{
			if ((Object)(object)parent == (Object)null)
			{
				return null;
			}
			for (int i = 0; i < parent.childCount; i++)
			{
				Transform child = parent.GetChild(i);
				if ((Object)(object)child != (Object)(object)skip && NamesMatch(((Object)child).name, partName))
				{
					return child;
				}
			}
			return null;
		}

		private static void EnsureSightAlign(GearPartHolder holder, Transform carrier, Transform sight)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			Transform val = FindNamed(carrier, "Sight_Align");
			if ((Object)(object)val == (Object)null)
			{
				val = new GameObject("Sight_Align").transform;
				val.SetParent(carrier, false);
				Renderer component = ((Component)sight).GetComponent<Renderer>();
				if ((Object)(object)component != (Object)null && component.enabled)
				{
					Transform obj = val;
					Bounds bounds = component.bounds;
					obj.position = ((Bounds)(ref bounds)).center;
				}
			}
			if ((Object)(object)holder.m_aSightLook == (Object)null || !holder.m_aSightLook.IsChildOf(carrier))
			{
				holder.m_aSightLook = val;
			}
			ItemEquippable componentInParent = ((Component)holder).GetComponentInParent<ItemEquippable>();
			if ((Object)(object)componentInParent != (Object)null && ((Object)(object)componentInParent.SightLookAlign == (Object)null || !componentInParent.SightLookAlign.IsChildOf(carrier)))
			{
				componentInParent.SightLookAlign = val;
			}
		}

		private static Transform FindNamed(Transform root, string partName)
		{
			if ((Object)(object)root == (Object)null)
			{
				return null;
			}
			if (NamesMatch(((Object)root).name, partName))
			{
				return root;
			}
			for (int i = 0; i < root.childCount; i++)
			{
				Transform val = FindNamed(root.GetChild(i), partName);
				if ((Object)(object)val != (Object)null)
				{
					return val;
				}
			}
			return null;
		}

		private static bool NamesMatch(string transformName, string partName)
		{
			if (string.Equals(transformName, partName, StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
			if (string.IsNullOrEmpty(transformName) || string.IsNullOrEmpty(partName) || transformName.Length <= partName.Length)
			{
				return false;
			}
			if (!transformName.StartsWith(partName, StringComparison.OrdinalIgnoreCase))
			{
				return false;
			}
			char c = transformName[partName.Length];
			if (c != '(')
			{
				return c == ' ';
			}
			return true;
		}

		private static bool TryIronMate(string sightName, out string mateName, out bool front)
		{
			mateName = null;
			front = false;
			if (string.IsNullOrEmpty(sightName) || sightName.Length <= "IronSights_".Length || !sightName.StartsWith("IronSights_", StringComparison.OrdinalIgnoreCase))
			{
				return false;
			}
			string text = sightName.Substring("IronSights_".Length);
			if (text.EndsWith("_Copy_Copy", StringComparison.OrdinalIgnoreCase))
			{
				text = text.Substring(0, text.Length - "_Copy_Copy".Length);
			}
			else if (text.EndsWith("_Copy", StringComparison.OrdinalIgnoreCase))
			{
				text = text.Substring(0, text.Length - "_Copy".Length);
			}
			bool flag = text.EndsWith("_B", StringComparison.OrdinalIgnoreCase);
			front = text.EndsWith("_F", StringComparison.OrdinalIgnoreCase);
			if (!front && !flag)
			{
				return false;
			}
			string text2 = text.Substring(0, text.Length - 2);
			if (text2.Length == 0)
			{
				return false;
			}
			mateName = sightName.Substring(0, "IronSights_".Length) + text2 + (front ? "_B" : "_F");
			return true;
		}

		private static void Reapply()
		{
			try
			{
				Il2CppArrayBase<GearPartHolder> val = Object.FindObjectsOfType<GearPartHolder>();
				if (val != null)
				{
					for (int i = 0; i < val.Count; i++)
					{
						ApplyScope(val[i]);
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.LogSource.LogDebug((object)("Alignment refresh: " + ex.Message));
			}
		}

		private static void ApplyLocal(Transform target, float vertical, float depth)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)target == (Object)null))
			{
				Vector3 val = OffsetAxes(((Object)target).GetInstanceID(), target.localPosition, vertical, depth);
				Vector3 val2 = val - target.localPosition;
				if (((Vector3)(ref val2)).sqrMagnitude > 1E-08f)
				{
					target.localPosition = val;
				}
			}
		}

		private static Vector3 OffsetAxes(long id, Vector3 position, float vertical, float depth)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: 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_0052: Unknown result type (might be due to invalid IL or missing references)
			if (Places.TryGetValue(id, out var value))
			{
				Vector3 val = position - value.Written;
				if (((Vector3)(ref val)).sqrMagnitude <= 1E-08f)
				{
					if (Mathf.Abs(value.Vertical - vertical) <= 1E-07f && Mathf.Abs(value.Depth - depth) <= 1E-07f)
					{
						return position;
					}
					position.y += vertical - value.Vertical;
					position.z += depth - value.Depth;
					goto IL_0094;
				}
			}
			position.y += vertical;
			position.z += depth;
			goto IL_0094;
			IL_0094:
			Places[id] = new PlaceState
			{
				Written = position,
				Vertical = vertical,
				Depth = depth
			};
			return position;
		}

		private static bool IsTracked(Transform target)
		{
			if ((Object)(object)target != (Object)null)
			{
				return Places.ContainsKey(((Object)target).GetInstanceID());
			}
			return false;
		}

		private static void ForgetTransform(Transform target)
		{
			if ((Object)(object)target != (Object)null)
			{
				Places.Remove(((Object)target).GetInstanceID());
			}
		}

		private static void FollowBarrel(GearPartHolder holder, Transform target, string weaponKey)
		{
			if ((Object)(object)holder == (Object)null || (Object)(object)target == (Object)null || string.IsNullOrEmpty(weaponKey))
			{
				return;
			}
			Transform transform = ((Component)holder).transform;
			if ((Object)(object)transform == (Object)null)
			{
				return;
			}
			if (BarrelPaths.TryGetValue(weaponKey, out var value))
			{
				Transform val = Walk(transform, value);
				if ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)target && !val.IsChildOf(target))
				{
					ItemEquippable componentInParent = ((Component)holder).GetComponentInParent<ItemEquippable>();
					if ((Object)(object)target.parent == (Object)(object)val || (Object)(object)componentInParent == (Object)null || !componentInParent.IsReloading)
					{
						Adopt(target, val, null);
					}
				}
				return;
			}
			long key = ((Object)transform).GetInstanceID();
			ItemEquippable componentInParent2 = ((Component)holder).GetComponentInParent<ItemEquippable>();
			bool num = (Object)(object)componentInParent2 != (Object)null && componentInParent2.IsReloading;
			if (!Watches.TryGetValue(key, out var value2))
			{
				value2 = new ReloadWatch();
				Watches[key] = value2;
			}
			if (!num)
			{
				if (value2.SawReload && BarrelLogged.Add(weaponKey))
				{
					Plugin.LogSource.LogInfo((object)(ScopeCatalog.WeaponTitleFor(weaponKey, null) + " reload did not move a separate barrel bone. Largest change was " + value2.BestName + " at " + value2.BestScore.ToString("0.0", CultureInfo.InvariantCulture) + ". " + DescribeFront(holder)));
				}
				Capture(transform, target, value2);
				value2.SawReload = false;
				value2.BestScore = 0f;
				value2.BestName = "none";
				return;
			}
			if (value2.Nodes.Count == 0)
			{
				Capture(transform, target, value2);
				value2.SawReload = true;
				return;
			}
			value2.SawReload = true;
			Transform val2 = Pick(value2, target, holder.m_aMuzzle);
			if ((Object)(object)val2 == (Object)null || (Object)(object)val2 == (Object)(object)target || (Object)(object)val2 == (Object)(object)transform || val2.IsChildOf(target))
			{
				return;
			}
			string text = PathFrom(val2, transform);
			if (!string.IsNullOrEmpty(text))
			{
				BarrelPaths[weaponKey] = text;
				Adopt(target, val2, value2);
				if (BarrelLogged.Add(weaponKey))
				{
					Plugin.LogSource.LogInfo((object)(ScopeCatalog.WeaponTitleFor(weaponKey, null) + " sight follows " + text + " during reload."));
				}
			}
		}

		private static void DropBarrel(GearPartHolder holder)
		{
			if ((Object)(object)holder != (Object)null)
			{
				Watches.Remove(((Object)((Component)holder).transform).GetInstanceID());
			}
		}

		private static void Adopt(Transform target, Transform bone, ReloadWatch watch)
		{
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: 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_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_006a: 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_007c: 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)
			if ((Object)(object)target.parent == (Object)(object)bone)
			{
				return;
			}
			long key = ((Object)target).GetInstanceID();
			PlaceState value;
			bool flag = Places.TryGetValue(key, out value);
			target.SetParent(bone, true);
			if (watch != null)
			{
				int num = watch.Nodes.IndexOf(bone);
				if (num >= 0)
				{
					Quaternion val = watch.WorldRotations[num];
					target.localRotation = Quaternion.Inverse(val) * watch.SightRotation;
					target.localPosition = Quaternion.Inverse(val) * (watch.SightPosition - watch.WorldPositions[num]);
				}
			}
			Places[key] = new PlaceState
			{
				Written = target.localPosition,
				Vertical = (flag ? value.Vertical : 0f),
				Depth = (flag ? value.Depth : 0f)
			};
		}

		private static void Capture(Transform root, Transform sight, ReloadWatch watch)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			watch.SightPosition = sight.position;
			watch.SightRotation = sight.rotation;
			watch.Nodes.Clear();
			watch.LocalPositions.Clear();
			watch.LocalRotations.Clear();
			watch.WorldPositions.Clear();
			watch.WorldRotations.Clear();
			AddNodes(root, sight, watch);
		}

		private static void AddNodes(Transform node, Transform sight, ReloadWatch watch)
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)node == (Object)null) && watch.Nodes.Count < 512 && !((Object)(object)node == (Object)(object)sight) && !node.IsChildOf(sight))
			{
				watch.Nodes.Add(node);
				watch.LocalPositions.Add(node.localPosition);
				watch.LocalRotations.Add(node.localRotation);
				watch.WorldPositions.Add(node.position);
				watch.WorldRotations.Add(node.rotation);
				for (int i = 0; i < node.childCount; i++)
				{
					AddNodes(node.GetChild(i), sight, watch);
				}
			}
		}

		private static Transform Pick(ReloadWatch watch, Transform sight, Transform muzzle)
		{
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			Transform val = null;
			float num = 0f;
			bool flag = false;
			Transform val2 = null;
			float num2 = 0f;
			int num3 = int.MaxValue;
			Transform val3 = null;
			float num4 = 0f;
			Transform val4 = ((watch.Nodes.Count > 0) ? watch.Nodes[0] : null);
			bool flag2 = (Object)(object)muzzle != (Object)null && (Object)(object)val4 != (Object)null && ((Object)(object)muzzle == (Object)(object)val4 || muzzle.IsChildOf(val4));
			for (int i = 0; i < watch.Nodes.Count; i++)
			{
				Transform val5 = watch.Nodes[i];
				if ((Object)(object)val5 == (Object)null || (Object)(object)val5 == (Object)(object)val4 || (Object)(object)val5 == (Object)(object)sight || val5.IsChildOf(sight))
				{
					continue;
				}
				float num5 = Quaternion.Angle(watch.LocalRotations[i], val5.localRotation);
				Vector3 val6 = val5.localPosition - watch.LocalPositions[i];
				float num6 = ((Vector3)(ref val6)).magnitude * 200f;
				float num7 = num5 + num6;
				if (num7 > watch.BestScore)
				{
					watch.BestScore = num7;
					watch.BestName = PathFrom(val5, val4);
				}
				if (IsBarrelName(((Object)val5).name))
				{
					flag = true;
					if (num7 >= 1.5f)
					{
						int num8 = DepthUnder(val5, val4);
						if (num7 > num2 + 0.01f || (Mathf.Abs(num7 - num2) <= 0.01f && num8 < num3))
						{
							val2 = val5;
							num2 = num7;
							num3 = num8;
						}
					}
				}
				else
				{
					if (num7 < 1.5f || IsIgnoredMover(((Object)val5).name))
					{
						continue;
					}
					if (flag2 && ((Object)(object)val5 == (Object)(object)muzzle || muzzle.IsChildOf(val5)))
					{
						if (num7 > num)
						{
							val = val5;
							num = num7;
						}
					}
					else if (num7 > num4)
					{
						val3 = val5;
						num4 = num7;
					}
				}
			}
			if ((Object)(object)val2 != (Object)null)
			{
				return val2;
			}
			if (flag)
			{
				return null;
			}
			Transform result = val3;
			float num9 = num4;
			if ((Object)(object)val != (Object)null && num > num9)
			{
				result = val;
				num9 = num;
			}
			return result;
		}

		private static Transform Walk(Transform root, string path)
		{
			if ((Object)(object)root == (Object)null || string.IsNullOrEmpty(path))
			{
				return null;
			}
			string[] array = path.Split('/');
			Transform val = root;
			for (int i = 0; i < array.Length; i++)
			{
				Transform val2 = null;
				for (int j = 0; j < val.childCount; j++)
				{
					Transform child = val.GetChild(j);
					if (string.Equals(((Object)child).name, array[i], StringComparison.Ordinal))
					{
						val2 = child;
						break;
					}
				}
				if ((Object)(object)val2 == (Object)null)
				{
					return null;
				}
				val = val2;
			}
			return val;
		}

		private static string PathFrom(Transform node, Transform root)
		{
			if ((Object)(object)node == (Object)null || (Object)(object)node == (Object)(object)root)
			{
				return null;
			}
			List<string> list = new List<string>();
			Transform val = node;
			int num = 0;
			while ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)root && num++ < 32)
			{
				list.Add(((Object)val).name);
				val = val.parent;
			}
			if ((Object)(object)val != (Object)(object)root)
			{
				return null;
			}
			list.Reverse();
			return string.Join("/", list);
		}

		private static int DepthUnder(Transform node, Transform root)
		{
			int num = 0;
			Transform val = node;
			while ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)root && num < 32)
			{
				num++;
				val = val.parent;
			}
			return num;
		}

		private static string DescribeFront(GearPartHolder holder)
		{
			Transform val = (((Object)(object)holder.FrontPart != (Object)null) ? holder.FrontPart.transform : null);
			if ((Object)(object)val == (Object)null)
			{
				return "No front part.";
			}
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append(((Object)val).name).Append(" children:");
			int num = Math.Min(val.childCount, 12);
			for (int i = 0; i < num; i++)
			{
				stringBuilder.Append(' ').Append(((Object)val.GetChild(i)).name);
			}
			Animator frontPartAnimator = holder.FrontPartAnimator;
			if ((Object)(object)frontPartAnimator != (Object)null && (Object)(object)frontPartAnimator.runtimeAnimatorController != (Object)null && frontPartAnimator.runtimeAnimatorController.animationClips != null)
			{
				stringBuilder.Append(". Clips:");
				AnimationClip[] array = Il2CppArrayBase<AnimationClip>.op_Implicit((Il2CppArrayBase<AnimationClip>)(object)frontPartAnimator.runtimeAnimatorController.animationClips);
				int num2 = 0;
				for (int j = 0; j < array.Length; j++)
				{
					if (num2 >= 6)
					{
						break;
					}
					if (!((Object)(object)array[j] == (Object)null))
					{
						stringBuilder.Append(' ').Append(((Object)array[j]).name);
						num2++;
					}
				}
			}
			return stringBuilder.ToString();
		}

		private static bool IsBarrelName(string name)
		{
			if (string.IsNullOrEmpty(name) || name.IndexOf("align", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return false;
			}
			if (!ContainsToken(name, "barrel"))
			{
				return ContainsToken(name, "fold");
			}
			return true;
		}

		private static bool IsIgnoredMover(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return true;
			}
			if (!ContainsToken(name, "cylinder") && !ContainsToken(name, "drum") && !ContainsToken(name, "hammer") && !ContainsToken(name, "trigger") && !ContainsToken(name, "bullet") && !ContainsToken(name, "shell") && !ContainsToken(name, "casing") && !ContainsToken(name, "cartridge") && !ContainsToken(name, "mag") && !ContainsToken(name, "speedloader") && !ContainsToken(name, "ejector") && !ContainsToken(name, "sight") && !ContainsToken(name, "align") && !ContainsToken(name, "hand") && !ContainsToken(name, "flash"))
			{
				return string.Equals(name, "ScopeAlignCarrier", StringComparison.Ordinal);
			}
			return true;
		}

		private static bool ContainsToken(string name, string token)
		{
			return name.IndexOf(token, StringComparison.OrdinalIgnoreCase) >= 0;
		}

		private static bool IsIconCapture(GearPartHolder holder)
		{
			GearIconRendering current = GearIconRendering.Current;
			IconRenderJob val = (((Object)(object)current != (Object)null) ? current.m_currentJob : null);
			GameObject val2 = ((val != null) ? val.loadedGearGO : null);
			if ((Object)(object)val2 == (Object)null)
			{
				return false;
			}
			Transform transform = val2.transform;
			Transform transform2 = ((Component)holder).transform;
			if (!((Object)(object)transform2 == (Object)(object)transform))
			{
				return transform2.IsChildOf(transform);
			}
			return true;
		}

		private static bool IsLocalHolder(GearPartHolder holder)
		{
			if (LocalCache.TryGetValue(((Il2CppObjectBase)holder).Pointer, out var value))
			{
				return value;
			}
			value = true;
			Transform val = ((Component)holder).transform;
			while ((Object)(object)val != (Object)null)
			{
				CM_PlayerLobbyBar component = ((Component)val).GetComponent<CM_PlayerLobbyBar>();
				if ((Object)(object)component != (Object)null && (Object)(object)component.m_player != (Object)null)
				{
					value = component.m_player.IsLocal;
					break;
				}
				PlayerAgent component2 = ((Component)val).GetComponent<PlayerAgent>();
				if ((Object)(object)component2 != (Object)null)
				{
					value = ((Agent)component2).IsLocallyOwned;
					break;
				}
				val = val.parent;
			}
			LocalCache[((Il2CppObjectBase)holder).Pointer] = value;
			return value;
		}

		private static bool TryPlacement(GearPartHolder holder, out float scope, out float forward, out InventorySlot slot, out string weaponKey, out string sightName)
		{
			scope = 0f;
			forward = 0f;
			slot = (InventorySlot)0;
			weaponKey = null;
			sightName = null;
			if (!TryContext(holder, out slot, out weaponKey, out sightName))
			{
				return false;
			}
			ReadCurrent((Slot: slot, Weapon: weaponKey, Sight: sightName), out scope, out forward);
			return true;
		}

		private static bool TryContext(GearPartHolder holder, out InventorySlot slot, out string weaponKey, out string sightName)
		{
			slot = (InventorySlot)0;
			weaponKey = null;
			sightName = NameOf(((Object)(object)holder != (Object)null) ? holder.SightData : null);
			if (IsDefault(sightName))
			{
				return false;
			}
			GearIDRange val = holder.GearIDRange;
			if (val == null)
			{
				ItemEquippable componentInParent = ((Component)holder).GetComponentInParent<ItemEquippable>();
				val = (((Object)(object)componentInParent != (Object)null) ? componentInParent.GearIDRange : null);
			}
			if (val != null)
			{
				weaponKey = ScopeCatalog.WeaponKeyFor(val);
				if (weaponKey != null && TrySlot(weaponKey, sightName, out slot))
				{
					return true;
				}
			}
			if (!ScopeLoadoutCategory.TryEditing(out slot) || !TryEquipped(slot, out weaponKey, out var _, out var _))
			{
				weaponKey = null;
				return false;
			}
			return true;
		}

		private static bool TrySlot(string weaponKey, string sightName, out InventorySlot slot)
		{
			bool flag = string.Equals(EquippedKey((InventorySlot)1), weaponKey, StringComparison.Ordinal);
			bool flag2 = string.Equals(EquippedKey((InventorySlot)2), weaponKey, StringComparison.Ordinal);
			if (flag && !flag2)
			{
				slot = (InventorySlot)1;
				return true;
			}
			if (flag2 && !flag)
			{
				slot = (InventorySlot)2;
				return true;
			}
			if (flag && flag2)
			{
				bool flag3 = string.Equals(ScopeChoices.Display((InventorySlot)2), sightName, StringComparison.Ordinal);
				bool flag4 = string.Equals(ScopeChoices.Display((InventorySlot)1), sightName, StringComparison.Ordinal);
				slot = (InventorySlot)((!flag3 || flag4) ? 1 : 2);
				return true;
			}
			slot = (InventorySlot)0;
			return false;
		}

		private static string EquippedKey(InventorySlot slot)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			if (!TryEquipped(slot, out var weaponKey, out var _, out var _))
			{
				return null;
			}
			return weaponKey;
		}

		private static bool TryEquipped(InventorySlot slot, out string weaponKey, out string weaponTitle, out GearIDRange range)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			weaponKey = null;
			weaponTitle = null;
			range = null;
			BackpackItem localItem = PlayerBackpackManager.GetLocalItem(slot);
			range = ((localItem != null) ? localItem.GearIDRange : null);
			if (range == null)
			{
				return false;
			}
			weaponKey = ScopeCatalog.WeaponKeyFor(range);
			if (weaponKey == null)
			{
				return false;
			}
			weaponTitle = ScopeCatalog.WeaponTitleFor(weaponKey, range);
			return true;
		}

		private static bool TryTarget(InventorySlot slot, string sightName, out (InventorySlot Slot, string Weapon, string Sight) key, out string weaponTitle)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			key = default((InventorySlot, string, string));
			weaponTitle = null;
			if (!TryEquipped(slot, out var weaponKey, out weaponTitle, out var _) || !Stems.ContainsKey(sightName))
			{
				return false;
			}
			key = (Slot: slot, Weapon: weaponKey, Sight: sightName);
			return true;
		}

		private static bool TryValues(InventorySlot slot, string sightName, out float scope, out float forward, out string weaponTitle, out bool pending)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			scope = 0f;
			forward = 0f;
			weaponTitle = null;
			pending = false;
			if (!TryTarget(slot, sightName, out (InventorySlot, string, string) key, out weaponTitle))
			{
				return false;
			}
			ReadCurrent(key, out scope, out forward);
			ReadSaved(key, out var scope2, out var forward2);
			pending = Session.ContainsKey(key) && (Mathf.Abs(scope2 - scope) > 1E-05f || Mathf.Abs(forward2 - forward) > 1E-05f);
			return true;
		}

		private static void ReadCurrent((InventorySlot Slot, string Weapon, string Sight) key, out float scope, out float forward)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			if (Session.TryGetValue(key, out var value))
			{
				scope = Mathf.Clamp(value.x, -0.25f, 0.25f);
				forward = Mathf.Clamp(value.y, -0.25f, 0.25f);
			}
			else
			{
				ReadSaved(key, out scope, out forward);
			}
		}

		private static void ReadSaved((InventorySlot Slot, string Weapon, string Sight) key, out float scope, out float forward)
		{
			if (!Saved.TryGetValue(key, out var value))
			{
				BuiltIn(key.Weapon, key.Sight, out scope, out forward);
				return;
			}
			scope = Mathf.Clamp(value.Scope.Value, -0.25f, 0.25f);
			forward = Mathf.Clamp(value.Forward.Value, -0.25f, 0.25f);
		}

		private static void BuiltIn(string weaponKey, string sightName, out float scope, out float forward)
		{
			if (weaponKey == null || !ScopeCatalog.TryAlignmentDefault(weaponKey, out scope, out forward))
			{
				scope = 0f;
				forward = 0f;
			}
			if (sightName != null && SightForwardShift.TryGetValue(sightName, out var value))
			{
				forward += value;
			}
			scope = Snap(scope);
			forward = Snap(forward);
		}

		private static float Snap(float value)
		{
			return Mathf.Clamp(Mathf.Round(value * 1000f) / 1000f, -0.25f, 0.25f);
		}

		private static string Status(InventorySlot slot, string sightName, bool pending)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			if (pending)
			{
				return "Not saved yet.";
			}
			if (TryTarget(slot, sightName, out (InventorySlot, string, string) key, out string _) && Saved.ContainsKey(key))
			{
				return "Saved for this weapon and sight.";
			}
			return "Default alignment for this weapon.";
		}

		private static void Write((InventorySlot Slot, string Weapon, string Sight) key, float scope, float forward)
		{
			if (Ensure(key))
			{
				Pair pair = Saved[key];
				pair.Scope.Value = Mathf.Clamp(scope, -0.25f, 0.25f);
				pair.Forward.Value = Mathf.Clamp(forward, -0.25f, 0.25f);
			}
		}

		private static bool Ensure((InventorySlot Slot, string Weapon, string Sight) key)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			if (Saved.ContainsKey(key))
			{
				return true;
			}
			if (!Stems.TryGetValue(key.Sight, out var value))
			{
				return false;
			}
			string text = SlotName(key.Slot) + " weapon " + key.Weapon + ", sight " + key.Sight + ". Meters. Stored for this weapon and sight only.";
			BuiltIn(key.Weapon, key.Sight, out var scope, out var forward);
			Saved[key] = new Pair
			{
				Scope = ((BasePlugin)Plugin.Instance).Config.Bind<float>("Alignment", ConfigKey(key.Slot, key.Weapon, value, 0), scope, text + " Positive moves up."),
				Forward = ((BasePlugin)Plugin.Instance).Config.Bind<float>("Alignment", ConfigKey(key.Slot, key.Weapon, value, 1), forward, text + " Positive moves the sight forward, toward the muzzle.")
			};
			return true;
		}

		private static void LoadSaved()
		{
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			string path = Path.Combine(Paths.ConfigPath, "SelectableScopes.cfg");
			if (!File.Exists(path))
			{
				return;
			}
			bool flag = false;
			bool flag2 = false;
			HashSet<(InventorySlot, string, string)> hashSet = new HashSet<(InventorySlot, string, string)>();
			string[] array = File.ReadAllLines(path);
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length >= 2 && text[0] == '[' && text[text.Length - 1] == ']')
				{
					flag = string.Equals(text.Substring(1, text.Length - 2), "Alignment", StringComparison.Ordinal);
				}
				else
				{
					if (!flag || text.Length == 0 || text[0] == '#')
					{
						continue;
					}
					int num = text.IndexOf('=');
					if (num <= 0)
					{
						continue;
					}
					string text2 = text.Substring(0, num).Trim();
					InventorySlot slot;
					string weaponKey;
					string stem;
					int kind;
					float result;
					if (text2.EndsWith(" Aim Hands Vertical", StringComparison.Ordinal))
					{
						if (!flag2)
						{
							flag2 = true;
							Plugin.LogSource.LogInfo((object)"Aim hands alignment entries are ignored. Forward and backward alignment replaces them.");
						}
					}
					else if (TryParseConfigKey(text2, out slot, out weaponKey, out stem, out kind))
					{
						if (SightsByStem.TryGetValue(stem, out var value))
						{
							hashSet.Add((slot, weaponKey, value));
						}
						else
						{
							Plugin.LogSource.LogWarning((object)("Unknown alignment entry '" + text2 + "'. It was not loaded."));
						}
					}
					else if ((text2.EndsWith(" Scope Vertical", StringComparison.Ordinal) || text2.EndsWith(" Scope Forward", StringComparison.Ordinal)) && float.TryParse(text.Substring(num + 1).Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out result) && Mathf.Abs(result) > 1E-05f)
					{
						Plugin.LogSource.LogInfo((object)("Ignored shared alignment '" + text2 + "' = " + Signed(result) + ". Save it again for each weapon."));
					}
				}
			}
			foreach (var item in hashSet)
			{
				Ensure(item);
			}
		}

		private static bool TryParseConfigKey(string key, out InventorySlot slot, out string weaponKey, out string stem, out int kind)
		{
			slot = (InventorySlot)0;
			weaponKey = null;
			stem = null;
			kind = 0;
			string text = null;
			if (key.EndsWith(" Scope Vertical", StringComparison.Ordinal))
			{
				text = " Scope Vertical";
			}
			else if (key.EndsWith(" Scope Forward", StringComparison.Ordinal))
			{
				kind = 1;
				text = " Scope Forward";
			}
			if (text == null)
			{
				return false;
			}
			string text2 = key.Substring(0, key.Length - text.Length);
			int num = text2.IndexOf(' ');
			if (num <= 0)
			{
				return false;
			}
			string a = text2.Substring(0, num);
			if (string.Equals(a, "Primary", StringComparison.Ordinal))
			{
				slot = (InventorySlot)1;
			}
			else
			{
				if (!string.Equals(a, "Special", StringComparison.Ordinal))
				{
					return false;
				}
				slot = (InventorySlot)2;
			}
			string text3 = text2.Substring(num + 1);
			int num2 = text3.IndexOf(' ');
			if (num2 <= 0)
			{
				return false;
			}
			weaponKey = text3.Substring(0, num2);
			stem = text3.Substring(num2 + 1);
			if (weaponKey.Length > 0)
			{
				return stem.Length > 0;
			}
			return false;
		}

		private static string ConfigKey(InventorySlot slot, string weaponKey, string stem, int kind)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			return SlotName(slot) + " " + weaponKey + " " + stem + ((kind == 0) ? " Scope Vertical" : " Scope Forward");
		}

		private static void LogIfChanged(InventorySlot slot, string weaponKey, string sightName, float scope, float forward)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: 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_00bb: 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)
			string key = SlotName(slot) + "|" + weaponKey + "|" + sightName;
			if (!Logged.TryGetValue(key, out var value) || !(Mathf.Abs(value.x - scope) < 1E-05f) || !(Mathf.Abs(value.y - forward) < 1E-05f))
			{
				bool num = !Logged.ContainsKey(key);
				Logged[key] = new Vector2(scope, forward);
				if (!num || !(Mathf.Abs(scope) < 1E-05f) || !(Mathf.Abs(forward) < 1E-05f))
				{
					Plugin.LogSource.LogInfo((object)("Alignment " + SlotName(slot) + " " + weaponKey + " / " + Label(sightName) + ": scope vertical " + Signed(scope) + ", scope forward " + Signed(forward) + "."));
				}
			}
		}

		private static string NameOf(GearSightPartDataBlock block)
		{
			if (block == null)
			{
				return null;
			}
			if (NamesById.TryGetValue(((GameDataBlockBase<GearSightPartDataBlock>)(object)block).persistentID, out var value))
			{
				return value;
			}
			if (!string.IsNullOrWhiteSpace(((GameDataBlockBase<GearSightPartDataBlock>)(object)block).name))
			{
				return ((GameDataBlockBase<GearSightPartDataBlock>)(object)block).name;
			}
			return null;
		}

		private static string Stem(string sightName, uint sightId, HashSet<string> used)
		{
			StringBuilder stringBuilder = new StringBuilder(sightName.Length);
			foreach (char c in sightName)
			{
				bool flag;
				switch (c)
				{
				case '\t':
				case '\n':
				case '\r':
				case ' ':
				case '#':
				case '=':
				case '[':
				case ']':
					flag = true;
					break;
				default:
					flag = false;
					break;
				}
				bool flag2 = flag;
				stringBuilder.Append(flag2 ? '_' : c);
			}
			string text = stringBuilder.ToString().Trim('_');
			if (text.Length == 0)
			{
				text = "Sight";
			}
			if (!used.Add(text))
			{
				text = text + "_" + sightId.ToString(CultureInfo.InvariantCulture);
				used.Add(text);
			}
			return text;
		}

		private static string Label(string sightName)
		{
			string text = ScopeCatalog.Title(sightName);
			if (string.IsNullOrWhiteSpace(text) || string.Equals(text, sightName, StringComparison.Ordinal))
			{
				return sightName;
			}
			return sightName + " (" + text + ")";
		}

		private static string SlotName(InventorySlot slot)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			if ((int)slot != 2)
			{
				return "Primary";
			}
			return "Special";
		}

		private static string Signed(float value)
		{
			return value.ToString("+0.0000;-0.0000", CultureInfo.InvariantCulture);
		}

		private static bool IsDefault(string sightName)
		{
			if (!string.IsNullOrEmpty(sightName))
			{
				return string.Equals(sightName, "Default", StringComparison.Ordinal);
			}
			return true;
		}
	}
	internal static class ScopeApplier
	{
		private static readonly HashSet<string> ReportedChoices = new HashSet<string>(StringComparer.Ordinal);

		private static readonly HashSet<uint> ReportedFailures = new HashSet<uint>();

		internal static bool IsWriting { get; private set; }

		internal static void ApplyConfigured()
		{
			if (IsWriting || !Plugin.Ready || !Plugin.Enabled.Value || ScopeCatalog.Weapons == null)
			{
				return;
			}
			foreach (KeyValuePair<uint, ConfigEntry<string>> weapon in ScopeCatalog.Weapons)
			{
				if (TryRewrite(weapon.Key, null, out var block, out var updated, out var choice))
				{
					IsWriting = true;
					try
					{
						block.GearJSON = updated;
					}
					finally
					{
						IsWriting = false;
					}
					Plugin.LogSource.LogInfo((object)(((GameDataBlockBase<PlayerOfflineGearDataBlock>)(object)block).name + " sight set to " + choice + "."));
				}
			}
		}

		private static bool TryRewrite(uint persistentId, string gearJson, out PlayerOfflineGearDataBlock block, out string updated, out string choice)
		{
			block = null;
			updated = gearJson;
			choice = null;
			if (ScopeCatalog.Weapons == null || !ScopeCatalog.Weapons.TryGetValue(persistentId, out var value))
			{
				return false;
			}
			choice = value.Value;
			if (string.IsNullOrEmpty(choice) || string.Equals(choice, "Default", StringComparison.Ordinal))
			{
				return false;
			}
			if (!ScopeCatalog.Sights.TryGetValue(choice, out var value2))
			{
				if (ReportedChoices.Add(persistentId + ":" + choice))
				{
					Plugin.LogSource.LogWarning((object)("Unknown sight '" + choice + "' for gear " + persistentId + ". Leaving that weapon unchanged."));
				}
				return false;
			}
			if (gearJson == null)
			{
				try
				{
					block = GameDataBlockBase<PlayerOfflineGearDataBlock>.GetBlock(persistentId);
				}
				catch (Exception ex)
				{
					Plugin.LogSource.LogDebug((object)("Gear " + persistentId + " was not readable: " + ex.Message));
					return false;
				}
				if (block == null)
				{
					return false;
				}
				gearJson = block.GearJSON;
			}
			SightEditResult num = GearJsonEditor.TrySetSight(gearJson, value2, out updated);
			if (num == SightEditResult.Failed && ReportedFailures.Add(persistentId))
			{
				Plugin.LogSource.LogWarning((object)("Could not write a sight component on gear " + persistentId + "."));
			}
			return num == SightEditResult.Updated;
		}
	}
	internal static class ScopeCatalog
	{
		private struct AuthoredSight
		{
			public bool HadSight;

			public uint SightId;
		}

		private struct AlignmentDefault
		{
			public float Vertical;

			public float Forward;
		}

		private static readonly Dictionary<string, string> WeaponKeys = new Dictionary<string, string>(StringComparer.Ordinal);

		private static readonly Dictionary<string, string> WeaponTitles = new Dictionary<string, string>(StringComparer.Ordinal);

		private static readonly Dictionary<string, AuthoredSight> AuthoredSights = new Dictionary<string, AuthoredSight>(StringComparer.Ordinal);

		private static readonly Dictionary<uint, List<string>> SightUsers = new Dictionary<uint, List<string>>();

		private static readonly Dictionary<string, AlignmentDefault> AlignmentDefaults = new Dictionary<string, AlignmentDefault>(StringComparer.Ordinal);

		private static readonly HashSet<string> BarrelDatablocks = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "RevolverHEL", "RevolverSemi" };

		private static readonly HashSet<string> BarrelKeys = new HashSet<string>(StringComparer.Ordinal);

		private static readonly Dictionary<string, AlignmentDefault> AlignmentPresets = new Dictionary<string, AlignmentDefault>(StringComparer.OrdinalIgnoreCase)
		{
			{
				"HighCalPistol",
				new AlignmentDefault
				{
					Vertical = -0.035f,
					Forward = -0.085f
				}
			},
			{
				"PistolSemi",
				new AlignmentDefault
				{
					Vertical = -0.035f,
					Forward = -0.07f
				}
			},
			{
				"PistolBurst",
				new AlignmentDefault
				{
					Vertical = -0.02f,
					Forward = -0.072f
				}
			},
			{
				"PistolAuto",
				new AlignmentDefault
				{
					Vertical = 0.005f,
					Forward = 0.048f
				}
			},
			{
				"Autopistol staggering",
				new AlignmentDefault
				{
					Vertical = 0.005f,
					Forward = 0.02f
				}
			},
			{
				"RevolverHEL",
				new AlignmentDefault
				{
					Vertical = -0.05f,
					Forward = 0.065f
				}
			},
			{
				"RevolverSemi",
				new AlignmentDefault
				{
					Vertical = -0.05f,
					Forward = 0.16f
				}
			},
			{
				"ShotgunSemi",
				new AlignmentDefault
				{
					Vertical = 0f,
					Forward = -0.06f
				}
			},
			{
				"BullpupAuto",
				new AlignmentDefault
				{
					Vertical = 0.05f,
					Forward = -0.02f
				}
			},
			{
				"ShotgunAuto",
				new AlignmentDefault
				{
					Vertical = 0.02f,
					Forward = 0.17f
				}
			},
			{
				"SMGAuto",
				new AlignmentDefault
				{
					Vertical = 0.015f,
					Forward = 0.111f
				}
			},
			{
				"PDWAuto",
				new AlignmentDefault
				{
					Vertical = -0.015f,
					Forward = -0.135f
				}
			},
			{
				"ShotgunRanged",
				new AlignmentDefault
				{
					Vertical = 0.02f,
					Forward = 0.115f
				}
			},
			{
				"SMGHeavyAuto",
				new AlignmentDefault
				{
					Vertical = 0.02f,
					Forward = 0.111f
				}
			},
			{
				"SMGBurst",
				new AlignmentDefault
				{
					Vertical = 0f,
					Forward = -0.07f
				}
			},
			{
				"HELGun",
				new AlignmentDefault
				{
					Vertical = 0f,
					Forward = -0.1f
				}
			},
			{
				"DMRSemi",
				new AlignmentDefault
				{
					Vertical = 0f,
					Forward = 0f
				}
			},
			{
				"DMRBurst",
				new AlignmentDefault
				{
					Vertical = 0f,
					Forward = -0.05f
				}
			},
			{
				"HELRifleSemi",
				new AlignmentDefault
				{
					Vertical = 0f,
					Forward = -0.1f
				}
			},
			{
				"MachinegunAuto",
				new AlignmentDefault
				{
					Vertical = 0.015f,
					Forward = 0.17f
				}
			},
			{
				"ShotgunShort",
				new AlignmentDefault
				{
					Vertical = 0.005f,
					Forward = 0.035f
				}
			}
		};

		private static readonly HashSet<string> HiddenNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private static eGearComponent[] Components;

		internal static Dictionary<string, uint> Sights { get; private set; }

		internal static string[] SightNames { get; private set; }

		internal static Dictionary<uint, ConfigEntry<string>> Weapons { get; private set; }

		internal static void BuildAndApply()
		{
			if (!Plugin.Ready)
			{
				if (!TryBuild())
				{
					return;
				}
				Plugin.Ready = true;
			}
			ScopeApplier.ApplyConfigured();
		}

		private static bool TryBuild()
		{
			//IL_0300: Unknown result type (might be due to invalid IL or missing references)
			//IL_030a: Expected O, but got Unknown
			Il2CppArrayBase<GearSightPartDataBlock> allBlocks = GameDataBlockBase<GearSightPartDataBlock>.GetAllBlocks();
			Il2CppArrayBase<PlayerOfflineGearDataBlock> allBlocks2 = GameDataBlockBase<PlayerOfflineGearDataBlock>.GetAllBlocks();
			if (allBlocks == null || allBlocks.Count == 0 || allBlocks2 == null)
			{
				Plugin.LogSource.LogWarning((object)"Sight datablocks are not loaded yet.");
				return false;
			}
			Sights = new Dictionary<string, uint>(StringComparer.Ordinal);
			HiddenNames.Clear();
			List<string> list = new List<string> { "Default" };
			int num = 0;
			int num2 = 0;
			foreach (GearSightPartDataBlock item in allBlocks)
			{
				if (item == null)
				{
					continue;
				}
				if (IsFrontIronSight(((GameDataBlockBase<GearSightPartDataBlock>)(object)item).name))
				{
					num++;
					continue;
				}
				if (IsHiddenScope(item))
				{
					num2++;
					continue;
				}
				string text = ((GameDataBlockBase<GearSightPartDataBlock>)(object)item).name;
				if (string.IsNullOrWhiteSpace(text))
				{
					text = "ID_" + ((GameDataBlockBase<GearSightPartDataBlock>)(object)item).persistentID;
				}
				if (string.Equals(text, "Default", StringComparison.Ordinal) || Sights.ContainsKey(text))
				{
					text = text + "#" + ((GameDataBlockBase<GearSightPartDataBlock>)(object)item).persistentID;
				}
				if (!Sights.ContainsKey(text))
				{
					Sights.Add(text, ((GameDataBlockBase<GearSightPartDataBlock>)(object)item).persistentID);
					list.Add(text);
				}
			}
			list.Sort(1, list.Count - 1, StringComparer.OrdinalIgnoreCase);
			SightNames = list.ToArray();
			AcceptableValueList<string> val = new AcceptableValueList<string>(SightNames);
			ScopeChoices.Bind(val);
			Weapons = new Dictionary<uint, ConfigEntry<string>>();
			SightUsers.Clear();
			HashSet<string> usedSections = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "General", "Lobby", "Alignment" };
			Dictionary<string, string> dictionary = ReadExistingSights();
			int num3 = 0;
			foreach (PlayerOfflineGearDataBlock item2 in allBlocks2)
			{
				if (item2 != null && !string.IsNullOrEmpty(item2.GearJSON) && !Weapons.ContainsKey(((GameDataBlockBase<PlayerOfflineGearDataBlock>)(object)item2).persistentID) && ShouldConfigure(item2.GearJSON))
				{
					string text2 = SectionName(item2, usedSections);
					if (dictionary.TryGetValue(text2, out var value) && !string.Equals(value, "Default", StringComparison.Ordinal) && !Sights.ContainsKey(value))
					{
						Plugin.LogSource.LogWarning((object)("Unknown sight '" + value + "' for " + text2 + ". Leaving that weapon unchanged."));
					}
					string text3 = GearJsonEditor.GetDisplayName(item2.GearJSON) ?? ((GameDataBlockBase<PlayerOfflineGearDataBlock>)(object)item2).name;
					ConfigEntry<string> value2 = ((BasePlugin)Plugin.Instance).Config.Bind<string>(text2, "Sight", "Default", new ConfigDescription("In-game name: " + text3 + ". Persistent ID: " + ((GameDataBlockBase<PlayerOfflineGearDataBlock>)(object)item2).persistentID + ". Default keeps the current sight. Any other value is a GearSightPart datablock name.", (AcceptableValueBase)(object)val, Array.Empty<object>()));
					Weapons.Add(((GameDataBlockBase<PlayerOfflineGearDataBlock>)(object)item2).persistentID, value2);
					IndexWeapon(item2, text3);
					num3++;
				}
			}
			foreach (List<string> value3 in SightUsers.Values)
			{
				value3.Sort(StringComparer.OrdinalIgnoreCase);
			}
			ScopeAlignment.Bind(SightNames);
			((BasePlugin)Plugin.Instance).Config.Save();
			Plugin.LogSource.LogInfo((object)("Selectable Scopes catalog: " + Sights.Count + " sights, " + num3 + " weapons."));
			if (num > 0)
			{
				Plugin.LogSource.LogInfo((object)("Left out " + num + " front iron sights. The rear sight is the one that aims."));
			}
			if (num2 > 0)
			{
				Plugin.LogSource.LogInfo((object)("Left out " + num2 + " unusable sights (Sight 11, IRON_SIGHTS 2B, IRON_SIGHTS 11)."));
			}
			return true;
		}

		internal static string WeaponKeyFor(GearIDRange range)
		{
			if (range == null)
			{
				return null;
			}
			string text = null;
			try
			{
				text = Signature(range);
			}
			catch (Exception ex)
			{
				Plugin.LogSource.LogDebug((object)("Weapon signature: " + ex.Message));
			}
			if (text != null && WeaponKeys.TryGetValue(text, out var value))
			{
				return value;
			}
			uint compID = range.GetCompID((eGearComponent)2);
			uint compID2 = range.GetCompID((eGearComponent)16);
			if (compID == 0 && compID2 == 0)
			{
				string publicGearName = range.PublicGearName;
				if (!string.IsNullOrWhiteSpace(publicGearName))
				{
					return "name-" + SanitizeKey(publicGearName);
				}
				return null;
			}
			return "cat-" + compID.ToString(CultureInfo.InvariantCulture) + "-rec-" + compID2.ToString(CultureInfo.InvariantCulture);
		}

		internal static string WeaponTitleFor(string weaponKey, GearIDRange range)
		{
			if (weaponKey != null && WeaponTitles.TryGetValue(weaponKey, out var value) && !string.IsNullOrWhiteSpace(value))
			{
				return value;
			}
			if (range != null && !string.IsNullOrWhiteSpace(range.PublicGearName))
			{
				return range.PublicGearName;
			}
			return "Weapon";
		}

		internal static bool TryGetAuthoredSight(GearIDRange range, out bool hadSight, out uint sightId)
		{
			hadSight = false;
			sightId = 0u;
			if (range == null)
			{
				return false;
			}
			string text;
			try
			{
				text = Signature(range);
			}
			catch (Exception ex)
			{
				Plugin.LogSource.LogDebug((object)("Weapon signature: " + ex.Message));
				return false;
			}
			if (text == null || !AuthoredSights.TryGetValue(text, out var value))
			{
				return false;
			}
			hadSight = value.HadSight;
			sightId = value.SightId;
			return true;
		}

		internal static bool TryAlignmentDefault(string weaponKey, out float vertical, out float forward)
		{
			vertical = 0f;
			forward = 0f;
			if (weaponKey == null || !AlignmentDefaults.TryGetValue(weaponKey, out var value))
			{
				return false;
			}
			vertical = value.Vertical;
			forward = value.Forward;
			return true;
		}

		private static void IndexWeapon(PlayerOfflineGearDataBlock block, string display)
		{
			string text = ((GameDataBlockBase<PlayerOfflineGearDataBlock>)(object)block).persistentID.ToString(CultureInfo.InvariantCulture);
			if (!WeaponTitles.ContainsKey(text))
			{
				WeaponTitles.Add(text, string.IsNullOrWhiteSpace(display) ? ((GameDataBlockBase<PlayerOfflineGearDataBlock>)(object)block).name : display);
			}
			string text2 = GearJsonEditor.ComponentSignature(block.GearJSON);
			if (text2 != null)
			{
				if (!WeaponKeys.ContainsKey(text2))
				{
					WeaponKeys.Add(text2, text);
				}
				if (!AuthoredSights.ContainsKey(text2))
				{
					long value;
					bool flag = GearJsonEditor.TryGetComponentValue(block.GearJSON, 21, out value) && value >= 0 && value <= uint.MaxValue;
					AuthoredSights.Add(text2, new AuthoredSight
					{
						HadSight = flag,
						SightId = (uint)(flag ? value : 0u)
					});
				}
			}
			string display2 = (string.IsNullOrWhiteSpace(display) ? ((GameDataBlockBase<PlayerOfflineGearDataBlock>)(object)block).name : display);
			if (GearJsonEditor.TryGetComponentValue(block.GearJSON, 21, out var value2))
			{
				RememberSightUser(value2, display2);
			}
			RememberAlignmentDefault(text, ((GameDataBlockBase<PlayerOfflineGearDataBlock>)(object)block).name, display);
			if (((GameDataBlockBase<PlayerOfflineGearDataBlock>)(object)block).name != null && BarrelDatablocks.Contains(((GameDataBlockBase<PlayerOfflineGearDataBlock>)(object)block).name))
			{
				BarrelKeys.Add(text);
			}
		}

		internal static bool FollowsBarrel(string weaponKey)
		{
			if (weaponKey != null)
			{
				return BarrelKeys.Contains(weaponKey);
			}
			return false;
		}

		private static void RememberAlignmentDefault(string weaponKey, string datablockName, string display)
		{
			if (!AlignmentDefaults.ContainsKey(weaponKey) && TryAlignmentPreset(datablockName, out var preset))
			{
				AlignmentDefaults.Add(weaponKey, preset);
				string text = (string.IsNullOrWhiteSpace(display) ? datablockName : display);
				Plugin.LogSource.LogInfo((object)("Default alignment for " + text + ": scope vertical " + preset.Vertical.ToString("+0.0000;-0.0000", CultureInfo.InvariantCulture) + ", scope forward " + preset.Forward.ToString("+0.0000;-0.0000", CultureInfo.InvariantCulture) + "."));
			}
		}

		private static bool TryAlignmentPreset(string datablockName, out AlignmentDefault preset)
		{
			if (datablockName != null && AlignmentPresets.TryGetValue(datablockName, out preset))
			{
				return true;
			}
			preset = default(AlignmentDefault);
			return false;
		}

		private static string Signature(GearIDRange range)
		{
			//IL_0031: 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_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Invalid comparison between Unknown and I4
			//IL_003b: 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)
			//IL_004e: Expected I4, but got Unknown
			if (Components == null)
			{
				Components = (eGearComponent[])Enum.GetValues(typeof(eGearComponent));
			}
			Dictionary<int, long> dictionary = new Dictionary<int, long>();
			for (int i = 0; i < Components.Length; i++)
			{
				eGearComponent val = Components[i];
				if ((int)val != 0 && (int)val != 21)
				{
					uint compID = range.GetCompID(val);
					if (compID != 0)
					{
						dictionary[(int)val] = compID;
					}
				}
			}
			return GearJsonEditor.FormatSignature(dictionary);
		}

		private static string SanitizeKey(string name)
		{
			StringBuilder stringBuilder = new StringBuilder(name.Length);
			foreach (char c in name)
			{
				bool flag;
				switch (c)
				{
				case '\t':
				case '\n':
				case '\r':
				case ' ':
				case '#':
				case '=':
				case '[':
				case ']':
					flag = true;
					break;
				default:
					flag = false;
					break;
				}
				bool flag2 = flag;
				stringBuilder.Append(flag2 ? '_' : c);
			}
			string text = stringBuilder.ToString().Trim('_');
			if (text.Length != 0)
			{
				return text;
			}
			return "weapon";
		}

		private static bool IsFrontIronSight(string name)
		{
			if (string.IsNullOrEmpty(name) || name.Length <= "IronSights_".Length || !name.StartsWith("IronSights_", StringComparison.OrdinalIgnoreCase))
			{
				return false;
			}
			string text = name.Substring("IronSights_".Length);
			while (text.EndsWith("_Copy", StringComparison.OrdinalIgnoreCase))
			{
				text = text.Substring(0, text.Length - "_Copy".Length);
			}
			return text.EndsWith("_F", StringComparison.OrdinalIgnoreCase);
		}

		internal static bool IsUnusableScope(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return false;
			}
			if (HiddenNames.Contains(name))
			{
				return true;
			}
			int num = name.IndexOf('#');
			if (num >= 0)
			{
				name = name.Substring(0, num);
			}
			while (name.EndsWith("_Copy", StringComparison.OrdinalIgnoreCase))
			{
				name = name.Substring(0, name.Length - "_Copy".Length);
			}
			if (HiddenNames.Contains(name) || string.Equals(name, "Sight_11", StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
			string text = CompactLabel(name);
			if (!(text == "IRONSIGHTS2B"))
			{
				return text == "IRONSIGHTS11";
			}
			return true;
		}

		private static bool IsHiddenScope(GearSightPartDataBlock block)
		{
			if (!IsUnusableScope(((GameDataBlockBase<GearSightPartDataBlock>)(object)block).name) && !IsUnusableScope(TitleOf(block, ((GameDataBlockBase<GearSightPartDataBlock>)(object)block).name)))
			{
				return false;
			}
			if (!string.IsNullOrEmpty(((GameDataBlockBase<GearSightPartDataBlock>)(object)block).name))
			{
				HiddenNames.Add(((GameDataBlockBase<GearSightPartDataBlock>)(object)block).name);
			}
			return true;
		}

		private static string CompactLabel(string name)
		{
			StringBuilder stringBuilder = new StringBuilder(name.Length);
			foreach (char c in name)
			{
				if (c != ' ' && c != '_' && c != '#')
				{
					stringBuilder.Append(char.ToUpperInvariant(c));
				}
			}
			return stringBuilder.ToString();
		}

		private static bool ShouldConfigure(string gearJson)
		{
			if (GearJsonEditor.TryGetComponentValue(gearJson, 21, out var _))
			{
				return true;
			}
			return IsFirearm(gearJson);
		}

		private static bool IsFirearm(string gearJson)
		{
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Invalid comparison between Unknown and I4
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Invalid comparison between Unknown and I4
			if (!GearJsonEditor.TryGetComponentValue(gearJson, 2, out var value))
			{
				return false;
			}
			GearCategoryDataBlock block;
			try
			{
				block = GameDataBlockBase<GearCategoryDataBlock>.GetBlock((uint)value);
			}
			catch (Exception ex)
			{
				Plugin.LogSource.LogDebug((object)("Category " + value + " was not readable: " + ex.Message));
				return false;
			}
			if (block == null)
			{
				return false;
			}
			ItemDataBlock block2;
			try
			{
				block2 = GameDataBlockBase<ItemDataBlock>.GetBlock(block.BaseItem);
			}
			catch (Exception ex2)
			{
				Plugin.LogSource.LogDebug((object)("Item " + block.BaseItem + " was not readable: " + ex2.Message));
				return false;
			}
			if (block2 == null)
			{
				return false;
			}
			InventorySlot inventorySlot = block2.inventorySlot;
			if ((int)inventorySlot != 1)
			{
				return (int)inventorySlot == 2;
			}
			return true;
		}

		private static Dictionary<string, string> ReadExistingSights()
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.Ordinal);
			string path = Path.Combine(Paths.ConfigPath, "SelectableScopes.cfg");
			if (!File.Exists(path))
			{
				return dictionary;
			}
			string text = null;
			string[] array = File.ReadAllLines(path);
			for (int i = 0; i < array.Length; i++)
			{
				string text2 = array[i].Trim();
				if (text2.Length >= 2 && text2[0] == '[' && text2[text2.Length - 1] == ']')
				{
					text = text2.Substring(1, text2.Length - 2);
				}
				else if (text != null && text2.Length != 0 && text2[0] != '#')
				{
					int num = text2.IndexOf('=');
					if (num > 0 && string.Equals(text2.Substring(0, num).Trim(), "Sight", StringComparison.Ordinal))
					{
						dictionary[text] = text2.Substring(num + 1).Trim();
					}
				}
			}
			return dictionary;
		}

		internal static string Title(string sightName)
		{
			if (IsDefault(sightName))
			{
				return "Default";
			}
			return TitleOf(Find(sightName), sightName);
		}

		private static string TitleOf(GearSightPartDataBlock block, string fallback)
		{
			object obj;
			if (block == null)
			{
				obj = null;
			}
			else
			{
				GearPartGeneralData general = block.General;
				obj = ((general != null) ? general.PublicName : null);
			}
			string text = (string)obj;
			if (!string.IsNullOrWhiteSpace(text))
			{
				string text2 = Localize(text.Trim());
				if (HasLetter(text2))
				{
					if (text2.IndexOf('_') < 0 || text2.IndexOf(' ') >= 0)
					{
						return text2;
					}
					return Prettify(text2);
				}
			}
			return Prettify((block != null && !string.IsNullOrWhiteSpace(((GameDataBlockBase<GearSightPartDataBlock>)(object)block).name)) ? ((GameDataBlockBase<GearSightPartDataBlock>)(object)block).name : fallback);
		}

		internal static string MenuLabel(string sightName)
		{
			string text = Title(sightName);
			if (IsDefault(sightName))
			{
				return text;
			}
			string text2 = BriefUsers(sightName);
			if (text2 != null)
			{
				return text + " — " + text2;
			}
			return text;
		}

		internal static string Detail(string sightName)
		{
			if (IsDefault(sightName))
			{
				return "Keeps the sight on the weapon";
			}
			GearSightPartDataBlock obj = Find(sightName);
			GearSightPartProperties val = ((obj != null) ? obj.SightProperties : null);
			if (val == null || val.WorldFovZoom <= 0)
			{
				return "No zoom";
			}
			return "FOV " + val.WorldFovZoom.ToString(CultureInfo.InvariantCulture);
		}

		internal static string Describe(string sightName, InventorySlot slot, bool selected)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			string value = (((int)slot == 2) ? "special" : "primary");
			StringBuilder stringBuilder = new StringBuilder();
			if (selected)
			{
				stringBuilder.Append("Selected for your ").Append(value).Append('.')
					.Append('\n')
					.Append('\n');
			}
			if (IsDefault(sightName))
			{
				stringBuilder.Append("Keeps the sight already attached to the weapon.");
			}
			else
			{
				AppendStats(stringBuilder, sightName);
				stringBuilder.Append("Replaces the sight on your equipped ").Append(value).Append('.');
			}
			stringBuilder.Append('\n').Append("Updates the equipped weapon and its icon.");
			return stringBuilder.ToString();
		}

		private static void AppendStats(StringBuilder builder, string sightName)
		{
			AppendUsers(builder, sightName);
			GearSightPartDataBlock val = Find(sightName);
			GearSightPartProperties val2 = ((val != null) ? val.SightProperties : null);
			if (val2 == null || val2.WorldFovZoom <= 0)
			{
				builder.Append("Does not zoom.").Append('\n');
			}
			else
			{
				builder.Append("FOV ").Append(val2.WorldFovZoom.ToString(CultureInfo.InvariantCulture)).Append(". A lower number magnifies more.")
					.Append('\n');
			}
			if (val2 != null && val2.GearFovZoom > 0)
			{
				builder.Append("Sight model framing: FOV ").Append(val2.GearFovZoom.ToString(CultureInfo.InvariantCulture)).Append('\n');
			}
			string value = ((val != null && !string.IsNullOrWhiteSpace(((GameDataBlockBase<GearSightPartDataBlock>)(object)val).name)) ? ((GameDataBlockBase<GearSightPartDataBlock>)(object)val).name : sightName);
			builder.Append("Datablock: ").Append(value).Append('\n');
		}

		private static void AppendUsers(StringBuilder builder, string sightName)
		{
			List<string> list = UsersOf(sightName);
			if (list == null)
			{
				builder.Append("Not the stock sight on any weapon.").Append('\n');
				return;
			}
			builder.Append("Stock sight on: ");
			for (int i = 0; i < list.Count; i++)
			{
				if (i > 0)
				{
					builder.Append(", ");
				}
				builder.Append(list[i]);
			}
			builder.Append('\n');
		}

		private static string BriefUsers(string sightName)
		{
			List<string> list = UsersOf(sightName);
			if (list == null)
			{
				return null;
			}
			if (list.Count == 1)
			{
				return list[0];
			}
			string text = list[0] + ", " + list[1];
			int num = list.Count - 2;
			if (num > 0)
			{
				text = text + " +" + num.ToString(CultureInfo.InvariantCulture);
			}
			return text;
		}

		private static List<string> UsersOf(string sightName)
		{
			if (Sights == null || string.IsNullOrEmpty(sightName) || !Sights.TryGetValue(sightName, out var value))
			{
				return null;
			}
			if (!SightUsers.TryGetValue(value, out var value2) || value2.Count == 0)
			{
				return null;
			}
			return value2;
		}

		private static void RememberSightUser(long sightValue, string display)
		{
			if (sightValue <= 0 || sightValue > uint.MaxValue || string.IsNullOrWhiteSpace(display))
			{
				return;
			}
			uint key = (uint)sightValue;
			if (!SightUsers.TryGetValue(key, out var value))
			{
				value = new List<string>();
				SightUsers.Add(key, value);
			}
			for (int i = 0; i < value.Count; i++)
			{
				if (string.Equals(value[i], display, StringComparison.OrdinalIgnoreCase))
				{
					return;
				}
			}
			value.Add(display);
		}

		private static bool HasLetter(string value)
		{
			if (string.IsNullOrWhiteSpace(value))
			{
				return false;
			}
			for (int i = 0; i < value.Length; i++)
			{
				if (char.IsLetter(value[i]))
				{
					return true;
				}
			}
			return false;
		}

		private static bool IsDefault(string sightName)
		{
			if (!string.IsNullOrEmpty(sightName))
			{
				return string.Equals(sightName, "Default", StringComparison.Ordinal);
			}
			return true;
		}

		private static GearSightPartDataBlock Find(string sightName)
		{
			if (Sights == null || string.IsNullOrEmpty(sightName) || !Sights.TryGetValue(sightName, out var value))
			{
				return null;
			}
			try
			{
				return GameDataBlockBase<GearSightPartDataBlock>.GetBlock(value);
			}
			catch (Exception ex)
			{
				Plugin.LogSource.LogDebug((object)("Sight " + sightName + " was not readable: " + ex.Message));
				return null;
			}
		}

		private static string Localize(string value)
		{
			if (!uint.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
			{
				return value;
			}
			try
			{
				string text = Text.Get(result);
				return string.IsNullOrWhiteSpace(text) ? value : text;
			}
			catch (Exception ex)
			{
				Plugin.LogSource.LogDebug((object)("Sight name " + result + " was not localized: " + ex.Message));
				return value;
			}
		}

		private static string Prettify(string name)
		{
			if (string.IsNullOrWhiteSpace(name))
			{
				return "Sight";
			}
			string text = name.Replace('_', ' ').Replace('#', ' ').Trim();
			while (text.Contains("  ", StringComparison.Ordinal))
			{
				text = text.Replace("  ", " ", StringComparison.Ordinal);
			}
			if (text.StartsWith("Sight ", StringComparison.OrdinalIgnoreCase))
			{
				string text2 = text.Substring("Sight ".Length).Trim();
				if (text2.Length > 0 && char.IsLetter(text2[0]))
				{
					text = text2;
				}
			}
			if (text.Length != 0)
			{
				return text;
			}
			return name;
		}

		private static string SectionName(PlayerOfflineGearDataBlock gear, HashSet<string> usedSections)
		{
			string text = ((GameDataBlockBase<PlayerOfflineGearDataBlock>)(object)gear).name;
			if (string.IsNullOrWhiteSpace(text))
			{
				text = "Gear" + ((GameDataBlockBase<PlayerOfflineGearDataBlock>)(object)gear).persistentID;
			}
			StringBuilder stringBuilder = new StringBuilder(text.Length);
			string text2 = text;
			foreach (char c in text2)
			{
				bool flag;
				switch (c)
				{
				case '\t':
				case '\n':
				case '\r':
				case '[':
				case ']':
					flag = true;
					break;
				default:
					flag = false;
					break;
				}
				bool flag2 = flag;
				stringBuilder.Append(flag2 ? '_' : c);
			}
			string text3 = stringBuilder.ToString().Trim();
			if (text3.Length == 0)
			{
				text3 = "Gear" + ((GameDataBlockBase<PlayerOfflineGearDataBlock>)(object)gear).persistentID;
			}
			if (!usedSections.Add(text3))
			{
				text3 = text3 + "_" + ((GameDataBlockBase<PlayerOfflineGearDataBlock>)(object)gear).persistentID;
				usedSections.Add(text3);
			}
			return text3;
		}
	}
	internal static class ScopeChoices
	{
		internal static ConfigEntry<string> Primary { get; private set; }

		internal static ConfigEntry<string> Special { get; private set; }

		internal static void Bind(AcceptableValueList<string> acceptable)
		{
			Primary = BindEntry("Primary Sight", "Sight used on your equipped primary. Default keeps the weapon's current sight.", acceptable);
			Special = BindEntry("Special Sight", "Sight used on your equipped special. Default keeps the weapon's current sight.", acceptable);
		}

		internal static string Display(InventorySlot slot)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			ConfigEntry<string> val = Entry(slot);
			if (val == null || string.IsNullOrEmpty(val.Value))
			{
				return "Default";
			}
			return val.Value;
		}

		internal static void Set(InventorySlot slot, string sightName)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			ConfigEntry<string> val = Entry(slot);
			if (val != null)
			{
				val.Value = sightName;
				((BasePlugin)Plugin.Instance).Config.Save();
			}
		}

		internal static bool TryGetSightId(InventorySlot slot, out uint sightId)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			sightId = 0u;
			ConfigEntry<string> val = Entry(slot);
			if (val == null || ScopeCatalog.Sights == null)
			{
				return false;
			}
			string value = val.Value;
			if (string.IsNullOrEmpty(value) || string.Equals(value, "Default", StringComparison.Ordinal))
			{
				return false;
			}
			return ScopeCatalog.Sights.TryGetValue(value, out sightId);
		}

		private static ConfigEntry<string> BindEntry(string key, string description, AcceptableValueList<string> acceptable)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Expected O, but got Unknown
			ConfigEntry<string> val = ((BasePlugin)Plugin.Instance).Config.Bind<string>("Lobby", key, "Default", new ConfigDescription(description, (AcceptableValueBase)(object)acceptable, Array.Empty<object>()));
			if (ScopeCatalog.IsUnusableScope(val.Value))
			{
				Plugin.LogSource.LogInfo((object)("Lobby " + key + " was " + val.Value + ". That scope is no longer in the menu, so the slot is back on Default."));
				val.Value = "Default";
			}
			if (!string.IsNullOrEmpty(val.Value) && !string.Equals(val.Value, "Default", StringComparison.Ordinal) && (ScopeCatalog.Sights == null || !ScopeCatalog.Sights.ContainsKey(val.Value)))
			{
				Plugin.LogSource.LogWarning((object)("Unknown sight '" + val.Value + "' for Lobby " + key + ". That slot will keep the weapon's current sight."));
			}
			return val;
		}

		private static ConfigEntry<string> Entry(InventorySlot slot)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Invalid comparison between Unknown and I4
			if ((int)slot == 1)
			{
				return Primary;
			}
			if ((int)slot == 2)
			{
				return Special;
			}
			return null;
		}
	}
	internal static class ScopeCrosshair
	{
		private const string SightName = "Sight_11";

		private static ItemDataBlock Forced;

		private static bool OriginalValue;

		private static bool Holding;

		private static bool Logged;

		internal static void Prepare(FirstPersonItemHolder holder)
		{
			if (!Enabled())
			{
				Release();
			}
			else
			{
				if (!IsLocal(holder))
				{
					return;
				}
				ItemEquippable wieldedItem = holder.WieldedItem;
				if ((Object)(object)wieldedItem == (Object)null || !IsSight11(wieldedItem))
				{
					Release();
					return;
				}
				ItemDataBlock itemDataBlock = ((Item)wieldedItem).ItemDataBlock;
				if (itemDataBlock == null)
				{
					Release();
					return;
				}
				if (!Holding || Forced == null || ((GameDataBlockBase<ItemDataBlock>)(object)Forced).persistentID != ((GameDataBlockBase<ItemDataBlock>)(object)itemDataBlock).persistentID)
				{
					Release();
					OriginalValue = itemDataBlock.ShowCrosshairWhenAiming;
					Forced = itemDataBlock;
					Holding = true;
				}
				if (!itemDataBlock.ShowCrosshairWhenAiming)
				{
					itemDataBlock.ShowCrosshairWhenAiming = true;
				}
			}
		}

		internal static void Keep(FirstPersonItemHolder holder)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Invalid comparison between Unknown and I4
			if (!Enabled() || (Object)(object)holder == (Object)null || (int)holder.m_currentState != 3 || !IsLocal(holder))
			{
				return;
			}
			ItemEquippable wieldedItem = holder.WieldedItem;
			if (!((Object)(object)wieldedItem == (Object)null) && IsSight11(wieldedItem))
			{
				LerpingPairFloat crosshairAlpha = holder.CrosshairAlpha;
				if (crosshairAlpha != null && (((LerpingPair<float>)(object)crosshairAlpha).Target < 0.999f || ((LerpingPair<float>)(object)crosshairAlpha).Start < 0.999f))
				{
					((LerpingPair<float>)(object)crosshairAlpha).Start = 1f;
					((LerpingPair<float>)(object)crosshairAlpha).Target = 1f;
				}
				Show(wieldedItem);
			}
		}

		private static void Show(ItemEquippable weapon)
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: 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_007b: Unknown result type (might be due to invalid IL or missing references)
			if (!InFirstPerson() || (Object)(object)GuiManager.Current == (Object)null)
			{
				return;
			}
			CrosshairGuiLayer crosshairLayer = GuiManager.CrosshairLayer;
			ItemDataBlock itemDataBlock = ((Item)weapon).ItemDataBlock;
			if (crosshairLayer == null || itemDataBlock == null)
			{
				return;
			}
			float num;
			try
			{
				num = CellSettingsApply.ApplyHUDCrosshairOpacity(1f);
			}
			catch (Exception ex)
			{
				Plugin.LogSource.LogDebug((object)("Crosshair opacity: " + ex.Message));
				num = 1f;
			}
			if (!(num <= 0.001f))
			{
				Crosshair val = itemDataBlock.crosshair;
				if ((int)val == 0)
				{
					val = (Crosshair)1;
				}
				if (crosshairLayer.m_currentCrosshair != val)
				{
					crosshairLayer.ShowCrosshair(val);
				}
				if (crosshairLayer.GetCrosshairAlpha() < num * 0.9f)
				{
					crosshairLayer.SetCrosshairAlpha(num);
				}
				if (!Logged)
				{
					Logged = true;
					Plugin.LogSource.LogInfo((object)"Sight 11 keeps the crosshair while aiming.");
				}
			}
		}

		private static bool InFirstPerson()
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Invalid comparison between Unknown and I4
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Invalid comparison between Unknown and I4
			if ((Object)(object)FocusStateManager.Current == (Object)null)
			{
				return false;
			}
			eFocusState currentState = FocusStateManager.CurrentState;
			if ((int)currentState != 4)
			{
				return (int)currentState == 9;
			}
			return true;
		}

		private static void Release()
		{
			if (Holding)
			{
				Holding = false;
				ItemDataBlock forced = Forced;
				Forced = null;
				if (forced != null)
				{
					forced.ShowCrosshairWhenAiming = OriginalValue;
				}
			}
		}

		private static bool IsSight11(ItemEquippable weapon)
		{
			GearPartHolder gearPartHolder = weapon.GearPartHolder;
			GearSightPartDataBlock val = (((Object)(object)gearPartHolder != (Object)null) ? gearPartHolder.SightData : null);
			if (val != null)
			{
				return string.Equals(((GameDataBlockBase<GearSightPartDataBlock>)(object)val).name, "Sight_11", StringComparison.OrdinalIgnoreCase);
			}
			return false;
		}

		private static bool IsLocal(FirstPersonItemHolder holder)
		{
			if ((Object)(object)holder == (Object)null)
			{
				return false;
			}
			PlayerAgent owner = holder.m_owner;
			if ((Object)(object)owner != (Object)null)
			{
				return ((Agent)owner).IsLocallyOwned;
			}
			PlayerAgent val = default(PlayerAgent);
			if (PlayerManager.TryGetLocalPlayerAgent(ref val) && (Object)(object)val != (Object)null)
			{
				return (Object)(object)val.FPItemHolder == (Object)(object)holder;
			}
			return false;
		}

		private static bool Enabled()
		{
			if (Plugin.Enabled != null)
			{
				return Plugin.Enabled.Value;
			}
			return false;
		}
	}
	internal static class ScopeLoadoutCategory
	{
		private const string ButtonName = "SelectableScopes.Button";

		private const string CardName = "SelectableScopes.Sight";

		private const string ScopeUpId = "\u0001scope-up";

		private const string ScopeDownId = "\u0001scope-down";

		private const string ForwardId = "\u0001scope-forward";

		private const string BackId = "\u0001scope-back";

		private const string SaveId = "\u0001align-save";

		private const string ResetId = "\u0001align-reset";

		private static readonly Color SaveColor = new Color(0.62f, 1f, 0.62f, 1f);

		private static readonly Color ResetColor = new Color(1f, 0.78f, 0.45f, 1f);

		private static readonly Color Accent = new Color(0.54f, 0.71f, 0.98f, 1f);

		private static readonly Dictionary<IntPtr, CM_LobbyScrollItem> Buttons = new Dictionary<IntPtr, CM_LobbyScrollItem>();

		private static readonly Dictionary<IntPtr, CM_PlayerLobbyBar> ButtonBars = new Dictionary<IntPtr, CM_PlayerLobbyBar>();

		private static int ReleasedFrame = -1;

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

		private static readonly List<string> RowNames = new List<st