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 dictionary)) { return true; } List list = new List(); foreach (KeyValuePair 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(); sphereCollider = (((Object)(object)ball != (Object)null) ? ball.Collider : ((Component)this).GetComponent()); ballRigidbody = ((Component)this).GetComponent(); originalRenderers = ballRenderersField?.GetValue(ball) as Renderer[]; if (originalRenderers == null || originalRenderers.Length == 0) { originalRenderers = ((Component)this).GetComponentsInChildren(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(); } 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(); if ((Object)(object)cubeCollider == (Object)null) { cubeCollider = ((Component)this).gameObject.AddComponent(); } } 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(); 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(); 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 ActiveSabotages = new Dictionary(); private static readonly Dictionary BallMorphs = new Dictionary(); 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 injectedPools = new HashSet(); private static readonly Dictionary trackedPoolSpawnPercents = new Dictionary(); 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 restoreDistanceMetersConfig; private ConfigEntry spawnChancePercentConfig; private ConfigEntry leaderSpawnChancePercentConfig; private ConfigEntry hopImpulseConfig; private ConfigEntry fallRescueThresholdConfig; private ConfigEntry cubeMassMultiplierConfig; private ConfigEntry tintPickupEnabledConfig; private ConfigEntry tintPickupColorHexConfig; private ConfigEntry debugGrantHotkeyEnabledConfig; private ConfigEntry 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("Gameplay", "RestoreDistanceMeters", 3f, "Minimum stroke distance before a sabotaged cube ball can restore once it stops."); spawnChancePercentConfig = ((BaseUnityPlugin)this).Config.Bind("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("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("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("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("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("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("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("Debug", "EnableGrantHotkey", true, "When enabled, pressing F8 grants the local player one Ball Saboteur item."); debugSelfSabotageHotkeyEnabledConfig = ((BaseUnityPlugin)this).Config.Bind("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), "k__BackingField"); itemDataPrefabField = AccessTools.Field(typeof(ItemData), "k__BackingField"); itemDataMaxUsesField = AccessTools.Field(typeof(ItemData), "k__BackingField"); itemDataCanUsageAffectBallsField = AccessTools.Field(typeof(ItemData), "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), "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.write = WriteBallSabotageState; Reader.read = ReadBallSabotageState; networkSerializersRegistered = true; } NetworkClient.ReplaceHandler((Action)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 list = null; foreach (KeyValuePair 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(); } 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(); } 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(); } 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 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(val, val2.transform); ((Object)customPickupPrefab).name = "BallSaboteurPickupRuntime"; PhysicalItem component = customPickupPrefab.GetComponent(); if ((Object)(object)component != (Object)null && physicalItemTypeField != null) { physicalItemTypeField.SetValue(component, CustomItemType); } ApplyPickupTint(customPickupPrefab); NetworkIdentity component2 = customPickupPrefab.GetComponent(); 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(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() : null); if ((Object)(object)val == (Object)null && (Object)(object)target != (Object)null) { Entity component = ((Component)target).GetComponent(); 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, 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(); 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(); 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(); 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(); if ((Object)(object)value == (Object)null) { value = ((Component)ball).gameObject.AddComponent(); } 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)(object)stringDatabase).GetTable(TableReference.op_Implicit("Data"), (Locale)null); if (!((Object)(object)table == (Object)null)) { string text = "ITEM_" + 1001; if (((DetailedLocalizationTable)(object)table).GetEntryFromReference(TableEntryReference.op_Implicit(text)) == null) { ((DetailedLocalizationTable)(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 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.HasInstance) { SingletonBehaviour.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 dictionary = matchSetupRulesTotalWeightPerPoolField.GetValue(rules) as Dictionary; IDictionary dictionary2 = matchSetupRulesSpawnChanceWeightsField.GetValue(rules) as IDictionary; List list = matchSetupRulesSpawnChanceSlidersField.GetValue(rules) as List; 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 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 itemPools = settings.ItemPools; if (itemPools == null || itemPools.Count == 0) { return 0f; } if (SingletonBehaviour.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; } } }