Decompiled source of BallSaboteur v0.2.1

BallSaboteur.dll

Decompiled 2 months ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Mirror;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.Localization;
using UnityEngine.Localization.Settings;
using UnityEngine.Localization.Tables;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("BallSaboteur")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("BallSaboteur")]
[assembly: AssemblyTitle("BallSaboteur")]
[assembly: AssemblyVersion("1.0.0.0")]
[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 BallSaboteur
{
	[BepInPlugin("sbg.ballsaboteur", "BallSaboteur", "0.2.1")]
	public sealed class Plugin : BaseUnityPlugin
	{
		[HarmonyPatch(typeof(ItemCollection), "Initialize")]
		private static class Patch_ItemCollection_Initialize
		{
			private static void Postfix(ItemCollection __instance)
			{
				EnsureCustomItemRegistered(__instance);
			}
		}

		[HarmonyPatch(typeof(ItemCollection), "OnEnable")]
		private static class Patch_ItemCollection_OnEnable
		{
			private static void Postfix(ItemCollection __instance)
			{
				EnsureCustomItemRegistered(__instance);
			}
		}

		[HarmonyPatch(typeof(ItemCollection), "get_Count")]
		private static class Patch_ItemCollection_Count
		{
			private static void Postfix(ref int __result)
			{
				if (customItemData != null)
				{
					__result++;
				}
			}
		}

		[HarmonyPatch(typeof(ItemCollection), "GetItemAtIndex")]
		private static class Patch_ItemCollection_GetItemAtIndex
		{
			private static bool Prefix(ItemCollection __instance, int index, ref ItemData __result)
			{
				//IL_0070: Unknown result type (might be due to invalid IL or missing references)
				//IL_007a: Invalid comparison between Unknown and I4
				//IL_007f: 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_00e3: Unknown result type (might be due to invalid IL or missing references)
				if (customItemData == null || itemCollectionItemsField == null || itemCollectionMapField == null)
				{
					return true;
				}
				if (!(itemCollectionItemsField.GetValue(__instance) is ItemData[] array) || index < array.Length)
				{
					return true;
				}
				if (!(itemCollectionMapField.GetValue(__instance) is Dictionary<ItemType, ItemData> dictionary))
				{
					return true;
				}
				List<ItemType> list = new List<ItemType>();
				foreach (KeyValuePair<ItemType, ItemData> item in dictionary)
				{
					if ((int)item.Key >= 1000)
					{
						list.Add(item.Key);
					}
				}
				list.Sort((ItemType a, ItemType b) => ((int)a).CompareTo((int)b));
				int num = index - array.Length;
				if (num < 0 || num >= list.Count)
				{
					return true;
				}
				if (list[num] != CustomItemType)
				{
					return true;
				}
				__result = customItemData;
				return false;
			}
		}

		[HarmonyPatch(typeof(PlayerInventory), "GetEffectivelyEquippedItem")]
		private static class Patch_PlayerInventory_GetEffectivelyEquippedItem
		{
			private static void Postfix(ref ItemType __result)
			{
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				//IL_0007: Invalid comparison between I4 and Unknown
				//IL_000a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0010: Expected I4, but got Unknown
				if ((int)__result == (int)CustomItemType)
				{
					__result = (ItemType)(int)OrbitalLaserItemType;
				}
			}
		}

		[HarmonyPatch(typeof(PlayerInventory), "TryUseItem")]
		private static class Patch_PlayerInventory_TryUseItem
		{
			private static bool Prefix(PlayerInventory __instance, bool isAirhornReaction, ref bool shouldEatInput, ref bool __result)
			{
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Unknown result type (might be due to invalid IL or missing references)
				//IL_0016: Unknown result type (might be due to invalid IL or missing references)
				if (isAirhornReaction)
				{
					return true;
				}
				if (InvokeInventoryGetEffectiveSlot(__instance, __instance.EquippedItemIndex).itemType != CustomItemType)
				{
					return true;
				}
				__result = TryUseCustomItem(__instance, ref shouldEatInput);
				return false;
			}
		}

		[HarmonyPatch(typeof(PlayerInventory), "OnBUpdate")]
		private static class Patch_PlayerInventory_OnBUpdate
		{
			private static bool Prefix(PlayerInventory __instance)
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Unknown result type (might be due to invalid IL or missing references)
				if (InvokeInventoryGetEffectiveSlot(__instance, __instance.EquippedItemIndex).itemType != CustomItemType)
				{
					return true;
				}
				UpdateCustomLockOnTargeting(__instance);
				return false;
			}
		}

		[HarmonyPatch(typeof(PlayerInventory), "UserCode_CmdActivateOrbitalLaser__Hittable__Vector3__ItemUseId")]
		private static class Patch_PlayerInventory_CmdActivateOrbitalLaser
		{
			private static bool Prefix(ItemUseId itemUseId, Hittable target)
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				return !TryApplyCustomSabotageOnServer(itemUseId, target);
			}
		}

		[HarmonyPatch(typeof(PlayerGolfer), "OnPlayerHitOwnBall")]
		private static class Patch_PlayerGolfer_OnPlayerHitOwnBall
		{
			private static void Postfix(PlayerGolfer __instance)
			{
				MarkShotStarted(__instance);
			}
		}

		[HarmonyPatch(typeof(NetworkClient), "RegisterMessageHandlers")]
		private static class Patch_NetworkClient_RegisterMessageHandlers
		{
			private static void Postfix()
			{
				RegisterNetworkHandlers();
			}
		}

		[HarmonyPatch(typeof(HotkeyUi), "SetName")]
		private static class Patch_HotkeyUi_SetName
		{
			private static void Prefix(ref LocalizedString localizedName)
			{
				//IL_001f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0024: Unknown result type (might be due to invalid IL or missing references)
				//IL_0029: Unknown result type (might be due to invalid IL or missing references)
				if (customItemData != null)
				{
					PlayerInventory localPlayerInventory = GameManager.LocalPlayerInventory;
					if (!((Object)(object)localPlayerInventory == (Object)null) && InvokeInventoryGetEffectiveSlot(localPlayerInventory, localPlayerInventory.EquippedItemIndex).itemType == CustomItemType)
					{
						localizedName = customItemData.LocalizedName;
					}
				}
			}
		}

		[HarmonyPatch(typeof(LocalizedString), "GetLocalizedString", new Type[] { })]
		private static class Patch_LocalizedString_GetLocalizedString
		{
			private static void Postfix(ref string __result)
			{
				try
				{
					TryRegisterCustomLocalizationEntry();
					if (!string.IsNullOrEmpty(__result) && (__result == CustomItemLocalizationFallback || __result.IndexOf("ITEM_" + 1001, StringComparison.Ordinal) >= 0))
					{
						__result = "Ball Saboteur";
					}
				}
				catch
				{
				}
			}
		}

		[HarmonyPatch(typeof(MatchSetupRules), "SpawnChanceUpdated", new Type[] { typeof(ItemPoolId) })]
		private static class Patch_MatchSetupRules_SpawnChanceUpdated
		{
			private static bool Prefix(MatchSetupRules __instance, ItemPoolId itemPoolId)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				return !HandleUnsupportedMatchSetupSpawnChanceUpdated(__instance, itemPoolId);
			}
		}

		[HarmonyPatch(typeof(MatchSetupRules), "GetWeight")]
		private static class Patch_MatchSetupRules_GetWeight
		{
			private static void Postfix(MatchSetupRules __instance, int poolIndex, ItemType itemType, ref float __result)
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				if (itemType == CustomItemType && customItemData != null)
				{
					__result = GetVirtualWeightForCustomItem(__instance, poolIndex);
				}
			}
		}

		[HarmonyPatch(typeof(MatchSetupRules), "GetItemPoolTotalWeight")]
		private static class Patch_MatchSetupRules_GetItemPoolTotalWeight
		{
			private static void Postfix(MatchSetupRules __instance, int index, ref float __result)
			{
				if (customItemData != null)
				{
					__result += GetVirtualWeightForCustomItem(__instance, index);
				}
			}
		}

		[HarmonyPatch(typeof(ItemSpawnerSettings), "GetRandomItemFor")]
		private static class Patch_ItemSpawnerSettings_GetRandomItemFor
		{
			private static void Postfix(ItemSpawnerSettings __instance, PlayerInfo player, ref ItemType __result)
			{
				//IL_0028: Unknown result type (might be due to invalid IL or missing references)
				//IL_002e: Expected I4, but got Unknown
				if (customItemData != null)
				{
					float spawnChancePercentForPlayer = GetSpawnChancePercentForPlayer(__instance, player);
					if (!(spawnChancePercentForPlayer <= 0f) && Random.value <= spawnChancePercentForPlayer / 100f)
					{
						__result = (ItemType)(int)CustomItemType;
					}
				}
			}
		}

		private struct BallSabotageStateMessage : NetworkMessage
		{
			public uint BallNetId;

			public bool IsActive;
		}

		private sealed class ActiveSabotage
		{
			public uint BallNetId;

			public GolfBall Ball;

			public PlayerGolfer Owner;

			public Vector3 StrokeStartPosition;

			public bool WaitingForBallToStop;
		}

		private sealed class RuntimeBallMorph : MonoBehaviour
		{
			private const float CornerRadiusFraction = 0.25f;

			private GolfBall ball;

			private SphereCollider sphereCollider;

			private BoxCollider cubeCollider;

			private SphereCollider[] cornerColliders;

			private GameObject cubeVisual;

			private Renderer[] originalRenderers;

			private Rigidbody ballRigidbody;

			private CollisionDetectionMode originalCollisionMode;

			private bool hasStoredCollisionMode;

			private float originalMass;

			private bool hasStoredMass;

			private void Awake()
			{
				ball = ((Component)this).GetComponent<GolfBall>();
				sphereCollider = (((Object)(object)ball != (Object)null) ? ball.Collider : ((Component)this).GetComponent<SphereCollider>());
				ballRigidbody = ((Component)this).GetComponent<Rigidbody>();
				originalRenderers = ballRenderersField?.GetValue(ball) as Renderer[];
				if (originalRenderers == null || originalRenderers.Length == 0)
				{
					originalRenderers = ((Component)this).GetComponentsInChildren<Renderer>(true);
				}
			}

			public void ApplyCube()
			{
				//IL_0063: Unknown result type (might be due to invalid IL or missing references)
				//IL_0056: 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_00a8: 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_00ba: Unknown result type (might be due to invalid IL or missing references)
				//IL_030b: Unknown result type (might be due to invalid IL or missing references)
				//IL_031c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0331: Unknown result type (might be due to invalid IL or missing references)
				//IL_0337: Unknown result type (might be due to invalid IL or missing references)
				//IL_0221: Unknown result type (might be due to invalid IL or missing references)
				//IL_0226: Unknown result type (might be due to invalid IL or missing references)
				//IL_0168: Unknown result type (might be due to invalid IL or missing references)
				//IL_0178: Unknown result type (might be due to invalid IL or missing references)
				//IL_017d: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)sphereCollider == (Object)null)
				{
					sphereCollider = ((Component)this).GetComponent<SphereCollider>();
				}
				float num = (((Object)(object)sphereCollider != (Object)null) ? (sphereCollider.radius * 2f) : 0.45f);
				float num2 = num * 0.5f;
				Vector3 val = (((Object)(object)sphereCollider != (Object)null) ? sphereCollider.center : Vector3.zero);
				if ((Object)(object)cubeCollider == (Object)null)
				{
					cubeCollider = ((Component)this).GetComponent<BoxCollider>();
					if ((Object)(object)cubeCollider == (Object)null)
					{
						cubeCollider = ((Component)this).gameObject.AddComponent<BoxCollider>();
					}
				}
				cubeCollider.center = val;
				cubeCollider.size = Vector3.one * num;
				if ((Object)(object)sphereCollider != (Object)null)
				{
					((Collider)cubeCollider).sharedMaterial = ((Collider)sphereCollider).sharedMaterial;
				}
				((Collider)cubeCollider).enabled = true;
				float num3 = num2 * 0.25f;
				float num4 = num2 - num3 / Mathf.Sqrt(3f);
				if (cornerColliders == null)
				{
					cornerColliders = (SphereCollider[])(object)new SphereCollider[8];
				}
				int num5 = 0;
				for (int i = -1; i <= 1; i += 2)
				{
					for (int j = -1; j <= 1; j += 2)
					{
						for (int k = -1; k <= 1; k += 2)
						{
							SphereCollider val2 = cornerColliders[num5];
							if ((Object)(object)val2 == (Object)null)
							{
								val2 = ((Component)this).gameObject.AddComponent<SphereCollider>();
								cornerColliders[num5] = val2;
							}
							val2.center = val + new Vector3((float)i * num4, (float)j * num4, (float)k * num4);
							val2.radius = num3;
							if ((Object)(object)sphereCollider != (Object)null)
							{
								((Collider)val2).sharedMaterial = ((Collider)sphereCollider).sharedMaterial;
							}
							((Collider)val2).enabled = true;
							num5++;
						}
					}
				}
				if ((Object)(object)sphereCollider != (Object)null)
				{
					((Collider)sphereCollider).enabled = false;
				}
				if ((Object)(object)ballRigidbody != (Object)null)
				{
					if (!hasStoredCollisionMode)
					{
						originalCollisionMode = ballRigidbody.collisionDetectionMode;
						hasStoredCollisionMode = true;
					}
					ballRigidbody.collisionDetectionMode = (CollisionDetectionMode)2;
					if (!hasStoredMass)
					{
						originalMass = ballRigidbody.mass;
						hasStoredMass = true;
					}
					float num6 = (((Object)(object)Instance != (Object)null) ? Instance.cubeMassMultiplierConfig.Value : 1f);
					ballRigidbody.mass = originalMass * Mathf.Max(num6, 0.01f);
				}
				if ((Object)(object)cubeVisual == (Object)null)
				{
					cubeVisual = GameObject.CreatePrimitive((PrimitiveType)3);
					((Object)cubeVisual).name = "BallSaboteurCubeVisual";
					cubeVisual.transform.SetParent(((Component)this).transform, false);
					Collider component = cubeVisual.GetComponent<Collider>();
					if ((Object)(object)component != (Object)null)
					{
						Object.Destroy((Object)(object)component);
					}
				}
				cubeVisual.transform.localPosition = val;
				cubeVisual.transform.localRotation = Quaternion.identity;
				cubeVisual.transform.localScale = Vector3.one * num;
				cubeVisual.SetActive(true);
				if (originalRenderers == null)
				{
					return;
				}
				Renderer[] array = originalRenderers;
				foreach (Renderer val3 in array)
				{
					if ((Object)(object)val3 != (Object)null)
					{
						val3.enabled = false;
					}
				}
			}

			public void RestoreSphere()
			{
				//IL_0082: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)sphereCollider != (Object)null)
				{
					((Collider)sphereCollider).enabled = true;
				}
				if ((Object)(object)cubeCollider != (Object)null)
				{
					((Collider)cubeCollider).enabled = false;
				}
				if (cornerColliders != null)
				{
					SphereCollider[] array = cornerColliders;
					foreach (SphereCollider val in array)
					{
						if ((Object)(object)val != (Object)null)
						{
							((Collider)val).enabled = false;
						}
					}
				}
				if ((Object)(object)ballRigidbody != (Object)null && hasStoredCollisionMode)
				{
					ballRigidbody.collisionDetectionMode = originalCollisionMode;
					hasStoredCollisionMode = false;
				}
				if ((Object)(object)ballRigidbody != (Object)null && hasStoredMass)
				{
					ballRigidbody.mass = originalMass;
					hasStoredMass = false;
				}
				if ((Object)(object)cubeVisual != (Object)null)
				{
					cubeVisual.SetActive(false);
				}
				if (originalRenderers == null)
				{
					return;
				}
				Renderer[] array2 = originalRenderers;
				foreach (Renderer val2 in array2)
				{
					if ((Object)(object)val2 != (Object)null)
					{
						val2.enabled = true;
					}
				}
			}
		}

		public const string ModGuid = "sbg.ballsaboteur";

		public const string ModName = "BallSaboteur";

		public const string ModVersion = "0.2.1";

		internal const int CustomItemTypeRaw = 1001;

		internal const string CustomItemDisplayName = "Ball Saboteur";

		internal static readonly string CustomItemLocalizationFallback = $"Data/ITEM_{1001}";

		internal static readonly ItemType CustomItemType = (ItemType)1001;

		private static readonly ItemType OrbitalLaserItemType = (ItemType)10;

		internal static Plugin Instance;

		internal static ManualLogSource Log;

		private static readonly Dictionary<uint, ActiveSabotage> ActiveSabotages = new Dictionary<uint, ActiveSabotage>();

		private static readonly Dictionary<uint, RuntimeBallMorph> BallMorphs = new Dictionary<uint, RuntimeBallMorph>();

		private static FieldInfo itemCollectionMapField;

		private static FieldInfo itemCollectionItemsField;

		private static FieldInfo physicalItemTypeField;

		private static FieldInfo itemDataTypeField;

		private static FieldInfo itemDataPrefabField;

		private static FieldInfo itemDataMaxUsesField;

		private static FieldInfo itemDataCanUsageAffectBallsField;

		private static FieldInfo itemDataNameField;

		private static FieldInfo networkIdentityAssetIdField;

		private static uint customPickupAssetId;

		private static FieldInfo ballRenderersField;

		private static FieldInfo itemPoolSpawnChancesField;

		private static Type itemSpawnChanceType;

		private static FieldInfo itemSpawnChanceItemField;

		private static FieldInfo itemSpawnChanceWeightField;

		private static readonly HashSet<ItemPool> injectedPools = new HashSet<ItemPool>();

		private static readonly Dictionary<ItemPool, float> trackedPoolSpawnPercents = new Dictionary<ItemPool, float>();

		private static MethodInfo objectMemberwiseCloneMethod;

		private static MethodInfo updateOrbitalLaserLockOnTargetMethod;

		private static MethodInfo inventoryGetEffectiveSlotMethod;

		private static MethodInfo inventoryCanUseEquippedItemMethod;

		private static MethodInfo inventoryCancelItemUseMethod;

		private static MethodInfo inventoryCancelItemFlourishMethod;

		private static MethodInfo inventorySetItemUseTimestampMethod;

		private static MethodInfo inventoryIncrementAndGetCurrentItemUseIdMethod;

		private static MethodInfo inventoryDecrementUseFromSlotAtMethod;

		private static MethodInfo inventoryRemoveIfOutOfUsesMethod;

		private static MethodInfo inventoryCmdActivateOrbitalLaserMethod;

		private static MethodInfo inventorySetLockOnTargetMethod;

		private static MethodInfo inventoryOnActivatedOrbitalLaserMethod;

		private static MethodInfo inventoryCmdAddItemMethod;

		private static Type onBUpdateDisplayClassType;

		private static FieldInfo onBUpdateDisplayThisField;

		private static FieldInfo onBUpdateDisplaySlotField;

		private static FieldInfo matchSetupRulesCurrentItemPoolDirtyField;

		private static FieldInfo matchSetupRulesCurrentItemPoolIndexField;

		private static FieldInfo matchSetupRulesSpawnChanceWeightsField;

		private static FieldInfo matchSetupRulesTotalWeightPerPoolField;

		private static FieldInfo matchSetupRulesSpawnChanceSlidersField;

		private static FieldInfo matchSetupRulesItemOrderLookupField;

		private static MethodInfo matchSetupRulesServerUpdateSpawnChanceValueMethod;

		private static MethodInfo matchSetupRulesUpdateTotalWeightForPoolMethod;

		private static MethodInfo matchSetupRulesGetCurrentItemPoolMethod;

		private static MethodInfo matchSetupRulesUpdateSliderGreyedOutMethod;

		private static ItemData customItemData;

		private static GameObject customPickupPrefab;

		private static bool networkSerializersRegistered;

		private ConfigEntry<float> restoreDistanceMetersConfig;

		private ConfigEntry<float> spawnChancePercentConfig;

		private ConfigEntry<float> leaderSpawnChancePercentConfig;

		private ConfigEntry<float> hopImpulseConfig;

		private ConfigEntry<float> fallRescueThresholdConfig;

		private ConfigEntry<float> cubeMassMultiplierConfig;

		private ConfigEntry<bool> tintPickupEnabledConfig;

		private ConfigEntry<string> tintPickupColorHexConfig;

		private ConfigEntry<bool> debugGrantHotkeyEnabledConfig;

		private ConfigEntry<bool> debugSelfSabotageHotkeyEnabledConfig;

		private bool debugGrantPressedLastFrame;

		private bool debugSelfSabotagePressedLastFrame;

		private static bool localizationEntryRegistered;

		private void Awake()
		{
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			restoreDistanceMetersConfig = ((BaseUnityPlugin)this).Config.Bind<float>("Gameplay", "RestoreDistanceMeters", 3f, "Minimum stroke distance before a sabotaged cube ball can restore once it stops.");
			spawnChancePercentConfig = ((BaseUnityPlugin)this).Config.Bind<float>("Gameplay", "SpawnChancePercent", 5f, "Target chance (0-100) for Ball Saboteur to roll from 'behind the leader' and mobility pools. Computed against each pool's existing total weight at injection time; changes require a game restart.");
			leaderSpawnChancePercentConfig = ((BaseUnityPlugin)this).Config.Bind<float>("Gameplay", "LeaderSpawnChancePercent", 0f, "Target chance (0-100) for Ball Saboteur to roll from the 'In the lead' pool. Defaults to 0 so leaders don't receive the item.");
			hopImpulseConfig = ((BaseUnityPlugin)this).Config.Bind<float>("Gameplay", "ActivationHopImpulse", 3f, "Upward velocity (m/s) applied to the ball at sabotage activation so the cube spawns airborne. Skipped if the ball is mid-flight. Set to 0 to disable.");
			fallRescueThresholdConfig = ((BaseUnityPlugin)this).Config.Bind<float>("Gameplay", "FallRescueThresholdMeters", 15f, "If a sabotaged ball falls more than this many meters below its stroke-start position, it is rescued to the stroke-start position and the sabotage ends. Guards against cube corners tunneling through terrain.");
			cubeMassMultiplierConfig = ((BaseUnityPlugin)this).Config.Bind<float>("Gameplay", "CubeMassMultiplier", 3f, "Rigidbody mass is multiplied by this factor while the ball is a cube. Higher values make the cube roll less and thud harder on landing. Restored to the original mass when the sabotage ends. 1.0 disables.");
			tintPickupEnabledConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Visuals", "TintPickupModel", true, "Tint the dropped Ball Saboteur pickup model so it's visually distinct from the vanilla Orbital Laser. Disable to render in the Orbital Laser's original colors.");
			tintPickupColorHexConfig = ((BaseUnityPlugin)this).Config.Bind<string>("Visuals", "TintPickupColorHex", "#B53AFF", "Hex color (e.g. #B53AFF) applied to the pickup model when tinting is enabled. Multiplies the original material color, so dark base materials may darken further.");
			debugGrantHotkeyEnabledConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "EnableGrantHotkey", true, "When enabled, pressing F8 grants the local player one Ball Saboteur item.");
			debugSelfSabotageHotkeyEnabledConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "EnableSelfSabotageHotkey", true, "When enabled and hosting locally, pressing F9 sabotages the local player's own ball (bypasses self-target guard for testing).");
			CacheReflection();
			RegisterNetworkHandlers();
			new Harmony("sbg.ballsaboteur").PatchAll();
			Log.LogInfo((object)"BallSaboteur v0.2.1 loaded.");
		}

		private void Update()
		{
			UpdateDebugHotkey();
			UpdateActiveSabotages();
		}

		private static void CacheReflection()
		{
			itemCollectionMapField = AccessTools.Field(typeof(ItemCollection), "allItemData");
			itemCollectionItemsField = AccessTools.Field(typeof(ItemCollection), "items");
			physicalItemTypeField = AccessTools.Field(typeof(PhysicalItem), "itemType");
			itemDataTypeField = AccessTools.Field(typeof(ItemData), "<Type>k__BackingField");
			itemDataPrefabField = AccessTools.Field(typeof(ItemData), "<Prefab>k__BackingField");
			itemDataMaxUsesField = AccessTools.Field(typeof(ItemData), "<MaxUses>k__BackingField");
			itemDataCanUsageAffectBallsField = AccessTools.Field(typeof(ItemData), "<CanUsageAffectBalls>k__BackingField");
			itemDataNameField = AccessTools.Field(typeof(ItemData), "name");
			networkIdentityAssetIdField = AccessTools.Field(typeof(NetworkIdentity), "_assetId");
			ballRenderersField = AccessTools.Field(typeof(GolfBall), "renderers");
			itemPoolSpawnChancesField = AccessTools.Field(typeof(ItemPool), "spawnChances");
			itemSpawnChanceType = typeof(ItemPool).GetNestedType("ItemSpawnChance");
			itemSpawnChanceItemField = itemSpawnChanceType?.GetField("item");
			itemSpawnChanceWeightField = itemSpawnChanceType?.GetField("spawnChanceWeight");
			objectMemberwiseCloneMethod = typeof(object).GetMethod("MemberwiseClone", BindingFlags.Instance | BindingFlags.NonPublic);
			updateOrbitalLaserLockOnTargetMethod = AccessTools.Method(typeof(PlayerInventory), "<OnBUpdate>g__UpdateOrbitalLaserLockOnTarget|108_3", (Type[])null, (Type[])null);
			inventoryGetEffectiveSlotMethod = AccessTools.Method(typeof(PlayerInventory), "GetEffectiveSlot", (Type[])null, (Type[])null);
			inventoryCanUseEquippedItemMethod = AccessTools.Method(typeof(PlayerInventory), "CanUseEquippedItem", (Type[])null, (Type[])null);
			inventoryCancelItemUseMethod = AccessTools.Method(typeof(PlayerInventory), "CancelItemUse", (Type[])null, (Type[])null);
			inventoryCancelItemFlourishMethod = AccessTools.Method(typeof(PlayerInventory), "CancelItemFlourish", (Type[])null, (Type[])null);
			inventorySetItemUseTimestampMethod = AccessTools.Method(typeof(PlayerInventory), "set_ItemUseTimestamp", (Type[])null, (Type[])null);
			inventoryIncrementAndGetCurrentItemUseIdMethod = AccessTools.Method(typeof(PlayerInventory), "IncrementAndGetCurrentItemUseId", (Type[])null, (Type[])null);
			inventoryDecrementUseFromSlotAtMethod = AccessTools.Method(typeof(PlayerInventory), "DecrementUseFromSlotAt", (Type[])null, (Type[])null);
			inventoryRemoveIfOutOfUsesMethod = AccessTools.Method(typeof(PlayerInventory), "RemoveIfOutOfUses", (Type[])null, (Type[])null);
			inventoryCmdActivateOrbitalLaserMethod = AccessTools.Method(typeof(PlayerInventory), "CmdActivateOrbitalLaser", (Type[])null, (Type[])null);
			inventorySetLockOnTargetMethod = AccessTools.Method(typeof(PlayerInventory), "SetLockOnTarget", (Type[])null, (Type[])null);
			inventoryOnActivatedOrbitalLaserMethod = AccessTools.Method(typeof(PlayerInventory), "OnActivatedOrbitalLaser", (Type[])null, (Type[])null);
			inventoryCmdAddItemMethod = AccessTools.Method(typeof(PlayerInventory), "CmdAddItem", (Type[])null, (Type[])null);
			onBUpdateDisplayClassType = AccessTools.Inner(typeof(PlayerInventory), "<>c__DisplayClass108_0");
			onBUpdateDisplayThisField = AccessTools.Field(onBUpdateDisplayClassType, "<>4__this");
			onBUpdateDisplaySlotField = AccessTools.Field(onBUpdateDisplayClassType, "effectiveSlot");
			matchSetupRulesCurrentItemPoolDirtyField = AccessTools.Field(typeof(MatchSetupRules), "currentItemPoolDirty");
			matchSetupRulesCurrentItemPoolIndexField = AccessTools.Field(typeof(MatchSetupRules), "currentItemPoolIndex");
			matchSetupRulesSpawnChanceWeightsField = AccessTools.Field(typeof(MatchSetupRules), "spawnChanceWeights");
			matchSetupRulesTotalWeightPerPoolField = AccessTools.Field(typeof(MatchSetupRules), "totalWeightPerPool");
			matchSetupRulesSpawnChanceSlidersField = AccessTools.Field(typeof(MatchSetupRules), "spawnChanceSliders");
			matchSetupRulesItemOrderLookupField = AccessTools.Field(typeof(MatchSetupRules), "itemOrderLookup");
			matchSetupRulesServerUpdateSpawnChanceValueMethod = AccessTools.Method(typeof(MatchSetupRules), "ServerUpdateSpawnChanceValue", (Type[])null, (Type[])null);
			matchSetupRulesUpdateTotalWeightForPoolMethod = AccessTools.Method(typeof(MatchSetupRules), "UpdateTotalWeightForPool", (Type[])null, (Type[])null);
			matchSetupRulesGetCurrentItemPoolMethod = AccessTools.Method(typeof(MatchSetupRules), "GetCurrentItemPool", (Type[])null, (Type[])null);
			matchSetupRulesUpdateSliderGreyedOutMethod = AccessTools.Method(typeof(MatchSetupRules), "UpdateSliderGreyedOut", (Type[])null, (Type[])null);
		}

		private static void RegisterNetworkHandlers()
		{
			if (!networkSerializersRegistered)
			{
				Writer<BallSabotageStateMessage>.write = WriteBallSabotageState;
				Reader<BallSabotageStateMessage>.read = ReadBallSabotageState;
				networkSerializersRegistered = true;
			}
			NetworkClient.ReplaceHandler<BallSabotageStateMessage>((Action<BallSabotageStateMessage>)OnBallSabotageStateMessage, false);
			TryRegisterCustomPickupPrefab();
		}

		private static void WriteBallSabotageState(NetworkWriter writer, BallSabotageStateMessage message)
		{
			NetworkWriterExtensions.WriteUInt(writer, message.BallNetId);
			NetworkWriterExtensions.WriteBool(writer, message.IsActive);
		}

		private static BallSabotageStateMessage ReadBallSabotageState(NetworkReader reader)
		{
			BallSabotageStateMessage result = default(BallSabotageStateMessage);
			result.BallNetId = NetworkReaderExtensions.ReadUInt(reader);
			result.IsActive = NetworkReaderExtensions.ReadBool(reader);
			return result;
		}

		private static void OnBallSabotageStateMessage(BallSabotageStateMessage message)
		{
			if (TryGetGolfBall(message.BallNetId, out var ball))
			{
				if (message.IsActive)
				{
					EnsureCubeApplied(ball);
				}
				else
				{
					EnsureSphereApplied(ball);
				}
			}
		}

		private void UpdateDebugHotkey()
		{
			Keyboard current = Keyboard.current;
			if (debugGrantHotkeyEnabledConfig.Value)
			{
				bool flag = current != null && ((ButtonControl)current.f8Key).isPressed;
				if (flag && !debugGrantPressedLastFrame)
				{
					PlayerInventory localPlayerInventory = GameManager.LocalPlayerInventory;
					if ((Object)(object)localPlayerInventory != (Object)null)
					{
						TryDebugGrantItem(localPlayerInventory);
					}
				}
				debugGrantPressedLastFrame = flag;
			}
			if (debugSelfSabotageHotkeyEnabledConfig.Value)
			{
				bool flag2 = current != null && ((ButtonControl)current.f9Key).isPressed;
				if (flag2 && !debugSelfSabotagePressedLastFrame)
				{
					TryDebugSelfSabotage();
				}
				debugSelfSabotagePressedLastFrame = flag2;
			}
		}

		private static void TryDebugSelfSabotage()
		{
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			if (!NetworkServer.active)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogWarning((object)"Self-sabotage hotkey requires hosting locally (server authority).");
				}
				return;
			}
			PlayerInventory localPlayerInventory = GameManager.LocalPlayerInventory;
			PlayerInfo val = (((Object)(object)localPlayerInventory != (Object)null) ? localPlayerInventory.PlayerInfo : null);
			PlayerGolfer val2 = (((Object)(object)val != (Object)null) ? val.AsGolfer : null);
			GolfBall val3 = (((Object)(object)val2 != (Object)null) ? val2.OwnBall : null);
			if ((Object)(object)val3 == (Object)null || (Object)(object)((NetworkBehaviour)val3).netIdentity == (Object)null)
			{
				ManualLogSource log2 = Log;
				if (log2 != null)
				{
					log2.LogWarning((object)"Self-sabotage: local player has no owned ball yet.");
				}
				return;
			}
			uint netId = ((NetworkBehaviour)val3).netId;
			ActiveSabotages[netId] = new ActiveSabotage
			{
				BallNetId = netId,
				Ball = val3,
				Owner = val2,
				StrokeStartPosition = ResolveRescueAnchor(val3),
				WaitingForBallToStop = false
			};
			ApplyActivationHopIfStationary(val3);
			EnsureCubeApplied(val3);
			BroadcastSabotageState(netId, isActive: true);
			ManualLogSource log3 = Log;
			if (log3 != null)
			{
				log3.LogInfo((object)$"Self-sabotage applied to {((Object)val2).name} (ball net id {netId}).");
			}
		}

		private void UpdateActiveSabotages()
		{
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			if (!NetworkServer.active || ActiveSabotages.Count == 0)
			{
				return;
			}
			List<uint> list = null;
			foreach (KeyValuePair<uint, ActiveSabotage> activeSabotage in ActiveSabotages)
			{
				ActiveSabotage value = activeSabotage.Value;
				if ((Object)(object)value.Ball == (Object)null || (Object)(object)value.Owner == (Object)null)
				{
					if (list == null)
					{
						list = new List<uint>();
					}
					list.Add(activeSabotage.Key);
				}
				else if (value.StrokeStartPosition.y - ((Component)value.Ball).transform.position.y > fallRescueThresholdConfig.Value)
				{
					RescueFallenBall(value);
					if (list == null)
					{
						list = new List<uint>();
					}
					list.Add(activeSabotage.Key);
				}
				else
				{
					if (!value.WaitingForBallToStop || value.Owner.IsSwinging || !value.Ball.IsStationary)
					{
						continue;
					}
					value.WaitingForBallToStop = false;
					if (Vector3.Distance(value.StrokeStartPosition, ((Component)value.Ball).transform.position) >= restoreDistanceMetersConfig.Value)
					{
						if (list == null)
						{
							list = new List<uint>();
						}
						list.Add(activeSabotage.Key);
					}
				}
			}
			if (list == null)
			{
				return;
			}
			foreach (uint item in list)
			{
				RestoreSabotage(item);
			}
		}

		private void RestoreSabotage(uint ballNetId)
		{
			if (ActiveSabotages.TryGetValue(ballNetId, out var value))
			{
				ActiveSabotages.Remove(ballNetId);
				if ((Object)(object)value.Ball != (Object)null)
				{
					EnsureSphereApplied(value.Ball);
				}
				BroadcastSabotageState(ballNetId, isActive: false);
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogInfo((object)$"Restored sphere on ball net id {ballNetId}.");
				}
			}
		}

		internal static void EnsureCustomItemRegistered(ItemCollection collection)
		{
			//IL_0042: 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_008d: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)collection == (Object)null || itemCollectionMapField == null || !(itemCollectionMapField.GetValue(collection) is Dictionary<ItemType, ItemData> dictionary) || (customItemData != null && dictionary.ContainsKey(CustomItemType)))
			{
				return;
			}
			if (!dictionary.TryGetValue(OrbitalLaserItemType, out var value) || value == null)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogWarning((object)"Ball Saboteur could not find Orbital Laser data to clone.");
				}
				return;
			}
			EnsureCustomPickupPrefab(value);
			if (!((Object)(object)customPickupPrefab == (Object)null))
			{
				customItemData = CloneItemData(value, customPickupPrefab);
				dictionary[CustomItemType] = customItemData;
				ManualLogSource log2 = Log;
				if (log2 != null)
				{
					log2.LogInfo((object)"Registered Ball Saboteur runtime item.");
				}
			}
		}

		private static void EnsureCustomPickupPrefab(ItemData orbitalLaserData)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Expected O, but got Unknown
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)customPickupPrefab != (Object)null)
			{
				TryRegisterCustomPickupPrefab();
				return;
			}
			object? obj = itemDataPrefabField?.GetValue(orbitalLaserData);
			GameObject val = (GameObject)((obj is GameObject) ? obj : null);
			if (!((Object)(object)val == (Object)null))
			{
				GameObject val2 = new GameObject("BallSaboteurPrefabRoot");
				((Object)val2).hideFlags = (HideFlags)61;
				val2.SetActive(false);
				Object.DontDestroyOnLoad((Object)(object)val2);
				customPickupPrefab = Object.Instantiate<GameObject>(val, val2.transform);
				((Object)customPickupPrefab).name = "BallSaboteurPickupRuntime";
				PhysicalItem component = customPickupPrefab.GetComponent<PhysicalItem>();
				if ((Object)(object)component != (Object)null && physicalItemTypeField != null)
				{
					physicalItemTypeField.SetValue(component, CustomItemType);
				}
				ApplyPickupTint(customPickupPrefab);
				NetworkIdentity component2 = customPickupPrefab.GetComponent<NetworkIdentity>();
				if ((Object)(object)component2 != (Object)null && networkIdentityAssetIdField != null)
				{
					customPickupAssetId = ComputeCustomPickupAssetId();
					networkIdentityAssetIdField.SetValue(component2, 0u);
					TryRegisterCustomPickupPrefab();
				}
			}
		}

		private static void ApplyPickupTint(GameObject prefab)
		{
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Expected O, but got Unknown
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)prefab == (Object)null || (Object)(object)Instance == (Object)null || !Instance.tintPickupEnabledConfig.Value)
			{
				return;
			}
			Color val = default(Color);
			if (!ColorUtility.TryParseHtmlString(Instance.tintPickupColorHexConfig.Value, ref val))
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogWarning((object)("Invalid TintPickupColorHex '" + Instance.tintPickupColorHexConfig.Value + "'; skipping pickup tint."));
				}
				return;
			}
			Renderer[] componentsInChildren = prefab.GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val2 in componentsInChildren)
			{
				if ((Object)(object)val2 == (Object)null)
				{
					continue;
				}
				Material[] sharedMaterials = val2.sharedMaterials;
				if (sharedMaterials == null || sharedMaterials.Length == 0)
				{
					continue;
				}
				Material[] array = (Material[])(object)new Material[sharedMaterials.Length];
				for (int j = 0; j < sharedMaterials.Length; j++)
				{
					Material val3 = sharedMaterials[j];
					if (!((Object)(object)val3 == (Object)null))
					{
						Material val4 = new Material(val3);
						if (val4.HasProperty("_Color"))
						{
							val4.color *= val;
						}
						if (val4.HasProperty("_BaseColor"))
						{
							val4.SetColor("_BaseColor", val4.GetColor("_BaseColor") * val);
						}
						if (val4.HasProperty("_EmissionColor"))
						{
							val4.SetColor("_EmissionColor", val4.GetColor("_EmissionColor") * val);
						}
						array[j] = val4;
					}
				}
				val2.sharedMaterials = array;
			}
		}

		private static uint ComputeCustomPickupAssetId()
		{
			string text = "sbg.ballsaboteur:pickup:" + 1001;
			uint num = 2166136261u;
			for (int i = 0; i < text.Length; i++)
			{
				num = (num ^ text[i]) * 16777619;
			}
			if (num != 0)
			{
				return num;
			}
			return 1u;
		}

		private static void TryRegisterCustomPickupPrefab()
		{
			if ((Object)(object)customPickupPrefab == (Object)null || customPickupAssetId == 0 || (NetworkClient.prefabs.TryGetValue(customPickupAssetId, out var value) && (Object)(object)value == (Object)(object)customPickupPrefab))
			{
				return;
			}
			try
			{
				NetworkClient.RegisterPrefab(customPickupPrefab, customPickupAssetId);
			}
			catch (Exception ex)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogWarning((object)("Ball Saboteur prefab registration failed: " + ex.Message));
				}
			}
		}

		private static ItemData CloneItemData(ItemData source, GameObject prefab)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Expected O, but got Unknown
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			ItemData val = (ItemData)objectMemberwiseCloneMethod.Invoke(source, null);
			itemDataTypeField?.SetValue(val, CustomItemType);
			itemDataPrefabField?.SetValue(val, prefab);
			itemDataMaxUsesField?.SetValue(val, 1);
			itemDataCanUsageAffectBallsField?.SetValue(val, true);
			itemDataNameField?.SetValue(val, null);
			return val;
		}

		internal static bool TryUseCustomItem(PlayerInventory inventory, ref bool shouldEatInput)
		{
			//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_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			shouldEatInput = true;
			if ((Object)(object)inventory == (Object)null || !((NetworkBehaviour)inventory).isLocalPlayer)
			{
				return false;
			}
			InventorySlot equippedSlot = InvokeInventoryGetEffectiveSlot(inventory, inventory.EquippedItemIndex);
			if (equippedSlot.itemType != CustomItemType)
			{
				return false;
			}
			ItemData equippedItemData = null;
			bool shouldEatInput2 = true;
			bool isFlourish = false;
			if (!InvokeInventoryCanUseEquippedItem(inventory, altUse: false, isAirhornReaction: false, ref equippedSlot, ref equippedItemData, ref shouldEatInput2, ref isFlourish))
			{
				shouldEatInput = shouldEatInput2;
				return false;
			}
			if (isFlourish)
			{
				shouldEatInput = false;
				return false;
			}
			LockOnTarget lockOnTarget = inventory.LockOnTarget;
			Entity val = (((Object)(object)lockOnTarget != (Object)null) ? lockOnTarget.AsEntity : null);
			PlayerInfo val2 = (((Object)(object)val != (Object)null && val.IsPlayer) ? val.PlayerInfo : null);
			PlayerInfo playerInfo = inventory.PlayerInfo;
			if ((Object)(object)val2 == (Object)null || (Object)(object)playerInfo == (Object)null || (Object)(object)val2 == (Object)(object)playerInfo)
			{
				shouldEatInput = false;
				return false;
			}
			PlayerGolfer asGolfer = val2.AsGolfer;
			GolfBall val3 = (((Object)(object)asGolfer != (Object)null) ? asGolfer.OwnBall : null);
			Hittable asHittable = val2.AsHittable;
			if ((Object)(object)val3 == (Object)null || (Object)(object)asHittable == (Object)null)
			{
				shouldEatInput = false;
				return false;
			}
			inventoryCancelItemUseMethod?.Invoke(inventory, null);
			inventoryCancelItemFlourishMethod?.Invoke(inventory, null);
			inventorySetItemUseTimestampMethod?.Invoke(inventory, new object[1] { Time.timeAsDouble });
			playerInfo.CancelEmote(false);
			ItemUseId val4 = InvokeInventoryIncrementAndGetCurrentItemUseId(inventory, CustomItemType);
			inventoryCmdActivateOrbitalLaserMethod?.Invoke(inventory, new object[3]
			{
				asHittable,
				((Component)val3).transform.position,
				val4
			});
			inventoryDecrementUseFromSlotAtMethod?.Invoke(inventory, new object[1] { inventory.EquippedItemIndex });
			inventorySetLockOnTargetMethod?.Invoke(inventory, new object[1]);
			try
			{
				inventoryOnActivatedOrbitalLaserMethod?.Invoke(inventory, null);
			}
			catch (Exception ex)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogDebug((object)("Local Orbital Laser activation reuse failed: " + ex.Message));
				}
			}
			inventoryRemoveIfOutOfUsesMethod?.Invoke(inventory, new object[1] { inventory.EquippedItemIndex });
			shouldEatInput = false;
			return true;
		}

		internal static void UpdateCustomLockOnTargeting(PlayerInventory inventory)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)inventory == (Object)null) && !(updateOrbitalLaserLockOnTargetMethod == null) && !(onBUpdateDisplayClassType == null))
			{
				InventorySlot val = InvokeInventoryGetEffectiveSlot(inventory, inventory.EquippedItemIndex);
				if (val.itemType == CustomItemType)
				{
					object obj = Activator.CreateInstance(onBUpdateDisplayClassType);
					onBUpdateDisplayThisField.SetValue(obj, inventory);
					onBUpdateDisplaySlotField.SetValue(obj, val);
					updateOrbitalLaserLockOnTargetMethod.Invoke(inventory, new object[1] { obj });
				}
			}
		}

		internal static bool TryApplyCustomSabotageOnServer(ItemUseId itemUseId, Hittable target)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			if (itemUseId.itemType != CustomItemType)
			{
				return false;
			}
			if (!NetworkServer.active)
			{
				return true;
			}
			PlayerInfo val = (((Object)(object)target != (Object)null) ? ((Component)target).GetComponent<PlayerInfo>() : null);
			if ((Object)(object)val == (Object)null && (Object)(object)target != (Object)null)
			{
				Entity component = ((Component)target).GetComponent<Entity>();
				if ((Object)(object)component != (Object)null)
				{
					val = component.PlayerInfo;
				}
			}
			PlayerGolfer val2 = (((Object)(object)val != (Object)null) ? val.AsGolfer : null);
			GolfBall val3 = (((Object)(object)val2 != (Object)null) ? val2.OwnBall : null);
			if ((Object)(object)val3 == (Object)null || (Object)(object)((NetworkBehaviour)val3).netIdentity == (Object)null)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogWarning((object)"Ball Saboteur activation had no valid target ball.");
				}
				return true;
			}
			uint netId = ((NetworkBehaviour)val3).netId;
			ActiveSabotages[netId] = new ActiveSabotage
			{
				BallNetId = netId,
				Ball = val3,
				Owner = val2,
				StrokeStartPosition = ResolveRescueAnchor(val3),
				WaitingForBallToStop = false
			};
			ApplyActivationHopIfStationary(val3);
			EnsureCubeApplied(val3);
			BroadcastSabotageState(netId, isActive: true);
			ManualLogSource log2 = Log;
			if (log2 != null)
			{
				log2.LogInfo((object)("Applied Ball Saboteur to " + ((Object)val2).name + "."));
			}
			return true;
		}

		internal static void MarkShotStarted(PlayerGolfer golfer)
		{
			//IL_0038: 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_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			if (!NetworkServer.active || (Object)(object)golfer == (Object)null)
			{
				return;
			}
			GolfBall ownBall = golfer.OwnBall;
			if (!((Object)(object)ownBall == (Object)null) && ActiveSabotages.TryGetValue(((NetworkBehaviour)ownBall).netId, out var value))
			{
				value.StrokeStartPosition = ownBall.ServerLastStrokePosition;
				if (value.StrokeStartPosition == Vector3.zero)
				{
					value.StrokeStartPosition = ((Component)ownBall).transform.position;
				}
				value.WaitingForBallToStop = true;
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogInfo((object)$"Shot started on sabotaged ball {((NetworkBehaviour)ownBall).netId} from {value.StrokeStartPosition}.");
				}
			}
		}

		private static void BroadcastSabotageState(uint ballNetId, bool isActive)
		{
			if (NetworkServer.active)
			{
				BallSabotageStateMessage ballSabotageStateMessage = default(BallSabotageStateMessage);
				ballSabotageStateMessage.BallNetId = ballNetId;
				ballSabotageStateMessage.IsActive = isActive;
				NetworkServer.SendToAll<BallSabotageStateMessage>(ballSabotageStateMessage, 0, false);
			}
		}

		private static bool TryGetGolfBall(uint ballNetId, out GolfBall ball)
		{
			ball = null;
			if (!NetworkClient.spawned.TryGetValue(ballNetId, out var value) || (Object)(object)value == (Object)null)
			{
				return false;
			}
			ball = ((Component)value).GetComponent<GolfBall>();
			return (Object)(object)ball != (Object)null;
		}

		private static InventorySlot InvokeInventoryGetEffectiveSlot(PlayerInventory inventory, int index)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			if (inventoryGetEffectiveSlotMethod == null)
			{
				return default(InventorySlot);
			}
			return (InventorySlot)inventoryGetEffectiveSlotMethod.Invoke(inventory, new object[1] { index });
		}

		private static bool InvokeInventoryCanUseEquippedItem(PlayerInventory inventory, bool altUse, bool isAirhornReaction, ref InventorySlot equippedSlot, ref ItemData equippedItemData, ref bool shouldEatInput, ref bool isFlourish)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: 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)
			if (inventoryCanUseEquippedItemMethod == null)
			{
				return false;
			}
			object[] array = new object[6] { altUse, isAirhornReaction, equippedSlot, equippedItemData, shouldEatInput, isFlourish };
			bool result = (bool)inventoryCanUseEquippedItemMethod.Invoke(inventory, array);
			equippedSlot = (InventorySlot)array[2];
			object obj = array[3];
			equippedItemData = (ItemData)((obj is ItemData) ? obj : null);
			shouldEatInput = (bool)array[4];
			isFlourish = (bool)array[5];
			return result;
		}

		private static ItemUseId InvokeInventoryIncrementAndGetCurrentItemUseId(PlayerInventory inventory, ItemType itemType)
		{
			//IL_0025: 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)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			if (inventoryIncrementAndGetCurrentItemUseIdMethod == null)
			{
				return default(ItemUseId);
			}
			return (ItemUseId)inventoryIncrementAndGetCurrentItemUseIdMethod.Invoke(inventory, new object[1] { itemType });
		}

		private static void InvokeInventoryCmdAddItem(PlayerInventory inventory, ItemType itemType)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			inventoryCmdAddItemMethod?.Invoke(inventory, new object[1] { itemType });
		}

		private static void TryDebugGrantItem(PlayerInventory inventory)
		{
			//IL_004a: 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)
			int num = ((customItemData == null) ? 1 : customItemData.MaxUses);
			if (NetworkServer.active)
			{
				bool flag = inventory.ServerTryAddItem(CustomItemType, num);
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogInfo((object)$"Granted Ball Saboteur to local player via ServerTryAddItem (result: {flag}).");
				}
			}
			else
			{
				InvokeInventoryCmdAddItem(inventory, CustomItemType);
				ManualLogSource log2 = Log;
				if (log2 != null)
				{
					log2.LogInfo((object)"Sent CmdAddItem for Ball Saboteur (requires cheats enabled on host to succeed).");
				}
			}
		}

		private static Vector3 ResolveRescueAnchor(GolfBall ball)
		{
			//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_0016: 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_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ball == (Object)null)
			{
				return Vector3.zero;
			}
			Vector3 serverLastStrokePosition = ball.ServerLastStrokePosition;
			if (!(serverLastStrokePosition != Vector3.zero))
			{
				return ((Component)ball).transform.position;
			}
			return serverLastStrokePosition;
		}

		private static void ApplyActivationHopIfStationary(GolfBall ball)
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ball == (Object)null || (Object)(object)Instance == (Object)null)
			{
				return;
			}
			float value = Instance.hopImpulseConfig.Value;
			if (!(value <= 0f) && ball.IsStationary)
			{
				Rigidbody component = ((Component)ball).GetComponent<Rigidbody>();
				if (!((Object)(object)component == (Object)null))
				{
					component.AddForce(Vector3.up * value, (ForceMode)2);
				}
			}
		}

		private static void RescueFallenBall(ActiveSabotage sabotage)
		{
			//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_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: 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_00aa: Unknown result type (might be due to invalid IL or missing references)
			Vector3 position = ((Component)sabotage.Ball).transform.position;
			Rigidbody component = ((Component)sabotage.Ball).GetComponent<Rigidbody>();
			if ((Object)(object)component != (Object)null)
			{
				component.linearVelocity = Vector3.zero;
				component.angularVelocity = Vector3.zero;
				component.position = sabotage.StrokeStartPosition;
			}
			((Component)sabotage.Ball).transform.position = sabotage.StrokeStartPosition;
			ManualLogSource log = Log;
			if (log != null)
			{
				log.LogInfo((object)$"Rescued sabotaged ball {sabotage.BallNetId} from y={position.y:F1} → {sabotage.StrokeStartPosition} (fell {sabotage.StrokeStartPosition.y - position.y:F1}m).");
			}
		}

		private static void EnsureCubeApplied(GolfBall ball)
		{
			if ((Object)(object)ball == (Object)null || (Object)(object)((NetworkBehaviour)ball).netIdentity == (Object)null)
			{
				return;
			}
			uint netId = ((NetworkBehaviour)ball).netId;
			if (!BallMorphs.TryGetValue(netId, out var value) || (Object)(object)value == (Object)null)
			{
				value = ((Component)ball).GetComponent<RuntimeBallMorph>();
				if ((Object)(object)value == (Object)null)
				{
					value = ((Component)ball).gameObject.AddComponent<RuntimeBallMorph>();
				}
				BallMorphs[netId] = value;
			}
			value.ApplyCube();
		}

		private static void EnsureSphereApplied(GolfBall ball)
		{
			if (!((Object)(object)ball == (Object)null) && !((Object)(object)((NetworkBehaviour)ball).netIdentity == (Object)null))
			{
				uint netId = ((NetworkBehaviour)ball).netId;
				if (BallMorphs.TryGetValue(netId, out var value) && (Object)(object)value != (Object)null)
				{
					value.RestoreSphere();
				}
				BallMorphs.Remove(netId);
			}
		}

		private static void EnsurePoolInjected(ItemPool pool, float targetPercent)
		{
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if ((Object)(object)pool == (Object)null || (Object)(object)Instance == (Object)null || customItemData == null || itemPoolSpawnChancesField == null || itemSpawnChanceType == null || itemSpawnChanceItemField == null || itemSpawnChanceWeightField == null || injectedPools.Contains(pool) || !(itemPoolSpawnChancesField.GetValue(pool) is Array array))
				{
					return;
				}
				for (int i = 0; i < array.Length; i++)
				{
					object value = array.GetValue(i);
					if ((ItemType)itemSpawnChanceItemField.GetValue(value) == CustomItemType)
					{
						injectedPools.Add(pool);
						return;
					}
				}
				float num = Mathf.Clamp(targetPercent, 0f, 99.99f);
				float num2 = num / 100f;
				float totalSpawnChanceWeight = pool.TotalSpawnChanceWeight;
				float num3 = ((num2 <= 0f) ? 0f : (num2 * totalSpawnChanceWeight / (1f - num2)));
				object obj = Activator.CreateInstance(itemSpawnChanceType);
				itemSpawnChanceItemField.SetValue(obj, CustomItemType);
				itemSpawnChanceWeightField.SetValue(obj, num3);
				Array array2 = Array.CreateInstance(itemSpawnChanceType, array.Length + 1);
				Array.Copy(array, array2, array.Length);
				array2.SetValue(obj, array.Length);
				itemPoolSpawnChancesField.SetValue(pool, array2);
				pool.UpdateTotalWeight();
				injectedPools.Add(pool);
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogInfo((object)$"Injected Ball Saboteur into item pool '{((Object)pool).name}' at weight {num3:F3} (target {num:F1}%).");
				}
			}
			catch (Exception arg)
			{
				ManualLogSource log2 = Log;
				if (log2 != null)
				{
					log2.LogError((object)$"EnsurePoolInjected failed for pool '{((pool != null) ? ((Object)pool).name : null)}': {arg}");
				}
			}
		}

		private static void TrackPoolSpawnPercent(ItemPool pool, float targetPercent)
		{
			if (!((Object)(object)pool == (Object)null))
			{
				trackedPoolSpawnPercents[pool] = Mathf.Clamp(targetPercent, 0f, 100f);
			}
		}

		internal static void TryRegisterCustomLocalizationEntry()
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			if (localizationEntryRegistered)
			{
				return;
			}
			try
			{
				LocalizedStringDatabase stringDatabase = LocalizationSettings.StringDatabase;
				if (stringDatabase == null)
				{
					return;
				}
				StringTable table = ((LocalizedDatabase<StringTable, StringTableEntry>)(object)stringDatabase).GetTable(TableReference.op_Implicit("Data"), (Locale)null);
				if (!((Object)(object)table == (Object)null))
				{
					string text = "ITEM_" + 1001;
					if (((DetailedLocalizationTable<StringTableEntry>)(object)table).GetEntryFromReference(TableEntryReference.op_Implicit(text)) == null)
					{
						((DetailedLocalizationTable<StringTableEntry>)(object)table).AddEntry(text, "Ball Saboteur");
					}
					localizationEntryRegistered = true;
					ManualLogSource log = Log;
					if (log != null)
					{
						log.LogInfo((object)("Registered localization entry 'Data/" + text + "' = 'Ball Saboteur'."));
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log2 = Log;
				if (log2 != null)
				{
					log2.LogWarning((object)("Localization registration failed (name will show as fallback): " + ex.Message));
				}
				localizationEntryRegistered = true;
			}
		}

		private static bool TryGetMatchSetupSliderIndex(MatchSetupRules rules, ItemType itemType, out int sliderIndex)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Expected I4, but got Unknown
			sliderIndex = -1;
			if ((Object)(object)rules == (Object)null || matchSetupRulesItemOrderLookupField == null || matchSetupRulesSpawnChanceSlidersField == null)
			{
				return false;
			}
			int num = itemType - 1;
			if (num < 0)
			{
				return false;
			}
			if (!(matchSetupRulesItemOrderLookupField.GetValue(rules) is int[] array) || num >= array.Length)
			{
				return false;
			}
			if (!(matchSetupRulesSpawnChanceSlidersField.GetValue(rules) is List<SliderOption> list))
			{
				return false;
			}
			sliderIndex = array[num];
			if (sliderIndex >= 0)
			{
				return sliderIndex < list.Count;
			}
			return false;
		}

		private static bool CurrentMatchSetupPoolContainsUiUnsupportedItem(MatchSetupRules rules)
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)rules == (Object)null || matchSetupRulesGetCurrentItemPoolMethod == null)
			{
				return false;
			}
			object? obj = matchSetupRulesGetCurrentItemPoolMethod.Invoke(rules, null);
			ItemPool val = (ItemPool)((obj is ItemPool) ? obj : null);
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			ItemSpawnChance[] spawnChances = val.SpawnChances;
			for (int i = 0; i < spawnChances.Length; i++)
			{
				if (!TryGetMatchSetupSliderIndex(rules, spawnChances[i].item, out var _))
				{
					return true;
				}
			}
			return false;
		}

		private static bool HandleUnsupportedMatchSetupSpawnChanceUpdated(MatchSetupRules rules, ItemPoolId itemPoolId)
		{
			//IL_000c: 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_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)rules == (Object)null)
			{
				return false;
			}
			if (TryGetMatchSetupSliderIndex(rules, itemPoolId.itemType, out var _))
			{
				return false;
			}
			if (((NetworkBehaviour)rules).isServer && matchSetupRulesServerUpdateSpawnChanceValueMethod != null)
			{
				matchSetupRulesServerUpdateSpawnChanceValueMethod.Invoke(rules, new object[1] { itemPoolId });
			}
			if (((matchSetupRulesCurrentItemPoolIndexField != null) ? ((int)matchSetupRulesCurrentItemPoolIndexField.GetValue(rules)) : (-1)) == itemPoolId.itemPoolIndex && matchSetupRulesCurrentItemPoolDirtyField != null)
			{
				matchSetupRulesCurrentItemPoolDirtyField.SetValue(rules, true);
			}
			if (matchSetupRulesUpdateTotalWeightForPoolMethod != null)
			{
				matchSetupRulesUpdateTotalWeightForPoolMethod.Invoke(rules, new object[1] { itemPoolId.itemPoolIndex });
			}
			if (PauseMenu.IsPaused && SingletonBehaviour<PauseMenu>.HasInstance)
			{
				SingletonBehaviour<PauseMenu>.Instance.UpdateItemProbabilites();
			}
			return true;
		}

		private static bool RunSafeMatchSetupUpdate(MatchSetupRules rules)
		{
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_017d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)rules == (Object)null)
			{
				return false;
			}
			if (!MatchSetupMenu.IsActive || matchSetupRulesCurrentItemPoolDirtyField == null)
			{
				return false;
			}
			if (!(bool)matchSetupRulesCurrentItemPoolDirtyField.GetValue(rules))
			{
				return false;
			}
			if (matchSetupRulesCurrentItemPoolIndexField == null || matchSetupRulesTotalWeightPerPoolField == null || matchSetupRulesSpawnChanceWeightsField == null || matchSetupRulesSpawnChanceSlidersField == null || matchSetupRulesGetCurrentItemPoolMethod == null || matchSetupRulesUpdateSliderGreyedOutMethod == null)
			{
				return false;
			}
			int num = (int)matchSetupRulesCurrentItemPoolIndexField.GetValue(rules);
			Dictionary<int, float> dictionary = matchSetupRulesTotalWeightPerPoolField.GetValue(rules) as Dictionary<int, float>;
			IDictionary<ItemPoolId, float> dictionary2 = matchSetupRulesSpawnChanceWeightsField.GetValue(rules) as IDictionary<ItemPoolId, float>;
			List<SliderOption> list = matchSetupRulesSpawnChanceSlidersField.GetValue(rules) as List<SliderOption>;
			object? obj = matchSetupRulesGetCurrentItemPoolMethod.Invoke(rules, null);
			ItemPool val = (ItemPool)((obj is ItemPool) ? obj : null);
			if (dictionary == null || dictionary2 == null || list == null || (Object)(object)val == (Object)null)
			{
				return false;
			}
			dictionary.TryGetValue(num, out var value);
			foreach (SliderOption item in list)
			{
				if (!((Selectable)item.Slider).interactable)
				{
					item.SetValueText($"{0:0.#}%");
					matchSetupRulesUpdateSliderGreyedOutMethod.Invoke(rules, new object[1] { item });
				}
			}
			ItemSpawnChance[] spawnChances = val.SpawnChances;
			foreach (ItemSpawnChance val2 in spawnChances)
			{
				if (TryGetMatchSetupSliderIndex(rules, val2.item, out var sliderIndex))
				{
					SliderOption val3 = list[sliderIndex];
					dictionary2.TryGetValue(ItemPoolId.Get(num, val2.item), out var value2);
					float num2 = ((value > float.Epsilon) ? (value2 / value) : 0f);
					val3.SetValueText($"{num2 * 100f:0.#}%");
					matchSetupRulesUpdateSliderGreyedOutMethod.Invoke(rules, new object[1] { val3 });
				}
			}
			matchSetupRulesCurrentItemPoolDirtyField.SetValue(rules, false);
			return true;
		}

		private static float GetVirtualWeightForCustomItem(MatchSetupRules rules, int poolIndex)
		{
			if ((Object)(object)Instance == (Object)null || (Object)(object)rules == (Object)null || matchSetupRulesTotalWeightPerPoolField == null)
			{
				return 0f;
			}
			float num = ((poolIndex == 1) ? Instance.leaderSpawnChancePercentConfig.Value : Instance.spawnChancePercentConfig.Value);
			if (num <= 0f)
			{
				return 0f;
			}
			if (!(matchSetupRulesTotalWeightPerPoolField.GetValue(rules) is Dictionary<int, float> dictionary) || !dictionary.TryGetValue(poolIndex, out var value) || value <= 0f)
			{
				return 0f;
			}
			float num2 = Mathf.Clamp(num, 0f, 99.99f) / 100f;
			return num2 * value / (1f - num2);
		}

		private static float GetSpawnChancePercentForPlayer(ItemSpawnerSettings settings, PlayerInfo player)
		{
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Invalid comparison between Unknown and I4
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			//IL_019d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: 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)
			//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Instance == (Object)null || (Object)(object)settings == (Object)null || (Object)(object)player == (Object)null || (Object)(object)player.AsGolfer == (Object)null)
			{
				return 0f;
			}
			if ((Object)(object)settings.AheadOfBallItemPool != (Object)null && player.AsGolfer.IsAheadOfBall)
			{
				return Instance.spawnChancePercentConfig.Value;
			}
			List<ItemPoolData> itemPools = settings.ItemPools;
			if (itemPools == null || itemPools.Count == 0)
			{
				return 0f;
			}
			if (SingletonBehaviour<DrivingRangeManager>.HasInstance || itemPools.Count == 1)
			{
				return Instance.spawnChancePercentConfig.Value;
			}
			if ((Object)(object)GolfHoleManager.MainHole == (Object)null)
			{
				return Instance.spawnChancePercentConfig.Value;
			}
			float num = 0f;
			Vector3 val;
			if ((int)CourseManager.MatchState <= 3)
			{
				Vector3 position = ((Component)GolfHoleManager.MainHole).transform.position;
				float num2 = float.MaxValue;
				foreach (PlayerGolfer serverMatchParticipant in CourseManager.ServerMatchParticipants)
				{
					if ((Object)(object)serverMatchParticipant == (Object)null || serverMatchParticipant.IsMatchResolved)
					{
						continue;
					}
					PlayerInfo playerInfo = serverMatchParticipant.PlayerInfo;
					if (!((Object)(object)playerInfo == (Object)null) && !((Object)(object)playerInfo.AsSpectator == (Object)null) && !playerInfo.AsSpectator.IsSpectating)
					{
						val = position - ((Component)serverMatchParticipant).transform.position;
						float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
						if (sqrMagnitude < num2)
						{
							num2 = sqrMagnitude;
						}
					}
				}
				if (num2 < float.MaxValue)
				{
					num = Mathf.Sqrt(num2);
				}
			}
			val = ((Component)GolfHoleManager.MainHole).transform.position - ((Component)player).transform.position;
			float num3 = ((Vector3)(ref val)).magnitude - num;
			int num4 = itemPools.Count - 1;
			for (int i = 0; i < itemPools.Count - 1; i++)
			{
				if (num3 <= itemPools[i + 1].minDistanceBehindLeader)
				{
					num4 = i;
					break;
				}
			}
			if (num4 != 0)
			{
				return Instance.spawnChancePercentConfig.Value;
			}
			return Instance.leaderSpawnChancePercentConfig.Value;
		}
	}
}