Decompiled source of Reloadable Hand Cannons v1.0.0

tony4twentys-Reloadable_Hand_Cannons.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ExitGames.Client.Photon;
using HarmonyLib;
using Photon.Pun;
using Photon.Realtime;
using UnityEngine;
using UnityEngine.SceneManagement;
using Zorro.Core;
using Zorro.Core.Serizalization;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[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.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace Hand_Cannon;

public enum HandCannonAmmoType
{
	Empty,
	Piton,
	NormalRope,
	AntiRope
}
[BepInPlugin("tony4twentys.Reloadable_Hand_Cannons", "Reloadable_Hand_Cannons", "1.0.0")]
public class HandCannonPlugin : BaseUnityPlugin
{
	public const string PluginGuid = "tony4twentys.Reloadable_Hand_Cannons";

	public const string PluginName = "Reloadable_Hand_Cannons";

	public const string PluginVersion = "1.0.0";

	internal static ConfigEntry<float> PitonSpawnChanceConfig;

	internal static ConfigEntry<bool> AllowBackpackReloadConfig;

	internal static ConfigEntry<KeyCode> ReloadKeyConfig;

	private Harmony _harmony;

	public static ManualLogSource Log { get; private set; }

	internal static HandCannonPlugin Instance { get; private set; }

	private void Awake()
	{
		//IL_0085: Unknown result type (might be due to invalid IL or missing references)
		//IL_008f: Expected O, but got Unknown
		//IL_0107: 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_0058: 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 (HandCannonReloadPickerUI.IsOpen)
		{
			if (Input.GetKeyDown((KeyCode)27) || Input.GetKeyDown(ReloadKeyConfig.Value))
			{
				Log.LogInfo((object)"[Reload] picker cancelled.");
				HandCannonReloadPickerUI.Close();
			}
			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) && !((Object)(object)component == (Object)null) && HandCannonHelper.IsEmptyForReload(currentItem, component) && !currentItem.isUsingPrimary)
				{
					HandCannonReloadExecutor.TryManualReload(localCharacter);
				}
			}
		}
	}

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

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

	private static void WarnIfConflictingModsLoaded()
	{
		Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
		foreach (Assembly assembly in assemblies)
		{
			string name = assembly.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;
}
internal static class GameItemIds
{
	public const ushort RopeCannonItemId = 63;

	public const ushort AntiRopeCannonItemId = 64;

	public const ushort AntiRopeSpoolItemId = 1;

	public const ushort RopeSpoolItemId = 65;

	public const ushort ClimbingSpikeItemId = 18;
}
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 bool IsHandCannon(Item item)
	{
		if ((Object)(object)item == (Object)null || (Object)(object)((Component)item).GetComponent<RopeShooter>() == (Object)null)
		{
			return false;
		}
		return item.itemID == 63 || item.itemID == 64;
	}

	public static bool CanTagItems()
	{
		return !PhotonNetwork.InRoom || PhotonNetwork.IsMasterClient;
	}

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

	public static bool IsEmptyForReload(Item item, RopeShooter shooter)
	{
		if ((Object)(object)item == (Object)null || (Object)(object)shooter == (Object)null)
		{
			return false;
		}
		if (!shooter.HasAmmo)
		{
			return true;
		}
		OptionableIntItemData data = item.GetData<OptionableIntItemData>((DataEntryKey)2);
		if (data != null && data.HasData && data.Value <= 0)
		{
			return true;
		}
		return false;
	}
}
internal static class HandCannonFireHelper
{
	public static void ConsumeFiredShot(RopeShooter shooter, Item item)
	{
		//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)shooter == (Object)null || (Object)(object)item == (Object)null || !HandCannonHelper.IsHandCannon(item) || !shooter.HasAmmo)
		{
			return;
		}
		IntItemData data = ((ItemComponent)shooter).GetData<IntItemData>((DataEntryKey)12, (Func<IntItemData>)(() => ItemDataHelper.CreateIntItemData(0)));
		data.Value = 0;
		OptionableIntItemData data2 = item.GetData<OptionableIntItemData>((DataEntryKey)2);
		if (data2 != null)
		{
			data2.Value = 0;
		}
		item.SetUseRemainingPercentage(0f);
		shooter.cantReFire = false;
		((ItemComponent)shooter).OnInstanceDataSet();
		Optionable<byte> valueOrDefault = (item.holderCharacter?.refs?.items?.currentSelectedSlot).GetValueOrDefault();
		object obj;
		if (valueOrDefault.IsSome)
		{
			Character holderCharacter = item.holderCharacter;
			if ((Object)(object)((holderCharacter != null) ? holderCharacter.player : null) != (Object)null)
			{
				obj = item.holderCharacter.player.GetItemSlot(valueOrDefault.Value);
				goto IL_0135;
			}
		}
		obj = null;
		goto IL_0135;
		IL_0135:
		ItemSlot slot = (ItemSlot)obj;
		HandCannonAmmoHelper.SetLoadedAmmo(item, slot, HandCannonAmmoType.Empty);
		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 static HandCannonNetProxy _proxy;

	public static void EnsureProxy()
	{
		//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)(object)val);
			_proxy = val.AddComponent<HandCannonNetProxy>();
		}
	}

	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 val = ((MonoBehaviourPun)item).photonView ?? item.view;
		if ((Object)(object)val == (Object)null || (!val.IsMine && !PhotonNetwork.IsMasterClient))
		{
			return;
		}
		try
		{
			val.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_009a: Unknown result type (might be due to invalid IL or missing references)
		//IL_009f: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b0: Expected O, but got Unknown
		if ((Object)(object)item == (Object)null || !PhotonNetwork.InRoom)
		{
			return;
		}
		PhotonView val = ((MonoBehaviourPun)item).photonView ?? item.view;
		if ((Object)(object)val == (Object)null || (!val.IsMine && !PhotonNetwork.IsMasterClient))
		{
			return;
		}
		HandCannonAmmoType loadedAmmo = HandCannonAmmoHelper.GetLoadedAmmo(item);
		try
		{
			PhotonNetwork.RaiseEvent((byte)191, (object)new object[4]
			{
				100,
				(byte)0,
				val.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)
	{
		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 void BroadcastAllHandCannons()
	{
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0041: 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_0085: 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)
	{
		//IL_0060: Unknown result type (might be due to invalid IL or missing references)
		//IL_006e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0073: 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;
		}
		Character holderCharacter2 = item.holderCharacter;
		Optionable<byte> valueOrDefault = (holderCharacter2.refs?.items?.currentSelectedSlot).GetValueOrDefault();
		if (valueOrDefault.IsSome)
		{
			ItemSlot itemSlot = holderCharacter2.player.GetItemSlot(valueOrDefault.Value);
			if (itemSlot != null && !itemSlot.IsEmpty())
			{
				HandCannonAmmoHelper.CopyAmmoToSlot(item, itemSlot);
				SyncHolderInventory(holderCharacter2);
			}
		}
	}

	public static void ScheduleSpawnResync(Item item)
	{
		EnsureProxy();
		if ((Object)(object)_proxy != (Object)null && (Object)(object)item != (Object)null)
		{
			((MonoBehaviour)_proxy).StartCoroutine(SpawnResyncRoutine(item));
		}
	}

	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);
		}
	}

	internal static void HandleEvent(EventData eventData)
	{
		if (eventData != null && eventData.Code == 191 && eventData.CustomData is object[] array && array.Length >= 4 && Convert.ToInt32(array[0]) == 100 && Convert.ToByte(array[1]) == 0)
		{
			int itemViewId = Convert.ToInt32(array[2]);
			HandCannonAmmoType ammo = (HandCannonAmmoType)Convert.ToInt32(array[3]);
			ApplyRemoteAmmo(itemViewId, ammo);
		}
	}
}
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_0015: 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_008b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0090: Unknown result type (might be due to invalid IL or missing references)
		//IL_007d: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)item == (Object)null)
		{
			return HandCannonAmmoType.Empty;
		}
		if (item.HasData(HandCannonDataEntryKey.LoadedAmmoType))
		{
			return (HandCannonAmmoType)item.GetData<IntItemData>(HandCannonDataEntryKey.LoadedAmmoType).Value;
		}
		Character holderCharacter = item.holderCharacter;
		Optionable<byte> valueOrDefault = (holderCharacter?.refs?.items?.currentSelectedSlot).GetValueOrDefault();
		if (valueOrDefault.IsSome && (Object)(object)((holderCharacter != null) ? holderCharacter.player : null) != (Object)null)
		{
			ItemSlot itemSlot = holderCharacter.player.GetItemSlot(valueOrDefault.Value);
			if (itemSlot == null || itemSlot.IsEmpty() || itemSlot.data == null)
			{
				return HandCannonAmmoType.Empty;
			}
			return GetLoadedAmmo(itemSlot.data);
		}
		return HandCannonAmmoType.Empty;
	}

	public static HandCannonAmmoType GetLoadedAmmo(ItemInstanceData data)
	{
		//IL_0005: 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)
		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_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0051: 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_004a: Expected O, but got Unknown
		if ((Object)(object)item != (Object)null)
		{
			IntItemData data = item.GetData<IntItemData>(HandCannonDataEntryKey.LoadedAmmoType);
			data.Value = (int)ammoType;
		}
		if (slot != null)
		{
			if (slot.data == null)
			{
				slot.data = new ItemInstanceData(Guid.NewGuid());
			}
			ItemDataHelper.SetIntEntry(slot.data, HandCannonDataEntryKey.LoadedAmmoType, (int)ammoType);
		}
	}

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

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

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

	public static HandCannonAmmoType ResolveLoadedAmmoForName(Item item, ItemInstanceData data)
	{
		HandCannonAmmoType loadedAmmo = GetLoadedAmmo(data);
		if (loadedAmmo != HandCannonAmmoType.Empty)
		{
			return loadedAmmo;
		}
		return GetLoadedAmmo(item);
	}
}
internal static class ItemDataHelper
{
	public static void SetIntEntry(ItemInstanceData data, DataEntryKey key, int value)
	{
		//IL_0002: 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
		//IL_002d: 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_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0025: Expected O, but got Unknown
		//IL_003b: 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_0002: 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
		//IL_002d: 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_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: 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 flag = (Object)(object)shooter != (Object)null && shooter.HasAmmo && HandCannonAmmoHelper.GetLoadedAmmo(item) == HandCannonAmmoType.AntiRope;
		Antigrav val = ((Component)item).GetComponent<Antigrav>();
		if (flag)
		{
			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>();
		Item[] array2 = array;
		foreach (Item val in array2)
		{
			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 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>();

	public static void Sync(Item item, RopeShooter shooter)
	{
		if ((Object)(object)shooter?.hideOnFire == (Object)null || !HandCannonHelper.IsHandCannon(item))
		{
			return;
		}
		Transform transform = shooter.hideOnFire.transform;
		HandCannonAmmoType handCannonAmmoType = HandCannonAmmoHelper.GetLoadedAmmo(item);
		if (handCannonAmmoType == HandCannonAmmoType.Empty && shooter.HasAmmo)
		{
			handCannonAmmoType = ((item.itemID == 64) ? HandCannonAmmoType.AntiRope : HandCannonAmmoType.NormalRope);
		}
		if (!shooter.HasAmmo || handCannonAmmoType == HandCannonAmmoType.Empty || !shooter.hideOnFire.activeSelf)
		{
			ClearRuntimeVisuals(shooter, transform);
			return;
		}
		Transform val = FindDirectChild(transform, "Harpoon");
		Transform val2 = FindDirectChild(transform, "Rope");
		Transform val3 = EnsurePitonVisual(transform, val);
		bool flag = handCannonAmmoType == HandCannonAmmoType.Piton;
		bool flag2 = handCannonAmmoType == HandCannonAmmoType.NormalRope || handCannonAmmoType == HandCannonAmmoType.AntiRope;
		SetActiveIfPresent(val, flag2);
		SetActiveIfPresent(val3, flag);
		if (flag2)
		{
			ApplyRopeVisual(item, shooter, transform, val2, handCannonAmmoType);
		}
		else
		{
			HideSwappedRopeVisual(shooter, transform);
			SetActiveIfPresent(val2, active: false);
		}
		HideUnmanagedLoadedChildren(transform, val, val2, val3, flag, flag2);
	}

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

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

	private static Transform EnsureSwappedRopeVisual(RopeShooter shooter, Transform hideOnFire, Transform nativeRope, Transform referenceRope, bool wantsAntiVisual)
	{
		int instanceID = ((Object)shooter).GetInstanceID();
		Transform val = hideOnFire.Find("HandCannonSwappedRopeVisual");
		if ((Object)(object)val != (Object)null && SwappedRopeIsAntiVisual.TryGetValue(instanceID, out var value) && value == wantsAntiVisual)
		{
			return val;
		}
		if ((Object)(object)val != (Object)null)
		{
			Object.Destroy((Object)(object)((Component)val).gameObject);
			SwappedRopeIsAntiVisual.Remove(instanceID);
		}
		GameObject val2 = Object.Instantiate<GameObject>(((Component)referenceRope).gameObject, hideOnFire);
		((Object)val2).name = "HandCannonSwappedRopeVisual";
		ApplyAnchorTransform(val2.transform, nativeRope ?? referenceRope);
		StripGameplayComponents(val2);
		SwappedRopeIsAntiVisual[instanceID] = wantsAntiVisual;
		return val2.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 val3 = Object.Instantiate<GameObject>(val2, hideOnFire);
		((Object)val3).name = "HandCannonPitonVisual";
		ApplyAnchorTransform(val3.transform, harpoonAnchor);
		StripGameplayComponents(val3);
		ApplyPitonClimbingSpikeTransform(val3.transform);
		return val3.transform;
	}

	private static void ApplyPitonClimbingSpikeTransform(Transform pitonVisualRoot)
	{
		//IL_0082: Unknown result type (might be due to invalid IL or missing references)
		//IL_008e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0093: Unknown result type (might be due to invalid IL or missing references)
		//IL_009f: 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_0043: Unknown result type (might be due to invalid IL or missing references)
		//IL_0050: Unknown result type (might be due to invalid IL or missing references)
		//IL_005d: Unknown result type (might be due to invalid IL or missing references)
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_0034: 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(RopeShooter shooter, Transform hideOnFire)
	{
		Transform transform = hideOnFire.Find("HandCannonSwappedRopeVisual");
		SetActiveIfPresent(transform, active: false);
		SwappedRopeIsAntiVisual.Remove(((Object)shooter).GetInstanceID());
	}

	private static void ClearRuntimeVisuals(RopeShooter shooter, Transform hideOnFire)
	{
		HideSwappedRopeVisual(shooter, hideOnFire);
		Transform transform = hideOnFire.Find("HandCannonPitonVisual");
		SetActiveIfPresent(transform, active: false);
		Transform transform2 = FindDirectChild(hideOnFire, "Piton");
		SetActiveIfPresent(transform2, 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;
			ManualLogSource log = HandCannonPlugin.Log;
			if (log != null)
			{
				log.LogInfo((object)$"Resolved anti-rope reference from '{((Object)((Component)shooter).gameObject).name}' (length={shooter.length} segments).");
			}
			return true;
		}
		if (!_loggedFailure)
		{
			_loggedFailure = true;
			ManualLogSource log2 = HandCannonPlugin.Log;
			if (log2 != null)
			{
				log2.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" };
		string[] array2 = array;
		foreach (string text in array2)
		{
			GameObject val = Resources.Load<GameObject>(text);
			shooter = ((val != null) ? val.GetComponent<RopeShooter>() : null);
			if ((Object)(object)shooter != (Object)null)
			{
				return true;
			}
		}
		GameObject[] array3 = Resources.LoadAll<GameObject>("0_Items/");
		if (array3 == null)
		{
			return false;
		}
		GameObject[] array4 = array3;
		foreach (GameObject val2 in array4)
		{
			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()
	{
		RopeShooter shooter;
		return TryGetShooter(out shooter) ? shooter.ropeAnchorWithRopePref : null;
	}
}
internal static class NormalRopeCannonReference
{
	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;
			ManualLogSource log = HandCannonPlugin.Log;
			if (log != null)
			{
				log.LogInfo((object)$"Resolved normal rope reference from '{((Object)((Component)shooter).gameObject).name}' (length={shooter.length} segments).");
			}
			return true;
		}
		if (!_loggedFailure)
		{
			_loggedFailure = true;
			ManualLogSource log2 = HandCannonPlugin.Log;
			if (log2 != null)
			{
				log2.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" };
		string[] array2 = array;
		foreach (string text in array2)
		{
			GameObject val = Resources.Load<GameObject>(text);
			shooter = ((val != null) ? val.GetComponent<RopeShooter>() : null);
			if ((Object)(object)shooter?.ropeAnchorWithRopePref != (Object)null)
			{
				return true;
			}
		}
		GameObject[] array3 = Resources.LoadAll<GameObject>("0_Items/");
		if (array3 == null)
		{
			return false;
		}
		GameObject[] array4 = array3;
		foreach (GameObject val2 in array4)
		{
			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 float GetShotSegments()
	{
		if (TryGetShooter(out var shooter) && shooter.length > 0f)
		{
			return shooter.length;
		}
		return 30f;
	}

	public static GameObject GetAnchorPrefab()
	{
		RopeShooter shooter;
		return TryGetShooter(out shooter) ? shooter.ropeAnchorWithRopePref : null;
	}
}
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 GetCannonShotMeters(RopeShooter shooter)
	{
		return SegmentsToMeters(GetCannonShotSegments(shooter));
	}

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

	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 int CountFullReloadsAvailable(float remainingSegments, float requiredSegments)
	{
		if (requiredSegments <= 0.01f)
		{
			return 0;
		}
		return Mathf.FloorToInt(remainingSegments / requiredSegments);
	}

	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 => UsesPartialSlot || SlotsToConsumeEntirely.Count > 0;

	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 => IsPiton ? (PitonSlot != null) : RopePlan.IsValid;

	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 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;
		return text.IndexOf("ClimbingSpike", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("Piton", StringComparison.OrdinalIgnoreCase) >= 0;
	}

	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;
		}
		string text = ((Object)((Component)prefab).gameObject).name ?? string.Empty;
		return text.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;
		}
		string text = ((Object)((Component)prefab).gameObject).name ?? string.Empty;
		return text.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.hasBackpack)
		{
			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.hasBackpack)
		{
			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)
			{
				float remainingSegments = RopeSpoolLengthHelper.GetRemainingSegments(ropeSlot);
				if (RopeSpoolLengthHelper.CanFullyReload(remainingSegments, 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_0074: Unknown result type (might be due to invalid IL or missing references)
		//IL_007e: 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_001e: Unknown result type (might be due to invalid IL or missing references)
		//IL_005d: Unknown result type (might be due to invalid IL or missing references)
		if (slot.itemSlotID == 250)
		{
			character.player.EmptySlot(Optionable<byte>.Some((byte)250));
		}
		else if (IsBackpackSlot(character, slot))
		{
			slot.EmptyOut();
			if (syncBackpack)
			{
				SyncPlayerInventory(character);
			}
		}
		else
		{
			character.player.EmptySlot(Optionable<byte>.Some(slot.itemSlotID));
		}
		return true;
	}

	private static void SyncPlayerInventory(Character character)
	{
		//IL_006b: 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.hasBackpack)
		{
			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;
		foreach (ItemSlot val2 in itemSlots)
		{
			if (val2 == 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 || (Object)(object)shooter == (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));
		}
		if (CanUseAntiRopeAmmo(shooter) && HandCannonInventoryHelper.TryBuildBestRopePlan(ropeSpoolSlotsInOrder, AntiRopeCannonReference.GetShotSegments(), isAntiRopeAmmo: true, out var plan2))
		{
			float shotSegments = AntiRopeCannonReference.GetShotSegments();
			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)
	{
		if (HandCannonHelper.GetItem(shooter)?.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) || (Object)(object)shooter == (Object)null || !HandCannonHelper.IsEmptyForReload(cannonItem, shooter))
		{
			return false;
		}
		HandCannonPlugin.Log.LogInfo((object)"[Reload] manual reload requested.");
		List<HandCannonReloadPlan> list = HandCannonReloadPlanner.BuildAllValidPlans(holder, shooter);
		if (list.Count == 0)
		{
			HandCannonPlugin.Log.LogInfo((object)"[Reload] no ammo available for Hand Cannon reload.");
			return false;
		}
		HandCannonPlugin.Log.LogInfo((object)$"[Reload] {list.Count} reload option(s) available.");
		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)
	{
		//IL_005d: 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)
		if ((Object)(object)holder == (Object)null || (Object)(object)cannonItem == (Object)null || (Object)(object)shooter == (Object)null || plan == null || !plan.IsValid)
		{
			return false;
		}
		HandCannonPlugin.Log.LogInfo((object)("[Reload] executing plan: " + plan.DisplayLabel));
		Optionable<byte> currentSelectedSlot = holder.refs.items.currentSelectedSlot;
		ItemSlot val = (currentSelectedSlot.IsSome ? holder.player.GetItemSlot(currentSelectedSlot.Value) : null);
		if (!ApplySlotDataReload(cannonItem, shooter, plan.AmmoType, val))
		{
			return false;
		}
		if (!shooter.HasAmmo)
		{
			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))
			{
				HandCannonPlugin.Log.LogWarning((object)"[Reload] piton reload succeeded but piton could not be consumed.");
				return false;
			}
		}
		else if (!HandCannonInventoryHelper.TryApplyRopePlan(holder, plan.RopePlan))
		{
			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={shooter.HasAmmo}");
		return true;
	}

	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 = Mathf.Max(1, shooter.startAmmo);
		try
		{
			IntItemData data = ((ItemComponent)shooter).GetData<IntItemData>((DataEntryKey)12, (Func<IntItemData>)(() => ItemDataHelper.CreateIntItemData(ammo)));
			data.Value = ammo;
			OptionableIntItemData data2 = cannonItem.GetData<OptionableIntItemData>((DataEntryKey)2);
			if (data2 != null)
			{
				data2.Value = ammo;
			}
			HandCannonAmmoHelper.SetLoadedAmmo(cannonItem, cannonSlot, ammoType);
			cannonItem.SetUseRemainingPercentage(1f);
			shooter.cantReFire = false;
			((ItemComponent)shooter).OnInstanceDataSet();
			cannonItem.finishedCast = false;
			cannonItem.CancelUsePrimary();
			HandCannonPlugin.Log.LogInfo((object)$"[Reload] slot-data applied (ammo={HandCannonAmmoHelper.GetAmmoLabel(ammoType)}, hasAmmo={shooter.HasAmmo})");
			return shooter.HasAmmo;
		}
		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_005c: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: 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 WindowId = 63818;

	private static HandCannonReloadPickerUI _instance;

	private bool _visible;

	private List<HandCannonReloadPlan> _openPlans;

	private Action<HandCannonReloadPlan> _onSelected;

	private Rect _windowRect = new Rect(0f, 0f, 560f, 320f);

	private float _openedAt;

	public static bool IsOpen => (Object)(object)_instance != (Object)null && _instance._visible;

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

	public static void EnsureCreated()
	{
		//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)(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 Close()
	{
		ForceClose();
	}

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

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

	private void OnGUI()
	{
		//IL_0031: Unknown result type (might be due to invalid IL or missing references)
		//IL_003d: Unknown result type (might be due to invalid IL or missing references)
		//IL_004c: Expected O, but got Unknown
		//IL_0047: Unknown result type (might be due to invalid IL or missing references)
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		if (_visible && _openPlans != null)
		{
			Cursor.lockState = (CursorLockMode)0;
			Cursor.visible = true;
			_windowRect = GUI.Window(63818, _windowRect, new WindowFunction(DrawWindow), "Choose Hand Cannon Ammo");
		}
	}

	private void DrawWindow(int windowId)
	{
		//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
		//IL_0074: Unknown result type (might be due to invalid IL or missing references)
		//IL_010c: Unknown result type (might be due to invalid IL or missing references)
		List<HandCannonReloadPlan> openPlans = _openPlans;
		if (openPlans == null)
		{
			return;
		}
		float num = 28f;
		float num2 = ((Rect)(ref _windowRect)).width - 40f;
		for (int i = 0; i < openPlans.Count; i++)
		{
			HandCannonReloadPlan handCannonReloadPlan = openPlans[i];
			if (handCannonReloadPlan != null)
			{
				string text = $"{i + 1}. {handCannonReloadPlan.DisplayLabel}";
				if (GUI.Button(new Rect(20f, num, num2, 40f), text))
				{
					OnPlanSelected(handCannonReloadPlan);
					return;
				}
				num += 48f;
			}
		}
		if (GUI.Button(new Rect(20f, ((Rect)(ref _windowRect)).height - 42f, num2, 30f), "Cancel (Esc)"))
		{
			CloseInternal();
		}
		else
		{
			GUI.DragWindow(new Rect(0f, 0f, ((Rect)(ref _windowRect)).width, 24f));
		}
	}

	private void Open(List<HandCannonReloadPlan> plans, Action<HandCannonReloadPlan> onSelected)
	{
		//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
		_onSelected = onSelected;
		_openPlans = plans;
		_openedAt = Time.unscaledTime;
		float height = 90f + (float)plans.Count * 48f + 50f;
		((Rect)(ref _windowRect)).width = 560f;
		((Rect)(ref _windowRect)).height = height;
		((Rect)(ref _windowRect)).x = ((float)Screen.width - ((Rect)(ref _windowRect)).width) * 0.5f;
		((Rect)(ref _windowRect)).y = ((float)Screen.height - ((Rect)(ref _windowRect)).height) * 0.5f;
		_visible = true;
		HandCannonPlugin.Log.LogInfo((object)$"[Reload] picker opened with {plans.Count} option(s). Press 1-{plans.Count} to choose, click a button, or press {HandCannonPlugin.ReloadKeyConfig.Value}/Esc to cancel.");
		for (int i = 0; i < plans.Count; i++)
		{
			HandCannonPlugin.Log.LogInfo((object)$"[Reload]   {i + 1}: {plans[i].DisplayLabel}");
		}
	}

	private void OnPlanSelected(HandCannonReloadPlan plan)
	{
		Action<HandCannonReloadPlan> onSelected = _onSelected;
		CloseInternal();
		onSelected?.Invoke(plan);
	}

	private void CloseInternal()
	{
		_visible = false;
		_onSelected = null;
		_openPlans = null;
		Cursor.lockState = (CursorLockMode)1;
		Cursor.visible = false;
	}
}
internal static class HandCannonSpawnHelper
{
	public static void InitializeSpawnedHandCannon(Item item)
	{
		if (!HandCannonHelper.IsHandCannon(item))
		{
			return;
		}
		RopeShooter component = ((Component)item).GetComponent<RopeShooter>();
		if ((Object)(object)component == (Object)null)
		{
			return;
		}
		if (HandCannonAmmoHelper.GetLoadedAmmo(item) != HandCannonAmmoType.Empty)
		{
			HandCannonStateSync.Sync(item, component);
			return;
		}
		if (!component.HasAmmo)
		{
			HandCannonStateSync.Sync(item, component);
			return;
		}
		if (!HandCannonHelper.CanTagItems())
		{
			HandCannonStateSync.Sync(item, component);
			return;
		}
		HandCannonAmmoType ammoType;
		if (item.itemID == 64)
		{
			ammoType = HandCannonAmmoType.AntiRope;
		}
		else
		{
			float num = Random.Range(0f, 1f);
			ammoType = ((num < HandCannonPlugin.PitonSpawnChanceConfig.Value) ? HandCannonAmmoType.Piton : HandCannonAmmoType.NormalRope);
		}
		int ammo = Mathf.Max(1, component.startAmmo);
		HandCannonAmmoHelper.SetLoadedAmmo(item, null, ammoType);
		IntItemData data = ((ItemComponent)component).GetData<IntItemData>((DataEntryKey)12, (Func<IntItemData>)(() => ItemDataHelper.CreateIntItemData(ammo)));
		data.Value = ammo;
		OptionableIntItemData data2 = item.GetData<OptionableIntItemData>((DataEntryKey)2);
		if (data2 != null)
		{
			data2.Value = ammo;
		}
		item.SetUseRemainingPercentage(1f);
		((ItemComponent)component).OnInstanceDataSet();
		HandCannonStateSync.Sync(item, component);
		HandCannonNetworkSync.PushItemInstanceData(item);
		HandCannonNetworkSync.ScheduleSpawnResync(item);
		HandCannonPlugin.Log.LogInfo((object)("Hand Cannon " + ((Object)item).name + ": spawned loaded with " + HandCannonAmmoHelper.GetAmmoLabel(ammoType) + "."));
	}
}
internal static class PitonShotRegistry
{
	private static readonly HashSet<int> PitonAnchorViewIds = new HashSet<int>();

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

	public static bool MarkNextAnchorAsPiton { get; set; }

	public static void Register(int photonViewId)
	{
		PitonAnchorViewIds.Add(photonViewId);
	}

	public static bool TryConsume(int photonViewId)
	{
		return PitonAnchorViewIds.Remove(photonViewId);
	}

	public static void SnapshotKnownAnchors()
	{
		KnownAnchorViewIds.Clear();
		RopeAnchorProjectile[] array = Resources.FindObjectsOfTypeAll<RopeAnchorProjectile>();
		foreach (RopeAnchorProjectile val in array)
		{
			if ((Object)(object)val?.photonView != (Object)null)
			{
				KnownAnchorViewIds.Add(val.photonView.ViewID);
			}
		}
	}

	public static bool RegisterNewAnchors()
	{
		bool result = false;
		RopeAnchorProjectile[] array = Resources.FindObjectsOfTypeAll<RopeAnchorProjectile>();
		foreach (RopeAnchorProjectile val in array)
		{
			if (!((Object)(object)val?.photonView == (Object)null))
			{
				int viewID = val.photonView.ViewID;
				if (KnownAnchorViewIds.Add(viewID))
				{
					Register(viewID);
					result = true;
				}
			}
		}
		return result;
	}

	public static void ClearPendingMark()
	{
		MarkNextAnchorAsPiton = false;
	}
}
internal static class PitonPrefabCache
{
	public static string PrefabPath { get; private set; }

	public static bool EnsureInitialized(ManualLogSource logger)
	{
		if (!string.IsNullOrEmpty(PrefabPath))
		{
			return true;
		}
		GameObject obj = Resources.Load<GameObject>("0_Items/Piton");
		if (TryFromClimbingSpikeComponent((obj != null) ? obj.GetComponent<ClimbingSpikeComponent>() : null, logger))
		{
			return true;
		}
		GameObject[] array = Resources.LoadAll<GameObject>("0_Items/");
		if (array != null)
		{
			GameObject[] array2 = array;
			foreach (GameObject val in array2)
			{
				if (TryFromClimbingSpikeComponent(val.GetComponent<ClimbingSpikeComponent>(), logger))
				{
					return true;
				}
			}
		}
		ClimbingSpikeComponent[] array3 = Resources.FindObjectsOfTypeAll<ClimbingSpikeComponent>();
		foreach (ClimbingSpikeComponent component in array3)
		{
			if (TryFromClimbingSpikeComponent(component, logger))
			{
				return true;
			}
		}
		logger.LogWarning((object)"Could not resolve piton prefab yet.");
		return false;
	}

	private static bool TryFromClimbingSpikeComponent(ClimbingSpikeComponent component, ManualLogSource logger)
	{
		if ((Object)(object)component?.hammeredVersionPrefab == (Object)null)
		{
			return false;
		}
		PrefabPath = "0_Items/" + ((Object)component.hammeredVersionPrefab).name;
		logger.LogInfo((object)("Resolved piton prefab: " + PrefabPath));
		return true;
	}
}
internal static class PitonPlacementHelper
{
	public static void GetPlacement(RopeAnchorWithRope anchor, out Vector3 point, out Vector3 normal)
	{
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		//IL_0042: Unknown result type (might be due to invalid IL or missing references)
		//IL_0047: Unknown result type (might be due to invalid IL or missing references)
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		//IL_005e: Unknown result type (might be due to invalid IL or missing references)
		//IL_006b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0070: Unknown result type (might be due to invalid IL or missing references)
		//IL_0075: Unknown result type (might be due to invalid IL or missing references)
		//IL_0082: Unknown result type (might be due to invalid IL or missing references)
		//IL_0087: Unknown result type (might be due to invalid IL or missing references)
		//IL_0094: Unknown result type (might be due to invalid IL or missing references)
		//IL_0099: Unknown result type (might be due to invalid IL or missing references)
		//IL_009e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
		//IL_0100: Unknown result type (might be due to invalid IL or missing references)
		//IL_0105: Unknown result type (might be due to invalid IL or missing references)
		//IL_010c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0111: Unknown result type (might be due to invalid IL or missing references)
		//IL_0116: Unknown result type (might be due to invalid IL or missing references)
		//IL_0118: Unknown result type (might be due to invalid IL or missing references)
		//IL_0135: Unknown result type (might be due to invalid IL or missing references)
		//IL_013a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0142: Unknown result type (might be due to invalid IL or missing references)
		//IL_0147: Unknown result type (might be due to invalid IL or missing references)
		point = ((Component)anchor).transform.position;
		normal = -((Component)anchor).transform.up;
		int num = LayerMask.op_Implicit(HelperExtensions.ToLayerMask((LayerType)1));
		Vector3[] array = (Vector3[])(object)new Vector3[8]
		{
			-((Component)anchor).transform.forward,
			((Component)anchor).transform.forward,
			-((Component)anchor).transform.up,
			((Component)anchor).transform.up,
			-((Component)anchor).transform.right,
			((Component)anchor).transform.right,
			Vector3.down,
			Vector3.up
		};
		Vector3[] array2 = array;
		RaycastHit val2 = default(RaycastHit);
		for (int i = 0; i < array2.Length; i++)
		{
			Vector3 val = array2[i];
			if (!(((Vector3)(ref val)).sqrMagnitude < 0.0001f))
			{
				Vector3 normalized = ((Vector3)(ref val)).normalized;
				if (Physics.Raycast(point + normalized * 0.15f, -normalized, ref val2, 0.75f, num, (QueryTriggerInteraction)0))
				{
					point = ((RaycastHit)(ref val2)).point;
					normal = ((RaycastHit)(ref val2)).normal;
					break;
				}
			}
		}
	}
}
internal static class HandCannonAntiFireContext
{
	private static readonly Dictionary<int, HandCannonFireSwapState> ActiveFireSwaps = new Dictionary<int, HandCannonFireSwapState>();

	public static void Begin(RopeShooter shooter, HandCannonFireSwapState swapState)
	{
		if ((Object)(object)shooter != (Object)null)
		{
			ActiveFireSwaps[((Object)shooter).GetInstanceID()] = swapState;
		}
	}

	public static bool TryEnd(RopeShooter shooter, out HandCannonFireSwapState swapState)
	{
		swapState = default(HandCannonFireSwapState);
		if ((Object)(object)shooter == (Object)null)
		{
			return false;
		}
		int instanceID = ((Object)shooter).GetInstanceID();
		if (!ActiveFireSwaps.TryGetValue(instanceID, out swapState))
		{
			return false;
		}
		ActiveFireSwaps.Remove(instanceID);
		return true;
	}

	public static bool ShouldApplyAntiGravFallback(RopeShooter shooter)
	{
		HandCannonFireSwapState value;
		return (Object)(object)shooter != (Object)null && ActiveFireSwaps.TryGetValue(((Object)shooter).GetInstanceID(), out value) && value.PendingAntiGravFallback;
	}
}
internal struct HandCannonFireSwapState
{
	public bool SwappedAntiRopePrefab;

	public bool PendingAntiGravFallback;

	public GameObject OriginalAnchorPrefab;

	public float OriginalLength;
}
internal struct HandCannonFireState
{
	public bool IsHandCannon;

	public HandCannonAmmoType AmmoType;
}
[HarmonyPatch(typeof(RopeShooter), "OnInstanceDataSet")]
internal static class RopeShooter_OnInstanceDataSet_HandCannonVisual_Patch
{
	private static void Postfix(RopeShooter __instance)
	{
		Item item = HandCannonHelper.GetItem(__instance);
		if (HandCannonHelper.IsHandCannon(item))
		{
			HandCannonStateSync.Sync(item, __instance);
		}
	}
}
[HarmonyPatch(typeof(RopeShooter), "Sync_Rpc")]
internal static class RopeShooter_Sync_Rpc_HandCannonVisual_Patch
{
	private static void Postfix(RopeShooter __instance)
	{
		Item item = HandCannonHelper.GetItem(__instance);
		if (HandCannonHelper.IsHandCannon(item))
		{
			HandCannonStateSync.Sync(item, __instance);
		}
	}
}
[HarmonyPatch(typeof(Item), "SetItemInstanceDataRPC")]
internal static class Item_SetItemInstanceDataRPC_HandCannon_Patch
{
	private static void Postfix(Item __instance)
	{
		if (HandCannonHelper.IsHandCannon(__instance))
		{
			RopeShooter component = ((Component)__instance).GetComponent<RopeShooter>();
			if (!((Object)(object)component == (Object)null))
			{
				HandCannonStateSync.Sync(__instance, component);
			}
		}
	}
}
[HarmonyPatch(typeof(Item), "OnStash")]
internal static class Item_OnStash_HandCannon_Patch
{
	private static void Prefix(Item __instance)
	{
		HandCannonNetworkSync.PreserveAmmoOnStash(__instance);
	}
}
[HarmonyPatch(typeof(RunManager), "StartRun")]
internal static class RunManager_StartRun_Patch
{
	private static void Postfix()
	{
		PitonPrefabCache.EnsureInitialized(HandCannonPlugin.Log);
	}
}
[HarmonyPatch(typeof(Item), "Start")]
internal static class Item_Start_HandCannon_Patch
{
	private static void Postfix(Item __instance)
	{
		HandCannonSpawnHelper.InitializeSpawnedHandCannon(__instance);
	}
}
[HarmonyPatch(typeof(Item), "GetItemName")]
internal static class Item_GetItemName_HandCannon_Patch
{
	private static void Postfix(Item __instance, ItemInstanceData data, ref string __result)
	{
		if (HandCannonHelper.IsHandCannon(__instance))
		{
			HandCannonAmmoType ammoType = HandCannonAmmoHelper.ResolveLoadedAmmoForName(__instance, data);
			__result = HandCannonAmmoHelper.GetDisplayName(ammoType);
		}
	}
}
[HarmonyPatch(typeof(RopeShooter), "OnPrimaryFinishedCast")]
internal static class RopeShooter_OnPrimaryFinishedCast_HandCannon_Patch
{
	private static void CompleteFireAfterCast(RopeShooter shooter, HandCannonFireState state, string stage)
	{
		//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
		Item item = HandCannonHelper.GetItem(shooter);
		if ((Object)(object)item == (Object)null || !state.IsHandCannon)
		{
			return;
		}
		if (state.AmmoType == HandCannonAmmoType.Piton && !PitonShotRegistry.RegisterNewAnchors())
		{
			HandCannonPlugin.Log.LogWarning((object)("[Fire] piton shot fired but no anchor registered (" + stage + ")."));
		}
		object obj;
		if (!shooter.HasAmmo)
		{
			Optionable<byte> valueOrDefault = (item.holderCharacter?.refs?.items?.currentSelectedSlot).GetValueOrDefault();
			if (valueOrDefault.IsSome)
			{
				Character holderCharacter = item.holderCharacter;
				if ((Object)(object)((holderCharacter != null) ? holderCharacter.player : null) != (Object)null)
				{
					obj = item.holderCharacter.player.GetItemSlot(valueOrDefault.Value);
					goto IL_0104;
				}
			}
			obj = null;
			goto IL_0104;
		}
		goto IL_0128;
		IL_0104:
		ItemSlot slot = (ItemSlot)obj;
		HandCannonAmmoHelper.SetLoadedAmmo(item, slot, HandCannonAmmoType.Empty);
		HandCannonStateSync.Sync(item, shooter);
		HandCannonNetworkSync.PushAmmoState(item, slot, item.holderCharacter);
		goto IL_0128;
		IL_0128:
		PitonShotRegistry.ClearPendingMark();
	}

	private static void Prefix(RopeShooter __instance, ref HandCannonFireState __state)
	{
		__state = default(HandCannonFireState);
		Item item = HandCannonHelper.GetItem(__instance);
		if (!HandCannonHelper.IsHandCannon(item))
		{
			return;
		}
		__state.IsHandCannon = true;
		__state.AmmoType = HandCannonAmmoHelper.GetLoadedAmmo(item);
		PitonShotRegistry.SnapshotKnownAnchors();
		PitonShotRegistry.MarkNextAnchorAsPiton = __state.AmmoType == HandCannonAmmoType.Piton;
		if (__state.AmmoType == HandCannonAmmoType.Piton)
		{
			return;
		}
		HandCannonFireSwapState swapState = new HandCannonFireSwapState
		{
			OriginalAnchorPrefab = __instance.ropeAnchorWithRopePref,
			OriginalLength = __instance.length
		};
		if (__state.AmmoType == HandCannonAmmoType.NormalRope)
		{
			if (item.itemID == 64)
			{
				if (!NormalRopeCannonReference.TryGetShooter(out var shooter) || (Object)(object)shooter.ropeAnchorWithRopePref == (Object)null)
				{
					HandCannonPlugin.Log.LogWarning((object)"[Fire] normal rope anchor unavailable on anti-grav Hand Cannon; shot may still be anti-grav.");
					return;
				}
				swapState.SwappedAntiRopePrefab = true;
				__instance.ropeAnchorWithRopePref = shooter.ropeAnchorWithRopePref;
				__instance.length = shooter.length;
				HandCannonAntiFireContext.Begin(__instance, swapState);
				HandCannonPlugin.Log.LogInfo((object)"[Fire] normal rope shot via swapped anchor on anti-grav Hand Cannon.");
			}
		}
		else
		{
			if (__state.AmmoType != HandCannonAmmoType.AntiRope)
			{
				return;
			}
			if (item.itemID == 64)
			{
				swapState.PendingAntiGravFallback = true;
				HandCannonAntiFireContext.Begin(__instance, swapState);
				return;
			}
			GameObject anchorPrefab = AntiRopeCannonReference.GetAnchorPrefab();
			if ((Object)(object)anchorPrefab == (Object)null || !AntiRopeCannonReference.TryGetShooter(out var shooter2))
			{
				swapState.PendingAntiGravFallback = true;
				HandCannonAntiFireContext.Begin(__instance, swapState);
				HandCannonPlugin.Log.LogWarning((object)"[Fire] anti anchor unavailable; using antigrav rope fallback.");
			}
			else
			{
				swapState.SwappedAntiRopePrefab = true;
				__instance.ropeAnchorWithRopePref = anchorPrefab;
				__instance.length = shooter2.length;
				HandCannonAntiFireContext.Begin(__instance, swapState);
				HandCannonPlugin.Log.LogInfo((object)"[Fire] anti-grav rope shot via swapped anchor prefab.");
			}
		}
	}

	private static void Postfix(RopeShooter __instance, HandCannonFireState __state)
	{
		if (__state.IsHandCannon)
		{
			Item item = HandCannonHelper.GetItem(__instance);
			RestoreAntiSwap(__instance, clearLoadedState: false);
			HandCannonFireHelper.ConsumeFiredShot(__instance, item);
			CompleteFireAfterCast(__instance, __state, "Postfix");
		}
	}

	[HarmonyFinalizer]
	private static Exception Finalizer(Exception __exception, RopeShooter __instance, ref HandCannonFireState __state)
	{
		if (__exception != null)
		{
			HandCannonPlugin.Log.LogWarning((object)("[Fire] OnPrimaryFinishedCast threw: " + __exception.GetType().Name + ": " + __exception.Message));
			RestoreAntiSwap(__instance, clearLoadedState: false);
		}
		if (__state.IsHandCannon)
		{
			Item item = HandCannonHelper.GetItem(__instance);
			HandCannonFireHelper.ConsumeFiredShot(__instance, item);
		}
		CompleteFireAfterCast(__instance, __state, "Finalizer");
		return null;
	}

	private static void RestoreAntiSwap(RopeShooter shooter, bool clearLoadedState)
	{
		//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
		if (!HandCannonAntiFireContext.TryEnd(shooter, out var swapState))
		{
			return;
		}
		if (swapState.SwappedAntiRopePrefab)
		{
			shooter.ropeAnchorWithRopePref = swapState.OriginalAnchorPrefab;
			shooter.length = swapState.OriginalLength;
		}
		if (!clearLoadedState)
		{
			return;
		}
		Item item = HandCannonHelper.GetItem(shooter);
		Optionable<byte> valueOrDefault = ((item == null) ? ((Optionable<byte>?)null) : item.holderCharacter?.refs?.items?.currentSelectedSlot).GetValueOrDefault();
		object obj2;
		if (valueOrDefault.IsSome)
		{
			object obj;
			if (item == null)
			{
				obj = null;
			}
			else
			{
				Character holderCharacter = item.holderCharacter;
				obj = ((holderCharacter != null) ? holderCharacter.player : null);
			}
			if ((Object)obj != (Object)null)
			{
				obj2 = item.holderCharacter.player.GetItemSlot(valueOrDefault.Value);
				goto IL_00fb;
			}
		}
		obj2 = null;
		goto IL_00fb;
		IL_00fb:
		ItemSlot slot = (ItemSlot)obj2;
		HandCannonAmmoHelper.SetLoadedAmmo(item, slot, HandCannonAmmoType.Empty);
		HandCannonStateSync.Sync(item, shooter);
	}
}
[HarmonyPatch(typeof(RopeAnchorProjectile), "GetShot")]
internal static class RopeAnchorProjectile_GetShot_HandCannon_Patch
{
	private static void Prefix(RopeAnchorProjectile __instance)
	{
		if (PitonShotRegistry.MarkNextAnchorAsPiton && (Object)(object)__instance?.photonView != (Object)null)
		{
			PitonShotRegistry.Register(__instance.photonView.ViewID);
		}
	}

	private static void Postfix(RopeAnchorProjectile __instance)
	{
		RopeShooter val = FindSourceShooter();
		if (!((Object)(object)val == (Object)null) && HandCannonAntiFireContext.ShouldApplyAntiGravFallback(val))
		{
			Rope componentInChildren = ((Component)__instance).GetComponentInChildren<Rope>(true);
			if ((Object)(object)componentInChildren == (Object)null)
			{
				HandCannonPlugin.Log.LogWarning((object)"[Fire] anti-grav fallback failed: rope not found on anchor.");
			}
			else
			{
				componentInChildren.antigrav = true;
			}
		}
	}

	private static RopeShooter FindSourceShooter()
	{
		Character localCharacter = Character.localCharacter;
		object obj;
		if (localCharacter == null)
		{
			obj = null;
		}
		else
		{
			CharacterData data = localCharacter.data;
			obj = ((data != null) ? data.currentItem : null);
		}
		Item val = (Item)obj;
		if ((Object)(object)val == (Object)null || !HandCannonHelper.IsHandCannon(val))
		{
			return null;
		}
		return ((Component)val).GetComponent<RopeShooter>();
	}
}
[HarmonyPatch(typeof(RopeAnchorWithRope), "SpawnRope")]
internal static class RopeAnchorWithRope_SpawnRope_HandCannon_Patch
{
	private static bool Prefix(RopeAnchorWithRope __instance, ref Rope __result)
	{
		//IL_0071: Unknown result type (might be due to invalid IL or missing references)
		//IL_0073: Unknown result type (might be due to invalid IL or missing references)
		//IL_0075: Unknown result type (might be due to invalid IL or missing references)
		//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_009e: Unknown result type (might be due to invalid IL or missing references)
		if (!((MonoBehaviourPun)__instance).photonView.IsMine || !PitonShotRegistry.TryConsume(((MonoBehaviourPun)__instance).photonView.ViewID))
		{
			return true;
		}
		ManualLogSource log = HandCannonPlugin.Log;
		if (log == null || !PitonPrefabCache.EnsureInitialized(log))
		{
			if (log != null)
			{
				log.LogWarning((object)"[Fire] piton prefab unavailable; falling back to rope.");
			}
			return true;
		}
		try
		{
			PitonPlacementHelper.GetPlacement(__instance, out var point, out var normal);
			PhotonNetwork.Instantiate(PitonPrefabCache.PrefabPath, point, Quaternion.LookRotation(-normal, Vector3.up), (byte)0, (object[])null);
			PhotonNetwork.Destroy(((Component)__instance).gameObject);
			log.LogInfo((object)$"[Fire] placed piton at {point}");
		}
		catch (Exception ex)
		{
			log.LogError((object)("[Fire] piton placement failed: " + ex.Message));
			return true;
		}
		__result = null;
		return false;
	}
}
[HarmonyPatch(typeof(Action_ReduceUses), "RunAction")]
internal static class Action_ReduceUses_RunAction_HandCannon_Patch
{
	private static bool Prefix(Action_ReduceUses __instance)
	{
		Item actionItem = HandCannonHelper.GetActionItem((ItemActionBase)(object)__instance);
		if (!HandCannonHelper.IsHandCannon(actionItem))
		{
			return true;
		}
		return false;
	}
}
[HarmonyPatch(typeof(Item), "ConsumeDelayed")]
internal static class Item_ConsumeDelayed_HandCannon_Patch
{
	private static bool Prefix(Item __instance)
	{
		if (!HandCannonHelper.IsHandCannon(__instance))
		{
			return true;
		}
		HandCannonPlugin.Log.LogInfo((object)("Blocked ConsumeDelayed for Hand Cannon " + ((Object)__instance).name));
		return false;
	}
}