Decompiled source of EnemyOrbMerge v4.0.0

EnemyOrbMerge.dll

Decompiled a month 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 HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using UnityEngine;
using UnityEngine.SceneManagement;

[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.EnemyOrbMerge
{
	[BepInPlugin("REPOJP.EnemyOrbMerge", "EnemyOrbMerge", "4.0.0")]
	public sealed class EnemyOrbMergePlugin : BaseUnityPlugin
	{
		private struct PendingPair
		{
			public readonly int FirstId;

			public readonly int SecondId;

			public readonly ulong Key;

			public PendingPair(int firstId, int secondId, ulong key)
			{
				FirstId = firstId;
				SecondId = secondId;
				Key = key;
			}
		}

		private sealed class OrbEntry
		{
			public readonly EnemyValuable EnemyValuable;

			public readonly ValuableObject ValuableObject;

			public readonly PhysGrabObject PhysGrabObject;

			public readonly PhysGrabObjectImpactDetector ImpactDetector;

			public readonly PhotonView PhotonView;

			public readonly GameObject GameObject;

			public readonly int Id;

			public readonly int SortToken;

			public readonly float RegisteredTime;

			public readonly List<Collider> Colliders = new List<Collider>();

			public int MergeCount;

			public float NextMergeAllowedTime;

			public float InvincibilityEffectEndTime;

			public float NextInvincibilityEffectRefreshTime;

			public Bounds CachedBounds;

			public bool HasCachedBounds;

			public float CachedRadius;

			private OrbEntry(EnemyValuable enemyValuable)
			{
				//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
				EnemyValuable = enemyValuable;
				GameObject = ((Component)enemyValuable).gameObject;
				ValuableObject = ((Component)enemyValuable).GetComponent<ValuableObject>();
				PhysGrabObject = ((Component)enemyValuable).GetComponent<PhysGrabObject>();
				ImpactDetector = ((Component)enemyValuable).GetComponent<PhysGrabObjectImpactDetector>();
				PhotonView = ((Component)enemyValuable).GetComponent<PhotonView>();
				Id = (((Object)(object)PhotonView != (Object)null && PhotonView.ViewID > 0) ? PhotonView.ViewID : ((Object)GameObject).GetInstanceID());
				SortToken = Id;
				RegisteredTime = Time.time;
				MergeCount = 0;
				NextMergeAllowedTime = 0f;
				InvincibilityEffectEndTime = 0f;
				NextInvincibilityEffectRefreshTime = 0f;
				CachedBounds = default(Bounds);
				HasCachedBounds = false;
				CachedRadius = 0f;
				RefreshColliders();
				UpdateCachedBounds();
			}

			public static OrbEntry Create(EnemyValuable enemyValuable)
			{
				if ((Object)(object)enemyValuable == (Object)null)
				{
					return null;
				}
				ValuableObject component = ((Component)enemyValuable).GetComponent<ValuableObject>();
				PhysGrabObject component2 = ((Component)enemyValuable).GetComponent<PhysGrabObject>();
				if ((Object)(object)component == (Object)null || (Object)(object)component2 == (Object)null)
				{
					return null;
				}
				return new OrbEntry(enemyValuable);
			}

			public void RefreshColliders()
			{
				Colliders.Clear();
				if ((Object)(object)GameObject == (Object)null)
				{
					return;
				}
				Collider[] componentsInChildren = GameObject.GetComponentsInChildren<Collider>(true);
				foreach (Collider val in componentsInChildren)
				{
					if (IsColliderUsable(val))
					{
						Colliders.Add(val);
					}
				}
			}

			public void UpdateCachedBounds()
			{
				//IL_003c: 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_0045: Unknown result type (might be due to invalid IL or missing references)
				//IL_004a: 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)
				HasCachedBounds = TryGetCombinedBounds(Colliders, out var bounds);
				if (!HasCachedBounds)
				{
					CachedBounds = default(Bounds);
					CachedRadius = 0f;
				}
				else
				{
					CachedBounds = bounds;
					Vector3 extents = ((Bounds)(ref bounds)).extents;
					CachedRadius = ((Vector3)(ref extents)).magnitude;
				}
			}

			public string Describe()
			{
				string text = (((Object)(object)GameObject != (Object)null) ? ((Object)GameObject).name : "null");
				return text + "#" + Id + "(mergeCount=" + MergeCount + ")";
			}
		}

		[HarmonyPatch(typeof(EnemyValuable), "Start")]
		private static class EnemyValuableStartPatch
		{
			private static void Postfix(EnemyValuable __instance)
			{
				EnemyOrbMergePlugin instance = Instance;
				if (!((Object)(object)instance == (Object)null))
				{
					instance.RegisterEnemyOrb(__instance);
				}
			}
		}

		[HarmonyPatch(typeof(PhysGrabObjectImpactDetector), "OnCollisionStay")]
		private static class PhysGrabObjectImpactDetectorOnCollisionStayPatch
		{
			private static bool Prefix(PhysGrabObjectImpactDetector __instance, Collision collision)
			{
				EnemyOrbMergePlugin instance = Instance;
				if ((Object)(object)instance == (Object)null)
				{
					return true;
				}
				if (instance.ShouldIgnoreOrbToOrbBreak(__instance, collision))
				{
					instance.LogDebug("Queued orb-to-orb merge pair");
					return false;
				}
				return true;
			}
		}

		public const string PluginGuid = "REPOJP.EnemyOrbMerge";

		public const string PluginName = "EnemyOrbMerge";

		public const string PluginVersion = "4.0.0";

		internal static EnemyOrbMergePlugin Instance;

		private static readonly FieldInfo ValuableCurrentField = AccessTools.Field(typeof(ValuableObject), "dollarValueCurrent");

		private static readonly FieldInfo ValuableOriginalField = AccessTools.Field(typeof(ValuableObject), "dollarValueOriginal");

		private static readonly FieldInfo ValuableSetField = AccessTools.Field(typeof(ValuableObject), "dollarValueSet");

		private static readonly FieldInfo EnemyValuableIndestructibleTimerField = AccessTools.Field(typeof(EnemyValuable), "indestructibleTimer");

		private static readonly FieldInfo EnemyValuableIndestructibleLerpField = AccessTools.Field(typeof(EnemyValuable), "indestructibleLerp");

		private static readonly FieldInfo EnemyValuableOuterMaterialField = AccessTools.Field(typeof(EnemyValuable), "outerMaterial");

		private static readonly FieldInfo ImpactDetectorIndestructibleSpawnTimerField = AccessTools.Field(typeof(PhysGrabObjectImpactDetector), "indestructibleSpawnTimer");

		private static readonly FieldInfo ImpactDetectorIsIndestructibleField = AccessTools.Field(typeof(PhysGrabObjectImpactDetector), "isIndestructible");

		private Harmony harmony;

		private ConfigEntry<float> mergeCheckInterval;

		private ConfigEntry<float> mergeArmDelay;

		private ConfigEntry<float> remergeCooldown;

		private ConfigEntry<float> contactSkin;

		private ConfigEntry<int> maxOrbValue;

		private ConfigEntry<int> maxOrbMergeCount;

		private ConfigEntry<int> maxQueuedPairsPerTick;

		private ConfigEntry<float> fallbackPairScanInterval;

		private ConfigEntry<float> indestructibleTargetCacheInterval;

		private ConfigEntry<bool> enableFallbackPairScan;

		private ConfigEntry<bool> debugLog;

		private ConfigEntry<bool> playNetworkMergeEffect;

		private ConfigEntry<bool> playInvincibilityElectricityEffect;

		private readonly Dictionary<int, OrbEntry> trackedOrbs = new Dictionary<int, OrbEntry>();

		private readonly HashSet<int> busyOrbIds = new HashSet<int>();

		private readonly List<int> removeBuffer = new List<int>();

		private readonly List<OrbEntry> scanBuffer = new List<OrbEntry>();

		private readonly Queue<PendingPair> pendingPairQueue = new Queue<PendingPair>(32);

		private readonly HashSet<ulong> pendingPairKeys = new HashSet<ulong>();

		private readonly HashSet<PhysGrabObject> indestructibleTargets = new HashSet<PhysGrabObject>();

		private float mergeCheckTimer;

		private float rescanTimer;

		private float fallbackPairScanTimer;

		private float nextIndestructibleTargetCacheRefreshTime;

		private Type itemOrbIndestructibleType;

		private Type itemOrbType;

		private Type itemDroneIndestructibleType;

		private Type itemDroneType;

		private FieldInfo itemOrbIndestructibleItemOrbField;

		private FieldInfo itemOrbItemActiveField;

		private FieldInfo itemOrbObjectAffectedField;

		private FieldInfo itemDroneIndestructibleItemDroneField;

		private FieldInfo itemDroneItemActivatedField;

		private FieldInfo itemDroneMagnetActiveField;

		private FieldInfo itemDroneMagnetTargetPhysGrabObjectField;

		private void Awake()
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Expected O, but got Unknown
			try
			{
				Instance = this;
				((Component)this).transform.parent = null;
				((Object)((Component)this).gameObject).hideFlags = (HideFlags)61;
				Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
				BindConfig();
				CacheIndestructibleReflection();
				harmony = new Harmony("REPOJP.EnemyOrbMerge");
				harmony.PatchAll();
				SceneManager.sceneLoaded += OnSceneLoaded;
				ResetTrackingState();
				LogInfo("Plugin loaded v4.0.0");
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Awake failed\n" + ex));
			}
		}

		private void OnDestroy()
		{
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
			try
			{
				SceneManager.sceneLoaded -= OnSceneLoaded;
			}
			catch
			{
			}
			if (harmony != null)
			{
				harmony.UnpatchSelf();
				harmony = null;
			}
		}

		private void BindConfig()
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Expected O, but got Unknown
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Expected O, but got Unknown
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Expected O, but got Unknown
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Expected O, but got Unknown
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Expected O, but got Unknown
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Expected O, but got Unknown
			//IL_0189: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Expected O, but got Unknown
			//IL_01e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f2: Expected O, but got Unknown
			//IL_0226: Unknown result type (might be due to invalid IL or missing references)
			//IL_0230: Expected O, but got Unknown
			mergeCheckInterval = ((BaseUnityPlugin)this).Config.Bind<float>("General", "MergeCheckInterval", 0.05f, new ConfigDescription("Interval in seconds for processing queued orb merge contacts.接触済みオーブのマージ処理間隔", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 1f), Array.Empty<object>()));
			mergeArmDelay = ((BaseUnityPlugin)this).Config.Bind<float>("General", "MergeArmDelay", 0.2f, new ConfigDescription("Delay in seconds after orb spawn before merge checks begin.オーブ生成後にマージ判定を開始するまでの待機秒数", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>()));
			remergeCooldown = ((BaseUnityPlugin)this).Config.Bind<float>("General", "ReMergeCooldown", 0.05f, new ConfigDescription("Cooldown in seconds before a merged orb can merge again.マージ後のオーブが再度マージ可能になるまでの待機秒数", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>()));
			contactSkin = ((BaseUnityPlugin)this).Config.Bind<float>("General", "ContactSkin", 0.02f, new ConfigDescription("Extra contact distance used only by fallback pair scan.フォールバック全ペア判定時のみ使う接触余裕距離", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.001f, 0.5f), Array.Empty<object>()));
			maxOrbValue = ((BaseUnityPlugin)this).Config.Bind<int>("General", "MaxOrbValue", 0, new ConfigDescription("Maximum merged orb value. 0 = unlimited.マージ後オーブの金額上限 0 = 無制限", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1000000), Array.Empty<object>()));
			maxOrbMergeCount = ((BaseUnityPlugin)this).Config.Bind<int>("General", "MaxOrbMergeCount", 0, new ConfigDescription("Maximum number of successful merges allowed per orb lineage. 0 = unlimited.オーブ系統ごとの成功マージ回数上限 0 = 無制限", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1000), Array.Empty<object>()));
			maxQueuedPairsPerTick = ((BaseUnityPlugin)this).Config.Bind<int>("Performance", "MaxQueuedPairsPerTick", 8, new ConfigDescription("Maximum queued orb contact pairs processed per tick. 1 tickあたりの接触ペア処理上限", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 64), Array.Empty<object>()));
			enableFallbackPairScan = ((BaseUnityPlugin)this).Config.Bind<bool>("Performance", "EnableFallbackPairScan", true, "Enable a slow fallback pair scan for rare cases where collision callbacks do not enqueue contact pairs.接触イベントが入らない稀な状況用の低頻度全ペア判定");
			fallbackPairScanInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Performance", "FallbackPairScanInterval", 0.5f, new ConfigDescription("Fallback all-pair scan interval seconds. Lower values are heavier.フォールバック全ペア判定間隔 秒数が小さいほど重い", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 5f), Array.Empty<object>()));
			indestructibleTargetCacheInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Performance", "IndestructibleTargetCacheInterval", 0.15f, new ConfigDescription("Interval seconds for refreshing indestructible orb/drone target cache.不滅オーブ/ドローン対象キャッシュ更新間隔", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 2f), Array.Empty<object>()));
			debugLog = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugLog", false, "Enable debug logging.デバッグログ出力");
			playNetworkMergeEffect = ((BaseUnityPlugin)this).Config.Bind<bool>("Effect", "PlayNetworkMergeEffect", true, "Play a built-in network visible impact effect at merge position.マージ位置で既存のネットワーク可視衝突エフェクトを再生");
			playInvincibilityElectricityEffect = ((BaseUnityPlugin)this).Config.Bind<bool>("Effect", "PlayInvincibilityElectricityEffect", true, "Play built-in death pit electricity visuals while merged orb invincibility is active.マージ後無敵中に既存の穴落下ビリビリ演出を再生");
		}

		private void CacheIndestructibleReflection()
		{
			itemOrbIndestructibleType = AccessTools.TypeByName("ItemOrbIndestructible");
			itemOrbType = AccessTools.TypeByName("ItemOrb");
			if (itemOrbIndestructibleType != null)
			{
				itemOrbIndestructibleItemOrbField = AccessTools.Field(itemOrbIndestructibleType, "itemOrb");
			}
			if (itemOrbType != null)
			{
				itemOrbItemActiveField = AccessTools.Field(itemOrbType, "itemActive");
				itemOrbObjectAffectedField = AccessTools.Field(itemOrbType, "objectAffected");
			}
			itemDroneIndestructibleType = AccessTools.TypeByName("ItemDroneIndestructible");
			itemDroneType = AccessTools.TypeByName("ItemDrone");
			if (itemDroneIndestructibleType != null)
			{
				itemDroneIndestructibleItemDroneField = AccessTools.Field(itemDroneIndestructibleType, "itemDrone");
			}
			if (itemDroneType != null)
			{
				itemDroneItemActivatedField = AccessTools.Field(itemDroneType, "itemActivated");
				itemDroneMagnetActiveField = AccessTools.Field(itemDroneType, "magnetActive");
				itemDroneMagnetTargetPhysGrabObjectField = AccessTools.Field(itemDroneType, "magnetTargetPhysGrabObject");
			}
		}

		private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
		{
			ResetTrackingState();
		}

		private void Update()
		{
			if (CanRunLogic())
			{
				UpdateInvincibilityElectricityEffects();
				if (rescanTimer > 0f)
				{
					rescanTimer -= Time.deltaTime;
				}
				else
				{
					rescanTimer = 1f;
					RescanSceneOrbs();
					CleanupTrackedOrbs();
				}
				if (mergeCheckTimer > 0f)
				{
					mergeCheckTimer -= Time.deltaTime;
					return;
				}
				mergeCheckTimer = Mathf.Max(0.01f, mergeCheckInterval.Value);
				ProcessOrbMerges();
			}
		}

		internal void RegisterEnemyOrb(EnemyValuable enemyValuable)
		{
			if (CanTrackEnemyOrb(enemyValuable))
			{
				OrbEntry orbEntry = OrbEntry.Create(enemyValuable);
				if (orbEntry != null && !trackedOrbs.ContainsKey(orbEntry.Id))
				{
					trackedOrbs.Add(orbEntry.Id, orbEntry);
					LogDebug("Tracked orb added: " + orbEntry.Describe());
				}
			}
		}

		internal bool ShouldIgnoreOrbToOrbBreak(PhysGrabObjectImpactDetector selfDetector, Collision collision)
		{
			if (!CanRunLogic())
			{
				return false;
			}
			EnemyValuable enemyValuableFromDetector = GetEnemyValuableFromDetector(selfDetector);
			if ((Object)(object)enemyValuableFromDetector == (Object)null)
			{
				return false;
			}
			PhysGrabObjectImpactDetector impactDetectorFromCollision = GetImpactDetectorFromCollision(collision);
			EnemyValuable enemyValuableFromDetector2 = GetEnemyValuableFromDetector(impactDetectorFromCollision);
			if ((Object)(object)enemyValuableFromDetector2 == (Object)null)
			{
				return false;
			}
			if ((Object)(object)enemyValuableFromDetector == (Object)(object)enemyValuableFromDetector2)
			{
				return false;
			}
			if (!CanTrackEnemyOrb(enemyValuableFromDetector) || !CanTrackEnemyOrb(enemyValuableFromDetector2))
			{
				return false;
			}
			RegisterEnemyOrb(enemyValuableFromDetector);
			RegisterEnemyOrb(enemyValuableFromDetector2);
			EnqueueContactPair(enemyValuableFromDetector, enemyValuableFromDetector2);
			return true;
		}

		private bool CanRunLogic()
		{
			if ((Object)(object)GameManager.instance == (Object)null)
			{
				return false;
			}
			return SemiFunc.IsMasterClientOrSingleplayer();
		}

		private void ResetTrackingState()
		{
			trackedOrbs.Clear();
			busyOrbIds.Clear();
			removeBuffer.Clear();
			scanBuffer.Clear();
			pendingPairQueue.Clear();
			pendingPairKeys.Clear();
			indestructibleTargets.Clear();
			mergeCheckTimer = 0f;
			rescanTimer = 0f;
			fallbackPairScanTimer = 0f;
			nextIndestructibleTargetCacheRefreshTime = 0f;
		}

		private void RescanSceneOrbs()
		{
			EnemyValuable[] array = Object.FindObjectsOfType<EnemyValuable>(true);
			for (int i = 0; i < array.Length; i++)
			{
				RegisterEnemyOrb(array[i]);
			}
		}

		private void CleanupTrackedOrbs()
		{
			removeBuffer.Clear();
			foreach (KeyValuePair<int, OrbEntry> trackedOrb in trackedOrbs)
			{
				if (!IsEntryValid(trackedOrb.Value))
				{
					removeBuffer.Add(trackedOrb.Key);
				}
			}
			for (int i = 0; i < removeBuffer.Count; i++)
			{
				int num = removeBuffer[i];
				trackedOrbs.Remove(num);
				busyOrbIds.Remove(num);
			}
		}

		private void ProcessOrbMerges()
		{
			CleanupTrackedOrbs();
			RefreshIndestructibleTargetCacheIfNeeded();
			int num = Mathf.Clamp(maxQueuedPairsPerTick.Value, 1, 64);
			int num2 = 0;
			while (pendingPairQueue.Count > 0 && num2 < num)
			{
				PendingPair pendingPair = pendingPairQueue.Dequeue();
				pendingPairKeys.Remove(pendingPair.Key);
				num2++;
				if (!trackedOrbs.TryGetValue(pendingPair.FirstId, out var value) || !trackedOrbs.TryGetValue(pendingPair.SecondId, out var value2) || !IsEntryReady(value) || !IsEntryReady(value2) || !TryMerge(value, value2))
				{
					continue;
				}
				return;
			}
			if (enableFallbackPairScan.Value)
			{
				if (fallbackPairScanTimer > 0f)
				{
					fallbackPairScanTimer -= Mathf.Max(0.01f, mergeCheckInterval.Value);
					return;
				}
				fallbackPairScanTimer = Mathf.Max(0.1f, fallbackPairScanInterval.Value);
				ProcessFallbackPairScan();
			}
		}

		private void ProcessFallbackPairScan()
		{
			scanBuffer.Clear();
			foreach (KeyValuePair<int, OrbEntry> trackedOrb in trackedOrbs)
			{
				OrbEntry value = trackedOrb.Value;
				if (IsEntryReady(value))
				{
					value.UpdateCachedBounds();
					scanBuffer.Add(value);
				}
			}
			for (int i = 0; i < scanBuffer.Count; i++)
			{
				OrbEntry orbEntry = scanBuffer[i];
				if (!IsEntryReady(orbEntry))
				{
					continue;
				}
				for (int j = i + 1; j < scanBuffer.Count; j++)
				{
					OrbEntry orbEntry2 = scanBuffer[j];
					if (IsEntryReady(orbEntry2) && AreOrbsTouchingFallback(orbEntry, orbEntry2) && TryMerge(orbEntry, orbEntry2))
					{
						return;
					}
				}
			}
		}

		private void EnqueueContactPair(EnemyValuable firstEnemyValuable, EnemyValuable secondEnemyValuable)
		{
			OrbEntry trackedEntry = GetTrackedEntry(firstEnemyValuable);
			OrbEntry trackedEntry2 = GetTrackedEntry(secondEnemyValuable);
			if (trackedEntry != null && trackedEntry2 != null && trackedEntry.Id != trackedEntry2.Id)
			{
				ulong num = BuildPairKey(trackedEntry.Id, trackedEntry2.Id);
				if (!pendingPairKeys.Contains(num))
				{
					pendingPairKeys.Add(num);
					pendingPairQueue.Enqueue(new PendingPair(trackedEntry.Id, trackedEntry2.Id, num));
				}
			}
		}

		private OrbEntry GetTrackedEntry(EnemyValuable enemyValuable)
		{
			if ((Object)(object)enemyValuable == (Object)null)
			{
				return null;
			}
			OrbEntry orbEntry = OrbEntry.Create(enemyValuable);
			if (orbEntry == null)
			{
				return null;
			}
			if (trackedOrbs.TryGetValue(orbEntry.Id, out var value))
			{
				return value;
			}
			trackedOrbs.Add(orbEntry.Id, orbEntry);
			return orbEntry;
		}

		private static ulong BuildPairKey(int firstId, int secondId)
		{
			uint num = (uint)Mathf.Min(firstId, secondId);
			uint num2 = (uint)Mathf.Max(firstId, secondId);
			return ((ulong)num << 32) | num2;
		}

		private bool TryMerge(OrbEntry first, OrbEntry second)
		{
			//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0217: Unknown result type (might be due to invalid IL or missing references)
			if (!IsEntryValid(first) || !IsEntryValid(second))
			{
				return false;
			}
			if (busyOrbIds.Contains(first.Id) || busyOrbIds.Contains(second.Id))
			{
				return false;
			}
			int num = Mathf.Max(0, Mathf.FloorToInt(GetDollarValueCurrent(first.ValuableObject)));
			int num2 = Mathf.Max(0, Mathf.FloorToInt(GetDollarValueCurrent(second.ValuableObject)));
			int mergedValue = num + num2;
			int num3 = Mathf.Max(first.MergeCount, second.MergeCount) + 1;
			if (IsMergeBlockedByMaxValue(num, num2, mergedValue))
			{
				ApplyBlockedInvincibility(first, second);
				LogDebug("Merge blocked by max value: first=" + first.Describe() + " second=" + second.Describe() + " mergedValue=" + mergedValue + " max=" + GetConfiguredMaxOrbValue());
				return true;
			}
			if (IsMergeBlockedByMaxMergeCount(first.MergeCount, second.MergeCount, num3))
			{
				ApplyBlockedInvincibility(first, second);
				LogDebug("Merge blocked by max merge count: first=" + first.Describe() + " second=" + second.Describe() + " mergedCount=" + num3 + " max=" + GetConfiguredMaxOrbMergeCount());
				return true;
			}
			OrbEntry orbEntry = SelectWinner(first, second);
			OrbEntry orbEntry2 = ((orbEntry.Id == first.Id) ? second : first);
			busyOrbIds.Add(orbEntry.Id);
			busyOrbIds.Add(orbEntry2.Id);
			try
			{
				Vector3 mergeEffectPosition = GetMergeEffectPosition(orbEntry, orbEntry2);
				ApplyMergedValue(orbEntry, mergedValue);
				ReapplyVanillaEnemyOrbInvincibility(orbEntry, allowElectricityEffect: true);
				PlayMergeEffects(orbEntry, mergeEffectPosition);
				ZeroOutLocalValue(orbEntry2);
				RemoveLoserFromDollarHaulList(orbEntry2);
				DestroyLoser(orbEntry2);
				trackedOrbs.Remove(orbEntry2.Id);
				indestructibleTargets.Remove(orbEntry2.PhysGrabObject);
				orbEntry.MergeCount = num3;
				orbEntry.NextMergeAllowedTime = Time.time + Mathf.Max(0f, remergeCooldown.Value);
				LogDebug("Merged orbs: winner=" + orbEntry.Describe() + " loser=" + orbEntry2.Describe() + " mergedValue=" + mergedValue + " mergedCount=" + num3);
				return true;
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Merge failed\n" + ex));
				return false;
			}
			finally
			{
				busyOrbIds.Remove(orbEntry2.Id);
				busyOrbIds.Remove(orbEntry.Id);
			}
		}

		private OrbEntry SelectWinner(OrbEntry first, OrbEntry second)
		{
			bool flag = IsIndestructibleEffectTarget(first);
			bool flag2 = IsIndestructibleEffectTarget(second);
			if (flag != flag2)
			{
				return flag ? first : second;
			}
			bool flag3 = IsHeldByAnyPlayer(first);
			bool flag4 = IsHeldByAnyPlayer(second);
			if (flag3 != flag4)
			{
				return flag3 ? first : second;
			}
			float dollarValueCurrent = GetDollarValueCurrent(first.ValuableObject);
			float dollarValueCurrent2 = GetDollarValueCurrent(second.ValuableObject);
			if (dollarValueCurrent > dollarValueCurrent2)
			{
				return first;
			}
			if (dollarValueCurrent2 > dollarValueCurrent)
			{
				return second;
			}
			return (first.SortToken <= second.SortToken) ? first : second;
		}

		private bool IsIndestructibleEffectTarget(OrbEntry entry)
		{
			if (entry == null || (Object)(object)entry.PhysGrabObject == (Object)null)
			{
				return false;
			}
			if (indestructibleTargets.Contains(entry.PhysGrabObject))
			{
				return true;
			}
			if ((Object)(object)entry.ImpactDetector != (Object)null && GetBoolField(ImpactDetectorIsIndestructibleField, entry.ImpactDetector, defaultValue: false))
			{
				return true;
			}
			return false;
		}

		private void RefreshIndestructibleTargetCacheIfNeeded()
		{
			if (!(Time.time < nextIndestructibleTargetCacheRefreshTime))
			{
				nextIndestructibleTargetCacheRefreshTime = Time.time + Mathf.Max(0.05f, indestructibleTargetCacheInterval.Value);
				indestructibleTargets.Clear();
				CacheIndestructibleOrbTargets();
				CacheIndestructibleDroneTargets();
			}
		}

		private void CacheIndestructibleOrbTargets()
		{
			if (itemOrbIndestructibleType == null || itemOrbType == null || itemOrbItemActiveField == null || itemOrbObjectAffectedField == null)
			{
				return;
			}
			Object[] array = Object.FindObjectsOfType(itemOrbIndestructibleType);
			foreach (Object obj in array)
			{
				Component val = (Component)(object)((obj is Component) ? obj : null);
				if ((Object)(object)val == (Object)null || !val.gameObject.activeInHierarchy)
				{
					continue;
				}
				object obj2 = null;
				try
				{
					if (itemOrbIndestructibleItemOrbField != null)
					{
						obj2 = itemOrbIndestructibleItemOrbField.GetValue(val);
					}
					if (obj2 == null)
					{
						obj2 = val.GetComponent(itemOrbType);
					}
				}
				catch
				{
					obj2 = null;
				}
				if (obj2 == null)
				{
					continue;
				}
				try
				{
					object value = itemOrbItemActiveField.GetValue(obj2);
					if (!(value is bool) || !(bool)value)
					{
						continue;
					}
					object value2 = itemOrbObjectAffectedField.GetValue(obj2);
					if (!(value2 is IEnumerable enumerable))
					{
						continue;
					}
					foreach (object item in enumerable)
					{
						PhysGrabObject val2 = (PhysGrabObject)((item is PhysGrabObject) ? item : null);
						if ((Object)(object)val2 != (Object)null)
						{
							indestructibleTargets.Add(val2);
						}
					}
				}
				catch
				{
				}
			}
		}

		private void CacheIndestructibleDroneTargets()
		{
			if (itemDroneIndestructibleType == null || itemDroneType == null || itemDroneItemActivatedField == null || itemDroneMagnetActiveField == null || itemDroneMagnetTargetPhysGrabObjectField == null)
			{
				return;
			}
			Object[] array = Object.FindObjectsOfType(itemDroneIndestructibleType);
			foreach (Object obj in array)
			{
				Component val = (Component)(object)((obj is Component) ? obj : null);
				if ((Object)(object)val == (Object)null || !val.gameObject.activeInHierarchy)
				{
					continue;
				}
				object obj2 = null;
				try
				{
					if (itemDroneIndestructibleItemDroneField != null)
					{
						obj2 = itemDroneIndestructibleItemDroneField.GetValue(val);
					}
					if (obj2 == null)
					{
						obj2 = val.GetComponent(itemDroneType);
					}
				}
				catch
				{
					obj2 = null;
				}
				if (obj2 == null)
				{
					continue;
				}
				try
				{
					object value = itemDroneItemActivatedField.GetValue(obj2);
					object value2 = itemDroneMagnetActiveField.GetValue(obj2);
					if (value is bool && (bool)value && value2 is bool && (bool)value2)
					{
						object value3 = itemDroneMagnetTargetPhysGrabObjectField.GetValue(obj2);
						PhysGrabObject val2 = (PhysGrabObject)((value3 is PhysGrabObject) ? value3 : null);
						if ((Object)(object)val2 != (Object)null)
						{
							indestructibleTargets.Add(val2);
						}
					}
				}
				catch
				{
				}
			}
		}

		private void ApplyMergedValue(OrbEntry winner, int mergedValue)
		{
			float num = Mathf.Max(0, mergedValue);
			SetDollarValuesLocal(winner.ValuableObject, num);
			if (SemiFunc.IsMultiplayer())
			{
				PhotonView photonView = winner.PhotonView;
				if ((Object)(object)photonView != (Object)null)
				{
					photonView.RPC("DollarValueSetRPC", (RpcTarget)3, new object[1] { num });
				}
			}
		}

		private void ReapplyVanillaEnemyOrbInvincibility(OrbEntry winner, bool allowElectricityEffect)
		{
			try
			{
				if (winner != null && !((Object)(object)winner.EnemyValuable == (Object)null) && !((Object)(object)winner.ImpactDetector == (Object)null))
				{
					EnemyValuable enemyValuable = winner.EnemyValuable;
					PhysGrabObjectImpactDetector impactDetector = winner.ImpactDetector;
					impactDetector.destroyDisable = true;
					SetImpactDetectorIndestructibleSpawnTimer(impactDetector, 0.1f);
					if ((Object)(object)winner.PhysGrabObject != (Object)null)
					{
						winner.PhysGrabObject.OverrideIndestructible(0.1f);
					}
					if (EnemyValuableIndestructibleTimerField != null)
					{
						EnemyValuableIndestructibleTimerField.SetValue(enemyValuable, 5f);
					}
					if (EnemyValuableIndestructibleLerpField != null)
					{
						EnemyValuableIndestructibleLerpField.SetValue(enemyValuable, 0f);
					}
					ApplyEnemyOrbIndestructibleVisuals(enemyValuable);
					if (allowElectricityEffect)
					{
						BeginInvincibilityElectricityEffect(winner);
					}
					else if (winner != null)
					{
						winner.InvincibilityEffectEndTime = 0f;
						winner.NextInvincibilityEffectRefreshTime = 0f;
					}
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("ReapplyVanillaEnemyOrbInvincibility failed\n" + ex));
			}
		}

		private void BeginInvincibilityElectricityEffect(OrbEntry winner)
		{
			if (!playInvincibilityElectricityEffect.Value)
			{
				if (winner != null)
				{
					winner.InvincibilityEffectEndTime = 0f;
					winner.NextInvincibilityEffectRefreshTime = 0f;
				}
			}
			else if (winner != null)
			{
				winner.InvincibilityEffectEndTime = Time.time + 5f;
				winner.NextInvincibilityEffectRefreshTime = 0f;
				RefreshInvincibilityElectricityEffect(winner);
			}
		}

		private void UpdateInvincibilityElectricityEffects()
		{
			if (!playInvincibilityElectricityEffect.Value)
			{
				return;
			}
			foreach (OrbEntry value in trackedOrbs.Values)
			{
				if (value != null && !(value.InvincibilityEffectEndTime <= Time.time) && !(value.NextInvincibilityEffectRefreshTime > Time.time))
				{
					RefreshInvincibilityElectricityEffect(value);
				}
			}
		}

		private void RefreshInvincibilityElectricityEffect(OrbEntry entry)
		{
			try
			{
				if (IsEntryValid(entry) && !((Object)(object)entry.PhysGrabObject == (Object)null))
				{
					entry.PhysGrabObject.DeathPitEffectCreate();
					entry.NextInvincibilityEffectRefreshTime = Time.time + 2.4f;
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("RefreshInvincibilityElectricityEffect failed\n" + ex));
			}
		}

		private static void SetImpactDetectorIndestructibleSpawnTimer(PhysGrabObjectImpactDetector impactDetector, float seconds)
		{
			if (!((Object)(object)impactDetector == (Object)null) && !(ImpactDetectorIndestructibleSpawnTimerField == null))
			{
				ImpactDetectorIndestructibleSpawnTimerField.SetValue(impactDetector, Mathf.Max(0f, seconds));
			}
		}

		private static void ApplyEnemyOrbIndestructibleVisuals(EnemyValuable enemyValuable)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)enemyValuable == (Object)null))
			{
				Material enemyOrbOuterMaterial = GetEnemyOrbOuterMaterial(enemyValuable);
				if (!((Object)(object)enemyOrbOuterMaterial == (Object)null))
				{
					int num = Shader.PropertyToID("_FresnelPower");
					int num2 = Shader.PropertyToID("_FresnelColor");
					enemyOrbOuterMaterial.SetFloat(num, enemyValuable.fresnelPowerIndestructible);
					enemyOrbOuterMaterial.SetColor(num2, enemyValuable.fresnelColorIndestructible);
				}
			}
		}

		private static Material GetEnemyOrbOuterMaterial(EnemyValuable enemyValuable)
		{
			if ((Object)(object)enemyValuable == (Object)null)
			{
				return null;
			}
			if (EnemyValuableOuterMaterialField != null)
			{
				object? value = EnemyValuableOuterMaterialField.GetValue(enemyValuable);
				Material val = (Material)((value is Material) ? value : null);
				if ((Object)(object)val != (Object)null)
				{
					return val;
				}
			}
			if ((Object)(object)enemyValuable.outerMeshRenderer == (Object)null)
			{
				return null;
			}
			Material material = ((Renderer)enemyValuable.outerMeshRenderer).material;
			if ((Object)(object)material != (Object)null && EnemyValuableOuterMaterialField != null)
			{
				EnemyValuableOuterMaterialField.SetValue(enemyValuable, material);
			}
			return material;
		}

		private void ZeroOutLocalValue(OrbEntry loser)
		{
			SetDollarValuesLocal(loser.ValuableObject, 0f);
		}

		private void RemoveLoserFromDollarHaulList(OrbEntry loser)
		{
			if (loser == null || (Object)(object)loser.GameObject == (Object)null)
			{
				return;
			}
			try
			{
				if ((Object)(object)RoundDirector.instance != (Object)null && RoundDirector.instance.dollarHaulList != null)
				{
					RoundDirector.instance.dollarHaulList.Remove(loser.GameObject);
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("RemoveLoserFromDollarHaulList failed\n" + ex));
			}
		}

		private void DestroyLoser(OrbEntry loser)
		{
			if (SemiFunc.IsMultiplayer())
			{
				if ((Object)(object)loser.GameObject != (Object)null)
				{
					PhotonNetwork.Destroy(loser.GameObject);
				}
			}
			else if ((Object)(object)loser.GameObject != (Object)null)
			{
				Object.Destroy((Object)(object)loser.GameObject);
			}
		}

		private int GetConfiguredMaxOrbValue()
		{
			if (maxOrbValue == null)
			{
				return 0;
			}
			return Mathf.Max(0, maxOrbValue.Value);
		}

		private bool IsMergeBlockedByMaxValue(int firstValue, int secondValue, int mergedValue)
		{
			int configuredMaxOrbValue = GetConfiguredMaxOrbValue();
			if (configuredMaxOrbValue <= 0)
			{
				return false;
			}
			if (firstValue >= configuredMaxOrbValue || secondValue >= configuredMaxOrbValue)
			{
				return true;
			}
			return mergedValue > configuredMaxOrbValue;
		}

		private int GetConfiguredMaxOrbMergeCount()
		{
			if (maxOrbMergeCount == null)
			{
				return 0;
			}
			return Mathf.Max(0, maxOrbMergeCount.Value);
		}

		private bool IsMergeBlockedByMaxMergeCount(int firstMergeCount, int secondMergeCount, int mergedCount)
		{
			int configuredMaxOrbMergeCount = GetConfiguredMaxOrbMergeCount();
			if (configuredMaxOrbMergeCount <= 0)
			{
				return false;
			}
			if (firstMergeCount >= configuredMaxOrbMergeCount || secondMergeCount >= configuredMaxOrbMergeCount)
			{
				return true;
			}
			return mergedCount > configuredMaxOrbMergeCount;
		}

		private void ApplyBlockedInvincibility(OrbEntry first, OrbEntry second)
		{
			try
			{
				ReapplyVanillaEnemyOrbInvincibility(first, allowElectricityEffect: false);
				ReapplyVanillaEnemyOrbInvincibility(second, allowElectricityEffect: false);
				float nextMergeAllowedTime = Time.time + Mathf.Max(0f, remergeCooldown.Value);
				if (first != null)
				{
					first.NextMergeAllowedTime = nextMergeAllowedTime;
				}
				if (second != null)
				{
					second.NextMergeAllowedTime = nextMergeAllowedTime;
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("ApplyBlockedInvincibility failed\n" + ex));
			}
		}

		private static bool IsHeldByAnyPlayer(OrbEntry entry)
		{
			if (entry == null || (Object)(object)entry.PhysGrabObject == (Object)null)
			{
				return false;
			}
			PhysGrabObject physGrabObject = entry.PhysGrabObject;
			if (physGrabObject.playerGrabbing != null && physGrabObject.playerGrabbing.Count > 0)
			{
				return true;
			}
			if (physGrabObject.grabbed || physGrabObject.grabbedLocal)
			{
				return true;
			}
			return false;
		}

		private bool AreOrbsTouchingFallback(OrbEntry first, OrbEntry second)
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: 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)
			//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_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			first.UpdateCachedBounds();
			second.UpdateCachedBounds();
			if (!first.HasCachedBounds || !second.HasCachedBounds)
			{
				return false;
			}
			float num = Mathf.Max(0.001f, contactSkin.Value);
			float num2 = first.CachedRadius + second.CachedRadius + num;
			Vector3 val = ((Bounds)(ref first.CachedBounds)).center - ((Bounds)(ref second.CachedBounds)).center;
			if (((Vector3)(ref val)).sqrMagnitude > num2 * num2)
			{
				return false;
			}
			Bounds cachedBounds = first.CachedBounds;
			Bounds cachedBounds2 = second.CachedBounds;
			((Bounds)(ref cachedBounds)).Expand(num * 2f);
			((Bounds)(ref cachedBounds2)).Expand(num * 2f);
			if (!((Bounds)(ref cachedBounds)).Intersects(cachedBounds2))
			{
				return false;
			}
			return AreEntryCollidersCloseEnough(first, second, num);
		}

		private static bool AreEntryCollidersCloseEnough(OrbEntry first, OrbEntry second, float skin)
		{
			for (int i = 0; i < first.Colliders.Count; i++)
			{
				Collider val = first.Colliders[i];
				if (!IsColliderUsable(val))
				{
					continue;
				}
				for (int j = 0; j < second.Colliders.Count; j++)
				{
					Collider val2 = second.Colliders[j];
					if (IsColliderUsable(val2) && AreCollidersCloseEnough(val, val2, skin))
					{
						return true;
					}
				}
			}
			return false;
		}

		private bool IsEntryReady(OrbEntry entry)
		{
			if (!IsEntryValid(entry))
			{
				return false;
			}
			if (busyOrbIds.Contains(entry.Id))
			{
				return false;
			}
			if (Time.time < entry.RegisteredTime + Mathf.Max(0f, mergeArmDelay.Value))
			{
				return false;
			}
			if (Time.time < entry.NextMergeAllowedTime)
			{
				return false;
			}
			return true;
		}

		private bool IsEntryValid(OrbEntry entry)
		{
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			if (entry == null)
			{
				return false;
			}
			if ((Object)(object)entry.EnemyValuable == (Object)null || (Object)(object)entry.GameObject == (Object)null || (Object)(object)entry.ValuableObject == (Object)null || (Object)(object)entry.PhysGrabObject == (Object)null)
			{
				return false;
			}
			Scene scene = entry.GameObject.scene;
			if (!((Scene)(ref scene)).IsValid())
			{
				return false;
			}
			if (!entry.GameObject.activeInHierarchy)
			{
				return false;
			}
			if (entry.PhysGrabObject.dead)
			{
				return false;
			}
			if (entry.Colliders == null || entry.Colliders.Count == 0)
			{
				entry.RefreshColliders();
			}
			return entry.Colliders != null && entry.Colliders.Count > 0;
		}

		private bool CanTrackEnemyOrb(EnemyValuable enemyValuable)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)enemyValuable == (Object)null)
			{
				return false;
			}
			if ((Object)(object)((Component)enemyValuable).gameObject == (Object)null)
			{
				return false;
			}
			Scene scene = ((Component)enemyValuable).gameObject.scene;
			if (!((Scene)(ref scene)).IsValid())
			{
				return false;
			}
			if ((Object)(object)((Component)enemyValuable).GetComponent<ValuableObject>() == (Object)null)
			{
				return false;
			}
			if ((Object)(object)((Component)enemyValuable).GetComponent<PhysGrabObject>() == (Object)null)
			{
				return false;
			}
			return true;
		}

		private void PlayMergeEffects(OrbEntry winner, Vector3 mergeEffectPosition)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			if (playNetworkMergeEffect.Value)
			{
				PlayNetworkVisibleMergeEffect(winner, mergeEffectPosition);
			}
		}

		private void PlayNetworkVisibleMergeEffect(OrbEntry winner, Vector3 mergeEffectPosition)
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (winner != null && !((Object)(object)winner.PhotonView == (Object)null) && !((Object)(object)winner.ImpactDetector == (Object)null))
				{
					if (SemiFunc.IsMultiplayer())
					{
						winner.PhotonView.RPC("ImpactEffectRPC", (RpcTarget)0, new object[1] { mergeEffectPosition });
					}
					else if ((Object)(object)AssetManager.instance != (Object)null)
					{
						AssetManager.instance.PhysImpactEffect(mergeEffectPosition);
					}
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("PlayNetworkVisibleMergeEffect failed\n" + ex));
			}
		}

		private Vector3 GetMergeEffectPosition(OrbEntry first, OrbEntry second)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: 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_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: 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_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			if (TryGetClosestPointsBetweenEntries(first, second, out var firstPoint, out var secondPoint))
			{
				return (firstPoint + secondPoint) * 0.5f;
			}
			if (first != null && second != null && (Object)(object)first.PhysGrabObject != (Object)null && (Object)(object)second.PhysGrabObject != (Object)null)
			{
				return (first.PhysGrabObject.centerPoint + second.PhysGrabObject.centerPoint) * 0.5f;
			}
			if (first != null && second != null && (Object)(object)first.GameObject != (Object)null && (Object)(object)second.GameObject != (Object)null)
			{
				return (first.GameObject.transform.position + second.GameObject.transform.position) * 0.5f;
			}
			return Vector3.zero;
		}

		private static bool TryGetClosestPointsBetweenEntries(OrbEntry first, OrbEntry second, out Vector3 firstPoint, out Vector3 secondPoint)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: 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_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			firstPoint = Vector3.zero;
			secondPoint = Vector3.zero;
			if (first == null || second == null)
			{
				return false;
			}
			float num = float.MaxValue;
			bool flag = false;
			for (int i = 0; i < first.Colliders.Count; i++)
			{
				Collider val = first.Colliders[i];
				if (!IsColliderUsable(val))
				{
					continue;
				}
				for (int j = 0; j < second.Colliders.Count; j++)
				{
					Collider val2 = second.Colliders[j];
					if (IsColliderUsable(val2))
					{
						Bounds bounds = val2.bounds;
						Vector3 val3 = val.ClosestPoint(((Bounds)(ref bounds)).center);
						Vector3 val4 = val2.ClosestPoint(val3);
						Vector3 val5 = val3 - val4;
						float sqrMagnitude = ((Vector3)(ref val5)).sqrMagnitude;
						if (!flag || sqrMagnitude < num)
						{
							num = sqrMagnitude;
							firstPoint = val3;
							secondPoint = val4;
							flag = true;
						}
					}
				}
			}
			return flag;
		}

		private void LogInfo(string message)
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)message);
		}

		private void LogDebug(string message)
		{
			if (debugLog.Value)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)message);
			}
		}

		private static PhysGrabObjectImpactDetector GetImpactDetectorFromCollision(Collision collision)
		{
			if (collision == null)
			{
				return null;
			}
			if ((Object)(object)collision.gameObject != (Object)null)
			{
				PhysGrabObjectImpactDetector component = collision.gameObject.GetComponent<PhysGrabObjectImpactDetector>();
				if ((Object)(object)component != (Object)null)
				{
					return component;
				}
			}
			if ((Object)(object)collision.transform != (Object)null)
			{
				PhysGrabObjectImpactDetector componentInParent = ((Component)collision.transform).GetComponentInParent<PhysGrabObjectImpactDetector>();
				if ((Object)(object)componentInParent != (Object)null)
				{
					return componentInParent;
				}
			}
			if ((Object)(object)collision.collider != (Object)null)
			{
				PhysGrabObjectImpactDetector componentInParent2 = ((Component)collision.collider).GetComponentInParent<PhysGrabObjectImpactDetector>();
				if ((Object)(object)componentInParent2 != (Object)null)
				{
					return componentInParent2;
				}
			}
			return null;
		}

		private static EnemyValuable GetEnemyValuableFromDetector(PhysGrabObjectImpactDetector detector)
		{
			if ((Object)(object)detector == (Object)null)
			{
				return null;
			}
			EnemyValuable component = ((Component)detector).GetComponent<EnemyValuable>();
			if ((Object)(object)component != (Object)null)
			{
				return component;
			}
			return ((Component)detector).GetComponentInParent<EnemyValuable>();
		}

		private static bool AreCollidersCloseEnough(Collider firstCollider, Collider secondCollider, float skin)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: 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_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			Bounds bounds = secondCollider.bounds;
			Vector3 val = firstCollider.ClosestPoint(((Bounds)(ref bounds)).center);
			Vector3 val2 = secondCollider.ClosestPoint(val);
			Vector3 val3 = val - val2;
			return ((Vector3)(ref val3)).sqrMagnitude <= skin * skin;
		}

		private static bool TryGetCombinedBounds(List<Collider> colliders, out Bounds bounds)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			bounds = default(Bounds);
			bool flag = false;
			for (int i = 0; i < colliders.Count; i++)
			{
				Collider val = colliders[i];
				if (IsColliderUsable(val))
				{
					if (!flag)
					{
						bounds = val.bounds;
						flag = true;
					}
					else
					{
						((Bounds)(ref bounds)).Encapsulate(val.bounds);
					}
				}
			}
			return flag;
		}

		private static bool IsColliderUsable(Collider collider)
		{
			return (Object)(object)collider != (Object)null && collider.enabled && !collider.isTrigger && ((Component)collider).gameObject.activeInHierarchy;
		}

		private static bool GetBoolField(FieldInfo fieldInfo, object instance, bool defaultValue)
		{
			if (fieldInfo == null || instance == null)
			{
				return defaultValue;
			}
			try
			{
				object value = fieldInfo.GetValue(instance);
				if (value is bool)
				{
					return (bool)value;
				}
			}
			catch
			{
				return defaultValue;
			}
			return defaultValue;
		}

		private static float GetDollarValueCurrent(ValuableObject valuableObject)
		{
			if ((Object)(object)valuableObject == (Object)null || ValuableCurrentField == null)
			{
				return 0f;
			}
			object value = ValuableCurrentField.GetValue(valuableObject);
			if (value == null)
			{
				return 0f;
			}
			return Convert.ToSingle(value);
		}

		private static void SetDollarValuesLocal(ValuableObject valuableObject, float value)
		{
			if (!((Object)(object)valuableObject == (Object)null))
			{
				if (ValuableOriginalField != null)
				{
					ValuableOriginalField.SetValue(valuableObject, value);
				}
				if (ValuableCurrentField != null)
				{
					ValuableCurrentField.SetValue(valuableObject, value);
				}
				if (ValuableSetField != null)
				{
					ValuableSetField.SetValue(valuableObject, true);
				}
			}
		}
	}
}