Decompiled source of ShopItemClone v4.0.0

ShopItemClone.dll

Decompiled 5 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: IgnoresAccessChecksTo("")]
[assembly: AssemblyCompany("REPOJP")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("zabuMod")]
[assembly: AssemblyTitle("zabuMod")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace REPOJP.ShopItemClone
{
	[BepInPlugin("REPOJP.ShopItemClone", "Shop Item Clone", "4.0.0")]
	public sealed class ShopItemClonePlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "REPOJP.ShopItemClone";

		public const string PluginName = "Shop Item Clone";

		public const string PluginVersion = "4.0.0";

		internal static ManualLogSource LogSource;

		internal static ShopItemClonePlugin Instance;

		internal static ConfigEntry<bool> CloneUpgrades;

		internal static ConfigEntry<bool> CloneMeleeWeapons;

		internal static ConfigEntry<bool> CloneGuns;

		internal static ConfigEntry<bool> CloneCarts;

		internal static ConfigEntry<bool> ClonePocketCarts;

		internal static ConfigEntry<bool> CloneDrones;

		internal static ConfigEntry<bool> CloneOrbs;

		internal static ConfigEntry<bool> CloneHealthPacks;

		internal static ConfigEntry<bool> CloneExplosives;

		internal static ConfigEntry<bool> CloneEnergyCrystals;

		internal static ConfigEntry<bool> CloneTrackers;

		internal static ConfigEntry<bool> CloneTools;

		internal static ConfigEntry<bool> CloneVehicles;

		internal static ConfigEntry<bool> CloneLaunchers;

		internal static ConfigEntry<bool> PreventCloneInExtractionPoint;

		internal static ConfigEntry<int> MaxUpgradesPerShop;

		internal static ConfigEntry<int> MaxMeleeWeaponsPerShop;

		internal static ConfigEntry<int> MaxGunsPerShop;

		internal static ConfigEntry<int> MaxCartsPerShop;

		internal static ConfigEntry<int> MaxPocketCartsPerShop;

		internal static ConfigEntry<int> MaxDronesPerShop;

		internal static ConfigEntry<int> MaxOrbsPerShop;

		internal static ConfigEntry<int> MaxHealthPacksPerShop;

		internal static ConfigEntry<int> MaxExplosivesPerShop;

		internal static ConfigEntry<int> MaxEnergyCrystalsPerShop;

		internal static ConfigEntry<int> MaxTrackersPerShop;

		internal static ConfigEntry<int> MaxToolsPerShop;

		internal static ConfigEntry<int> MaxVehiclesPerShop;

		internal static ConfigEntry<int> MaxLaunchersPerShop;

		internal static ConfigEntry<float> MinCloneIntervalSeconds;

		internal static ConfigEntry<bool> RequireImpactThreshold;

		internal static ConfigEntry<float> MinimumImpactForce;

		internal static ConfigEntry<bool> EnableDebugLog;

		private Harmony harmony;

		private void Awake()
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			try
			{
				Instance = this;
				LogSource = ((BaseUnityPlugin)this).Logger;
				((Component)this).gameObject.transform.parent = null;
				Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
				BindConfig();
				ShopItemCloneRuntime.EnsureCreated();
				harmony = new Harmony("REPOJP.ShopItemClone");
				harmony.PatchAll(typeof(ShopItemClonePlugin).Assembly);
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Shop Item Clone 4.0.0 loaded");
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Failure: Failed to initialize Shop Item Clone\n" + ex));
			}
		}

		private void OnDestroy()
		{
			try
			{
				if (harmony != null)
				{
					harmony.UnpatchSelf();
				}
			}
			catch
			{
			}
		}

		private void BindConfig()
		{
			//IL_020f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0219: Expected O, but got Unknown
			//IL_026c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0276: Expected O, but got Unknown
			CloneUpgrades = ((BaseUnityPlugin)this).Config.Bind<bool>("Category Toggle", "Clone Upgrades", true, "Clone item upgrades and player upgrades. アップグレード系アイテムの複製");
			CloneMeleeWeapons = ((BaseUnityPlugin)this).Config.Bind<bool>("Category Toggle", "Clone Melee Weapons", true, "Clone melee weapons. 近接武器の複製");
			CloneGuns = ((BaseUnityPlugin)this).Config.Bind<bool>("Category Toggle", "Clone Guns", true, "Clone guns. 銃の複製");
			CloneCarts = ((BaseUnityPlugin)this).Config.Bind<bool>("Category Toggle", "Clone Carts", true, "Clone carts. カートの複製");
			ClonePocketCarts = ((BaseUnityPlugin)this).Config.Bind<bool>("Category Toggle", "Clone Pocket Carts", true, "Clone pocket carts. ポケットカートの複製");
			CloneDrones = ((BaseUnityPlugin)this).Config.Bind<bool>("Category Toggle", "Clone Drones", true, "Clone drones. ドローンの複製");
			CloneOrbs = ((BaseUnityPlugin)this).Config.Bind<bool>("Category Toggle", "Clone Orbs", true, "Clone orbs. オーブの複製");
			CloneHealthPacks = ((BaseUnityPlugin)this).Config.Bind<bool>("Category Toggle", "Clone Health Packs", true, "Clone health packs. ヘルスパックの複製");
			CloneExplosives = ((BaseUnityPlugin)this).Config.Bind<bool>("Category Toggle", "Clone Explosives", true, "Clone grenades and mines. グレネードと地雷の複製");
			CloneEnergyCrystals = ((BaseUnityPlugin)this).Config.Bind<bool>("Category Toggle", "Clone Energy Crystals", true, "Clone energy crystals. エナジークリスタルの複製");
			CloneTrackers = ((BaseUnityPlugin)this).Config.Bind<bool>("Category Toggle", "Clone Trackers", true, "Clone trackers. トラッカー系アイテムの複製");
			CloneTools = ((BaseUnityPlugin)this).Config.Bind<bool>("Category Toggle", "Clone Tools", true, "Clone tools added in v0.4.0 and later. v0.4.0以降のツール系アイテムの複製");
			CloneVehicles = ((BaseUnityPlugin)this).Config.Bind<bool>("Category Toggle", "Clone Vehicles", true, "Clone vehicles added in v0.4.0 and later. v0.4.0以降の乗り物系アイテムの複製");
			CloneLaunchers = ((BaseUnityPlugin)this).Config.Bind<bool>("Category Toggle", "Clone Launchers", true, "Clone launchers and staff-like launcher items. ランチャーや杖系アイテムの複製");
			PreventCloneInExtractionPoint = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Prevent Clone In Extraction Point", true, "Prevent cloning while the held item is inside the extraction point. 納品所内にある手持ちアイテムの複製を禁止");
			MinCloneIntervalSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("General", "Min Clone Interval Seconds", 0.15f, new ConfigDescription("Minimum seconds between clones from the same source item. 同じ元アイテムから次に複製できるまでの最小秒数", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), Array.Empty<object>()));
			RequireImpactThreshold = ((BaseUnityPlugin)this).Config.Bind<bool>("Impact Requirement", "Require Impact Threshold", true, "Require impact force threshold instead of cloning on any ground contact. 地面接触だけでなく一定以上の衝撃を必要にする");
			MinimumImpactForce = ((BaseUnityPlugin)this).Config.Bind<float>("Impact Requirement", "Minimum Impact Force", 60f, new ConfigDescription("Minimum PhysGrabObjectImpactDetector impact force required for cloning. 20=Light, 80=Medium, 150=Heavy. 複製に必要な衝撃値 20=軽 80=中 150=強", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 200f), Array.Empty<object>()));
			MaxUpgradesPerShop = BindMax("Max Upgrades Per Shop", 0, "Max upgrade clones per shop. 0 means unlimited. ショップごとのアップグレード複製上限 0は無制限");
			MaxMeleeWeaponsPerShop = BindMax("Max Melee Weapons Per Shop", 0, "Max melee weapon clones per shop. 0 means unlimited. ショップごとの近接武器複製上限 0は無制限");
			MaxGunsPerShop = BindMax("Max Guns Per Shop", 0, "Max gun clones per shop. 0 means unlimited. ショップごとの銃複製上限 0は無制限");
			MaxCartsPerShop = BindMax("Max Carts Per Shop", 5, "Max cart clones per shop. 0 means unlimited. ショップごとのカート複製上限 0は無制限");
			MaxPocketCartsPerShop = BindMax("Max Pocket Carts Per Shop", 5, "Max pocket cart clones per shop. 0 means unlimited. ショップごとのポケットカート複製上限 0は無制限");
			MaxDronesPerShop = BindMax("Max Drones Per Shop", 0, "Max drone clones per shop. 0 means unlimited. ショップごとのドローン複製上限 0は無制限");
			MaxOrbsPerShop = BindMax("Max Orbs Per Shop", 0, "Max orb clones per shop. 0 means unlimited. ショップごとのオーブ複製上限 0は無制限");
			MaxHealthPacksPerShop = BindMax("Max Health Packs Per Shop", 0, "Max health pack clones per shop. 0 means unlimited. ショップごとのヘルスパック複製上限 0は無制限");
			MaxExplosivesPerShop = BindMax("Max Explosives Per Shop", 0, "Max explosive clones per shop. 0 means unlimited. ショップごとの爆破物複製上限 0は無制限");
			MaxEnergyCrystalsPerShop = BindMax("Max Energy Crystals Per Shop", 0, "Max energy crystal clones per shop. 0 means unlimited. ショップごとのエナジークリスタル複製上限 0は無制限");
			MaxTrackersPerShop = BindMax("Max Trackers Per Shop", 0, "Max tracker clones per shop. 0 means unlimited. ショップごとのトラッカー複製上限 0は無制限");
			MaxToolsPerShop = BindMax("Max Tools Per Shop", 0, "Max tool clones per shop. 0 means unlimited. ショップごとのツール複製上限 0は無制限");
			MaxVehiclesPerShop = BindMax("Max Vehicles Per Shop", 0, "Max vehicle clones per shop. 0 means unlimited. ショップごとの乗り物複製上限 0は無制限");
			MaxLaunchersPerShop = BindMax("Max Launchers Per Shop", 0, "Max launcher clones per shop. 0 means unlimited. ショップごとのランチャー複製上限 0は無制限");
			EnableDebugLog = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Enable Debug Log", false, "Enable detailed clone decision logs. 詳細な複製判定ログを出力");
		}

		private ConfigEntry<int> BindMax(string key, int defaultValue, string description)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			return ((BaseUnityPlugin)this).Config.Bind<int>("Category Max Per Shop", key, defaultValue, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 999), Array.Empty<object>()));
		}

		internal static void LogDebug(string message)
		{
			if (EnableDebugLog != null && EnableDebugLog.Value && LogSource != null)
			{
				LogSource.LogInfo((object)message);
			}
		}
	}
	internal sealed class ShopItemCloneRuntime : MonoBehaviour
	{
		private sealed class ContactState
		{
			internal PhysGrabObject Source;

			internal bool Latched;

			internal float LastValidGroundContactTime;

			internal float LastCloneTime;
		}

		[CompilerGenerated]
		private sealed class <FinalizeCloneAfterStart>d__52 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public PhysGrabObject source;

			public ItemAttributes sourceItem;

			public GameObject cloneObject;

			public ShopItemCloneRuntime <>4__this;

			private ItemAttributes <cloneItem>5__1;

			private PhotonView <clonePhotonView>5__2;

			private Rigidbody <sourceRb>5__3;

			private Rigidbody <cloneRb>5__4;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <FinalizeCloneAfterStart>d__52(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<cloneItem>5__1 = null;
				<clonePhotonView>5__2 = null;
				<sourceRb>5__3 = null;
				<cloneRb>5__4 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>2__current = null;
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					<>2__current = null;
					<>1__state = 2;
					return true;
				case 2:
					<>1__state = -1;
					if ((Object)(object)source == (Object)null || (Object)(object)sourceItem == (Object)null || (Object)(object)cloneObject == (Object)null)
					{
						return false;
					}
					<cloneItem>5__1 = cloneObject.GetComponent<ItemAttributes>();
					<clonePhotonView>5__2 = cloneObject.GetComponent<PhotonView>();
					<sourceRb>5__3 = source.rb;
					<cloneRb>5__4 = cloneObject.GetComponent<Rigidbody>();
					if ((Object)(object)<cloneItem>5__1 == (Object)null)
					{
						return false;
					}
					TryApplyInitialMotion(<sourceRb>5__3, <cloneRb>5__4);
					CopyItemValue(sourceItem, <cloneItem>5__1, <clonePhotonView>5__2);
					CopyToggleState(source, cloneObject);
					CopyBatteryState(source, cloneObject, <clonePhotonView>5__2);
					return false;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		[CompilerGenerated]
		private sealed class <IgnoreCloneCollisionTemporary>d__56 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public GameObject sourceObject;

			public GameObject cloneObject;

			public float duration;

			public ShopItemCloneRuntime <>4__this;

			private Collider[] <sourceColliders>5__1;

			private Collider[] <cloneColliders>5__2;

			private int <i>5__3;

			private Collider <sourceCollider>5__4;

			private int <j>5__5;

			private Collider <cloneCollider>5__6;

			private int <i>5__7;

			private Collider <sourceCollider>5__8;

			private int <j>5__9;

			private Collider <cloneCollider>5__10;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <IgnoreCloneCollisionTemporary>d__56(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<sourceColliders>5__1 = null;
				<cloneColliders>5__2 = null;
				<sourceCollider>5__4 = null;
				<cloneCollider>5__6 = null;
				<sourceCollider>5__8 = null;
				<cloneCollider>5__10 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_014b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0155: Expected O, but got Unknown
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					if ((Object)(object)sourceObject == (Object)null || (Object)(object)cloneObject == (Object)null)
					{
						return false;
					}
					<sourceColliders>5__1 = sourceObject.GetComponentsInChildren<Collider>(true);
					<cloneColliders>5__2 = cloneObject.GetComponentsInChildren<Collider>(true);
					<i>5__3 = 0;
					while (<i>5__3 < <sourceColliders>5__1.Length)
					{
						<sourceCollider>5__4 = <sourceColliders>5__1[<i>5__3];
						if (!((Object)(object)<sourceCollider>5__4 == (Object)null))
						{
							<j>5__5 = 0;
							while (<j>5__5 < <cloneColliders>5__2.Length)
							{
								<cloneCollider>5__6 = <cloneColliders>5__2[<j>5__5];
								if (!((Object)(object)<cloneCollider>5__6 == (Object)null))
								{
									Physics.IgnoreCollision(<sourceCollider>5__4, <cloneCollider>5__6, true);
									<cloneCollider>5__6 = null;
								}
								<j>5__5++;
							}
							<sourceCollider>5__4 = null;
						}
						<i>5__3++;
					}
					<>2__current = (object)new WaitForSeconds(duration);
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					if ((Object)(object)sourceObject == (Object)null || (Object)(object)cloneObject == (Object)null)
					{
						return false;
					}
					<i>5__7 = 0;
					while (<i>5__7 < <sourceColliders>5__1.Length)
					{
						<sourceCollider>5__8 = <sourceColliders>5__1[<i>5__7];
						if (!((Object)(object)<sourceCollider>5__8 == (Object)null))
						{
							<j>5__9 = 0;
							while (<j>5__9 < <cloneColliders>5__2.Length)
							{
								<cloneCollider>5__10 = <cloneColliders>5__2[<j>5__9];
								if (!((Object)(object)<cloneCollider>5__10 == (Object)null))
								{
									Physics.IgnoreCollision(<sourceCollider>5__8, <cloneCollider>5__10, false);
									<cloneCollider>5__10 = null;
								}
								<j>5__9++;
							}
							<sourceCollider>5__8 = null;
						}
						<i>5__7++;
					}
					return false;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		private const float DefaultCloneHeightOffset = 0.01f;

		private const float CartCloneHeightOffset = 1f;

		private const float VehicleCloneHeightOffset = 1f;

		private const float CloneCollisionIgnoreSeconds = 0.2f;

		private const float CloneImpactDisableSeconds = 0.2f;

		private const float RearmNoContactSeconds = 0.08f;

		private const float GroundNormalMinY = 0.35f;

		private const string CategoryUpgrades = "Upgrades";

		private const string CategoryMeleeWeapons = "MeleeWeapons";

		private const string CategoryGuns = "Guns";

		private const string CategoryCarts = "Carts";

		private const string CategoryPocketCarts = "PocketCarts";

		private const string CategoryDrones = "Drones";

		private const string CategoryOrbs = "Orbs";

		private const string CategoryHealthPacks = "HealthPacks";

		private const string CategoryExplosives = "Explosives";

		private const string CategoryEnergyCrystals = "EnergyCrystals";

		private const string CategoryTrackers = "Trackers";

		private const string CategoryTools = "Tools";

		private const string CategoryVehicles = "Vehicles";

		private const string CategoryLaunchers = "Launchers";

		private const string CategoryUnknown = "Unknown";

		private static ShopItemCloneRuntime instance;

		private static readonly FieldInfo ItemValueField = AccessTools.Field(typeof(ItemAttributes), "value");

		private static readonly FieldInfo InExtractionPointField = AccessTools.Field(typeof(RoomVolumeCheck), "inExtractionPoint");

		private static readonly FieldInfo ImpactForceField = AccessTools.Field(typeof(PhysGrabObjectImpactDetector), "impactForce");

		private static readonly MethodInfo BatteryFullPercentChangeMethod = AccessTools.Method(typeof(ItemBattery), "BatteryFullPercentChange", (Type[])null, (Type[])null);

		private readonly Dictionary<int, ContactState> contactStates = new Dictionary<int, ContactState>();

		private readonly Dictionary<string, int> categoryCloneCounts = new Dictionary<string, int>();

		private bool wasShopMainPhase;

		internal static ShopItemCloneRuntime Instance => instance;

		internal 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("ShopItemCloneRuntime");
				val.transform.parent = null;
				((Object)val).hideFlags = (HideFlags)61;
				Object.DontDestroyOnLoad((Object)(object)val);
				instance = val.AddComponent<ShopItemCloneRuntime>();
			}
		}

		private void Update()
		{
			try
			{
				bool flag = IsShopMainPhase();
				if (flag && !wasShopMainPhase)
				{
					ResetShopCounters();
				}
				else if (!flag && wasShopMainPhase)
				{
					ResetShopCounters();
					contactStates.Clear();
				}
				wasShopMainPhase = flag;
				if (contactStates.Count == 0)
				{
					return;
				}
				float unscaledTime = Time.unscaledTime;
				List<int> removeKeys = null;
				foreach (KeyValuePair<int, ContactState> contactState in contactStates)
				{
					ContactState value = contactState.Value;
					PhysGrabObject source = value.Source;
					if ((Object)(object)source == (Object)null)
					{
						AddRemoveKey(ref removeKeys, contactState.Key);
						continue;
					}
					bool flag2 = IsActuallyHeld(source);
					bool flag3 = IsShopMainPhase();
					if (!flag2 || !flag3)
					{
						AddRemoveKey(ref removeKeys, contactState.Key);
					}
					else if (unscaledTime - value.LastValidGroundContactTime > 0.08f)
					{
						value.Latched = false;
					}
				}
				if (removeKeys != null)
				{
					for (int i = 0; i < removeKeys.Count; i++)
					{
						contactStates.Remove(removeKeys[i]);
					}
				}
			}
			catch (Exception ex)
			{
				if (ShopItemClonePlugin.LogSource != null)
				{
					ShopItemClonePlugin.LogSource.LogError((object)("Failure: Runtime update failed\n" + ex));
				}
			}
		}

		private static void AddRemoveKey(ref List<int> removeKeys, int key)
		{
			if (removeKeys == null)
			{
				removeKeys = new List<int>();
			}
			removeKeys.Add(key);
		}

		internal void OnImpactStay(PhysGrabObjectImpactDetector detector, Collision collision)
		{
			try
			{
				if ((Object)(object)detector == (Object)null || collision == null || !IsHostAuthority() || !IsShopMainPhase())
				{
					return;
				}
				PhysGrabObject component = ((Component)detector).GetComponent<PhysGrabObject>();
				if ((Object)(object)component == (Object)null)
				{
					return;
				}
				ItemAttributes component2 = ((Component)component).GetComponent<ItemAttributes>();
				if ((Object)(object)component2 == (Object)null || (Object)(object)component2.item == (Object)null || (Object)(object)((Component)component).GetComponent<ValuableObject>() != (Object)null)
				{
					return;
				}
				string categoryKey = GetCategoryKey(component2);
				if (!IsCategoryEnabled(categoryKey) || !IsActuallyHeld(component) || (ShopItemClonePlugin.PreventCloneInExtractionPoint != null && ShopItemClonePlugin.PreventCloneInExtractionPoint.Value && IsInsideExtractionPoint(component)) || !IsGroundLikeEnvironmentCollision(collision))
				{
					return;
				}
				int instanceID = ((Object)component).GetInstanceID();
				if (!contactStates.TryGetValue(instanceID, out var value))
				{
					value = new ContactState();
					value.Source = component;
					value.LastCloneTime = -999f;
					contactStates[instanceID] = value;
				}
				float num = (value.LastValidGroundContactTime = Time.unscaledTime);
				if (value.Latched)
				{
					return;
				}
				float num2 = Mathf.Clamp(ShopItemClonePlugin.MinCloneIntervalSeconds.Value, 0f, 5f);
				if (num - value.LastCloneTime < num2)
				{
					return;
				}
				float impactForce = GetImpactForce(detector, collision);
				if (ShopItemClonePlugin.RequireImpactThreshold != null && ShopItemClonePlugin.RequireImpactThreshold.Value)
				{
					float num3 = Mathf.Clamp(ShopItemClonePlugin.MinimumImpactForce.Value, 0f, 200f);
					if (impactForce < num3)
					{
						ShopItemClonePlugin.LogDebug("Impact skipped category=" + categoryKey + " force=" + impactForce.ToString("0.00") + " required=" + num3.ToString("0.00"));
						return;
					}
				}
				if (!CanCloneCategoryThisShop(categoryKey))
				{
					ShopItemClonePlugin.LogDebug("Category max skipped category=" + categoryKey);
					return;
				}
				value.Latched = true;
				value.LastCloneTime = num;
				CloneHeldShopItem(component, component2, categoryKey);
			}
			catch (Exception ex)
			{
				if (ShopItemClonePlugin.LogSource != null)
				{
					ShopItemClonePlugin.LogSource.LogError((object)("Failure: Impact handling failed\n" + ex));
				}
			}
		}

		private static bool IsHostAuthority()
		{
			try
			{
				if (!GameManager.Multiplayer())
				{
					return true;
				}
			}
			catch
			{
				if (!PhotonNetwork.InRoom)
				{
					return true;
				}
			}
			return PhotonNetwork.IsMasterClient;
		}

		private static bool IsShopMainPhase()
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Invalid comparison between Unknown and I4
			try
			{
				if (!SemiFunc.RunIsShop())
				{
					return false;
				}
				if ((Object)(object)GameDirector.instance == (Object)null)
				{
					return false;
				}
				return (int)GameDirector.instance.currentState == 2;
			}
			catch
			{
				return false;
			}
		}

		private static string GetCategoryKey(ItemAttributes itemAttributes)
		{
			if ((Object)(object)itemAttributes == (Object)null || (Object)(object)itemAttributes.item == (Object)null)
			{
				return "Unknown";
			}
			string empty = string.Empty;
			try
			{
				empty = ((object)(itemType)(ref itemAttributes.item.itemType)).ToString();
			}
			catch
			{
				empty = string.Empty;
			}
			if (empty == "item_upgrade" || empty == "player_upgrade")
			{
				return "Upgrades";
			}
			switch (empty)
			{
			case "melee":
				return "MeleeWeapons";
			case "gun":
				return "Guns";
			case "cart":
				return "Carts";
			case "pocket_cart":
				return "PocketCarts";
			case "drone":
				return "Drones";
			case "orb":
				return "Orbs";
			case "healthPack":
				return "HealthPacks";
			default:
				if (!(empty == "mine"))
				{
					return empty switch
					{
						"power_crystal" => "EnergyCrystals", 
						"tracker" => "Trackers", 
						"tool" => "Tools", 
						"vehicle" => "Vehicles", 
						"launcher" => "Launchers", 
						_ => "Unknown", 
					};
				}
				goto case "grenade";
			case "grenade":
				return "Explosives";
			}
		}

		private static bool IsCategoryEnabled(string category)
		{
			return category switch
			{
				"Upgrades" => ShopItemClonePlugin.CloneUpgrades != null && ShopItemClonePlugin.CloneUpgrades.Value, 
				"MeleeWeapons" => ShopItemClonePlugin.CloneMeleeWeapons != null && ShopItemClonePlugin.CloneMeleeWeapons.Value, 
				"Guns" => ShopItemClonePlugin.CloneGuns != null && ShopItemClonePlugin.CloneGuns.Value, 
				"Carts" => ShopItemClonePlugin.CloneCarts != null && ShopItemClonePlugin.CloneCarts.Value, 
				"PocketCarts" => ShopItemClonePlugin.ClonePocketCarts != null && ShopItemClonePlugin.ClonePocketCarts.Value, 
				"Drones" => ShopItemClonePlugin.CloneDrones != null && ShopItemClonePlugin.CloneDrones.Value, 
				"Orbs" => ShopItemClonePlugin.CloneOrbs != null && ShopItemClonePlugin.CloneOrbs.Value, 
				"HealthPacks" => ShopItemClonePlugin.CloneHealthPacks != null && ShopItemClonePlugin.CloneHealthPacks.Value, 
				"Explosives" => ShopItemClonePlugin.CloneExplosives != null && ShopItemClonePlugin.CloneExplosives.Value, 
				"EnergyCrystals" => ShopItemClonePlugin.CloneEnergyCrystals != null && ShopItemClonePlugin.CloneEnergyCrystals.Value, 
				"Trackers" => ShopItemClonePlugin.CloneTrackers != null && ShopItemClonePlugin.CloneTrackers.Value, 
				"Tools" => ShopItemClonePlugin.CloneTools != null && ShopItemClonePlugin.CloneTools.Value, 
				"Vehicles" => ShopItemClonePlugin.CloneVehicles != null && ShopItemClonePlugin.CloneVehicles.Value, 
				"Launchers" => ShopItemClonePlugin.CloneLaunchers != null && ShopItemClonePlugin.CloneLaunchers.Value, 
				_ => false, 
			};
		}

		private bool CanCloneCategoryThisShop(string category)
		{
			int categoryMax = GetCategoryMax(category);
			if (categoryMax <= 0)
			{
				return true;
			}
			if (!categoryCloneCounts.TryGetValue(category, out var value))
			{
				value = 0;
			}
			return value < categoryMax;
		}

		private void RegisterCategoryClone(string category)
		{
			if (!categoryCloneCounts.TryGetValue(category, out var value))
			{
				value = 0;
			}
			categoryCloneCounts[category] = value + 1;
		}

		private static int GetCategoryMax(string category)
		{
			return category switch
			{
				"Upgrades" => ShopItemClonePlugin.MaxUpgradesPerShop.Value, 
				"MeleeWeapons" => ShopItemClonePlugin.MaxMeleeWeaponsPerShop.Value, 
				"Guns" => ShopItemClonePlugin.MaxGunsPerShop.Value, 
				"Carts" => ShopItemClonePlugin.MaxCartsPerShop.Value, 
				"PocketCarts" => ShopItemClonePlugin.MaxPocketCartsPerShop.Value, 
				"Drones" => ShopItemClonePlugin.MaxDronesPerShop.Value, 
				"Orbs" => ShopItemClonePlugin.MaxOrbsPerShop.Value, 
				"HealthPacks" => ShopItemClonePlugin.MaxHealthPacksPerShop.Value, 
				"Explosives" => ShopItemClonePlugin.MaxExplosivesPerShop.Value, 
				"EnergyCrystals" => ShopItemClonePlugin.MaxEnergyCrystalsPerShop.Value, 
				"Trackers" => ShopItemClonePlugin.MaxTrackersPerShop.Value, 
				"Tools" => ShopItemClonePlugin.MaxToolsPerShop.Value, 
				"Vehicles" => ShopItemClonePlugin.MaxVehiclesPerShop.Value, 
				"Launchers" => ShopItemClonePlugin.MaxLaunchersPerShop.Value, 
				_ => 0, 
			};
		}

		private static bool IsActuallyHeld(PhysGrabObject source)
		{
			if ((Object)(object)source == (Object)null)
			{
				return false;
			}
			if (!source.grabbed)
			{
				return false;
			}
			List<PhysGrabber> playerGrabbing = source.playerGrabbing;
			if (playerGrabbing == null || playerGrabbing.Count == 0)
			{
				return false;
			}
			for (int i = 0; i < playerGrabbing.Count; i++)
			{
				PhysGrabber val = playerGrabbing[i];
				if (!((Object)(object)val == (Object)null) && val.grabbed && !((Object)(object)val.grabbedPhysGrabObject != (Object)(object)source))
				{
					return true;
				}
			}
			return false;
		}

		private static bool IsInsideExtractionPoint(PhysGrabObject source)
		{
			if ((Object)(object)source == (Object)null)
			{
				return false;
			}
			RoomVolumeCheck val = ((Component)source).GetComponent<RoomVolumeCheck>();
			if ((Object)(object)val == (Object)null)
			{
				val = ((Component)source).GetComponentInChildren<RoomVolumeCheck>();
			}
			if ((Object)(object)val == (Object)null)
			{
				val = ((Component)source).GetComponentInParent<RoomVolumeCheck>();
			}
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			try
			{
				val.CheckSet();
				if (InExtractionPointField != null)
				{
					object value = InExtractionPointField.GetValue(val);
					if (value is bool)
					{
						return (bool)value;
					}
				}
			}
			catch
			{
			}
			return false;
		}

		private static bool IsGroundLikeEnvironmentCollision(Collision collision)
		{
			//IL_00f5: 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)
			if (collision == null)
			{
				return false;
			}
			Transform transform = collision.transform;
			if ((Object)(object)transform == (Object)null)
			{
				return false;
			}
			MaterialSurface val = ((Component)transform).GetComponent<MaterialSurface>();
			if ((Object)(object)val == (Object)null)
			{
				val = ((Component)transform).GetComponentInParent<MaterialSurface>();
			}
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			if ((Object)(object)((Component)transform).GetComponentInParent<PhysGrabObject>() != (Object)null)
			{
				return false;
			}
			if (((Component)transform).CompareTag("Player") || (Object)(object)((Component)transform).GetComponentInParent<PlayerAvatar>() != (Object)null)
			{
				return false;
			}
			if ((Object)(object)((Component)transform).GetComponentInParent<EnemyRigidbody>() != (Object)null)
			{
				return false;
			}
			if ((Object)(object)((Component)transform).GetComponentInParent<PhysGrabCart>() != (Object)null)
			{
				return false;
			}
			float num = -1f;
			ContactPoint[] contacts = collision.contacts;
			for (int i = 0; i < contacts.Length; i++)
			{
				if (((ContactPoint)(ref contacts[i])).normal.y > num)
				{
					num = ((ContactPoint)(ref contacts[i])).normal.y;
				}
			}
			return num >= 0.35f;
		}

		private static float GetImpactForce(PhysGrabObjectImpactDetector detector, Collision collision)
		{
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			float num = 0f;
			try
			{
				if (ImpactForceField != null && (Object)(object)detector != (Object)null)
				{
					object value = ImpactForceField.GetValue(detector);
					if (value is float)
					{
						num = (float)value;
					}
				}
			}
			catch
			{
				num = 0f;
			}
			if (num > 0.01f)
			{
				return num;
			}
			try
			{
				Rigidbody val = (((Object)(object)detector != (Object)null) ? ((Component)detector).GetComponent<Rigidbody>() : null);
				float num2 = (((Object)(object)val != (Object)null) ? Mathf.Max(val.mass, 1f) : 1f);
				Vector3 relativeVelocity = collision.relativeVelocity;
				num = ((Vector3)(ref relativeVelocity)).magnitude * num2 * 10f;
			}
			catch
			{
				num = 0f;
			}
			return num;
		}

		private static bool IsCartLikeCategory(string category)
		{
			return category == "Carts" || category == "PocketCarts" || category == "Vehicles";
		}

		private Vector3 GetCloneSpawnPosition(PhysGrabObject source, ItemAttributes sourceItem, string category)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			float num = 0.01f;
			if (category == "Carts" || category == "PocketCarts")
			{
				num = 1f;
			}
			else if (category == "Vehicles")
			{
				num = 1f;
			}
			return ((Component)source).transform.position + Vector3.up * num;
		}

		private void ResetShopCounters()
		{
			categoryCloneCounts.Clear();
		}

		private void CloneHeldShopItem(PhysGrabObject source, ItemAttributes sourceItem, string category)
		{
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: 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_0101: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)source == (Object)null || (Object)(object)sourceItem == (Object)null || (Object)(object)sourceItem.item == (Object)null || !CanCloneCategoryThisShop(category))
			{
				return;
			}
			string text = null;
			if (sourceItem.item.prefab != null)
			{
				text = sourceItem.item.prefab.ResourcePath;
			}
			if (string.IsNullOrEmpty(text))
			{
				if (ShopItemClonePlugin.LogSource != null)
				{
					ShopItemClonePlugin.LogSource.LogWarning((object)("Skipped clone because resource path was missing: " + ((Object)source).name));
				}
				return;
			}
			Vector3 cloneSpawnPosition = GetCloneSpawnPosition(source, sourceItem, category);
			Quaternion rotation = ((Component)source).transform.rotation;
			GameObject val = null;
			if (GameManager.Multiplayer())
			{
				val = PhotonNetwork.InstantiateRoomObject(text, cloneSpawnPosition, rotation, (byte)0, (object[])null);
			}
			else
			{
				GameObject prefab = sourceItem.item.prefab.Prefab;
				if ((Object)(object)prefab != (Object)null)
				{
					val = Object.Instantiate<GameObject>(prefab, cloneSpawnPosition, rotation);
				}
			}
			if (!((Object)(object)val == (Object)null))
			{
				RegisterCategoryClone(category);
				Rigidbody rb = source.rb;
				Rigidbody component = val.GetComponent<Rigidbody>();
				TryApplyInitialMotion(rb, component);
				PhysGrabObjectImpactDetector component2 = val.GetComponent<PhysGrabObjectImpactDetector>();
				if ((Object)(object)component2 != (Object)null)
				{
					component2.ImpactDisable(0.2f);
				}
				((MonoBehaviour)this).StartCoroutine(FinalizeCloneAfterStart(source, sourceItem, val));
				((MonoBehaviour)this).StartCoroutine(IgnoreCloneCollisionTemporary(((Component)source).gameObject, val, 0.2f));
				ShopItemClonePlugin.LogDebug("Cloned item=" + ((Object)source).name + " category=" + category);
			}
		}

		private static void TryApplyInitialMotion(Rigidbody sourceRb, Rigidbody cloneRb)
		{
			//IL_002c: 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)
			if ((Object)(object)sourceRb == (Object)null || (Object)(object)cloneRb == (Object)null || cloneRb.isKinematic)
			{
				return;
			}
			try
			{
				cloneRb.velocity = sourceRb.velocity;
				cloneRb.angularVelocity = sourceRb.angularVelocity;
			}
			catch
			{
			}
		}

		[IteratorStateMachine(typeof(<FinalizeCloneAfterStart>d__52))]
		private IEnumerator FinalizeCloneAfterStart(PhysGrabObject source, ItemAttributes sourceItem, GameObject cloneObject)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <FinalizeCloneAfterStart>d__52(0)
			{
				<>4__this = this,
				source = source,
				sourceItem = sourceItem,
				cloneObject = cloneObject
			};
		}

		private static void CopyItemValue(ItemAttributes sourceItem, ItemAttributes cloneItem, PhotonView clonePhotonView)
		{
			if (ItemValueField == null || (Object)(object)sourceItem == (Object)null || (Object)(object)cloneItem == (Object)null)
			{
				return;
			}
			try
			{
				object value = ItemValueField.GetValue(sourceItem);
				if (value is int)
				{
					int num = (int)value;
					if (GameManager.Multiplayer() && (Object)(object)clonePhotonView != (Object)null)
					{
						clonePhotonView.RPC("GetValueRPC", (RpcTarget)0, new object[1] { num });
					}
					else
					{
						cloneItem.GetValueRPC(num);
					}
				}
			}
			catch
			{
			}
		}

		private static void CopyToggleState(PhysGrabObject source, GameObject cloneObject)
		{
			if ((Object)(object)source == (Object)null || (Object)(object)cloneObject == (Object)null)
			{
				return;
			}
			try
			{
				ItemToggle component = ((Component)source).GetComponent<ItemToggle>();
				ItemToggle component2 = cloneObject.GetComponent<ItemToggle>();
				if ((Object)(object)component != (Object)null && (Object)(object)component2 != (Object)null && component2.toggleState != component.toggleState)
				{
					component2.ToggleItem(component.toggleState, -1);
				}
			}
			catch
			{
			}
		}

		private static void CopyBatteryState(PhysGrabObject source, GameObject cloneObject, PhotonView clonePhotonView)
		{
			if ((Object)(object)source == (Object)null || (Object)(object)cloneObject == (Object)null)
			{
				return;
			}
			try
			{
				ItemBattery component = ((Component)source).GetComponent<ItemBattery>();
				ItemBattery component2 = cloneObject.GetComponent<ItemBattery>();
				if (!((Object)(object)component == (Object)null) && !((Object)(object)component2 == (Object)null) && component2.batteryBars > 0)
				{
					int num = Mathf.Clamp(Mathf.RoundToInt(component.batteryLife / (100f / (float)component2.batteryBars)), 0, component2.batteryBars);
					if (GameManager.Multiplayer() && (Object)(object)clonePhotonView != (Object)null)
					{
						clonePhotonView.RPC("BatteryFullPercentChangeRPC", (RpcTarget)0, new object[2] { num, false });
					}
					else if (BatteryFullPercentChangeMethod != null)
					{
						BatteryFullPercentChangeMethod.Invoke(component2, new object[2] { num, false });
					}
				}
			}
			catch
			{
			}
		}

		[IteratorStateMachine(typeof(<IgnoreCloneCollisionTemporary>d__56))]
		private IEnumerator IgnoreCloneCollisionTemporary(GameObject sourceObject, GameObject cloneObject, float duration)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <IgnoreCloneCollisionTemporary>d__56(0)
			{
				<>4__this = this,
				sourceObject = sourceObject,
				cloneObject = cloneObject,
				duration = duration
			};
		}
	}
	[HarmonyPatch(typeof(PhysGrabObjectImpactDetector), "OnCollisionStay")]
	internal static class PhysGrabObjectImpactDetectorPatch
	{
		private static void Postfix(PhysGrabObjectImpactDetector __instance, Collision collision)
		{
			ShopItemCloneRuntime instance = ShopItemCloneRuntime.Instance;
			if (!((Object)(object)instance == (Object)null))
			{
				instance.OnImpactStay(__instance, collision);
			}
		}
	}
}