Decompiled source of Reloadable Hand Cannons v1.1.1

tony4twentys-Reloadable Hand Cannons.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ExitGames.Client.Photon;
using HarmonyLib;
using Photon.Pun;
using Photon.Realtime;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using Zorro.Core;
using Zorro.Core.Serizalization;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: AssemblyTitle("Reloadable_Hand_Cannons")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Reloadable_Hand_Cannons")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("0f5e42e3-bb26-4c24-9f5b-39f5ae0867ad")]
[assembly: AssemblyFileVersion("1.1.1.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.1.0")]
[module: UnverifiableCode]
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}
namespace Hand_Cannon
{
	public enum HandCannonAmmoType
	{
		Empty,
		Piton,
		NormalRope,
		AntiRope,
		Chain
	}
	[BepInPlugin("tony4twentys.Reloadable_Hand_Cannons", "Reloadable_Hand_Cannons", "1.1.1")]
	public class HandCannonPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "tony4twentys.Reloadable_Hand_Cannons";

		public const string PluginName = "Reloadable_Hand_Cannons";

		public const string PluginVersion = "1.1.1";

		internal static ConfigEntry<float> PitonSpawnChanceConfig;

		internal static ConfigEntry<bool> AllowBackpackReloadConfig;

		internal static ConfigEntry<KeyCode> ReloadKeyConfig;

		private Harmony _harmony;

		public static HandCannonPlugin Instance { get; private set; }

		public static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Expected O, but got Unknown
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			PitonSpawnChanceConfig = ((BaseUnityPlugin)this).Config.Bind<float>("Spawn", "Piton Spawn Chance", 0.5f, "Chance (0.0 to 1.0) for a Hand Cannon (item 63) to spawn loaded with a piton instead of normal rope.");
			AllowBackpackReloadConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Reload", "Allow Backpack Reload", true, "When true, pressing the reload key can consume ammo from a worn backpack.");
			ReloadKeyConfig = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Reload", "Reload Key", (KeyCode)114, "Press this key while holding an empty Hand Cannon to reload from inventory.");
			WarnIfConflictingModsLoaded();
			_harmony = new Harmony("tony4twentys.Reloadable_Hand_Cannons");
			try
			{
				_harmony.PatchAll();
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Reloadable Hand Cannons harmony patching failed: " + ex.Message));
			}
			HandCannonReloadPickerUI.EnsureCreated();
			HandCannonNetworkSync.EnsureProxy();
			SceneManager.sceneLoaded += OnSceneLoaded;
			((BaseUnityPlugin)this).Logger.LogInfo((object)($"Reloadable Hand Cannons loaded. Piton spawn chance: {PitonSpawnChanceConfig.Value:P0}. " + string.Format("Reload key: {0}. Backpack reload: {1}.", ReloadKeyConfig.Value, AllowBackpackReloadConfig.Value ? "enabled" : "disabled")));
		}

		private void Update()
		{
			//IL_0032: 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)
			if (HandCannonReloadPickerUI.IsOpen)
			{
				if (Input.GetKeyDown((KeyCode)27) || Input.GetKeyDown(ReloadKeyConfig.Value))
				{
					HandCannonReloadPickerUI.ForceClose();
				}
				else
				{
					HandCannonReloadPickerUI.TryKeyboardSelect();
				}
			}
			else
			{
				if (!Input.GetKeyDown(ReloadKeyConfig.Value))
				{
					return;
				}
				Character localCharacter = Character.localCharacter;
				object obj;
				if (localCharacter == null)
				{
					obj = null;
				}
				else
				{
					CharacterData data = localCharacter.data;
					obj = ((data != null) ? data.currentItem : null);
				}
				if (!((Object)obj == (Object)null))
				{
					Item currentItem = localCharacter.data.currentItem;
					RopeShooter component = ((Component)currentItem).GetComponent<RopeShooter>();
					if (HandCannonHelper.IsHandCannon(currentItem) && HandCannonHelper.IsEmptyForReload(currentItem, component) && !currentItem.isUsingPrimary)
					{
						HandCannonReloadExecutor.TryManualReload(localCharacter);
					}
				}
			}
		}

		private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
		{
			HandCannonUi.InvalidateFont();
			HandCannonReloadPickerUI.ForceClose();
			HandCannonIcon.QueueLiveRefresh();
		}

		private void OnDestroy()
		{
			SceneManager.sceneLoaded -= OnSceneLoaded;
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			HandCannonReloadPickerUI.DestroyInstance();
			Instance = null;
			Log = null;
		}

		private static void WarnIfConflictingModsLoaded()
		{
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			for (int i = 0; i < assemblies.Length; i++)
			{
				string name = assemblies[i].GetName().Name;
				if (string.Equals(name, "Piton Launcher", StringComparison.Ordinal) || string.Equals(name, "Reloadable Rope Canons", StringComparison.Ordinal) || string.Equals(name, "Hand Cannon", StringComparison.Ordinal))
				{
					Log.LogWarning((object)("Detected '" + name + "' — disable it to avoid Harmony conflicts with Reloadable Hand Cannons."));
				}
			}
		}
	}
	internal static class HandCannonDataEntryKey
	{
		public static readonly DataEntryKey LoadedAmmoType = (DataEntryKey)74;

		public static readonly DataEntryKey BridgeFrame = (DataEntryKey)75;

		public static readonly DataEntryKey BridgeLauncherType = (DataEntryKey)76;
	}
	internal static class GameItemIds
	{
		public const ushort RopeCannonItemId = 63;

		public const ushort AntiRopeCannonItemId = 64;

		public const ushort ChainLauncherItemId = 17;

		public const ushort AntiRopeSpoolItemId = 1;

		public const ushort RopeSpoolItemId = 65;

		public const ushort ClimbingSpikeItemId = 18;
	}
	internal static class BridgeLauncherCompat
	{
		private const string BridgeLauncherTypeName = "Bridge_Launcher.BridgeLauncherDataEntryKey";

		private const string LauncherTypeFieldName = "LauncherType";

		private static int _resolveAttempts;

		private static bool _hasKey;

		private static DataEntryKey _launcherTypeKey;

		public static bool IsBridgeLauncher(Item item)
		{
			return IsBridgeLauncher(item, ((Object)(object)item != (Object)null) ? item.data : null);
		}

		public static bool IsBridgeLauncher(Item item, ItemInstanceData data)
		{
			if (HasOurBridgeFlag(item, data))
			{
				return true;
			}
			if (HasForeignBridgeFlag(item, data))
			{
				RememberBridgeFrame(item, HandCannonInventoryHelper.FindSlotForItem(item));
				return true;
			}
			return false;
		}

		public static void CaptureIdentity(Item item)
		{
			if ((Object)(object)item != (Object)null && HasForeignBridgeFlag(item, item.data))
			{
				RememberBridgeFrame(item, HandCannonInventoryHelper.FindSlotForItem(item));
			}
		}

		public static void RequestHostTag(Item item)
		{
			if ((Object)(object)item == (Object)null)
			{
				return;
			}
			try
			{
				Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
				for (int i = 0; i < assemblies.Length; i++)
				{
					Type type = assemblies[i].GetType("Bridge_Launcher.BridgeNet");
					if (!(type == null))
					{
						MethodInfo method = type.GetMethod("ScheduleHostTag", BindingFlags.Static | BindingFlags.Public);
						if (method != null)
						{
							method.Invoke(null, new object[1] { item });
						}
						break;
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = HandCannonPlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("[Bridge] host tag request failed: " + ex.Message));
				}
			}
		}

		public static void CopyBridgeFrameToSlot(Item item, ItemSlot slot)
		{
			if (!((Object)(object)item == (Object)null) && slot != null && IsBridgeLauncher(item))
			{
				RememberBridgeFrame(item, slot);
			}
		}

		private static bool HasOurBridgeFlag(Item item, ItemInstanceData data)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)item != (Object)null && item.HasData(HandCannonDataEntryKey.BridgeFrame))
			{
				IntItemData data2 = item.GetData<IntItemData>(HandCannonDataEntryKey.BridgeFrame);
				if (data2 != null && data2.Value != 0)
				{
					return true;
				}
			}
			return ReadOurBridgeFlag(data);
		}

		private static bool ReadOurBridgeFlag(ItemInstanceData data)
		{
			//IL_0004: 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)
			if (data == null || !data.HasData(HandCannonDataEntryKey.BridgeFrame))
			{
				return false;
			}
			IntItemData val = default(IntItemData);
			if (data.TryGetDataEntry<IntItemData>(HandCannonDataEntryKey.BridgeFrame, ref val) && val != null)
			{
				return val.Value != 0;
			}
			return false;
		}

		private static bool HasForeignBridgeFlag(Item item, ItemInstanceData data)
		{
			//IL_0002: 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_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: 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 (HasBoolFlag(item, data, HandCannonDataEntryKey.BridgeLauncherType))
			{
				return true;
			}
			if (!TryGetLauncherTypeKey(out var key))
			{
				return false;
			}
			try
			{
				if ((Object)(object)item != (Object)null && item.HasData(key))
				{
					BoolItemData data2 = item.GetData<BoolItemData>(key);
					if (data2 != null && data2.Value)
					{
						return true;
					}
				}
				BoolItemData val = default(BoolItemData);
				if (data != null && data.HasData(key) && data.TryGetDataEntry<BoolItemData>(key, ref val))
				{
					return val?.Value ?? false;
				}
			}
			catch
			{
			}
			return false;
		}

		private static bool HasBoolFlag(Item item, ItemInstanceData data, DataEntryKey key)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: 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)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if ((Object)(object)item != (Object)null && item.HasData(key))
				{
					BoolItemData data2 = item.GetData<BoolItemData>(key);
					if (data2 != null && data2.Value)
					{
						return true;
					}
				}
				BoolItemData val = default(BoolItemData);
				if (data != null && data.HasData(key) && data.TryGetDataEntry<BoolItemData>(key, ref val))
				{
					return val?.Value ?? false;
				}
			}
			catch
			{
			}
			return false;
		}

		private static void RememberBridgeFrame(Item item, ItemSlot slot)
		{
			//IL_000a: 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_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			if ((Object)(object)item != (Object)null)
			{
				item.GetData<IntItemData>(HandCannonDataEntryKey.BridgeFrame).Value = 1;
			}
			if (slot != null)
			{
				if (slot.data == null)
				{
					slot.data = new ItemInstanceData(Guid.NewGuid());
				}
				ItemDataHelper.SetIntEntry(slot.data, HandCannonDataEntryKey.BridgeFrame, 1);
			}
		}

		private static bool TryGetLauncherTypeKey(out DataEntryKey key)
		{
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Expected I4, but got Unknown
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			if (!_hasKey && _resolveAttempts < 8)
			{
				_resolveAttempts++;
				Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
				foreach (Assembly assembly in assemblies)
				{
					Type type = assembly.GetType("Bridge_Launcher.BridgeLauncherDataEntryKey");
					if (type == null && assembly.GetName().Name != null && assembly.GetName().Name.IndexOf("Bridge", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						type = assembly.GetType("Bridge_Shooter.BridgeLauncherDataEntryKey");
					}
					if (type == null)
					{
						continue;
					}
					FieldInfo field = type.GetField("LauncherType", BindingFlags.Static | BindingFlags.Public);
					if (!(field == null))
					{
						object value = field.GetValue(null);
						if (value is DataEntryKey)
						{
							_launcherTypeKey = (DataEntryKey)value;
							_hasKey = true;
						}
						break;
					}
				}
			}
			key = (DataEntryKey)(int)_launcherTypeKey;
			return _hasKey;
		}
	}
	internal static class HandCannonHelper
	{
		public static Item GetItem(RopeShooter shooter)
		{
			if ((Object)(object)shooter == (Object)null)
			{
				return null;
			}
			return ((ItemComponent)shooter).item ?? ((Component)shooter).GetComponent<Item>();
		}

		public static Item GetItem(VineShooter vine)
		{
			if ((Object)(object)vine == (Object)null)
			{
				return null;
			}
			return ((ItemComponent)vine).item ?? ((Component)vine).GetComponent<Item>();
		}

		public static bool IsChainLauncher(Item item)
		{
			if ((Object)(object)item != (Object)null && item.itemID == 17)
			{
				return (Object)(object)((Component)item).GetComponent<VineShooter>() != (Object)null;
			}
			return false;
		}

		public static bool IsHandCannon(Item item)
		{
			if ((Object)(object)item == (Object)null)
			{
				return false;
			}
			if (IsChainLauncher(item))
			{
				return true;
			}
			if ((Object)(object)((Component)item).GetComponent<RopeShooter>() == (Object)null)
			{
				return false;
			}
			if (item.itemID != 63)
			{
				return item.itemID == 64;
			}
			return true;
		}

		public static bool HasCharge(Item item)
		{
			if ((Object)(object)item == (Object)null)
			{
				return false;
			}
			if (IsChainLauncher(item))
			{
				return !ChainLooksSpent(item);
			}
			RopeShooter component = ((Component)item).GetComponent<RopeShooter>();
			if ((Object)(object)component != (Object)null)
			{
				return component.HasAmmo;
			}
			if (item.HasData((DataEntryKey)2))
			{
				OptionableIntItemData data = item.GetData<OptionableIntItemData>((DataEntryKey)2);
				if (data != null && data.HasData)
				{
					return data.Value > 0;
				}
			}
			return true;
		}

		public static bool HasSpentUses(Item item)
		{
			if ((Object)(object)item == (Object)null || !item.HasData((DataEntryKey)2))
			{
				return false;
			}
			OptionableIntItemData data = item.GetData<OptionableIntItemData>((DataEntryKey)2);
			if (data != null && data.HasData)
			{
				return data.Value <= 0;
			}
			return false;
		}

		public static bool ChainLooksSpent(Item item)
		{
			if ((Object)(object)item == (Object)null)
			{
				return true;
			}
			if (HasSpentUses(item))
			{
				return true;
			}
			VineShooter component = ((Component)item).GetComponent<VineShooter>();
			if ((Object)(object)component != (Object)null && (Object)(object)component.disableOnFire != (Object)null)
			{
				_ = component.disableOnFire.activeSelf;
				return false;
			}
			return false;
		}

		public static bool CanTagItems()
		{
			if (PhotonNetwork.InRoom)
			{
				return PhotonNetwork.IsMasterClient;
			}
			return true;
		}

		public static Item GetActionItem(ItemActionBase action)
		{
			if (action == null)
			{
				return null;
			}
			return ((Component)action).GetComponent<Item>();
		}

		public static bool IsEmptyForReload(Item item, RopeShooter shooter)
		{
			if ((Object)(object)item == (Object)null)
			{
				return false;
			}
			if (HandCannonAmmoHelper.GetEffectiveAmmo(item) == HandCannonAmmoType.Empty)
			{
				return true;
			}
			if ((Object)(object)shooter != (Object)null)
			{
				if (!shooter.HasAmmo)
				{
					return true;
				}
				OptionableIntItemData data = item.GetData<OptionableIntItemData>((DataEntryKey)2);
				if (data != null && data.HasData && data.Value <= 0)
				{
					return true;
				}
				return false;
			}
			return !HasCharge(item);
		}
	}
	internal static class ItemUsabilityHelper
	{
		private static readonly FieldInfo OverrideUsabilityField = AccessTools.Field(typeof(Item), "overrideUsability");

		public static void SetOverrideUsability(Item item, bool usable)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)item == (Object)null) && !(OverrideUsabilityField == null))
			{
				OverrideUsabilityField.SetValue(item, Optionable<bool>.Some(usable));
			}
		}
	}
	internal static class HandCannonFireHelper
	{
		public static void ConsumeFiredShot(RopeShooter shooter, Item item)
		{
			ConsumeFiredShot(item, shooter);
		}

		public static void ConsumeFiredShot(Item item, RopeShooter shooter)
		{
			if (!((Object)(object)item == (Object)null) && HandCannonHelper.IsHandCannon(item))
			{
				if ((Object)(object)shooter == (Object)null)
				{
					shooter = ((Component)item).GetComponent<RopeShooter>();
				}
				ItemSlot slot = HandCannonInventoryHelper.FindSlotForItem(item);
				HandCannonAmmoHelper.SetLoadedAmmo(item, slot, HandCannonAmmoType.Empty);
				HandCannonAmmoHelper.ClearUses(item);
				VineShooter component = ((Component)item).GetComponent<VineShooter>();
				if ((Object)(object)component != (Object)null && (Object)(object)component.disableOnFire != (Object)null)
				{
					component.disableOnFire.SetActive(false);
				}
				if ((Object)(object)shooter != (Object)null)
				{
					shooter.cantReFire = false;
					((ItemComponent)shooter).OnInstanceDataSet();
				}
				HandCannonStateSync.Sync(item, shooter);
				HandCannonNetworkSync.PushAmmoState(item, slot, item.holderCharacter);
			}
		}
	}
	internal static class HandCannonStateSync
	{
		public static void Sync(Item item, RopeShooter shooter)
		{
			HandCannonAntigravSync.Sync(item, shooter);
			HandCannonVisualSync.Sync(item, shooter);
		}
	}
	internal static class HandCannonNetworkSync
	{
		public const byte EventCode = 191;

		public const int ProtocolVersion = 100;

		private const byte SubAmmoSync = 0;

		private const byte SubAmmoRequest = 1;

		private static HandCannonNetProxy _proxy;

		private static readonly Dictionary<int, HandCannonAmmoType> AmmoByViewId = new Dictionary<int, HandCannonAmmoType>();

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

		public static PhotonView GetItemView(Item item)
		{
			if ((Object)(object)item == (Object)null)
			{
				return null;
			}
			try
			{
				PhotonView photonView = ((MonoBehaviourPun)item).photonView;
				if ((Object)(object)photonView != (Object)null)
				{
					return photonView;
				}
			}
			catch
			{
			}
			return ((Component)item).GetComponent<PhotonView>();
		}

		public static void RememberAmmo(int viewId, HandCannonAmmoType ammo)
		{
			if (viewId > 0)
			{
				AmmoByViewId[viewId] = ammo;
			}
		}

		public static bool TryGetRememberedAmmo(int viewId, out HandCannonAmmoType ammo)
		{
			return AmmoByViewId.TryGetValue(viewId, out ammo);
		}

		public static bool TryGetRememberedAmmo(Item item, out HandCannonAmmoType ammo)
		{
			ammo = HandCannonAmmoType.Empty;
			PhotonView itemView = GetItemView(item);
			if ((Object)(object)itemView != (Object)null)
			{
				return TryGetRememberedAmmo(itemView.ViewID, out ammo);
			}
			return false;
		}

		public static void EnsureProxy()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			if (!((Object)(object)_proxy != (Object)null))
			{
				GameObject val = new GameObject("ReloadableHandCannonsNet");
				Object.DontDestroyOnLoad((Object)val);
				_proxy = val.AddComponent<HandCannonNetProxy>();
			}
		}

		public static void StartCoroutineOnProxy(IEnumerator routine)
		{
			EnsureProxy();
			if ((Object)(object)_proxy != (Object)null && routine != null)
			{
				((MonoBehaviour)_proxy).StartCoroutine(routine);
			}
		}

		public static void PushAmmoState(Item item, ItemSlot slot, Character holder)
		{
			if ((Object)(object)item != (Object)null && slot != null)
			{
				HandCannonAmmoHelper.CopyAmmoToSlot(item, slot);
			}
			PushItemInstanceData(item);
			BroadcastAmmo(item);
			SyncHolderInventory(holder);
		}

		public static void PushItemInstanceData(Item item)
		{
			if (item?.data == null || !PhotonNetwork.InRoom)
			{
				return;
			}
			PhotonView itemView = GetItemView(item);
			if ((Object)(object)itemView == (Object)null || (!itemView.IsMine && !PhotonNetwork.IsMasterClient))
			{
				return;
			}
			RememberAmmo(itemView.ViewID, HandCannonAmmoHelper.GetLoadedAmmo(item));
			try
			{
				itemView.RPC("SetItemInstanceDataRPC", (RpcTarget)1, new object[1] { item.data });
			}
			catch (Exception ex)
			{
				ManualLogSource log = HandCannonPlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("[Sync] SetItemInstanceDataRPC failed: " + ex.Message));
				}
			}
			BroadcastAmmo(item);
		}

		public static void BroadcastAmmo(Item item)
		{
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			//IL_0090: Expected O, but got Unknown
			if ((Object)(object)item == (Object)null || !PhotonNetwork.InRoom)
			{
				return;
			}
			PhotonView itemView = GetItemView(item);
			if ((Object)(object)itemView == (Object)null || (!itemView.IsMine && !PhotonNetwork.IsMasterClient))
			{
				return;
			}
			HandCannonAmmoType loadedAmmo = HandCannonAmmoHelper.GetLoadedAmmo(item);
			RememberAmmo(itemView.ViewID, loadedAmmo);
			try
			{
				PhotonNetwork.RaiseEvent((byte)191, (object)new object[4]
				{
					100,
					(byte)0,
					itemView.ViewID,
					(int)loadedAmmo
				}, new RaiseEventOptions
				{
					Receivers = (ReceiverGroup)0
				}, SendOptions.SendReliable);
			}
			catch (Exception ex)
			{
				ManualLogSource log = HandCannonPlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("[Sync] RaiseEvent ammo failed: " + ex.Message));
				}
			}
		}

		public static void ApplyRemoteAmmo(int itemViewId, HandCannonAmmoType ammo)
		{
			RememberAmmo(itemViewId, ammo);
			PhotonView val = PhotonView.Find(itemViewId);
			if (!((Object)(object)val == (Object)null))
			{
				Item component = ((Component)val).GetComponent<Item>();
				if (HandCannonHelper.IsHandCannon(component))
				{
					HandCannonAmmoHelper.SetLoadedAmmo(component, null, ammo);
					RopeShooter component2 = ((Component)component).GetComponent<RopeShooter>();
					HandCannonStateSync.Sync(component, component2);
				}
			}
		}

		public static bool TryApplyRememberedAmmo(Item item)
		{
			if (!TryGetRememberedAmmo(item, out var ammo))
			{
				return false;
			}
			if (HandCannonAmmoHelper.GetLoadedAmmo(item) == ammo)
			{
				return true;
			}
			HandCannonAmmoHelper.SetLoadedAmmo(item, null, ammo);
			return true;
		}

		public static void RequestAmmoFromHost(Item item)
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0075: Expected O, but got Unknown
			if ((Object)(object)item == (Object)null || !PhotonNetwork.InRoom || PhotonNetwork.IsMasterClient)
			{
				return;
			}
			PhotonView itemView = GetItemView(item);
			if ((Object)(object)itemView == (Object)null)
			{
				return;
			}
			try
			{
				PhotonNetwork.RaiseEvent((byte)191, (object)new object[4]
				{
					100,
					(byte)1,
					itemView.ViewID,
					0
				}, new RaiseEventOptions
				{
					Receivers = (ReceiverGroup)2
				}, SendOptions.SendReliable);
			}
			catch (Exception ex)
			{
				ManualLogSource log = HandCannonPlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("[Sync] ammo request failed: " + ex.Message));
				}
			}
		}

		public static void RespondToAmmoRequest(int itemViewId)
		{
			if (PhotonNetwork.IsMasterClient)
			{
				PhotonView val = PhotonView.Find(itemViewId);
				Item item = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<Item>() : null);
				if (HandCannonHelper.IsHandCannon(item))
				{
					BroadcastAmmo(item);
					PushItemInstanceData(item);
				}
			}
		}

		public static void BroadcastAllHandCannons()
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			if (!PhotonNetwork.IsMasterClient)
			{
				return;
			}
			Item[] array = Resources.FindObjectsOfTypeAll<Item>();
			foreach (Item val in array)
			{
				if (HandCannonHelper.IsHandCannon(val) && !((Object)(object)((Component)val).gameObject == (Object)null))
				{
					Scene scene = ((Component)val).gameObject.scene;
					if (((Scene)(ref scene)).IsValid())
					{
						BroadcastAmmo(val);
						PushItemInstanceData(val);
					}
				}
			}
		}

		public static void SyncHolderInventory(Character character)
		{
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)((character != null) ? character.player : null) == (Object)null || !PhotonNetwork.InRoom || !character.IsLocal)
			{
				return;
			}
			PhotonView component = ((Component)character.player).GetComponent<PhotonView>();
			if ((Object)(object)component == (Object)null)
			{
				return;
			}
			try
			{
				InventorySyncData val = default(InventorySyncData);
				((InventorySyncData)(ref val))..ctor(character.player.itemSlots, character.player.backpackSlot, character.player.tempFullSlot);
				component.RPC("SyncInventoryRPC", (RpcTarget)1, new object[2]
				{
					IBinarySerializable.ToManagedArray<InventorySyncData>(val),
					false
				});
			}
			catch (Exception ex)
			{
				ManualLogSource log = HandCannonPlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("[Sync] SyncInventoryRPC failed: " + ex.Message));
				}
			}
		}

		public static void PreserveAmmoOnStash(Item item)
		{
			if (!HandCannonHelper.IsHandCannon(item))
			{
				return;
			}
			Character holderCharacter = item.holderCharacter;
			if (!((Object)(object)((holderCharacter != null) ? holderCharacter.player : null) == (Object)null))
			{
				ItemSlot val = HandCannonInventoryHelper.FindSlotForItem(item);
				if (val != null && !val.IsEmpty())
				{
					HandCannonAmmoHelper.CopyAmmoToSlot(item, val);
					SyncHolderInventory(item.holderCharacter);
				}
			}
		}

		public static void PreserveAmmoBeforeDrop(Item item)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			if (!HandCannonHelper.IsHandCannon(item))
			{
				return;
			}
			Character holderCharacter = item.holderCharacter;
			if ((Object)(object)((holderCharacter != null) ? holderCharacter.player : null) == (Object)null)
			{
				return;
			}
			ItemSlot val = HandCannonInventoryHelper.FindSlotForItem(item);
			if (val == null || val.IsEmpty() || !item.HasData(HandCannonDataEntryKey.LoadedAmmoType))
			{
				return;
			}
			try
			{
				HandCannonAmmoHelper.CopyAmmoToSlot(item, val);
			}
			catch (Exception ex)
			{
				ManualLogSource log = HandCannonPlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("[Sync] preserve ammo before drop failed: " + ex.Message));
				}
			}
		}

		public static void ScheduleSpawnResync(Item item)
		{
			StartCoroutineOnProxy(SpawnResyncRoutine(item));
		}

		public static void ScheduleClientCatchup(Item item)
		{
			StartCoroutineOnProxy(ClientCatchupRoutine(item));
		}

		public static bool TryBeginDeferredTag(int viewId)
		{
			return DeferredTagInFlight.Add(viewId);
		}

		public static void EndDeferredTag(int viewId)
		{
			DeferredTagInFlight.Remove(viewId);
		}

		private static IEnumerator SpawnResyncRoutine(Item item)
		{
			yield return null;
			if ((Object)(object)item != (Object)null)
			{
				PushItemInstanceData(item);
			}
			yield return (object)new WaitForSeconds(0.35f);
			if ((Object)(object)item != (Object)null)
			{
				PushItemInstanceData(item);
			}
		}

		private static IEnumerator ClientCatchupRoutine(Item item)
		{
			float[] waits = new float[3] { 0.05f, 0.2f, 0.5f };
			for (int i = 0; i < waits.Length; i++)
			{
				yield return (object)new WaitForSeconds(waits[i]);
				if ((Object)(object)item == (Object)null)
				{
					break;
				}
				if (TryApplyRememberedAmmo(item) || HandCannonAmmoHelper.GetLoadedAmmo(item) != HandCannonAmmoType.Empty)
				{
					RopeShooter component = ((Component)item).GetComponent<RopeShooter>();
					HandCannonStateSync.Sync(item, component);
					break;
				}
				RequestAmmoFromHost(item);
			}
		}

		internal static void HandleEvent(EventData eventData)
		{
			if (eventData == null || eventData.Code != 191 || !(eventData.CustomData is object[] array) || array.Length < 3 || Convert.ToInt32(array[0]) != 100)
			{
				return;
			}
			byte b = Convert.ToByte(array[1]);
			int itemViewId = Convert.ToInt32(array[2]);
			switch (b)
			{
			case 1:
				RespondToAmmoRequest(itemViewId);
				break;
			case 0:
				if (array.Length >= 4)
				{
					HandCannonAmmoType ammo = (HandCannonAmmoType)Convert.ToInt32(array[3]);
					ApplyRemoteAmmo(itemViewId, ammo);
				}
				break;
			}
		}
	}
	internal sealed class HandCannonNetProxy : MonoBehaviourPunCallbacks, IOnEventCallback
	{
		public override void OnEnable()
		{
			((MonoBehaviourPunCallbacks)this).OnEnable();
			PhotonNetwork.AddCallbackTarget((object)this);
		}

		public override void OnDisable()
		{
			PhotonNetwork.RemoveCallbackTarget((object)this);
			((MonoBehaviourPunCallbacks)this).OnDisable();
		}

		public void OnPlayerEnteredRoom(Player _)
		{
			if (PhotonNetwork.IsMasterClient)
			{
				((MonoBehaviour)this).StartCoroutine(BroadcastAfterJoin());
			}
		}

		public void OnEvent(EventData photonEvent)
		{
			HandCannonNetworkSync.HandleEvent(photonEvent);
		}

		private IEnumerator BroadcastAfterJoin()
		{
			yield return (object)new WaitForSeconds(0.5f);
			HandCannonNetworkSync.BroadcastAllHandCannons();
		}
	}
	internal static class HandCannonAmmoHelper
	{
		public static HandCannonAmmoType GetLoadedAmmo(Item item)
		{
			//IL_000c: 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)
			if ((Object)(object)item == (Object)null)
			{
				return HandCannonAmmoType.Empty;
			}
			if (item.HasData(HandCannonDataEntryKey.LoadedAmmoType))
			{
				return (HandCannonAmmoType)item.GetData<IntItemData>(HandCannonDataEntryKey.LoadedAmmoType).Value;
			}
			if (HandCannonNetworkSync.TryGetRememberedAmmo(item, out var ammo))
			{
				return ammo;
			}
			ItemSlot val = HandCannonInventoryHelper.FindSlotForItem(item);
			if (val != null && !val.IsEmpty() && val.data != null)
			{
				return GetLoadedAmmo(val.data);
			}
			return HandCannonAmmoType.Empty;
		}

		public static HandCannonAmmoType GetLoadedAmmo(ItemInstanceData data)
		{
			//IL_0004: 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)
			if (data == null || !data.HasData(HandCannonDataEntryKey.LoadedAmmoType))
			{
				return HandCannonAmmoType.Empty;
			}
			IntItemData val = default(IntItemData);
			if (data.TryGetDataEntry<IntItemData>(HandCannonDataEntryKey.LoadedAmmoType, ref val))
			{
				return (HandCannonAmmoType)val.Value;
			}
			return HandCannonAmmoType.Empty;
		}

		public static void SetLoadedAmmo(Item item, ItemSlot slot, HandCannonAmmoType ammoType)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Expected O, but got Unknown
			if (slot == null)
			{
				slot = HandCannonInventoryHelper.FindSlotForItem(item);
			}
			if ((Object)(object)item != (Object)null)
			{
				item.GetData<IntItemData>(HandCannonDataEntryKey.LoadedAmmoType).Value = (int)ammoType;
				PhotonView itemView = HandCannonNetworkSync.GetItemView(item);
				if ((Object)(object)itemView != (Object)null)
				{
					HandCannonNetworkSync.RememberAmmo(itemView.ViewID, ammoType);
				}
			}
			if (slot != null)
			{
				if (slot.data == null)
				{
					slot.data = new ItemInstanceData(Guid.NewGuid());
				}
				ItemDataHelper.SetIntEntry(slot.data, HandCannonDataEntryKey.LoadedAmmoType, (int)ammoType);
			}
			HandCannonIcon.QueueLiveRefresh();
		}

		public static bool HasExplicitAmmoType(Item item)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)item != (Object)null && item.HasData(HandCannonDataEntryKey.LoadedAmmoType))
			{
				return true;
			}
			HandCannonAmmoType ammo;
			return HandCannonNetworkSync.TryGetRememberedAmmo(item, out ammo);
		}

		public static void ClearUses(Item item)
		{
			if ((Object)(object)item == (Object)null)
			{
				return;
			}
			RopeShooter component = ((Component)item).GetComponent<RopeShooter>();
			if ((Object)(object)component != (Object)null)
			{
				((ItemComponent)component).GetData<IntItemData>((DataEntryKey)12, (Func<IntItemData>)(() => ItemDataHelper.CreateIntItemData(0))).Value = 0;
			}
			OptionableIntItemData data = item.GetData<OptionableIntItemData>((DataEntryKey)2);
			if (data != null)
			{
				data.HasData = true;
				data.Value = 0;
			}
			item.SetUseRemainingPercentage(0f);
		}

		public static void CopyAmmoToSlot(Item item, ItemSlot slot)
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Expected O, but got Unknown
			if (!((Object)(object)item == (Object)null) && slot != null)
			{
				HandCannonAmmoType loadedAmmo = GetLoadedAmmo(item);
				if (slot.data == null)
				{
					slot.data = new ItemInstanceData((item.data != null) ? item.data.guid : Guid.NewGuid());
				}
				ItemDataHelper.SetIntEntry(slot.data, HandCannonDataEntryKey.LoadedAmmoType, (int)loadedAmmo);
				BridgeLauncherCompat.CopyBridgeFrameToSlot(item, slot);
			}
		}

		public static string GetAmmoLabel(HandCannonAmmoType ammoType)
		{
			return ammoType switch
			{
				HandCannonAmmoType.Piton => "piton", 
				HandCannonAmmoType.NormalRope => "rope", 
				HandCannonAmmoType.AntiRope => "anti-grav rope", 
				HandCannonAmmoType.Chain => "chain", 
				_ => "empty", 
			};
		}

		public static string GetDisplayName(HandCannonAmmoType ammoType)
		{
			return ammoType switch
			{
				HandCannonAmmoType.Piton => "Piton Cannon", 
				HandCannonAmmoType.NormalRope => "Rope Cannon", 
				HandCannonAmmoType.AntiRope => "Anti-Rope Cannon", 
				HandCannonAmmoType.Chain => "Chain Cannon", 
				_ => "Hand Cannon", 
			};
		}

		public static string GetDisplayName(Item item, HandCannonAmmoType ammoType)
		{
			return GetDisplayName(item, ((Object)(object)item != (Object)null) ? item.data : null, ammoType);
		}

		public static string GetDisplayName(Item item, ItemInstanceData data, HandCannonAmmoType ammoType)
		{
			Item item2 = FindLiveItemForName(item, data);
			if (ammoType == HandCannonAmmoType.Chain && BridgeLauncherCompat.IsBridgeLauncher(item2, data))
			{
				return "Bridge Cannon";
			}
			return GetDisplayName(ammoType);
		}

		public static HandCannonAmmoType ResolveLoadedAmmoForName(Item item, ItemInstanceData data)
		{
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			Item val = FindLiveItemForName(item, data);
			HandCannonAmmoType loadedAmmo = GetLoadedAmmo(val);
			if (loadedAmmo != HandCannonAmmoType.Empty)
			{
				return loadedAmmo;
			}
			loadedAmmo = GetLoadedAmmo(data);
			if (loadedAmmo != HandCannonAmmoType.Empty)
			{
				return loadedAmmo;
			}
			loadedAmmo = GetLoadedAmmo(item);
			if (loadedAmmo != HandCannonAmmoType.Empty)
			{
				return loadedAmmo;
			}
			Item item2 = (((Object)(object)val != (Object)null) ? val : item);
			if (HandCannonHelper.HasSpentUses(item2))
			{
				return HandCannonAmmoType.Empty;
			}
			if (HandCannonHelper.IsChainLauncher(item2) && !HandCannonHelper.ChainLooksSpent(item2))
			{
				return HandCannonAmmoType.Chain;
			}
			if (!HasExplicitAmmoType(item))
			{
				if (data == null)
				{
					return HandCannonAmmoType.Empty;
				}
				data.HasData(HandCannonDataEntryKey.LoadedAmmoType);
			}
			return HandCannonAmmoType.Empty;
		}

		private static Item FindLiveItemForName(Item item, ItemInstanceData data)
		{
			Guid guid = Guid.Empty;
			if (data != null)
			{
				guid = data.guid;
			}
			else if ((Object)(object)item != (Object)null && item.data != null)
			{
				guid = item.data.guid;
			}
			if (guid == Guid.Empty)
			{
				return item;
			}
			List<Item> aLL_ACTIVE_ITEMS = Item.ALL_ACTIVE_ITEMS;
			if (aLL_ACTIVE_ITEMS != null)
			{
				for (int i = 0; i < aLL_ACTIVE_ITEMS.Count; i++)
				{
					Item val = aLL_ACTIVE_ITEMS[i];
					if ((Object)(object)val != (Object)null && val.data != null && val.data.guid == guid)
					{
						return val;
					}
				}
			}
			return item;
		}

		public static HandCannonAmmoType GetEffectiveAmmo(Item item)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			HandCannonAmmoType loadedAmmo = GetLoadedAmmo(item);
			if (loadedAmmo != HandCannonAmmoType.Empty)
			{
				return loadedAmmo;
			}
			if (HandCannonHelper.HasSpentUses(item))
			{
				return HandCannonAmmoType.Empty;
			}
			if (HandCannonHelper.IsChainLauncher(item) && !HandCannonHelper.ChainLooksSpent(item))
			{
				return HandCannonAmmoType.Chain;
			}
			if (!HasExplicitAmmoType(item))
			{
				if (!((Object)(object)item != (Object)null))
				{
					return HandCannonAmmoType.Empty;
				}
				item.HasData(HandCannonDataEntryKey.LoadedAmmoType);
			}
			return HandCannonAmmoType.Empty;
		}
	}
	internal static class ItemDataHelper
	{
		public static void SetIntEntry(ItemInstanceData data, DataEntryKey key, int value)
		{
			//IL_0001: 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)
			//IL_0019: Expected O, but got Unknown
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			IntItemData val = default(IntItemData);
			if (data.TryGetDataEntry<IntItemData>(key, ref val))
			{
				val.Value = value;
				return;
			}
			IntItemData val2 = new IntItemData();
			((DataEntryValue)val2).Init();
			val2.Value = value;
			data.RegisterEntry<IntItemData>(key, val2);
		}

		public static void SetOptionableIntEntry(ItemInstanceData data, DataEntryKey key, int value)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			OptionableIntItemData val = default(OptionableIntItemData);
			if (data.TryGetDataEntry<OptionableIntItemData>(key, ref val))
			{
				val.HasData = true;
				val.Value = value;
				return;
			}
			OptionableIntItemData val2 = new OptionableIntItemData();
			((DataEntryValue)val2).Init();
			val2.HasData = true;
			val2.Value = value;
			data.RegisterEntry<OptionableIntItemData>(key, val2);
		}

		public static void SetFloatEntry(ItemInstanceData data, DataEntryKey key, float value)
		{
			//IL_0001: 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)
			//IL_0019: Expected O, but got Unknown
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			FloatItemData val = default(FloatItemData);
			if (data.TryGetDataEntry<FloatItemData>(key, ref val))
			{
				val.Value = value;
				return;
			}
			FloatItemData val2 = new FloatItemData();
			((DataEntryValue)val2).Init();
			val2.Value = value;
			data.RegisterEntry<FloatItemData>(key, val2);
		}

		public static IntItemData CreateIntItemData(int value)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Expected O, but got Unknown
			IntItemData val = new IntItemData();
			((DataEntryValue)val).Init();
			val.Value = value;
			return val;
		}
	}
	internal static class HandCannonAntigravSync
	{
		private static float? _cachedIntensity;

		public static void Sync(Item item, RopeShooter shooter)
		{
			if ((Object)(object)item == (Object)null)
			{
				return;
			}
			bool num = HandCannonHelper.HasCharge(item) && HandCannonAmmoHelper.GetLoadedAmmo(item) == HandCannonAmmoType.AntiRope;
			Antigrav val = ((Component)item).GetComponent<Antigrav>();
			if (num)
			{
				if ((Object)(object)val == (Object)null)
				{
					val = ((Component)item).gameObject.AddComponent<Antigrav>();
				}
				val.intensity = GetAntigravIntensity();
			}
			else if ((Object)(object)val != (Object)null)
			{
				Object.Destroy((Object)(object)val);
			}
		}

		private static float GetAntigravIntensity()
		{
			if (_cachedIntensity.HasValue)
			{
				return _cachedIntensity.Value;
			}
			Item[] array = Resources.FindObjectsOfTypeAll<Item>();
			foreach (Item val in array)
			{
				if (!((Object)(object)val == (Object)null) && val.itemID == 64)
				{
					Antigrav component = ((Component)val).GetComponent<Antigrav>();
					if ((Object)(object)component != (Object)null)
					{
						_cachedIntensity = component.intensity;
						return _cachedIntensity.Value;
					}
				}
			}
			_cachedIntensity = 1f;
			return _cachedIntensity.Value;
		}
	}
	internal static class HandCannonVisualSync
	{
		private const string PitonVisualObjectName = "HandCannonPitonVisual";

		private const string SwappedRopeVisualObjectName = "HandCannonSwappedRopeVisual";

		private const string SwappedHarpoonVisualObjectName = "HandCannonSwappedHarpoonVisual";

		private const string AmmoAttachObjectName = "HandCannonAmmoAttach";

		private const string PitonClimbingSpikeChildName = "ClimbingSpike";

		private static readonly Vector3 PitonClimbingSpikeLocalPosition = new Vector3(-0.0304f, -27.3382f, -4.3815f);

		private static readonly Vector3 PitonClimbingSpikeLocalEuler = new Vector3(270f, 180f, 0f);

		private static readonly Vector3 PitonClimbingSpikeLocalScale = Vector3.one;

		private static readonly Dictionary<int, bool> SwappedRopeIsAntiVisual = new Dictionary<int, bool>();

		private static readonly Dictionary<int, bool> ChainAttachIsAntiVisual = new Dictionary<int, bool>();

		private static int GetVisualId(RopeShooter shooter, Transform hideOnFire)
		{
			if ((Object)(object)shooter != (Object)null)
			{
				return ((Object)shooter).GetInstanceID();
			}
			if (!((Object)(object)hideOnFire != (Object)null))
			{
				return 0;
			}
			return ((Object)hideOnFire).GetInstanceID();
		}

		private static GameObject GetHideOnFire(RopeShooter shooter)
		{
			if ((Object)(object)shooter == (Object)null)
			{
				return null;
			}
			Transform val = FindNamedChild(((Component)shooter).transform, "HideOnFire") ?? FindNamedChild(((Component)shooter).transform, "hideOnFire");
			if ((Object)(object)val != (Object)null)
			{
				return ((Component)val).gameObject;
			}
			Transform val2 = FindNamedChild(((Component)shooter).transform, "Harpoon");
			if (!((Object)(object)val2 != (Object)null) || !((Object)(object)val2.parent != (Object)null))
			{
				return null;
			}
			return ((Component)val2.parent).gameObject;
		}

		private static Transform FindNamedChild(Transform root, string childName)
		{
			if ((Object)(object)root == (Object)null)
			{
				return null;
			}
			Transform val = root.Find(childName);
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
			for (int i = 0; i < root.childCount; i++)
			{
				Transform val2 = FindNamedChild(root.GetChild(i), childName);
				if ((Object)(object)val2 != (Object)null)
				{
					return val2;
				}
			}
			return null;
		}

		public static void Sync(Item item, RopeShooter shooter)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			if (!HandCannonHelper.IsHandCannon(item))
			{
				return;
			}
			HandCannonAmmoType handCannonAmmoType = HandCannonAmmoHelper.GetLoadedAmmo(item);
			bool flag = item.HasData(HandCannonDataEntryKey.LoadedAmmoType) && handCannonAmmoType == HandCannonAmmoType.Empty;
			bool flag2 = HandCannonHelper.HasCharge(item);
			if (handCannonAmmoType == HandCannonAmmoType.Empty && flag2 && !flag)
			{
				if (HandCannonHelper.IsChainLauncher(item))
				{
					handCannonAmmoType = HandCannonAmmoType.Chain;
				}
				else if (item.itemID == 64)
				{
					handCannonAmmoType = HandCannonAmmoType.AntiRope;
				}
			}
			VineShooter component = ((Component)item).GetComponent<VineShooter>();
			if ((Object)(object)component != (Object)null)
			{
				SyncChainLauncherVisuals(component, handCannonAmmoType, flag2, flag);
				return;
			}
			GameObject hideOnFire = GetHideOnFire(shooter);
			if ((Object)(object)hideOnFire == (Object)null)
			{
				return;
			}
			Transform transform = hideOnFire.transform;
			int visualId = GetVisualId(shooter, transform);
			if (!flag2 || handCannonAmmoType == HandCannonAmmoType.Empty)
			{
				ClearRuntimeVisuals(visualId, transform);
				if (!flag2 || flag)
				{
					SetActiveIfPresent(FindDirectChild(transform, "Harpoon"), active: false);
					SetActiveIfPresent(FindDirectChild(transform, "Rope"), active: false);
					SetActiveIfPresent(FindDirectChild(transform, "Piton"), active: false);
				}
				return;
			}
			if (!hideOnFire.activeSelf)
			{
				ClearRuntimeVisuals(visualId, transform);
				return;
			}
			Transform val = FindDirectChild(transform, "Harpoon");
			Transform val2 = FindDirectChild(transform, "Rope");
			Transform val3 = EnsurePitonVisual(transform, val);
			bool flag3 = handCannonAmmoType == HandCannonAmmoType.Piton;
			bool flag4 = handCannonAmmoType == HandCannonAmmoType.NormalRope || handCannonAmmoType == HandCannonAmmoType.AntiRope;
			SetActiveIfPresent(val, flag4);
			SetActiveIfPresent(val3, flag3);
			if (flag4)
			{
				ApplyRopeVisual(item, shooter, transform, val2, handCannonAmmoType, visualId);
			}
			else
			{
				HideSwappedRopeVisual(visualId, transform);
				SetActiveIfPresent(val2, active: false);
			}
			HideUnmanagedLoadedChildren(transform, val, val2, val3, flag3, flag4);
		}

		private static void ApplyRopeVisual(Item item, RopeShooter shooter, Transform hideOnFire, Transform nativeRope, HandCannonAmmoType ammo, int visualId)
		{
			bool flag = item.itemID == 64;
			bool flag2 = ammo == HandCannonAmmoType.AntiRope;
			if ((!flag || flag2) && !(!flag && flag2))
			{
				HideSwappedRopeVisual(visualId, hideOnFire);
				SetActiveIfPresent(nativeRope, active: true);
				return;
			}
			Transform referenceRopeTransform = GetReferenceRopeTransform(flag2);
			if ((Object)(object)referenceRopeTransform == (Object)null)
			{
				HideSwappedRopeVisual(visualId, hideOnFire);
				SetActiveIfPresent(nativeRope, active: true);
			}
			else
			{
				Transform transform = EnsureSwappedRopeVisual(visualId, hideOnFire, nativeRope, referenceRopeTransform, flag2);
				SetActiveIfPresent(nativeRope, active: false);
				SetActiveIfPresent(transform, active: true);
			}
		}

		private static void SyncChainLauncherVisuals(VineShooter vine, HandCannonAmmoType ammo, bool hasCharge, bool explicitlyEmpty)
		{
			if ((Object)(object)vine == (Object)null)
			{
				return;
			}
			Item item = HandCannonHelper.GetItem(vine);
			GameObject val = vine.disableOnFire;
			if ((Object)(object)val == (Object)null)
			{
				Transform val2 = FindNamedChild(((Component)vine).transform, "disableOnFire") ?? FindNamedChild(((Component)vine).transform, "DisableOnFire");
				val = (((Object)(object)val2 != (Object)null) ? ((Component)val2).gameObject : null);
			}
			DestroyLegacyChainRootOverlays(((Component)vine).transform);
			if ((Object)(object)val != (Object)null)
			{
				DestroyLegacyChainRootOverlays(val.transform);
			}
			bool flag = ammo == HandCannonAmmoType.Chain;
			bool flag2 = hasCharge && ammo == HandCannonAmmoType.Piton;
			bool flag3 = hasCharge && (ammo == HandCannonAmmoType.NormalRope || ammo == HandCannonAmmoType.AntiRope);
			if (!hasCharge || ammo == HandCannonAmmoType.Empty || flag)
			{
				if ((Object)(object)val != (Object)null)
				{
					val.SetActive(flag);
				}
				SetAmmoAttachActive(vine, active: false);
				return;
			}
			if ((Object)(object)val != (Object)null)
			{
				val.SetActive(false);
			}
			bool wantsAntiVisual = ammo == HandCannonAmmoType.AntiRope;
			Transform val3 = EnsureGoldAmmoAttach(vine, item, wantsAntiVisual);
			if ((Object)(object)val3 == (Object)null)
			{
				HandCannonPlugin.Log.LogWarning((object)"[Visual] chain launcher could not clone rope-cannon HideOnFire; ammo overlay skipped.");
				if ((Object)(object)val != (Object)null)
				{
					val.SetActive(true);
				}
				return;
			}
			((Component)val3).gameObject.SetActive(true);
			int instanceID = ((Object)val3).GetInstanceID();
			Transform val4 = FindDirectChild(val3, "Harpoon") ?? FindNamedChild(val3, "Harpoon");
			Transform val5 = FindDirectChild(val3, "Rope") ?? FindNamedChild(val3, "Rope");
			Transform val6 = EnsurePitonVisual(val3, val4);
			SetActiveIfPresent(val4, flag3);
			SetActiveIfPresent(val6, flag2);
			if (flag3)
			{
				ApplyRopeVisual(item, null, val3, val5, ammo, instanceID);
			}
			else
			{
				HideSwappedRopeVisual(instanceID, val3);
				SetActiveIfPresent(val5, active: false);
			}
			HideUnmanagedLoadedChildren(val3, val4, val5, val6, flag2, flag3);
		}

		private static void DestroyLegacyChainRootOverlays(Transform root)
		{
			if (!((Object)(object)root == (Object)null))
			{
				DestroyNamedChild(root, "HandCannonPitonVisual");
				DestroyNamedChild(root, "HandCannonSwappedRopeVisual");
				DestroyNamedChild(root, "HandCannonSwappedHarpoonVisual");
			}
		}

		private static void DestroyNamedChild(Transform parent, string childName)
		{
			if (!((Object)(object)parent == (Object)null))
			{
				Transform val = parent.Find(childName);
				if ((Object)(object)val != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)val).gameObject);
				}
			}
		}

		private static void SetAmmoAttachActive(VineShooter vine, bool active)
		{
			if (!((Object)(object)vine == (Object)null))
			{
				SetActiveIfPresent(((Component)vine).transform.Find("HandCannonAmmoAttach"), active);
			}
		}

		private static Transform EnsureGoldAmmoAttach(VineShooter vine, Item item, bool wantsAntiVisual)
		{
			if ((Object)(object)vine == (Object)null || (Object)(object)item == (Object)null)
			{
				return null;
			}
			int instanceID = ((Object)vine).GetInstanceID();
			Transform val = item.transform.Find("HandCannonAmmoAttach");
			if ((Object)(object)val != (Object)null && ChainAttachIsAntiVisual.TryGetValue(instanceID, out var value) && value == wantsAntiVisual)
			{
				return val;
			}
			if ((Object)(object)val != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)val).gameObject);
				ChainAttachIsAntiVisual.Remove(instanceID);
			}
			RopeShooter shooter;
			if (wantsAntiVisual)
			{
				AntiRopeCannonReference.TryGetShooter(out shooter);
			}
			else
			{
				NormalRopeCannonReference.TryGetShooter(out shooter);
			}
			if ((Object)(object)shooter == (Object)null && wantsAntiVisual)
			{
				NormalRopeCannonReference.TryGetShooter(out shooter);
			}
			GameObject hideOnFire = GetHideOnFire(shooter);
			Item item2 = HandCannonHelper.GetItem(shooter);
			if ((Object)(object)hideOnFire == (Object)null || (Object)(object)item2 == (Object)null)
			{
				return null;
			}
			GameObject obj = Object.Instantiate<GameObject>(hideOnFire, item.transform);
			((Object)obj).name = "HandCannonAmmoAttach";
			ApplyItemRelativeTransform(obj.transform, hideOnFire.transform, item2.transform);
			StripGameplayComponents(obj);
			DestroyLegacyChainRootOverlays(obj.transform);
			ChainAttachIsAntiVisual[instanceID] = wantsAntiVisual;
			return obj.transform;
		}

		private static void ApplyItemRelativeTransform(Transform target, Transform source, Transform sourceItem)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: 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_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: 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_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: 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_007b: 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_008c: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)target == (Object)null) && !((Object)(object)source == (Object)null) && !((Object)(object)sourceItem == (Object)null))
			{
				target.localPosition = sourceItem.InverseTransformPoint(source.position);
				target.localRotation = Quaternion.Inverse(sourceItem.rotation) * source.rotation;
				Vector3 lossyScale = source.lossyScale;
				Vector3 lossyScale2 = sourceItem.lossyScale;
				target.localScale = new Vector3(SafeDivide(lossyScale.x, lossyScale2.x), SafeDivide(lossyScale.y, lossyScale2.y), SafeDivide(lossyScale.z, lossyScale2.z));
			}
		}

		private static float SafeDivide(float numerator, float denominator)
		{
			if (Mathf.Abs(denominator) < 0.0001f)
			{
				return numerator;
			}
			return numerator / denominator;
		}

		private static Transform GetReferenceRopeTransform(bool wantsAntiVisual)
		{
			RopeShooter shooter;
			if (wantsAntiVisual)
			{
				AntiRopeCannonReference.TryGetShooter(out shooter);
			}
			else
			{
				NormalRopeCannonReference.TryGetShooter(out shooter);
			}
			GameObject hideOnFire = GetHideOnFire(shooter);
			if ((Object)(object)hideOnFire == (Object)null)
			{
				return null;
			}
			return FindDirectChild(hideOnFire.transform, "Rope");
		}

		private static Transform EnsureSwappedRopeVisual(int visualId, Transform hideOnFire, Transform nativeRope, Transform referenceRope, bool wantsAntiVisual)
		{
			Transform val = hideOnFire.Find("HandCannonSwappedRopeVisual");
			if ((Object)(object)val != (Object)null && SwappedRopeIsAntiVisual.TryGetValue(visualId, out var value) && value == wantsAntiVisual)
			{
				return val;
			}
			if ((Object)(object)val != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)val).gameObject);
				SwappedRopeIsAntiVisual.Remove(visualId);
			}
			GameObject obj = Object.Instantiate<GameObject>(((Component)referenceRope).gameObject, hideOnFire);
			((Object)obj).name = "HandCannonSwappedRopeVisual";
			ApplyAnchorTransform(obj.transform, nativeRope ?? referenceRope);
			StripGameplayComponents(obj);
			SwappedRopeIsAntiVisual[visualId] = wantsAntiVisual;
			return obj.transform;
		}

		private static Transform EnsurePitonVisual(Transform hideOnFire, Transform harpoonAnchor)
		{
			Transform val = FindDirectChild(hideOnFire, "Piton");
			val = val ?? hideOnFire.Find("HandCannonPitonVisual");
			if ((Object)(object)val != (Object)null)
			{
				ApplyPitonClimbingSpikeTransform(val);
				return val;
			}
			GameObject val2 = Resources.Load<GameObject>("0_Items/Piton");
			if ((Object)(object)val2 == (Object)null && PitonPrefabCache.EnsureInitialized(HandCannonPlugin.Log))
			{
				val2 = Resources.Load<GameObject>(PitonPrefabCache.PrefabPath);
			}
			if ((Object)(object)val2 == (Object)null)
			{
				return null;
			}
			GameObject obj = Object.Instantiate<GameObject>(val2, hideOnFire);
			((Object)obj).name = "HandCannonPitonVisual";
			ApplyAnchorTransform(obj.transform, harpoonAnchor);
			StripGameplayComponents(obj);
			ApplyPitonClimbingSpikeTransform(obj.transform);
			return obj.transform;
		}

		private static void ApplyPitonClimbingSpikeTransform(Transform pitonVisualRoot)
		{
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//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_0076: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)pitonVisualRoot == (Object)null)
			{
				return;
			}
			Transform val = pitonVisualRoot.Find("ClimbingSpike");
			if ((Object)(object)val == (Object)null)
			{
				for (int i = 0; i < pitonVisualRoot.childCount; i++)
				{
					Transform child = pitonVisualRoot.GetChild(i);
					if (((Object)child).name.IndexOf("ClimbingSpike", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						val = child;
						break;
					}
				}
			}
			if (!((Object)(object)val == (Object)null))
			{
				val.localPosition = PitonClimbingSpikeLocalPosition;
				val.localRotation = Quaternion.Euler(PitonClimbingSpikeLocalEuler);
				val.localScale = PitonClimbingSpikeLocalScale;
			}
		}

		private static void ApplyAnchorTransform(Transform visual, Transform anchor)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: 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)
			//IL_0014: 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_002a: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)visual == (Object)null))
			{
				if ((Object)(object)anchor == (Object)null)
				{
					visual.localPosition = Vector3.zero;
					visual.localRotation = Quaternion.identity;
					visual.localScale = Vector3.one;
				}
				else
				{
					visual.localPosition = anchor.localPosition;
					visual.localRotation = anchor.localRotation;
					visual.localScale = anchor.localScale;
				}
			}
		}

		private static void HideSwappedRopeVisual(int visualId, Transform hideOnFire)
		{
			SetActiveIfPresent(hideOnFire.Find("HandCannonSwappedRopeVisual"), active: false);
			SwappedRopeIsAntiVisual.Remove(visualId);
		}

		private static void ClearRuntimeVisuals(int visualId, Transform hideOnFire)
		{
			HideSwappedRopeVisual(visualId, hideOnFire);
			SetActiveIfPresent(hideOnFire.Find("HandCannonPitonVisual"), active: false);
			SetActiveIfPresent(FindDirectChild(hideOnFire, "Piton"), active: false);
		}

		private static void HideUnmanagedLoadedChildren(Transform hideOnFire, Transform harpoon, Transform nativeRope, Transform pitonVisual, bool showPiton, bool showRopeShot)
		{
			for (int i = 0; i < hideOnFire.childCount; i++)
			{
				Transform child = hideOnFire.GetChild(i);
				if (!((Object)(object)child == (Object)(object)harpoon) && !((Object)(object)child == (Object)(object)nativeRope) && !((Object)(object)child == (Object)(object)pitonVisual) && !(((Object)child).name == "HandCannonSwappedRopeVisual") && !(((Object)child).name == "SpawnPoint"))
				{
					bool active = (showPiton && ((Object)child).name == "Piton") || (showRopeShot && (((Object)child).name == "Harpoon" || ((Object)child).name == "Rope"));
					((Component)child).gameObject.SetActive(active);
				}
			}
		}

		private static Transform FindDirectChild(Transform parent, string childName)
		{
			if ((Object)(object)parent == (Object)null)
			{
				return null;
			}
			return parent.Find(childName);
		}

		private static void SetActiveIfPresent(Transform transform, bool active)
		{
			if (transform != null)
			{
				((Component)transform).gameObject.SetActive(active);
			}
		}

		private static void StripGameplayComponents(GameObject root)
		{
			if ((Object)(object)root == (Object)null)
			{
				return;
			}
			Component[] componentsInChildren = root.GetComponentsInChildren<Component>(true);
			foreach (Component val in componentsInChildren)
			{
				if (!((Object)(object)val == (Object)null) && !(val is Transform) && !(val is MeshFilter) && !(val is MeshRenderer) && !(val is SkinnedMeshRenderer) && !(val is ParticleSystem) && !(val is ParticleSystemRenderer) && !(val is LineRenderer) && !(val is TrailRenderer))
				{
					Object.Destroy((Object)(object)val);
				}
			}
		}
	}
	internal static class AntiRopeCannonReference
	{
		private static RopeShooter _cachedShooter;

		private static bool _loggedFailure;

		public static bool IsAvailable()
		{
			RopeShooter shooter;
			return TryGetShooter(out shooter);
		}

		public static bool TryGetShooter(out RopeShooter shooter)
		{
			if ((Object)(object)_cachedShooter != (Object)null)
			{
				shooter = _cachedShooter;
				return true;
			}
			if (TryResolveFromLoadedItems(out shooter) || TryResolveFromResources(out shooter) || TryResolveFromLoadedAssets(out shooter))
			{
				_cachedShooter = shooter;
				return true;
			}
			if (!_loggedFailure)
			{
				_loggedFailure = true;
				ManualLogSource log = HandCannonPlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)"Could not resolve anti-rope cannon reference. Anti-grav spool reloads disabled until available.");
				}
			}
			shooter = null;
			return false;
		}

		private static bool TryResolveFromLoadedItems(out RopeShooter shooter)
		{
			shooter = null;
			Item[] array = Resources.FindObjectsOfTypeAll<Item>();
			foreach (Item val in array)
			{
				if (!((Object)(object)val == (Object)null) && val.itemID == 64)
				{
					RopeShooter component = ((Component)val).GetComponent<RopeShooter>();
					if ((Object)(object)component?.ropeAnchorWithRopePref != (Object)null)
					{
						shooter = component;
						return true;
					}
				}
			}
			return false;
		}

		private static bool TryResolveFromResources(out RopeShooter shooter)
		{
			shooter = null;
			string[] array = new string[3] { "0_Items/Anti-Rope Cannon", "0_Items/RopeShooterAnti", "0_Items/AntiRope Cannon" };
			for (int i = 0; i < array.Length; i++)
			{
				GameObject val = Resources.Load<GameObject>(array[i]);
				shooter = ((val != null) ? val.GetComponent<RopeShooter>() : null);
				if ((Object)(object)shooter != (Object)null)
				{
					return true;
				}
			}
			GameObject[] array2 = Resources.LoadAll<GameObject>("0_Items/");
			if (array2 == null)
			{
				return false;
			}
			GameObject[] array3 = array2;
			foreach (GameObject val2 in array3)
			{
				if ((Object)(object)val2 == (Object)null)
				{
					continue;
				}
				Item component = val2.GetComponent<Item>();
				if (!((Object)(object)component == (Object)null) && component.itemID == 64)
				{
					shooter = val2.GetComponent<RopeShooter>();
					if ((Object)(object)shooter != (Object)null)
					{
						return true;
					}
				}
			}
			return false;
		}

		private static bool TryResolveFromLoadedAssets(out RopeShooter shooter)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			shooter = null;
			RopeShooter[] array = Resources.FindObjectsOfTypeAll<RopeShooter>();
			foreach (RopeShooter val in array)
			{
				if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).gameObject == (Object)null))
				{
					Scene scene = ((Component)val).gameObject.scene;
					if (!((Scene)(ref scene)).IsValid() && ((Component)val).GetComponent<Item>()?.itemID == 64)
					{
						shooter = val;
						return true;
					}
				}
			}
			return false;
		}

		public static float GetShotSegments()
		{
			if (TryGetShooter(out var shooter) && shooter.length > 0f)
			{
				return shooter.length;
			}
			return 30f;
		}

		public static GameObject GetAnchorPrefab()
		{
			if (!TryGetShooter(out var shooter))
			{
				return null;
			}
			return shooter.ropeAnchorWithRopePref;
		}

		public static Item GetItem()
		{
			if (!TryGetShooter(out var shooter))
			{
				return null;
			}
			return ((Component)shooter).GetComponent<Item>();
		}
	}
	internal static class NormalRopeCannonReference
	{
		private static RopeShooter _cachedShooter;

		private static bool _loggedFailure;

		public static bool TryGetShooter(out RopeShooter shooter)
		{
			if ((Object)(object)_cachedShooter != (Object)null)
			{
				shooter = _cachedShooter;
				return true;
			}
			if (TryResolveFromLoadedItems(out shooter) || TryResolveFromResources(out shooter) || TryResolveFromLoadedAssets(out shooter))
			{
				_cachedShooter = shooter;
				return true;
			}
			if (!_loggedFailure)
			{
				_loggedFailure = true;
				ManualLogSource log = HandCannonPlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)"Could not resolve normal rope cannon reference. Anti-grav Hand Cannon normal reloads may fire anti rope.");
				}
			}
			shooter = null;
			return false;
		}

		private static bool TryResolveFromLoadedItems(out RopeShooter shooter)
		{
			shooter = null;
			Item[] array = Resources.FindObjectsOfTypeAll<Item>();
			foreach (Item val in array)
			{
				if (!((Object)(object)val == (Object)null) && val.itemID == 63)
				{
					RopeShooter component = ((Component)val).GetComponent<RopeShooter>();
					if ((Object)(object)component?.ropeAnchorWithRopePref != (Object)null)
					{
						shooter = component;
						return true;
					}
				}
			}
			return false;
		}

		private static bool TryResolveFromResources(out RopeShooter shooter)
		{
			shooter = null;
			string[] array = new string[3] { "0_Items/Rope Cannon", "0_Items/RopeShooter", "0_Items/Rope CannonItem" };
			for (int i = 0; i < array.Length; i++)
			{
				GameObject val = Resources.Load<GameObject>(array[i]);
				shooter = ((val != null) ? val.GetComponent<RopeShooter>() : null);
				if ((Object)(object)shooter?.ropeAnchorWithRopePref != (Object)null)
				{
					return true;
				}
			}
			GameObject[] array2 = Resources.LoadAll<GameObject>("0_Items/");
			if (array2 == null)
			{
				return false;
			}
			GameObject[] array3 = array2;
			foreach (GameObject val2 in array3)
			{
				if ((Object)(object)val2 == (Object)null)
				{
					continue;
				}
				Item component = val2.GetComponent<Item>();
				if (!((Object)(object)component == (Object)null) && component.itemID == 63)
				{
					shooter = val2.GetComponent<RopeShooter>();
					if ((Object)(object)shooter?.ropeAnchorWithRopePref != (Object)null)
					{
						return true;
					}
				}
			}
			return false;
		}

		private static bool TryResolveFromLoadedAssets(out RopeShooter shooter)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			shooter = null;
			RopeShooter[] array = Resources.FindObjectsOfTypeAll<RopeShooter>();
			foreach (RopeShooter val in array)
			{
				if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).gameObject == (Object)null))
				{
					Scene scene = ((Component)val).gameObject.scene;
					if (!((Scene)(ref scene)).IsValid() && ((Component)val).GetComponent<Item>()?.itemID == 63)
					{
						shooter = val;
						return true;
					}
				}
			}
			return false;
		}

		public static GameObject GetAnchorPrefab()
		{
			if (!TryGetShooter(out var shooter))
			{
				return null;
			}
			return shooter.ropeAnchorWithRopePref;
		}

		public static Item GetItem()
		{
			if (!TryGetShooter(out var shooter))
			{
				return null;
			}
			return ((Component)shooter).GetComponent<Item>();
		}
	}
	internal static class RopeSpoolLengthHelper
	{
		public const float MetersPerSegment = 0.25f;

		public const float DefaultCannonShotSegments = 30f;

		public const float OutOfRopeSegmentThreshold = 2f;

		public const float DefaultSpoolStartFuel = 60f;

		private const float SegmentComparisonEpsilon = 0.01f;

		internal const float SegmentEpsilon = 0.01f;

		public static float SegmentsToMeters(float segments)
		{
			return segments * 0.25f;
		}

		public static float GetCannonShotSegments(RopeShooter shooter)
		{
			if ((Object)(object)shooter == (Object)null || shooter.length <= 0f)
			{
				return 30f;
			}
			return shooter.length;
		}

		public static float GetSpoolStartFuel(ItemSlot slot)
		{
			if ((Object)(object)((slot != null) ? slot.prefab : null) == (Object)null)
			{
				return 60f;
			}
			RopeSpool component = ((Component)slot.prefab).GetComponent<RopeSpool>();
			if (!((Object)(object)component != (Object)null))
			{
				return 60f;
			}
			return component.ropeStartFuel;
		}

		public static float GetRemainingSegments(ItemSlot slot)
		{
			if (slot == null || slot.IsEmpty())
			{
				return 0f;
			}
			float spoolStartFuel = GetSpoolStartFuel(slot);
			ItemInstanceData data = slot.data;
			if (data == null)
			{
				return spoolStartFuel;
			}
			FloatItemData val = default(FloatItemData);
			if (data.TryGetDataEntry<FloatItemData>((DataEntryKey)10, ref val))
			{
				return Mathf.Max(0f, val.Value);
			}
			FloatItemData val2 = default(FloatItemData);
			if (data.TryGetDataEntry<FloatItemData>((DataEntryKey)11, ref val2))
			{
				return Mathf.Max(0f, val2.Value * spoolStartFuel);
			}
			return spoolStartFuel;
		}

		public static bool HasReloadableLength(float segments)
		{
			return segments > 2.01f;
		}

		public static bool CanFullyReload(float segments, float requiredSegments)
		{
			return segments + 0.01f >= requiredSegments;
		}
	}
	internal readonly struct RopeReloadPlan
	{
		public ItemSlot PartialSlot { get; }

		public float SegmentsToDeduct { get; }

		public IReadOnlyList<ItemSlot> SlotsToConsumeEntirely { get; }

		public bool IsAntiRopeAmmo { get; }

		public bool UsesPartialSlot => PartialSlot != null;

		public bool IsValid
		{
			get
			{
				if (!UsesPartialSlot)
				{
					return SlotsToConsumeEntirely.Count > 0;
				}
				return true;
			}
		}

		public RopeReloadPlan(ItemSlot partialSlot, float segmentsToDeduct, IReadOnlyList<ItemSlot> slotsToConsumeEntirely, bool isAntiRopeAmmo)
		{
			PartialSlot = partialSlot;
			SegmentsToDeduct = segmentsToDeduct;
			SlotsToConsumeEntirely = slotsToConsumeEntirely ?? Array.Empty<ItemSlot>();
			IsAntiRopeAmmo = isAntiRopeAmmo;
		}
	}
	internal sealed class HandCannonReloadPlan
	{
		public HandCannonAmmoType AmmoType { get; private set; }

		public ItemSlot PitonSlot { get; private set; }

		public RopeReloadPlan RopePlan { get; private set; }

		public string DisplayLabel { get; private set; }

		public bool IsPiton => AmmoType == HandCannonAmmoType.Piton;

		public bool IsValid
		{
			get
			{
				if (!IsPiton)
				{
					return RopePlan.IsValid;
				}
				return PitonSlot != null;
			}
		}

		public static HandCannonReloadPlan ForPiton(ItemSlot slot)
		{
			return new HandCannonReloadPlan
			{
				AmmoType = HandCannonAmmoType.Piton,
				PitonSlot = slot,
				DisplayLabel = $"Piton — slot {slot.itemSlotID}"
			};
		}

		public static HandCannonReloadPlan ForRope(RopeReloadPlan ropePlan, HandCannonAmmoType ammoType, string displayLabel)
		{
			return new HandCannonReloadPlan
			{
				AmmoType = ammoType,
				RopePlan = ropePlan,
				DisplayLabel = displayLabel
			};
		}
	}
	internal static class HandCannonInventoryHelper
	{
		private const byte TempInventorySlotId = 250;

		public static ItemSlot FindSlotForItem(Item item)
		{
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			Character val = ((item != null) ? item.holderCharacter : null);
			if ((Object)(object)((val != null) ? val.player : null) == (Object)null)
			{
				return null;
			}
			if (item.data != null && item.data.guid != Guid.Empty)
			{
				ItemSlot val2 = FindSlotByGuid(val, item.data.guid);
				if (val2 != null)
				{
					return val2;
				}
			}
			CharacterData data = val.data;
			if ((Object)(object)((data != null) ? data.currentItem : null) == (Object)(object)item)
			{
				Optionable<byte> valueOrDefault = (val.refs?.items?.currentSelectedSlot).GetValueOrDefault();
				if (valueOrDefault.IsSome)
				{
					ItemSlot itemSlot = val.player.GetItemSlot(valueOrDefault.Value);
					if (itemSlot != null && !itemSlot.IsEmpty() && (Object)(object)itemSlot.prefab != (Object)null && itemSlot.prefab.itemID == item.itemID)
					{
						return itemSlot;
					}
				}
			}
			return null;
		}

		private static ItemSlot FindSlotByGuid(Character holder, Guid guid)
		{
			if ((Object)(object)((holder != null) ? holder.player : null) == (Object)null || guid == Guid.Empty)
			{
				return null;
			}
			ItemSlot val = FindSlotByGuid(holder.player.itemSlots, guid);
			if (val != null)
			{
				return val;
			}
			if (SlotHasGuid(holder.player.tempFullSlot, guid))
			{
				return holder.player.tempFullSlot;
			}
			if (SlotHasGuid((ItemSlot)(object)holder.player.backpackSlot, guid))
			{
				return (ItemSlot)(object)holder.player.backpackSlot;
			}
			BackpackData val2 = default(BackpackData);
			if (holder.player.backpackSlot != null && !((ItemSlot)holder.player.backpackSlot).IsEmpty() && ((ItemSlot)holder.player.backpackSlot).data != null && ((ItemSlot)holder.player.backpackSlot).data.TryGetDataEntry<BackpackData>((DataEntryKey)7, ref val2))
			{
				return FindSlotByGuid(val2.itemSlots, guid);
			}
			return null;
		}

		private static ItemSlot FindSlotByGuid(ItemSlot[] slots, Guid guid)
		{
			if (slots == null)
			{
				return null;
			}
			foreach (ItemSlot val in slots)
			{
				if (SlotHasGuid(val, guid))
				{
					return val;
				}
			}
			return null;
		}

		private static bool SlotHasGuid(ItemSlot slot, Guid guid)
		{
			if (slot != null && !slot.IsEmpty() && slot.data != null)
			{
				return slot.data.guid == guid;
			}
			return false;
		}

		public static bool IsHandheldPitonPrefab(Item prefab, ItemInstanceData slotData = null)
		{
			if ((Object)(object)prefab == (Object)null || (Object)(object)((Component)prefab).GetComponent<RopeShooter>() != (Object)null)
			{
				return false;
			}
			if (prefab.itemID == 18 || (Object)(object)((Component)prefab).GetComponent<ClimbingSpikeComponent>() != (Object)null)
			{
				IntItemData val = default(IntItemData);
				if (slotData != null && slotData.TryGetDataEntry<IntItemData>((DataEntryKey)1, ref val) && val.Value > 0)
				{
					return false;
				}
				return true;
			}
			string text = ((Object)((Component)prefab).gameObject).name ?? string.Empty;
			if (text.IndexOf("ClimbingSpike", StringComparison.OrdinalIgnoreCase) < 0)
			{
				return text.IndexOf("Piton", StringComparison.OrdinalIgnoreCase) >= 0;
			}
			return true;
		}

		public static bool IsRopeSpoolPrefab(Item prefab)
		{
			if ((Object)(object)prefab == (Object)null || (Object)(object)((Component)prefab).GetComponent<RopeShooter>() != (Object)null)
			{
				return false;
			}
			if ((Object)(object)((Component)prefab).GetComponent<RopeSpool>() != (Object)null)
			{
				return true;
			}
			if (prefab.itemID == 65 || prefab.itemID == 1)
			{
				return true;
			}
			return (((Object)((Component)prefab).gameObject).name ?? string.Empty).IndexOf("Rope Spool", StringComparison.OrdinalIgnoreCase) >= 0;
		}

		public static bool IsAntiRopeSpoolPrefab(Item prefab)
		{
			if ((Object)(object)prefab == (Object)null)
			{
				return false;
			}
			RopeSpool component = ((Component)prefab).GetComponent<RopeSpool>();
			if (component != null && component.isAntiRope)
			{
				return true;
			}
			if (prefab.itemID == 1)
			{
				return true;
			}
			return (((Object)((Component)prefab).gameObject).name ?? string.Empty).IndexOf("Anti-Rope Spool", StringComparison.OrdinalIgnoreCase) >= 0;
		}

		public static void RefreshPitonTracking(Character character)
		{
			if ((Object)(object)character?.refs?.items != (Object)null && character.player?.itemSlots != null)
			{
				character.refs.items.UpdateClimbingSpikeCount(character.player.itemSlots);
			}
		}

		public static List<ItemSlot> GetPitonSlotsInOrder(Character character)
		{
			List<ItemSlot> list = new List<ItemSlot>();
			if ((Object)(object)character == (Object)null)
			{
				return list;
			}
			RefreshPitonTracking(character);
			AppendPitonSlots(character.player?.itemSlots, list);
			if (character.player?.tempFullSlot != null && !character.player.tempFullSlot.IsEmpty() && IsHandheldPitonPrefab(character.player.tempFullSlot.prefab, character.player.tempFullSlot.data))
			{
				list.Add(character.player.tempFullSlot);
			}
			if (HandCannonPlugin.AllowBackpackReloadConfig.Value)
			{
				AppendBackpackPitonSlots(character, list);
			}
			return list;
		}

		private static void AppendPitonSlots(ItemSlot[] slots, List<ItemSlot> pitonSlots)
		{
			if (slots == null)
			{
				return;
			}
			foreach (ItemSlot val in slots)
			{
				if (val != null && !val.IsEmpty() && IsHandheldPitonPrefab(val.prefab, val.data))
				{
					pitonSlots.Add(val);
				}
			}
		}

		private static void AppendBackpackPitonSlots(Character character, List<ItemSlot> pitonSlots)
		{
			if (!((Object)(object)((character != null) ? character.player : null) == (Object)null) && character.player.backpackSlot != null && !((ItemSlot)character.player.backpackSlot).IsEmpty())
			{
				ItemInstanceData data = ((ItemSlot)character.player.backpackSlot).data;
				BackpackData val = default(BackpackData);
				if (data != null && data.TryGetDataEntry<BackpackData>((DataEntryKey)7, ref val))
				{
					AppendPitonSlots(val.itemSlots, pitonSlots);
				}
			}
		}

		public static List<ItemSlot> GetRopeSpoolSlotsInOrder(Character character)
		{
			List<ItemSlot> list = new List<ItemSlot>();
			if ((Object)(object)character == (Object)null)
			{
				return list;
			}
			AppendRopeSlots(character.player?.itemSlots, list);
			if (character.player?.tempFullSlot != null && !character.player.tempFullSlot.IsEmpty() && IsRopeSpoolPrefab(character.player.tempFullSlot.prefab))
			{
				list.Add(character.player.tempFullSlot);
			}
			if (HandCannonPlugin.AllowBackpackReloadConfig.Value)
			{
				AppendBackpackRopeSlots(character, list);
			}
			return list;
		}

		private static void AppendRopeSlots(ItemSlot[] slots, List<ItemSlot> ropeSlots)
		{
			if (slots == null)
			{
				return;
			}
			foreach (ItemSlot val in slots)
			{
				if (val != null && !val.IsEmpty() && IsRopeSpoolPrefab(val.prefab))
				{
					ropeSlots.Add(val);
				}
			}
		}

		private static void AppendBackpackRopeSlots(Character character, List<ItemSlot> ropeSlots)
		{
			if (!((Object)(object)((character != null) ? character.player : null) == (Object)null) && character.player.backpackSlot != null && !((ItemSlot)character.player.backpackSlot).IsEmpty())
			{
				ItemInstanceData data = ((ItemSlot)character.player.backpackSlot).data;
				BackpackData val = default(BackpackData);
				if (data != null && data.TryGetDataEntry<BackpackData>((DataEntryKey)7, ref val))
				{
					AppendRopeSlots(val.itemSlots, ropeSlots);
				}
			}
		}

		public static bool TryBuildBestRopePlan(List<ItemSlot> ropeSlots, float requiredSegments, bool isAntiRopeAmmo, out RopeReloadPlan plan)
		{
			if (HasPartialSpoolsForAmmoType(ropeSlots, requiredSegments, isAntiRopeAmmo) && TryBuildCombinedReloadPlan(ropeSlots, requiredSegments, isAntiRopeAmmo, out plan))
			{
				return true;
			}
			if (TryBuildSingleSpoolPlan(ropeSlots, requiredSegments, isAntiRopeAmmo, out plan))
			{
				return true;
			}
			return TryBuildCombinedReloadPlan(ropeSlots, requiredSegments, isAntiRopeAmmo, out plan);
		}

		private static bool HasPartialSpoolsForAmmoType(List<ItemSlot> ropeSlots, float requiredSegments, bool isAntiRopeAmmo)
		{
			foreach (ItemSlot ropeSlot in ropeSlots)
			{
				if (IsAntiRopeSpoolPrefab(ropeSlot.prefab) == isAntiRopeAmmo)
				{
					float remainingSegments = RopeSpoolLengthHelper.GetRemainingSegments(ropeSlot);
					if (RopeSpoolLengthHelper.HasReloadableLength(remainingSegments) && !RopeSpoolLengthHelper.CanFullyReload(remainingSegments, requiredSegments))
					{
						return true;
					}
				}
			}
			return false;
		}

		private static bool TryBuildSingleSpoolPlan(List<ItemSlot> ropeSlots, float requiredSegments, bool isAntiRopeAmmo, out RopeReloadPlan plan)
		{
			plan = default(RopeReloadPlan);
			foreach (ItemSlot ropeSlot in ropeSlots)
			{
				if (IsAntiRopeSpoolPrefab(ropeSlot.prefab) == isAntiRopeAmmo && RopeSpoolLengthHelper.CanFullyReload(RopeSpoolLengthHelper.GetRemainingSegments(ropeSlot), requiredSegments))
				{
					plan = new RopeReloadPlan(ropeSlot, requiredSegments, Array.Empty<ItemSlot>(), isAntiRopeAmmo);
					return true;
				}
			}
			return false;
		}

		private static bool TryBuildCombinedReloadPlan(List<ItemSlot> ropeSlots, float requiredSegments, bool isAntiRopeAmmo, out RopeReloadPlan plan)
		{
			plan = default(RopeReloadPlan);
			float num = 0f;
			List<ItemSlot> list = new List<ItemSlot>();
			foreach (ItemSlot ropeSlot in ropeSlots)
			{
				if (IsAntiRopeSpoolPrefab(ropeSlot.prefab) != isAntiRopeAmmo)
				{
					continue;
				}
				float remainingSegments = RopeSpoolLengthHelper.GetRemainingSegments(ropeSlot);
				if (RopeSpoolLengthHelper.HasReloadableLength(remainingSegments))
				{
					list.Add(ropeSlot);
					num += remainingSegments;
					if (RopeSpoolLengthHelper.CanFullyReload(num, requiredSegments))
					{
						plan = BuildCombinedReloadPlan(list, requiredSegments, isAntiRopeAmmo);
						return plan.IsValid;
					}
				}
			}
			return false;
		}

		private static RopeReloadPlan BuildCombinedReloadPlan(List<ItemSlot> spoolsInOrder, float requiredSegments, bool isAntiRopeAmmo)
		{
			float num = requiredSegments;
			List<ItemSlot> list = new List<ItemSlot>();
			ItemSlot val = null;
			float segmentsToDeduct = 0f;
			foreach (ItemSlot item in spoolsInOrder)
			{
				float remainingSegments = RopeSpoolLengthHelper.GetRemainingSegments(item);
				if (!(remainingSegments <= 2f))
				{
					if (!(num + 0.01f >= remainingSegments))
					{
						val = item;
						segmentsToDeduct = num;
						num = 0f;
						break;
					}
					list.Add(item);
					num -= remainingSegments;
				}
			}
			if (num > 0.01f)
			{
				return default(RopeReloadPlan);
			}
			if (val != null)
			{
				return new RopeReloadPlan(val, segmentsToDeduct, list, isAntiRopeAmmo);
			}
			return new RopeReloadPlan(null, 0f, list, isAntiRopeAmmo);
		}

		public static string BuildRopePlanLabel(RopeReloadPlan plan, float requiredSegments, HandCannonAmmoType ammoType)
		{
			string text = ((ammoType == HandCannonAmmoType.AntiRope) ? "Anti-grav rope" : "Rope");
			float num = RopeSpoolLengthHelper.SegmentsToMeters(requiredSegments);
			if (plan.SlotsToConsumeEntirely.Count == 0 && plan.UsesPartialSlot)
			{
				float segments = RopeSpoolLengthHelper.GetRemainingSegments(plan.PartialSlot) - plan.SegmentsToDeduct;
				return $"{text} — {num:0.##}m from slot {plan.PartialSlot.itemSlotID} ({RopeSpoolLengthHelper.SegmentsToMeters(segments):0.##}m left after)";
			}
			if (plan.SlotsToConsumeEntirely.Count > 0 && !plan.UsesPartialSlot)
			{
				return $"{text} — combine {plan.SlotsToConsumeEntirely.Count} spool(s) for {num:0.##}m";
			}
			if (plan.SlotsToConsumeEntirely.Count > 0 && plan.UsesPartialSlot)
			{
				return $"{text} — combine {plan.SlotsToConsumeEntirely.Count + 1} spool(s) for {num:0.##}m";
			}
			return $"{text} — {num:0.##}m shot";
		}

		public static bool TryApplyRopePlan(Character character, RopeReloadPlan plan)
		{
			if ((Object)(object)character == (Object)null || !plan.IsValid)
			{
				return false;
			}
			bool flag = false;
			if (plan.UsesPartialSlot)
			{
				if (!TryDeductSegmentsFromSlot(character, plan.PartialSlot, plan.SegmentsToDeduct))
				{
					return false;
				}
				flag = IsBackpackSlot(character, plan.PartialSlot);
			}
			foreach (ItemSlot item in plan.SlotsToConsumeEntirely)
			{
				if (!TryConsumeInventorySlot(character, item, syncBackpack: false))
				{
					return false;
				}
				flag = flag || IsBackpackSlot(character, item);
			}
			if (flag)
			{
				SyncPlayerInventory(character);
			}
			return true;
		}

		public static bool TryConsumePitonSlot(Character character, ItemSlot slot)
		{
			if ((Object)(object)character == (Object)null || slot == null)
			{
				return false;
			}
			bool result = TryConsumeInventorySlot(character, slot, syncBackpack: true);
			RefreshPitonTracking(character);
			return result;
		}

		private static bool TryDeductSegmentsFromSlot(Character character, ItemSlot slot, float segmentsToDeduct)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Expected O, but got Unknown
			if ((Object)(object)character == (Object)null || slot == null || slot.IsEmpty() || segmentsToDeduct <= 0f)
			{
				return false;
			}
			float spoolStartFuel = RopeSpoolLengthHelper.GetSpoolStartFuel(slot);
			float num = RopeSpoolLengthHelper.GetRemainingSegments(slot) - segmentsToDeduct;
			if (num <= 2f)
			{
				return TryConsumeInventorySlot(character, slot, syncBackpack: false);
			}
			if (slot.data == null)
			{
				slot.data = new ItemInstanceData(Guid.NewGuid());
			}
			ItemDataHelper.SetFloatEntry(slot.data, (DataEntryKey)10, num);
			float value = ((spoolStartFuel > 0f) ? Mathf.Clamp01(num / spoolStartFuel) : 0f);
			ItemDataHelper.SetFloatEntry(slot.data, (DataEntryKey)11, value);
			return true;
		}

		private static bool TryConsumeInventorySlot(Character character, ItemSlot slot, bool syncBackpack)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)((character != null) ? character.player : null) == (Object)null || slot == null)
			{
				return false;
			}
			try
			{
				if (slot.itemSlotID == 250)
				{
					character.player.EmptySlot(Optionable<byte>.Some((byte)250), true);
				}
				else if (IsBackpackSlot(character, slot))
				{
					slot.EmptyOut();
					if (syncBackpack)
					{
						SyncPlayerInventory(character);
					}
				}
				else
				{
					character.player.EmptySlot(Optionable<byte>.Some(slot.itemSlotID), true);
				}
			}
			catch (Exception ex)
			{
				HandCannonPlugin.Log.LogWarning((object)("[Reload] EmptySlot failed, using local EmptyOut: " + ex.Message));
				slot.EmptyOut();
				if (syncBackpack)
				{
					SyncPlayerInventory(character);
				}
			}
			if (!slot.IsEmpty())
			{
				slot.EmptyOut();
			}
			return slot.IsEmpty();
		}

		private static void SyncPlayerInventory(Character character)
		{
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			object obj;
			if (character == null)
			{
				obj = null;
			}
			else
			{
				Player player = character.player;
				obj = ((player != null) ? ((Component)player).GetComponent<PhotonView>() : null);
			}
			PhotonView val = (PhotonView)obj;
			if (!PhotonNetwork.IsMasterClient || (Object)(object)val == (Object)null)
			{
				return;
			}
			try
			{
				InventorySyncData val2 = default(InventorySyncData);
				((InventorySyncData)(ref val2))..ctor(character.player.itemSlots, character.player.backpackSlot, character.player.tempFullSlot);
				val.RPC("SyncInventoryRPC", (RpcTarget)1, new object[2]
				{
					IBinarySerializable.ToManagedArray<InventorySyncData>(val2),
					false
				});
			}
			catch (Exception ex)
			{
				HandCannonPlugin.Log.LogWarning((object)("[Reload] inventory sync skipped: " + ex.Message));
			}
		}

		public static bool IsBackpackSlot(Character character, ItemSlot slot)
		{
			if ((Object)(object)((character != null) ? character.player : null) == (Object)null || slot == null || character.player.backpackSlot == null || ((ItemSlot)character.player.backpackSlot).IsEmpty())
			{
				return false;
			}
			ItemInstanceData data = ((ItemSlot)character.player.backpackSlot).data;
			BackpackData val = default(BackpackData);
			if (data == null || !data.TryGetDataEntry<BackpackData>((DataEntryKey)7, ref val))
			{
				return false;
			}
			ItemSlot[] itemSlots = val.itemSlots;
			for (int i = 0; i < itemSlots.Length; i++)
			{
				if (itemSlots[i] == slot)
				{
					return true;
				}
			}
			return false;
		}
	}
	internal static class HandCannonReloadPlanner
	{
		public static List<HandCannonReloadPlan> BuildAllValidPlans(Character character, RopeShooter shooter)
		{
			List<HandCannonReloadPlan> list = new List<HandCannonReloadPlan>();
			if ((Object)(object)character == (Object)null)
			{
				return list;
			}
			foreach (ItemSlot item in HandCannonInventoryHelper.GetPitonSlotsInOrder(character))
			{
				list.Add(HandCannonReloadPlan.ForPiton(item));
			}
			List<ItemSlot> ropeSpoolSlotsInOrder = HandCannonInventoryHelper.GetRopeSpoolSlotsInOrder(character);
			float cannonShotSegments = RopeSpoolLengthHelper.GetCannonShotSegments(shooter);
			if (HandCannonInventoryHelper.TryBuildBestRopePlan(ropeSpoolSlotsInOrder, cannonShotSegments, isAntiRopeAmmo: false, out var plan))
			{
				string displayLabel = HandCannonInventoryHelper.BuildRopePlanLabel(plan, cannonShotSegments, HandCannonAmmoType.NormalRope);
				list.Add(HandCannonReloadPlan.ForRope(plan, HandCannonAmmoType.NormalRope, displayLabel));
			}
			float shotSegments = AntiRopeCannonReference.GetShotSegments();
			if (CanUseAntiRopeAmmo(shooter) && HandCannonInventoryHelper.TryBuildBestRopePlan(ropeSpoolSlotsInOrder, shotSegments, isAntiRopeAmmo: true, out var plan2))
			{
				string displayLabel2 = HandCannonInventoryHelper.BuildRopePlanLabel(plan2, shotSegments, HandCannonAmmoType.AntiRope);
				list.Add(HandCannonReloadPlan.ForRope(plan2, HandCannonAmmoType.AntiRope, displayLabel2));
			}
			return CollapseDistinctAmmoTypes(list);
		}

		private static List<HandCannonReloadPlan> CollapseDistinctAmmoTypes(List<HandCannonReloadPlan> plans)
		{
			List<HandCannonReloadPlan> list = new List<HandCannonReloadPlan>();
			HashSet<HandCannonAmmoType> hashSet = new HashSet<HandCannonAmmoType>();
			foreach (HandCannonReloadPlan plan in plans)
			{
				if (hashSet.Add(plan.AmmoType))
				{
					list.Add(plan);
				}
			}
			return list;
		}

		private static bool CanUseAntiRopeAmmo(RopeShooter shooter)
		{
			Item val = HandCannonHelper.GetItem(shooter);
			if ((Object)(object)val == (Object)null)
			{
				Character localCharacter = Character.localCharacter;
				object obj;
				if (localCharacter == null)
				{
					obj = null;
				}
				else
				{
					CharacterData data = localCharacter.data;
					obj = ((data != null) ? data.currentItem : null);
				}
				val = (Item)obj;
			}
			if (val?.itemID == 64)
			{
				return true;
			}
			return AntiRopeCannonReference.IsAvailable();
		}
	}
	internal static class HandCannonReloadExecutor
	{
		public static bool TryManualReload(Character holder)
		{
			Character obj = holder;
			object obj2;
			if (obj == null)
			{
				obj2 = null;
			}
			else
			{
				CharacterData data = obj.data;
				obj2 = ((data != null) ? data.currentItem : null);
			}
			if ((Object)obj2 == (Object)null)
			{
				return false;
			}
			Item cannonItem = holder.data.currentItem;
			RopeShooter shooter = ((Component)cannonItem).GetComponent<RopeShooter>();
			if (!HandCannonHelper.IsHandCannon(cannonItem) || !HandCannonHelper.IsEmptyForReload(cannonItem, shooter))
			{
				return false;
			}
			List<HandCannonReloadPlan> list = HandCannonReloadPlanner.BuildAllValidPlans(holder, shooter);
			if (list.Count == 0)
			{
				return false;
			}
			if (list.Count == 1)
			{
				return ExecutePlan(holder, cannonItem, shooter, list[0]);
			}
			HandCannonReloadPickerUI.Show(list, delegate(HandCannonReloadPlan plan)
			{
				ExecutePlan(holder, cannonItem, shooter, plan);
			});
			return true;
		}

		public static bool ExecutePlan(Character holder, Item cannonItem, RopeShooter shooter, HandCannonReloadPlan plan)
		{
			if ((Object)(object)holder == (Object)null || (Object)(object)cannonItem == (Object)null || plan == null || !plan.IsValid)
			{
				return false;
			}
			ItemSlot val = HandCannonInventoryHelper.FindSlotForItem(cannonItem);
			if (!ApplySlotDataReload(cannonItem, shooter, plan.AmmoType, val))
			{
				return false;
			}
			if (!HandCannonHelper.HasCharge(cannonItem))
			{
				HandCannonPlugin.Log.LogWarning((object)"[Reload] slot-data reload left Hand Cannon empty.");
				return false;
			}
			if (val != null && !val.IsEmpty())
			{
				ResetCannonSlotForFreshShot(val, shooter, plan.AmmoType);
			}
			if (plan.IsPiton)
			{
				if (!HandCannonInventoryHelper.TryConsumePitonSlot(holder, plan.PitonSlot))
				{
					RollbackFailedReload(cannonItem, shooter, val);
					HandCannonPlugin.Log.LogWarning((object)"[Reload] piton reload succeeded but piton could not be consumed.");
					return false;
				}
			}
			else if (!HandCannonInventoryHelper.TryApplyRopePlan(holder, plan.RopePlan))
			{
				RollbackFailedReload(cannonItem, shooter, val);
				HandCannonPlugin.Log.LogWarning((object)"[Reload] reload succeeded but rope could not be consumed.");
				return false;
			}
			HandCannonStateSync.Sync(cannonItem, shooter);
			HandCannonNetworkSync.PushAmmoState(cannonItem, val, holder);
			HandCannonPlugin.Log.LogInfo((object)$"[Reload] ok: loaded {HandCannonAmmoHelper.GetAmmoLabel(plan.AmmoType)}, hasAmmo={HandCannonHelper.HasCharge(cannonItem)}");
			return true;
		}

		private static void RollbackFailedReload(Item cannonItem, RopeShooter shooter, ItemSlot cannonSlot)
		{
			HandCannonAmmoHelper.SetLoadedAmmo(cannonItem, cannonSlot, HandCannonAmmoType.Empty);
			HandCannonAmmoHelper.ClearUses(cannonItem);
			HandCannonStateSync.Sync(cannonItem, shooter);
		}

		private static bool ApplySlotDataReload(Item cannonItem, RopeShooter shooter, HandCannonAmmoType ammoType, ItemSlot cannonSlot)
		{
			if (ammoType == HandCannonAmmoType.AntiRope && cannonItem.itemID != 64 && !AntiRopeCannonReference.IsAvailable())
			{
				HandCannonPlugin.Log.LogWarning((object)"[Reload] anti-grav reload failed: anti reference unavailable.");
				return false;
			}
			int ammo = ((!((Object)(object)shooter != (Object)null)) ? 1 : Mathf.Max(1, shooter.startAmmo));
			try
			{
				if ((Object)(object)shooter != (Object)null)
				{
					((ItemComponent)shooter).GetData<IntItemData>((DataEntryKey)12, (Func<IntItemData>)(() => ItemDataHelper.CreateIntItemData(ammo))).Value = ammo;
				}
				OptionableIntItemData data = cannonItem.GetData<OptionableIntItemData>((DataEntryKey)2);
				if (data != null)
				{
					data.Value = ammo;
					data.HasData = true;
				}
				HandCannonAmmoHelper.SetLoadedAmmo(cannonItem, cannonSlot, ammoType);
				cannonItem.SetUseRemainingPercentage(1f);
				VineShooter component = ((Component)cannonItem).GetComponent<VineShooter>();
				if ((Object)(object)component != (Object)null && (Object)(object)component.disableOnFire != (Object)null)
				{
					component.disableOnFire.SetActive(true);
				}
				if ((Object)(object)shooter != (Object)null)
				{
					shooter.cantReFire = false;
					((ItemComponent)shooter).OnInstanceDataSet();
				}
				cannonItem.finishedCast = false;
				cannonItem.CancelUsePrimary();
				return HandCannonHelper.HasCharge(cannonItem);
			}
			catch (Exception ex)
			{
				HandCannonPlugin.Log.LogError((object)("[Reload] slot-data failed: " + ex.GetType().Name + ": " + ex.Message));
				return false;
			}
		}

		private static void ResetCannonSlotForFreshShot(ItemSlot slot, RopeShooter shooter, HandCannonAmmoType ammoType)
		{
			//IL_0052: 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)
			//IL_0031: Expected O, but got Unknown
			int value = ((!((Object)(object)shooter != (Object)null)) ? 1 : Mathf.Max(1, shooter.startAmmo));
			if (slot.data == null)
			{
				slot.data = new ItemInstanceData(Guid.NewGuid());
			}
			ItemDataHelper.SetIntEntry(slot.data, (DataEntryKey)12, value);
			ItemDataHelper.SetOptionableIntEntry(slot.data, (DataEntryKey)2, value);
			ItemDataHelper.SetIntEntry(slot.data, HandCannonDataEntryKey.LoadedAmmoType, (int)ammoType);
		}
	}
	internal sealed class HandCannonReloadPickerUI : MonoBehaviour
	{
		private const int SortOrder = 3200;

		private static HandCannonReloadPickerUI _instance;

		private bool _visible;

		private bool _built;

		private List<HandCannonReloadPlan> _openPlans;

		private Action<HandCannonReloadPlan> _onSelected;

		private float _openedAt;

		private CursorLockMode _prevLock;

		private bool _prevCursorVisible;

		private RectTransform _panel;

		private Transform _optionsRoot;

		private TextMeshProUGUI _help;

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

		public static void TryKeyboardSelect()
		{
			//IL_002a: 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)
			if (!IsOpen || _instance?._openPlans == null)
			{
				return;
			}
			for (int i = 0; i < _instance._openPlans.Count && i < 9; i++)
			{
				int num = 49 + i;
				KeyCode val = (KeyCode)(257 + i);
				if (Input.GetKeyDown((KeyCode)num) || Input.GetKeyDown(val))
				{
					_instance.OnPlanSelected(_instance._openPlans[i]);
					break;
				}
			}
		}

		public static void EnsureCreated()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			if (!((Object)(object)_instance != (Object)null))
			{
				GameObject val = new GameObject("HandCannonReloadPickerUI");
				Object.DontDestroyOnLoad((Object)val);
				_instance = val.AddComponent<HandCannonReloadPickerUI>();
			}
		}

		public static void DestroyInstance()
		{
			if (!((Object)(object)_instance == (Object)null))
			{
				Object.Destroy((Object)(object)((Component)_instance).gameObject);
				_instance = null;
			}
		}

		public static void Show(List<HandCannonReloadPlan> plans, Action<HandCannonReloadPlan> onSelected)
		{
			EnsureCreated();
			_instance.Open(plans, onSelected);
		}

		public static void ForceClose()
		{
			if (!((Object)(object)_instance == (Object)null))
			{
				_instance.CloseInternal();
			}
		}

		private void Update()
		{
			if (_visible)
			{
				Cursor.lockState = (CursorLockMode)0;
				Cursor.visible = true;
				if (Time.unscaledTime - _openedAt > 120f)
				{
					HandCannonPlugin.Log.LogWarning((object)"[Reload] picker timed out, closing.");
					CloseInternal();
				}
			}
		}

		private void Build()
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Expected O, but got Unknown
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: 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_0085: Expected O, but got Unknown
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missin