Decompiled source of Splice Spools v1.0.0

tony4twentys-Splice Spools.dll

Decompiled a week ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Photon.Pun;
using UnityEngine;
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: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: AssemblyTitle("Splice Spools")]
[assembly: AssemblyDescription("PEAK BepInEx plugin that splices matching inventory rope spools into one longer spool.")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Splice Spools")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("4006febd-7a9f-457c-865d-a3d4e2a97d25")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}
namespace Splice_Spools
{
	[BepInPlugin("tony4twentys.Splice_Spools", "Splice_Spools", "1.0.0")]
	[BepInProcess("PEAK.exe")]
	public class SpliceSpoolsPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "tony4twentys.Splice_Spools";

		public const string PluginName = "Splice_Spools";

		public const string PluginVersion = "1.0.0";

		internal static ConfigEntry<KeyCode> SpliceKeyConfig;

		internal static ConfigEntry<bool> IncludeBackpackConfig;

		private Harmony _harmony;

		private string _toastText;

		private float _toastUntil;

		internal static bool PendingWeightRefresh;

		internal static SpliceSpoolsPlugin Instance { get; private set; }

		internal static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Expected O, but got Unknown
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			SpliceKeyConfig = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Splice", "Splice Key", (KeyCode)103, "Press this key to splice matching rope spools in your inventory into one longer spool per rope type.");
			IncludeBackpackConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Splice", "Include Backpack", true, "When true, leftover rope in a worn backpack is included when splicing.");
			_harmony = new Harmony("tony4twentys.Splice_Spools");
			_harmony.PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)($"Splice Spools loaded. Splice key: {SpliceKeyConfig.Value}. " + "Backpack: " + (IncludeBackpackConfig.Value ? "included" : "ignored") + "."));
		}

		private void Update()
		{
			//IL_001d: 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)
			if (PendingWeightRefresh)
			{
				PendingWeightRefresh = false;
				TryRefreshLocalWeight();
			}
			if ((int)SpliceKeyConfig.Value != 0 && Input.GetKeyDown(SpliceKeyConfig.Value) && !IsGameplayInputBlocked())
			{
				Character localCharacter = Character.localCharacter;
				if (!((Object)(object)((localCharacter != null) ? localCharacter.player : null) == (Object)null) && localCharacter.IsLocal)
				{
					SpliceExecutor.TrySplice(localCharacter, out var message);
					ShowToast(message);
					((BaseUnityPlugin)this).Logger.LogInfo((object)message);
				}
			}
		}

		private void OnGUI()
		{
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: 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_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Expected O, but got Unknown
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			if (!string.IsNullOrEmpty(_toastText) && !(Time.unscaledTime > _toastUntil))
			{
				Rect val = default(Rect);
				((Rect)(ref val))..ctor(((float)Screen.width - 520f) * 0.5f, 72f, 520f, 36f);
				GUI.color = new Color(0f, 0f, 0f, 0.55f);
				GUI.Box(val, GUIContent.none);
				GUI.color = Color.white;
				GUIStyle val2 = new GUIStyle(GUI.skin.label)
				{
					alignment = (TextAnchor)4,
					fontSize = 16,
					fontStyle = (FontStyle)1
				};
				GUI.Label(val, _toastText, val2);
			}
		}

		private void OnDestroy()
		{
			if (_harmony != null)
			{
				_harmony.UnpatchSelf();
			}
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
			Log = null;
		}

		internal static void TryRefreshLocalWeight()
		{
			Character localCharacter = Character.localCharacter;
			if ((Object)(object)localCharacter?.refs?.items == (Object)null)
			{
				return;
			}
			try
			{
				localCharacter.refs.items.RefreshAllCharacterCarryWeightRPC();
			}
			catch (Exception ex)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogWarning((object)("Weight refresh skipped: " + ex.Message));
				}
			}
		}

		internal void ShowToast(string text)
		{
			_toastText = text;
			_toastUntil = Time.unscaledTime + 2.5f;
		}

		private static bool IsGameplayInputBlocked()
		{
			if (GUIManager.InPauseMenu)
			{
				return true;
			}
			GUIManager instance = GUIManager.instance;
			return (Object)(object)instance != (Object)null && instance.windowBlockingInput;
		}
	}
	internal static class GameItemIds
	{
		public const ushort AntiRopeSpoolItemId = 1;

		public const ushort RopeSpoolItemId = 65;
	}
	internal static class RopeSpoolLengthHelper
	{
		public const float MetersPerSegment = 0.25f;

		public const float OutOfRopeSegmentThreshold = 2f;

		public const float DefaultSpoolStartFuel = 60f;

		public const float SegmentEpsilon = 0.01f;

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

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

		public static float GetSpoolStartFuel(ItemSlot slot)
		{
			return GetSpoolStartFuel((slot != null) ? slot.prefab : null);
		}

		public static float GetRemainingSegments(Item prefab, ItemInstanceData data)
		{
			float spoolStartFuel = GetSpoolStartFuel(prefab);
			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 float GetRemainingSegments(ItemSlot slot)
		{
			if (slot == null || slot.IsEmpty())
			{
				return 0f;
			}
			return GetRemainingSegments(slot.prefab, slot.data);
		}

		public static float GetRemainingMeters(Item prefab, ItemInstanceData data)
		{
			return SegmentsToMeters(GetRemainingSegments(prefab, data));
		}

		public static int GetScaledCarryWeight(Item prefab, ItemInstanceData data)
		{
			if ((Object)(object)prefab == (Object)null)
			{
				return 0;
			}
			int carryWeight = prefab.CarryWeight;
			if (!SpliceInventory.IsRopeSpoolPrefab(prefab))
			{
				return carryWeight;
			}
			float spoolStartFuel = GetSpoolStartFuel(prefab);
			if (spoolStartFuel <= 0.01f)
			{
				return carryWeight;
			}
			float remainingSegments = GetRemainingSegments(prefab, data);
			return Mathf.RoundToInt((float)carryWeight * (remainingSegments / spoolStartFuel));
		}

		public static bool HasSplicableLength(float segments)
		{
			return segments > 2.01f;
		}
	}
	internal static class ItemDataHelper
	{
		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);
		}
	}
	internal static class SpliceInventory
	{
		private const byte TempInventorySlotId = 250;

		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 ((Object)(object)component != (Object)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 List<ItemSlot> GetRopeSpoolSlots(Character character)
		{
			List<ItemSlot> list = new List<ItemSlot>();
			if ((Object)(object)((character != null) ? character.player : null) == (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 (SpliceSpoolsPlugin.IncludeBackpackConfig.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 (character.player.backpackSlot != null && !((ItemSlot)character.player.backpackSlot).IsEmpty())
			{
				ItemInstanceData data = ((ItemSlot)character.player.backpackSlot).data;
				BackpackData val = default(BackpackData);
				if (data != null && data.TryGetDataEntry<BackpackData>((DataEntryKey)7, ref val))
				{
					AppendRopeSlots(val.itemSlots, ropeSlots);
				}
			}
		}

		public static bool IsBackpackSlot(Character character, ItemSlot slot)
		{
			if ((Object)(object)((character != null) ? character.player : null) == (Object)null || slot == null || character.player.backpackSlot == null || ((ItemSlot)character.player.backpackSlot).IsEmpty())
			{
				return false;
			}
			ItemInstanceData data = ((ItemSlot)character.player.backpackSlot).data;
			BackpackData val = default(BackpackData);
			if (data == null || !data.TryGetDataEntry<BackpackData>((DataEntryKey)7, ref val) || val.itemSlots == null)
			{
				return false;
			}
			ItemSlot[] itemSlots = val.itemSlots;
			foreach (ItemSlot val2 in itemSlots)
			{
				if (val2 == slot)
				{
					return true;
				}
			}
			return false;
		}

		public static bool TryGetHeldSpool(Character character, out Item item, out RopeSpool spool)
		{
			object obj;
			if (character == null)
			{
				obj = null;
			}
			else
			{
				CharacterData data = character.data;
				obj = ((data != null) ? data.currentItem : null);
			}
			item = (Item)obj;
			spool = (((Object)(object)item != (Object)null) ? ((Component)item).GetComponent<RopeSpool>() : null);
			return (Object)(object)spool != (Object)null;
		}

		public static bool IsHeldSpoolBusy(Character character, bool isAntiRope)
		{
			if (!TryGetHeldSpool(character, out var item, out var spool))
			{
				return false;
			}
			if (spool.isAntiRope != isAntiRope)
			{
				return false;
			}
			return (Object)(object)spool.ropeInstance != (Object)null || (Object)(object)spool.rope != (Object)null || item.isUsingPrimary;
		}

		public static ItemSlot FindSlotForItem(Character character, Item item)
		{
			if ((Object)(object)((character != null) ? character.player : null) == (Object)null || (Object)(object)item == (Object)null)
			{
				return null;
			}
			if (item.data != null && item.data.guid != Guid.Empty)
			{
				ItemSlot val = FindSlotByGuid(character, item.data.guid);
				if (val != null)
				{
					return val;
				}
			}
			foreach (ItemSlot ropeSpoolSlot in GetRopeSpoolSlots(character))
			{
				if (SlotMatchesHeldItem(character, ropeSpoolSlot))
				{
					return ropeSpoolSlot;
				}
			}
			return null;
		}

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

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

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

		public static bool SlotMatchesHeldItem(Character character, ItemSlot slot)
		{
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: 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)
			object obj;
			if (character == null)
			{
				obj = null;
			}
			else
			{
				CharacterData data = character.data;
				obj = ((data != null) ? data.currentItem : null);
			}
			Item val = (Item)obj;
			if ((Object)(object)val == (Object)null || slot == null || slot.IsEmpty() || (Object)(object)slot.prefab == (Object)null)
			{
				return false;
			}
			if (val.data != null && slot.data != null && val.data.guid != Guid.Empty && val.data.guid == slot.data.guid)
			{
				return true;
			}
			Optionable<byte> valueOrDefault = (character.refs?.items?.currentSelectedSlot).GetValueOrDefault();
			return valueOrDefault.IsSome && valueOrDefault.Value == slot.itemSlotID && slot.prefab.itemID == val.itemID;
		}

		public static void ApplyFuelToSlot(ItemSlot slot, float remaining, float startFuel)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			if (slot.data == null)
			{
				slot.data = new ItemInstanceData(Guid.NewGuid());
			}
			ItemDataHelper.SetFloatEntry(slot.data, (DataEntryKey)10, remaining);
			float value = ((startFuel > 0f) ? Mathf.Clamp01(remaining / startFuel) : 0f);
			ItemDataHelper.SetFloatEntry(slot.data, (DataEntryKey)11, value);
		}

		public static void ApplyFuelToHeldItem(Item item, float remaining, float startFuel)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Expected O, but got Unknown
			if (!((Object)(object)item == (Object)null))
			{
				if (item.data == null)
				{
					item.data = new ItemInstanceData(Guid.NewGuid());
				}
				ItemDataHelper.SetFloatEntry(item.data, (DataEntryKey)10, remaining);
				RopeSpool component = ((Component)item).GetComponent<RopeSpool>();
				if ((Object)(object)component != (Object)null)
				{
					((ItemComponent)component).OnInstanceDataSet();
				}
				float useRemainingPercentage = ((startFuel > 0f) ? Mathf.Clamp01(remaining / startFuel) : 0f);
				item.SetUseRemainingPercentage(useRemainingPercentage);
			}
		}

		public static void EmptySlotLocal(Character character, ItemSlot slot)
		{
			if (slot == null)
			{
				return;
			}
			if (IsBackpackSlot(character, slot) || slot.itemSlotID == 250)
			{
				slot.EmptyOut();
				return;
			}
			ItemSlot itemSlot = character.player.GetItemSlot(slot.itemSlotID);
			if (itemSlot != null)
			{
				itemSlot.EmptyOut();
			}
		}

		public static void SyncInventory(Character character)
		{
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)((character != null) ? character.player : null) == (Object)null || !PhotonNetwork.InRoom)
			{
				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);
				byte[] array = IBinarySerializable.ToManagedArray<InventorySyncData>(val);
				component.RPC("SyncInventoryRPC", (RpcTarget)1, new object[2]
				{
					array,
					!PhotonNetwork.IsMasterClient
				});
				if (!PhotonNetwork.IsMasterClient)
				{
					component.RPC("SyncInventoryRPC", (RpcTarget)2, new object[2] { array, true });
				}
			}
			catch (Exception ex)
			{
				SpliceSpoolsPlugin.Log.LogWarning((object)("Inventory sync skipped: " + ex.Message));
			}
		}

		public static void PushHeldItemData(Item item)
		{
			if (item?.data == null || !PhotonNetwork.InRoom)
			{
				return;
			}
			PhotonView component = ((Component)item).GetComponent<PhotonView>();
			if ((Object)(object)component == (Object)null || (!component.IsMine && !PhotonNetwork.IsMasterClient))
			{
				return;
			}
			try
			{
				component.RPC("SetItemInstanceDataRPC", (RpcTarget)1, new object[1] { item.data });
			}
			catch (Exception ex)
			{
				SpliceSpoolsPlugin.Log.LogWarning((object)("Held spool sync skipped: " + ex.Message));
			}
		}
	}
	internal static class SpliceExecutor
	{
		public static bool TrySplice(Character character, out string message)
		{
			List<ItemSlot> ropeSpoolSlots = SpliceInventory.GetRopeSpoolSlots(character);
			List<ItemSlot> list = new List<ItemSlot>();
			List<ItemSlot> list2 = new List<ItemSlot>();
			CollectSplicable(ropeSpoolSlots, list, list2);
			bool flag = false;
			bool flag2 = false;
			float combinedMeters = 0f;
			float combinedMeters2 = 0f;
			int spoolCount = 0;
			int spoolCount2 = 0;
			if (list.Count >= 2)
			{
				if (SpliceInventory.IsHeldSpoolBusy(character, isAntiRope: false))
				{
					message = "Finish placing your rope before splicing.";
					return false;
				}
				flag = TrySpliceGroup(character, list, isAntiRope: false, out combinedMeters, out spoolCount);
			}
			if (list2.Count >= 2)
			{
				if (SpliceInventory.IsHeldSpoolBusy(character, isAntiRope: true))
				{
					if (flag)
					{
						SyncAfterSplice(character, null);
						message = BuildResultMessage(splicedNormal: true, spoolCount, combinedMeters, splicedAnti: false, 0, 0f) + " Anti-grav spool is in use.";
						return true;
					}
					message = "Finish placing your anti-grav rope before splicing.";
					return false;
				}
				flag2 = TrySpliceGroup(character, list2, isAntiRope: true, out combinedMeters2, out spoolCount2);
			}
			if (!flag && !flag2)
			{
				message = ((list.Count + list2.Count == 0) ? "No leftover rope spools to splice." : "Need at least two matching rope spools to splice.");
				return false;
			}
			SpliceInventory.TryGetHeldSpool(character, out var item, out var _);
			SyncAfterSplice(character, item);
			message = BuildResultMessage(flag, spoolCount, combinedMeters, flag2, spoolCount2, combinedMeters2);
			return true;
		}

		private static void CollectSplicable(List<ItemSlot> allSpools, List<ItemSlot> normal, List<ItemSlot> anti)
		{
			foreach (ItemSlot allSpool in allSpools)
			{
				float remainingSegments = RopeSpoolLengthHelper.GetRemainingSegments(allSpool);
				if (RopeSpoolLengthHelper.HasSplicableLength(remainingSegments))
				{
					if (SpliceInventory.IsAntiRopeSpoolPrefab(allSpool.prefab))
					{
						anti.Add(allSpool);
					}
					else
					{
						normal.Add(allSpool);
					}
				}
			}
		}

		private static bool TrySpliceGroup(Character character, List<ItemSlot> slots, bool isAntiRope, out float combinedMeters, out int spoolCount)
		{
			combinedMeters = 0f;
			spoolCount = slots.Count;
			if (slots.Count < 2)
			{
				return false;
			}
			ItemSlot val = ChooseTargetSlot(character, slots, isAntiRope);
			if (val == null)
			{
				return false;
			}
			float num = 0f;
			foreach (ItemSlot slot in slots)
			{
				num += RopeSpoolLengthHelper.GetRemainingSegments(slot);
			}
			if (!RopeSpoolLengthHelper.HasSplicableLength(num))
			{
				return false;
			}
			float spoolStartFuel = RopeSpoolLengthHelper.GetSpoolStartFuel(val);
			SpliceInventory.ApplyFuelToSlot(val, num, spoolStartFuel);
			if (SpliceInventory.SlotMatchesHeldItem(character, val))
			{
				SpliceInventory.ApplyFuelToHeldItem(character.data.currentItem, num, spoolStartFuel);
			}
			foreach (ItemSlot slot2 in slots)
			{
				if (slot2 != val)
				{
					SpliceInventory.EmptySlotLocal(character, slot2);
				}
			}
			combinedMeters = RopeSpoolLengthHelper.SegmentsToMeters(num);
			return true;
		}

		private static ItemSlot ChooseTargetSlot(Character character, List<ItemSlot> slots, bool isAntiRope)
		{
			foreach (ItemSlot slot in slots)
			{
				if (SpliceInventory.SlotMatchesHeldItem(character, slot) && SpliceInventory.IsAntiRopeSpoolPrefab(slot.prefab) == isAntiRope)
				{
					return slot;
				}
			}
			return slots[0];
		}

		private static void SyncAfterSplice(Character character, Item held)
		{
			SpliceInventory.SyncInventory(character);
			if ((Object)(object)held != (Object)null)
			{
				SpliceInventory.PushHeldItemData(held);
			}
			if ((Object)(object)character.refs?.items != (Object)null)
			{
				character.refs.items.RefreshAllCharacterCarryWeightRPC();
			}
			if (character.player?.itemsChangedAction != null)
			{
				character.player.itemsChangedAction(character.player.itemSlots);
			}
		}

		private static string BuildResultMessage(bool splicedNormal, int normalCount, float normalMeters, bool splicedAnti, int antiCount, float antiMeters)
		{
			if (splicedNormal && splicedAnti)
			{
				return $"Spliced {normalCount} rope spools into {normalMeters:0.##}m and {antiCount} anti-grav spools into {antiMeters:0.##}m.";
			}
			if (splicedAnti)
			{
				return $"Spliced {antiCount} anti-grav spools into {antiMeters:0.##}m.";
			}
			return $"Spliced {normalCount} rope spools into {normalMeters:0.##}m.";
		}
	}
	[HarmonyPatch(typeof(Item), "GetItemName")]
	internal static class Item_GetItemName_Splice_Patch
	{
		private static void Postfix(Item __instance, ItemInstanceData data, ref string __result)
		{
			try
			{
				if (SpliceInventory.IsRopeSpoolPrefab(__instance) && !string.IsNullOrEmpty(__result))
				{
					ItemInstanceData data2 = data ?? __instance.data;
					float remainingMeters = RopeSpoolLengthHelper.GetRemainingMeters(__instance, data2);
					__result = $"{__result} ({remainingMeters:0.#}m)";
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = SpliceSpoolsPlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("Name patch skipped: " + ex.Message));
				}
			}
		}
	}
	[HarmonyPatch(typeof(CharacterAfflictions), "UpdateWeight")]
	internal static class CharacterAfflictions_UpdateWeight_Splice_Patch
	{
		private static void Postfix(CharacterAfflictions __instance)
		{
			try
			{
				Character character = __instance.character;
				if (!((Object)(object)((character != null) ? character.player : null) == (Object)null))
				{
					int vanilla = 0;
					int scaled = 0;
					AddSpoolWeights(character.player.itemSlots, ref vanilla, ref scaled);
					AddSpoolWeights(GetBackpackInnerSlots(character), ref vanilla, ref scaled);
					AddSpoolWeights((ItemSlot[])(object)new ItemSlot[1] { character.player.GetItemSlot((byte)250) }, ref vanilla, ref scaled);
					if (vanilla != scaled)
					{
						float currentStatus = __instance.GetCurrentStatus((STATUSTYPE)7);
						int num = Mathf.RoundToInt(currentStatus / 0.025f);
						__instance.SetStatus((STATUSTYPE)7, 0.025f * (float)(num - vanilla + scaled), true);
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = SpliceSpoolsPlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("Weight patch skipped: " + ex.Message));
				}
			}
		}

		private static void AddSpoolWeights(ItemSlot[] slots, ref int vanilla, ref int scaled)
		{
			if (slots == null)
			{
				return;
			}
			foreach (ItemSlot val in slots)
			{
				if (val != null && !val.IsEmpty() && SpliceInventory.IsRopeSpoolPrefab(val.prefab))
				{
					vanilla += val.prefab.CarryWeight;
					scaled += RopeSpoolLengthHelper.GetScaledCarryWeight(val.prefab, val.data);
				}
			}
		}

		private static ItemSlot[] GetBackpackInnerSlots(Character character)
		{
			if (character.player.backpackSlot == null || ((ItemSlot)character.player.backpackSlot).IsEmpty())
			{
				return null;
			}
			ItemInstanceData data = ((ItemSlot)character.player.backpackSlot).data;
			BackpackData val = default(BackpackData);
			if (data == null || !data.TryGetDataEntry<BackpackData>((DataEntryKey)7, ref val))
			{
				return null;
			}
			return val.itemSlots;
		}
	}
	[HarmonyPatch(/*Could not decode attribute arguments.*/)]
	internal static class RopeSpool_RopeFuel_Splice_Patch
	{
		private static void Postfix(RopeSpool __instance, float value)
		{
			try
			{
				Item item = ((ItemComponent)__instance).item;
				Character val = (((Object)(object)item != (Object)null) ? item.holderCharacter : null);
				if (!((Object)(object)val == (Object)null) && val.IsLocal)
				{
					ItemSlot val2 = SpliceInventory.FindSlotForItem(val, item);
					if (val2 != null && val2.data != item.data)
					{
						SpliceInventory.ApplyFuelToSlot(val2, value, RopeSpoolLengthHelper.GetSpoolStartFuel(val2));
					}
					SpliceSpoolsPlugin.PendingWeightRefresh = true;
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = SpliceSpoolsPlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("Fuel sync skipped: " + ex.Message));
				}
			}
		}
	}
}