using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using FMOD; using FMOD.Studio; using FMODUnity; using HarmonyLib; using Microsoft.CodeAnalysis; using Mirror; using TMPro; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.Localization; using UnityEngine.Localization.Components; using UnityEngine.Localization.Settings; using UnityEngine.Localization.Tables; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("GameAssembly")] [assembly: IgnoresAccessChecksTo("SharedAssembly")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("com.github.BETCGaming.BallSwapItem")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.1.0")] [assembly: AssemblyInformationalVersion("1.0.1+e2919cd6113bc7dbe8ada3ebbffb6ea78be3d52a")] [assembly: AssemblyProduct("com.github.BETCGaming.BallSwapItem")] [assembly: AssemblyTitle("BallSwapItem")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.1.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [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 BepInEx { [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] [Conditional("CodeGeneration")] [Embedded] internal sealed class BepInAutoPluginAttribute : Attribute { public BepInAutoPluginAttribute(string? id = null, string? name = null, string? version = null) { } } } namespace BepInEx.Preloader.Core.Patching { [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] [Conditional("CodeGeneration")] [Embedded] internal sealed class PatcherAutoPluginAttribute : Attribute { public PatcherAutoPluginAttribute(string? id = null, string? name = null, string? version = null) { } } } namespace Microsoft.CodeAnalysis { [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace BallSwapItem { internal static class MirrorSerializers { public static void Register() { Writer.write = delegate(NetworkWriter writer, SwitcherooRequestMessage message) { writer.Write(message.Token); }; Reader.read = (NetworkReader reader) => new SwitcherooRequestMessage { Token = reader.Read() }; Writer.write = delegate { }; Reader.read = (NetworkReader _) => default(SwitcherooHelloMessage); Writer.write = delegate(NetworkWriter writer, SwitcherooArmedMessage message) { writer.Write(message.WindUpSeconds); writer.Write(message.UserName); }; Reader.read = (NetworkReader reader) => new SwitcherooArmedMessage { WindUpSeconds = reader.Read(), UserName = reader.Read() }; Writer.write = delegate(NetworkWriter writer, SwitcherooResultMessage message) { writer.Write(message.SwappedCount); }; Reader.read = (NetworkReader reader) => new SwitcherooResultMessage { SwappedCount = reader.Read() }; Writer.write = delegate(NetworkWriter writer, SwitcherooUseReplyMessage message) { writer.Write(message.Accepted); writer.Write(message.Reason); writer.Write(message.Token); }; Reader.read = (NetworkReader reader) => new SwitcherooUseReplyMessage { Accepted = reader.Read(), Reason = reader.Read(), Token = reader.Read() }; Writer.write = delegate(NetworkWriter writer, SwitcherooLockMessage message) { writer.Write(message.Locked); }; Reader.read = (NetworkReader reader) => new SwitcherooLockMessage { Locked = reader.Read() }; Plugin.Log.LogInfo((object)"Registered Switcheroo message serializers."); } } internal static class ModGate { private const float GraceSeconds = 10f; private static readonly Dictionary FirstSeen = new Dictionary(); private static readonly HashSet Modded = new HashSet(); public static void Reset() { FirstSeen.Clear(); Modded.Clear(); } public static void MarkModded(NetworkConnectionToClient conn) { if (conn != null && Modded.Add(conn.connectionId)) { Plugin.Log.LogInfo((object)$"Connection {conn.connectionId} has BallSwapItem installed."); } } public static void Tick() { if (!NetworkServer.active || !Plugin.BlockUnmodded.Value) { return; } float time = Time.time; List list = null; foreach (NetworkConnectionToClient value2 in NetworkServer.connections.Values) { if (value2 != null && !Modded.Contains(value2.connectionId) && (object)value2 != NetworkServer.localConnection && ((NetworkConnection)value2).isAuthenticated) { if (!FirstSeen.TryGetValue(value2.connectionId, out var value)) { FirstSeen[value2.connectionId] = time; } else if (time - value >= 10f) { (list ?? (list = new List())).Add(value2); } } } if (list == null) { return; } foreach (NetworkConnectionToClient item in list) { Plugin.Log.LogWarning((object)($"Disconnecting connection {item.connectionId}: BallSwapItem is not installed. " + "Set BlockUnmodded to false to allow unmodded players (the Switcheroo will then be withheld).")); FirstSeen.Remove(item.connectionId); ((NetworkConnection)item).Disconnect(); } } } internal sealed class ModRunner : MonoBehaviour { internal static ModRunner? Instance { get; private set; } private void Awake() { Instance = this; } private void Start() { SwitcherooAudio.Load(); } private void Update() { SwitcherooNetwork.EnsureHandlers(); SwitcherooNetwork.TrackHeldItems(); SwitcherooUi.Tick(); SwitcherooLocalization.EnsureApplied(); SwitcherooThrownItem.EnsureRegistered(); ModGate.Tick(); if (Plugin.DebugHotkeys.Value && Keyboard.current != null) { if (((ButtonControl)Keyboard.current[(Key)102]).wasPressedThisFrame) { GiveSwitcheroo(); } if (((ButtonControl)Keyboard.current[(Key)103]).wasPressedThisFrame) { ForceSwap(); } } } private static void GiveSwitcheroo() { if (!NetworkServer.active) { Plugin.Log.LogWarning((object)"F9 ignored: only the host can hand out items directly."); return; } PlayerInventory localPlayerInventory = GameManager.LocalPlayerInventory; if ((Object)(object)localPlayerInventory == (Object)null) { Plugin.Log.LogWarning((object)"F9 ignored: no local player inventory yet."); return; } bool flag = localPlayerInventory.ServerTryAddItem((ItemType)200, 1); Plugin.Log.LogInfo((object)(flag ? "Gave the local player a Switcheroo." : "Could not give a Switcheroo (inventory full?).")); } private static void ForceSwap() { if (!NetworkServer.active) { Plugin.Log.LogWarning((object)"F10 ignored: only the host can run a swap."); return; } string failureReason; int count = SwapService.TrySwap(out failureReason).Count; Plugin.Log.LogInfo((object)((count > 0) ? $"Swapped {count} balls." : ("Swap did not run: " + failureReason + "."))); } } [BepInPlugin("com.github.BETCGaming.BallSwapItem", "BallSwapItem", "1.0.1")] public class Plugin : BaseUnityPlugin { public const string Id = "com.github.BETCGaming.BallSwapItem"; internal static ManualLogSource Log { get; private set; } internal static ConfigEntry Enabled { get; private set; } internal static ConfigEntry WindUpSeconds { get; private set; } internal static ConfigEntry BlockUnmodded { get; private set; } internal static ConfigEntry SoundVolume { get; private set; } internal static ConfigEntry DenialVolume { get; private set; } internal static ConfigEntry DebugHotkeys { get; private set; } internal static ConfigEntry VerboseLogging { get; private set; } public static string Name => "BallSwapItem"; public static string Version => "1.0.1"; private void Awake() { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Expected O, but got Unknown //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Expected O, but got Unknown //IL_0151: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; Enabled = ((BaseUnityPlugin)this).Config.Bind("General", "Enabled", true, "Enable the Switcheroo item. When off, the item is not registered and never spawns."); WindUpSeconds = ((BaseUnityPlugin)this).Config.Bind("General", "WindUpSeconds", 3f, new ConfigDescription("Seconds between using the Switcheroo and the swap landing.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 10f), Array.Empty())); BlockUnmodded = ((BaseUnityPlugin)this).Config.Bind("Host", "BlockUnmodded", true, "Disconnect players who do not have this mod installed. They cannot handle the Switcheroo if the host hands them one. Host setting; ignored on clients."); SoundVolume = ((BaseUnityPlugin)this).Config.Bind("General", "SoundVolume", 0.8f, new ConfigDescription("Volume of the sound played once a swap resolves. This plays outside the game's FMOD mix, so the in-game volume sliders do not affect it.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); DenialVolume = ((BaseUnityPlugin)this).Config.Bind("General", "DenialVolume", 0.25f, new ConfigDescription("Volume of the sound played when a Switcheroo use is refused because the swap has already been used this round.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); DebugHotkeys = ((BaseUnityPlugin)this).Config.Bind("Debug", "DebugHotkeys", false, "F9 gives the local player a Switcheroo, F10 forces a swap. Host only, for testing."); MirrorSerializers.Register(); SwitcherooLocalization.Initialize(); VerboseLogging = ((BaseUnityPlugin)this).Config.Bind("Debug", "VerboseLogging", false, "Log every step of using the item and running a swap. For diagnosing a stuck or repeating use."); new Harmony("com.github.BETCGaming.BallSwapItem").PatchAll(); ((Component)this).gameObject.AddComponent(); Log.LogInfo((object)("Plugin " + Name + " v" + Version + " is loaded!")); } } internal static class SwapService { private static readonly Random Rng = new Random(); private static readonly Action UpdateNameTag = AccessTools.MethodDelegate>(AccessTools.Method(typeof(GolfBall), "UpdateNameTag", (Type[])null, (Type[])null), (object)null, true); public static List TrySwap(out string failureReason) { failureReason = string.Empty; if (!NetworkServer.active) { failureReason = "swap attempted off the server"; return new List(); } List list = CollectEligiblePlayers(); if (list.Count < 2) { failureReason = $"only {list.Count} eligible ball(s) in play"; return new List(); } GolfBall[] array = (GolfBall[])(object)new GolfBall[list.Count]; for (int i = 0; i < list.Count; i++) { array[i] = list[i].NetworkownBall; } Derange(array); for (int j = 0; j < list.Count; j++) { list[j].NetworkownBall = array[j]; array[j].Networkowner = list[j]; UpdateNameTag(array[j]); } return list; } public static int CountEligible() { return CollectEligiblePlayers().Count; } private static List CollectEligiblePlayers() { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) List list = new List(); foreach (PlayerGolfer matchParticipant in CourseManager.MatchParticipants) { if (!((Object)(object)matchParticipant == (Object)null) && !((Object)(object)matchParticipant.NetworkownBall == (Object)null) && (int)matchParticipant.MatchResolution == 0) { GolfBall networkownBall = matchParticipant.NetworkownBall; if (!networkownBall.isInHole && (int)networkownBall.OutOfBoundsReturnState == 0 && HasTeedOff(matchParticipant)) { list.Add(matchParticipant); } } } return list; } private static bool HasTeedOff(PlayerGolfer golfer) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) PlayerInfo playerInfo = golfer.PlayerInfo; if ((Object)(object)playerInfo == (Object)null) { return false; } PlayerState val = default(PlayerState); if (CourseManager.TryGetPlayerState(playerInfo.PlayerId.Guid, ref val)) { return val.matchStrokes > 0; } return false; } private static void Derange(T[] items) { T[] array = (T[])items.Clone(); for (int i = 0; i < 100; i++) { for (int num = items.Length - 1; num > 0; num--) { int num2 = Rng.Next(num + 1); int num3 = num; int num4 = num2; T val = items[num2]; T val2 = items[num]; items[num3] = val; items[num4] = val2; } bool flag = false; for (int j = 0; j < items.Length; j++) { if ((object)items[j] == (object)array[j]) { flag = true; break; } } if (!flag) { return; } } Array.Copy(array, items, array.Length); T val3 = items[^1]; Array.Copy(array, 0, items, 1, array.Length - 1); items[0] = val3; } } internal static class Switcheroo { public const ItemType Type = (ItemType)200; public const string DisplayName = "Switcheroo"; public const Rule OncePerHoleRule = (Rule)200; internal static readonly Color DeviceColor = new Color(1f, 0.4117647f, 0.7058824f, 1f); private const uint NetworkAssetId = 3047489537u; private static ItemData? itemData; private static bool failedOnce; private static GameObject? prefabHolder; private static GameObject PrefabHolder { get { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown if ((Object)(object)prefabHolder == (Object)null) { prefabHolder = new GameObject("BallSwapItemPrefabs"); prefabHolder.SetActive(false); Object.DontDestroyOnLoad((Object)(object)prefabHolder); } return prefabHolder; } } public static void Register(ItemCollection collection) { if (!Plugin.Enabled.Value || failedOnce) { return; } try { if (itemData == null) { itemData = BuildItemData(collection); } if (itemData != null) { if (Array.IndexOf(collection.items, itemData) < 0) { ItemData[] array = collection.items; Array.Resize(ref array, array.Length + 1); array[^1] = itemData; collection.items = array; } collection.allItemData[(ItemType)200] = itemData; } } catch (Exception arg) { failedOnce = true; Plugin.Log.LogError((object)$"Failed to register the Switcheroo item, it will not appear: {arg}"); } } private static ItemData? BuildItemData(ItemCollection collection) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown ItemData val = default(ItemData); if (!collection.TryGetItemData((ItemType)10, ref val)) { Plugin.Log.LogError((object)"Orbital Laser item data not found; cannot build the Switcheroo from it."); return null; } if ((Object)(object)val.Prefab == (Object)null) { return null; } ItemData val2 = new ItemData(); FieldInfo[] fields = typeof(ItemData).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { fieldInfo.SetValue(val2, fieldInfo.GetValue(val)); } val2.Type = (ItemType)200; val2.Icon = BuildIcon(); val2.Prefab = BuildPrefab(val.Prefab); val2.MaxUses = 1; val2.NonAimUse = (ItemNonAimingUse)2; val2.CanUsageAffectBalls = false; val2.CanUsageAffectTeammateBalls = false; val2.IsExplosive = false; val2.CanBreakBreakableIce = false; val2.CanHitProjectiles = false; val2.HitInfoFeedMessageIcon = (InfoFeedIconType)25; val2.name = null; val2.Initialize(); Plugin.Log.LogInfo((object)$"Built Switcheroo item data as ItemType {200}."); return val2; } private static GameObject BuildPrefab(GameObject donorPrefab) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) bool activeSelf = donorPrefab.activeSelf; donorPrefab.SetActive(false); GameObject val; try { val = Object.Instantiate(donorPrefab); } finally { donorPrefab.SetActive(activeSelf); } ((Object)val).name = "SwitcherooItem"; PhysicalItem val2 = default(PhysicalItem); if (val.TryGetComponent(ref val2)) { val2.itemType = (ItemType)200; } else { Plugin.Log.LogWarning((object)"Switcheroo pickup has no PhysicalItem component."); } val.transform.SetParent(PrefabHolder.transform, false); val.SetActive(true); Recolour(val); RegisterNetworkPrefab(val); return val; } private static void RegisterNetworkPrefab(GameObject prefab) { NetworkIdentity val = default(NetworkIdentity); if (!prefab.TryGetComponent(ref val)) { Plugin.Log.LogWarning((object)"Switcheroo pickup has no NetworkIdentity; it will not spawn when dropped."); return; } FieldInfo fieldInfo = AccessTools.Field(typeof(NetworkIdentity), "_assetId"); if ((object)fieldInfo == null) { Plugin.Log.LogWarning((object)"Mirror's assetId field was not found; dropped Switcheroos may appear as Orbital Lasers."); return; } fieldInfo.SetValue(val, 0u); NetworkClient.RegisterPrefab(prefab, 3047489537u); Plugin.Log.LogInfo((object)$"Registered the Switcheroo pickup as network asset {val.assetId}."); } public static void EnsureNetworkPrefabRegistered() { ItemData? obj = itemData; if (!((Object)(object)((obj != null) ? obj.Prefab : null) == (Object)null)) { NetworkClient.RegisterPrefab(itemData.Prefab, 3047489537u); } } internal static void Recolour(GameObject target) { //IL_0037: 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) Renderer[] componentsInChildren = target.GetComponentsInChildren(true); foreach (Renderer val in componentsInChildren) { Material[] materials = val.materials; foreach (Material val2 in materials) { if (val2.HasProperty("_BaseColor")) { val2.SetColor("_BaseColor", DeviceColor); } if (val2.HasProperty("_Color")) { val2.SetColor("_Color", DeviceColor); } } } } private static Sprite? BuildIcon() { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected O, but got Unknown //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) using Stream stream = typeof(Switcheroo).Assembly.GetManifestResourceStream("BallSwapItem.Assets.switcheroo-icon.png"); if (stream == null) { Plugin.Log.LogWarning((object)"Embedded Switcheroo icon missing; the item will show no icon."); return null; } byte[] array = new byte[stream.Length]; stream.Read(array, 0, array.Length); Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false); if (!ImageConversion.LoadImage(val, array)) { Plugin.Log.LogWarning((object)"Switcheroo icon failed to decode; the item will show no icon."); return null; } ((Object)val).name = "SwitcherooIcon"; Object.DontDestroyOnLoad((Object)(object)val); Sprite val2 = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f)); ((Object)val2).name = "SwitcherooIcon"; Object.DontDestroyOnLoad((Object)(object)val2); return val2; } } [HarmonyPatch(typeof(ItemCollection), "Initialize")] internal static class ItemCollectionInitializePatch { private static void Postfix(ItemCollection __instance) { Switcheroo.Register(__instance); } } [HarmonyPatch(/*Could not decode attribute arguments.*/)] internal static class ItemDataNamePatch { private static bool Prefix(ItemData __instance, ref string __result) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Invalid comparison between Unknown and I4 if ((int)__instance.Type != 200) { return true; } __result = "Switcheroo"; return false; } } internal static class SwitcherooAudio { private const string ResourceName = "BallSwapItem.Assets.bomboclat.mp3"; private static Sound sound; private static bool loaded; private static bool failed; public static void Load() { EnsureSound(); } public static void PlayDenial() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: 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_0021: 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_0031: Unknown result type (might be due to invalid IL or missing references) try { EventInstance val = RuntimeManager.CreateInstance(GameManager.AudioSettings.CosmeticsButtonSelectDisabled); ((EventInstance)(ref val)).setVolume(Mathf.Clamp01(Plugin.DenialVolume.Value)); ((EventInstance)(ref val)).start(); ((EventInstance)(ref val)).release(); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not play the denial sound: " + ex.Message)); } } public static void PlayAnticipation() { //IL_0005: 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_0012: Unknown result type (might be due to invalid IL or missing references) try { RuntimeManager.PlayOneShot(GameManager.AudioSettings.OrbitalLaserAnticipationEvent, default(Vector3)); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not play the wind-up cue: " + ex.Message)); } } public static void PlayPayoff() { //IL_0008: 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_0010: 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_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: 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_0056: 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) if (EnsureSound()) { System coreSystem = RuntimeManager.CoreSystem; Channel val2 = default(Channel); RESULT val = ((System)(ref coreSystem)).playSound(sound, default(ChannelGroup), false, ref val2); if ((int)val != 0) { Plugin.Log.LogWarning((object)$"Could not play the swap sound: {val}"); } else { ((Channel)(ref val2)).setVolume(Mathf.Clamp01(Plugin.SoundVolume.Value)); } } } private static bool EnsureSound() { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) if (loaded) { return true; } if (failed) { return false; } try { if (!RuntimeManager.IsInitialized) { return false; } byte[] array = ReadResource(); if (array == null) { failed = true; return false; } CREATESOUNDEXINFO val = new CREATESOUNDEXINFO { cbsize = Marshal.SizeOf(typeof(CREATESOUNDEXINFO)), length = (uint)array.Length }; System coreSystem = RuntimeManager.CoreSystem; RESULT val2 = ((System)(ref coreSystem)).createSound(array, (MODE)2305, ref val, ref sound); if ((int)val2 != 0) { failed = true; Plugin.Log.LogWarning((object)$"Could not decode the swap sound, it will not play: {val2}"); return false; } loaded = true; Plugin.Log.LogInfo((object)"Swap sound ready."); return true; } catch (Exception ex) { failed = true; Plugin.Log.LogWarning((object)("Could not prepare the swap sound, it will not play: " + ex.Message)); return false; } } private static byte[]? ReadResource() { using Stream stream = typeof(SwitcherooAudio).Assembly.GetManifestResourceStream("BallSwapItem.Assets.bomboclat.mp3"); if (stream == null) { Plugin.Log.LogWarning((object)"Swap sound missing from the plugin; it will not play."); return null; } using MemoryStream memoryStream = new MemoryStream(); stream.CopyTo(memoryStream); return memoryStream.ToArray(); } } internal static class SwitcherooEquipment { public const EquipmentType Type = (EquipmentType)200; private static EquipmentSettings? settings; private static bool failedOnce; public static void EnsureRegistered() { if (SingletonBehaviour.HasInstance) { EquipmentCollection equipmentCollection = SingletonBehaviour.Instance.equipmentCollection; if ((Object)(object)equipmentCollection != (Object)null) { Register(equipmentCollection); } } } public static void Register(EquipmentCollection collection) { if (!Plugin.Enabled.Value || failedOnce) { return; } try { if (settings == null) { settings = Build(collection); } if (settings != null) { if (Array.IndexOf(collection.equipment, settings) < 0) { EquipmentSettings[] array = collection.equipment; Array.Resize(ref array, array.Length + 1); array[^1] = settings; collection.equipment = array; } collection.equipmentDictionary[(EquipmentType)200] = settings; } } catch (Exception arg) { failedOnce = true; Plugin.Log.LogError((object)$"Could not register the Switcheroo's held model: {arg}"); } } private static EquipmentSettings? Build(EquipmentCollection collection) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Expected O, but got Unknown //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Expected O, but got Unknown EquipmentSettings val = default(EquipmentSettings); if (!collection.TryGetEquipmentSettings((EquipmentType)12, ref val) || (Object)(object)val.Prefab == (Object)null) { return null; } GameObject gameObject = ((Component)val.Prefab).gameObject; bool activeSelf = gameObject.activeSelf; gameObject.SetActive(false); GameObject val2; try { val2 = Object.Instantiate(gameObject); } finally { gameObject.SetActive(activeSelf); } ((Object)val2).name = "SwitcherooModel"; Switcheroo.Recolour(val2); GameObject val3 = new GameObject("SwitcherooEquipment"); val3.SetActive(false); Object.DontDestroyOnLoad((Object)(object)val3); val2.transform.SetParent(val3.transform, false); val2.SetActive(true); val2.transform.localPosition = Vector3.zero; val2.transform.localRotation = Quaternion.identity; Equipment val4 = val3.AddComponent(); val4.Type = (EquipmentType)200; Equipment val5 = default(Equipment); if (val2.TryGetComponent(ref val5)) { val5.Type = (EquipmentType)200; } EquipmentSettings val6 = new EquipmentSettings(); val6.Type = (EquipmentType)200; val6.Prefab = val4; Plugin.Log.LogInfo((object)$"Built the Switcheroo's held model as EquipmentType {200}."); return val6; } } [HarmonyPatch(typeof(EquipmentCollection), "Initialize")] internal static class EquipmentCollectionInitializePatch { private static void Postfix(EquipmentCollection __instance) { SwitcherooEquipment.Register(__instance); } } [HarmonyPatch(typeof(PlayerInventory), "LocalPlayerUpdateEquipmentSwitchers")] internal static class EquipmentSwitcherPatch { private static readonly MethodInfo? CanHoldEquipment = FindCanHoldEquipment(); private static bool Prefix(PlayerInventory __instance) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Invalid comparison between Unknown and I4 //IL_0031: Unknown result type (might be due to invalid IL or missing references) if ((int)__instance.GetEffectivelyEquippedItem(false) != 200) { return true; } SwitcherooEquipment.EnsureRegistered(); if ((object)CanHoldEquipment == null) { return true; } bool flag = (bool)CanHoldEquipment.Invoke(__instance, null); bool flag2 = !((Enum)__instance.thrownItem).HasFlag((Enum)(object)(ThrownItemHand)2); __instance.PlayerInfo.RightHandEquipmentSwitcher.SetEquipment((EquipmentType)((flag && flag2) ? 200 : 0)); __instance.PlayerInfo.LeftHandEquipmentSwitcher.SetEquipment((EquipmentType)0); return false; } private static MethodInfo? FindCanHoldEquipment() { MethodInfo methodInfo = AccessTools.GetDeclaredMethods(typeof(PlayerInventory)).FirstOrDefault((MethodInfo m) => m.Name.StartsWith("g__CanHoldEquipment", StringComparison.Ordinal) && m.ReturnType == typeof(bool) && m.GetParameters().Length == 0); if ((object)methodInfo == null) { Plugin.Log.LogWarning((object)"Could not find PlayerInventory's CanHoldEquipment; the Switcheroo will not show in hand."); } return methodInfo; } } internal static class SwitcherooLocalization { private static bool applied; public static void Initialize() { LocalizationManager.LanguageChanged += delegate { applied = false; }; } public static void EnsureApplied() { //IL_0016: 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) if (applied) { return; } try { StringTable val = ((LocalizedDatabase)(object)LocalizationSettings.StringDatabase)?.GetTable(TableReference.op_Implicit(((object)(StringTable)1/*cast due to .constrained prefix*/).ToString()), (Locale)null); if (!((Object)(object)val == (Object)null)) { string text = $"ITEM_{200}"; ((DetailedLocalizationTable)(object)val).AddEntry(text, "Switcheroo"); applied = true; Plugin.Log.LogInfo((object)("Registered the name 'Switcheroo' as " + text + ".")); } } catch (Exception ex) { applied = true; Plugin.Log.LogWarning((object)("Could not name the Switcheroo, its name may show as a key: " + ex.Message)); } } } [HarmonyPatch(typeof(PlayerAnimatorIo), "SetEquippedItem")] internal static class AnimatorEquippedItemPatch { private static void Prefix(ref ItemType equippedItem) { if ((int)equippedItem == 200) { equippedItem = (ItemType)10; } } } internal enum SwapRequestState { None, Waiting, Accepted, Denied } internal struct SwitcherooRequestMessage : NetworkMessage { public uint Token; } [StructLayout(LayoutKind.Sequential, Size = 1)] internal struct SwitcherooHelloMessage : NetworkMessage { } internal struct SwitcherooArmedMessage : NetworkMessage { public float WindUpSeconds; public string UserName; } internal enum SwitcherooDenialReason : byte { None, SwapInProgress, AlreadyUsedThisRound, NotHolding, TooSoon, NotEnoughBalls } internal struct SwitcherooUseReplyMessage : NetworkMessage { public bool Accepted; public byte Reason; public uint Token; } internal struct SwitcherooResultMessage : NetworkMessage { public int SwappedCount; } internal struct SwitcherooLockMessage : NetworkMessage { public bool Locked; } internal static class SwitcherooNetwork { private const double MinSecondsBetweenRequests = 1.0; private static readonly Dictionary LastRequestPerConnection = new Dictionary(); private const float BusyGraceSeconds = 1.5f; public const float ReplyTimeoutSeconds = 2f; private static double clientBusyUntil; private static uint pendingToken; private static bool serverLocked; private static bool holeHookInstalled; private static bool serverHandlerRegistered; private static bool clientHandlerRegistered; private static bool helloSent; private static bool swapPending; private const float HeldMemorySeconds = 5f; private static readonly Dictionary LastHeldSwitcheroo = new Dictionary(); public static bool LockedThisRound { get; private set; } public static bool SwapInProgress => Time.timeAsDouble < clientBusyUntil; public static SwapRequestState RequestState { get; private set; } = SwapRequestState.None; public static string DenialText { get; private set; } = "SWAP UNAVAILABLE"; public static void EnsureHandlers() { if (NetworkServer.active) { if (!serverHandlerRegistered) { NetworkServer.RegisterHandler((Action)OnServerRequest, true); NetworkServer.RegisterHandler((Action)OnServerHello, false); serverHandlerRegistered = true; Plugin.Log.LogInfo((object)"Registered Switcheroo server handlers."); } } else if (serverHandlerRegistered) { serverHandlerRegistered = false; swapPending = false; serverLocked = false; LastRequestPerConnection.Clear(); LastHeldSwitcheroo.Clear(); ModGate.Reset(); } if (NetworkClient.active) { if (!clientHandlerRegistered) { NetworkClient.RegisterHandler((Action)OnClientArmed, true); NetworkClient.RegisterHandler((Action)OnClientResult, true); NetworkClient.RegisterHandler((Action)OnClientLock, true); NetworkClient.RegisterHandler((Action)OnClientUseReply, true); clientHandlerRegistered = true; Switcheroo.EnsureNetworkPrefabRegistered(); Plugin.Log.LogInfo((object)"Registered Switcheroo client handlers."); } if (!helloSent && NetworkClient.isConnected) { NetworkConnectionToServer connection = NetworkClient.connection; if (connection != null && ((NetworkConnection)connection).isAuthenticated) { NetworkClient.Send(default(SwitcherooHelloMessage), 0); helloSent = true; } } } else { clientHandlerRegistered = false; helloSent = false; LockedThisRound = false; clientBusyUntil = 0.0; RequestState = SwapRequestState.None; } if (!holeHookInstalled) { CourseManager.CurrentHoleGlobalIndexChanged += OnHoleChanged; holeHookInstalled = true; } } private static void OnHoleChanged() { LockedThisRound = false; if (NetworkServer.active) { serverLocked = false; NetworkServer.SendToAll(new SwitcherooLockMessage { Locked = false }, 0, false); } } public static void RequestSwap() { if (!NetworkClient.active) { Plugin.Log.LogWarning((object)"Switcheroo used while not connected; ignoring."); RequestState = SwapRequestState.Denied; DenialText = "NOT CONNECTED"; } else { RequestState = SwapRequestState.Waiting; pendingToken++; NetworkClient.Send(new SwitcherooRequestMessage { Token = pendingToken }, 0); } } public static void TimeOutRequest() { RequestState = SwapRequestState.Denied; DenialText = "NO RESPONSE"; } private static void OnClientUseReply(SwitcherooUseReplyMessage message) { if (message.Token == pendingToken && RequestState == SwapRequestState.Waiting) { if (message.Accepted) { RequestState = SwapRequestState.Accepted; return; } SwitcherooDenialReason reason = (SwitcherooDenialReason)message.Reason; RequestState = SwapRequestState.Denied; DenialText = reason switch { SwitcherooDenialReason.SwapInProgress => "SWAP IN PROGRESS", SwitcherooDenialReason.AlreadyUsedThisRound => "ONCE PER ROUND", SwitcherooDenialReason.NotEnoughBalls => "NO BALLS TO SWAP", SwitcherooDenialReason.NotHolding => "NO SWITCHEROO", SwitcherooDenialReason.TooSoon => "TOO SOON", _ => "SWAP UNAVAILABLE", }; Trace.Log($"client: use denied by the host ({reason})"); } } private static void OnServerHello(NetworkConnectionToClient conn, SwitcherooHelloMessage message) { ModGate.MarkModded(conn); ((NetworkConnection)conn).Send(new SwitcherooLockMessage { Locked = serverLocked }, 0); } private static void OnServerRequest(NetworkConnectionToClient conn, SwitcherooRequestMessage message) { if (conn == null) { return; } double timeAsDouble = Time.timeAsDouble; if (LastRequestPerConnection.TryGetValue(conn.connectionId, out var value) && timeAsDouble - value < 1.0) { Plugin.Log.LogWarning((object)$"Ignoring rapid Switcheroo request from connection {conn.connectionId}."); Deny(conn, message.Token, SwitcherooDenialReason.TooSoon); return; } LastRequestPerConnection[conn.connectionId] = timeAsDouble; if (swapPending) { Plugin.Log.LogInfo((object)"Switcheroo request ignored: a swap is already counting down."); Deny(conn, message.Token, SwitcherooDenialReason.SwapInProgress); return; } if (serverLocked) { Plugin.Log.LogInfo((object)"Switcheroo request ignored: already used this round."); Deny(conn, message.Token, SwitcherooDenialReason.AlreadyUsedThisRound); return; } if (!SenderHoldsSwitcheroo(conn)) { Plugin.Log.LogWarning((object)$"Ignoring Switcheroo request from connection {conn.connectionId}: no Switcheroo in their inventory."); Deny(conn, message.Token, SwitcherooDenialReason.NotHolding); return; } int num = SwapService.CountEligible(); if (num < 2) { Plugin.Log.LogInfo((object)$"Switcheroo request refused: only {num} eligible ball(s) in play."); Deny(conn, message.Token, SwitcherooDenialReason.NotEnoughBalls); return; } PlayerInfo initiator = (((Object)(object)((NetworkConnection)conn).identity == (Object)null) ? null : ((Component)((NetworkConnection)conn).identity).GetComponent()); Trace.Log($"server: request accepted from connection {conn.connectionId}"); ((NetworkConnection)conn).Send(new SwitcherooUseReplyMessage { Accepted = true, Reason = 0, Token = message.Token }, 0); ModRunner? instance = ModRunner.Instance; if (instance != null) { ((MonoBehaviour)instance).StartCoroutine(RunSwap(initiator)); } } private static void Deny(NetworkConnectionToClient conn, uint token, SwitcherooDenialReason reason) { ((NetworkConnection)conn).Send(new SwitcherooUseReplyMessage { Accepted = false, Reason = (byte)reason, Token = token }, 0); } private static IEnumerator RunSwap(PlayerInfo? initiator) { swapPending = true; float num = Mathf.Max(0f, Plugin.WindUpSeconds.Value); NetworkServer.SendToAll(new SwitcherooArmedMessage { WindUpSeconds = num, UserName = NameOf(initiator) }, 0, false); Trace.Log($"server: armed, waiting {num:0.0}s"); yield return (object)new WaitForSeconds(num); Trace.Log("server: wind-up elapsed"); if (!NetworkServer.active) { Plugin.Log.LogWarning((object)"Swap abandoned: no longer the server when the wind-up elapsed."); swapPending = false; yield break; } string failureReason; List list = SwapService.TrySwap(out failureReason); if (list.Count == 0) { Plugin.Log.LogInfo((object)("Switcheroo used but no swap ran: " + failureReason + ".")); } else { Plugin.Log.LogInfo((object)$"Switcheroo swapped {list.Count} balls."); NetworkServer.SendToAll(new SwitcherooResultMessage { SwappedCount = list.Count }, 0, false); AnnounceInFeed(initiator, list); if (MatchSetupRules.GetValueAsBool((Rule)200)) { serverLocked = true; NetworkServer.SendToAll(new SwitcherooLockMessage { Locked = true }, 0, false); } } swapPending = false; } public static void TrackHeldItems() { if (!NetworkServer.active) { return; } float time = Time.time; foreach (NetworkConnectionToClient value in NetworkServer.connections.Values) { if (value != null && HoldsSwitcheroo(value)) { LastHeldSwitcheroo[value.connectionId] = time; } } } private static bool SenderHoldsSwitcheroo(NetworkConnectionToClient conn) { if (HoldsSwitcheroo(conn)) { return true; } if (LastHeldSwitcheroo.TryGetValue(conn.connectionId, out var value)) { return Time.time - value <= 5f; } return false; } private static bool HoldsSwitcheroo(NetworkConnectionToClient conn) { //IL_0039: 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_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_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Invalid comparison between Unknown and I4 //IL_0056: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)((NetworkConnection)conn).identity == (Object)null) { return false; } PlayerInfo component = ((Component)((NetworkConnection)conn).identity).GetComponent(); PlayerInventory val = ((component != null) ? component.Inventory : null); if ((Object)(object)val == (Object)null) { return false; } Enumerator enumerator = val.slots.GetEnumerator(); try { while (enumerator.MoveNext()) { InventorySlot current = enumerator.Current; if ((int)current.itemType == 200 && current.remainingUses > 0) { return true; } } } finally { ((IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose(); } return false; } private static string NameOf(PlayerInfo? player) { if ((Object)(object)player == (Object)null) { return string.Empty; } PlayerId playerId = player.PlayerId; string text; if (!((Object)(object)playerId == (Object)null)) { text = playerId.PlayerNameNoRichText; if (text == null) { return string.Empty; } } else { text = string.Empty; } return text; } private static void AnnounceInFeed(PlayerInfo? initiator, List swapped) { if ((Object)(object)initiator == (Object)null) { return; } try { foreach (PlayerGolfer item in swapped) { PlayerInfo playerInfo = item.PlayerInfo; if ((Object)(object)playerInfo != (Object)null && (Object)(object)playerInfo != (Object)(object)initiator) { InfoFeed.ShowItemHitMessage(initiator, playerInfo, (ItemType)200); } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not post the swap to the info feed: " + ex.Message)); } } private static void OnClientLock(SwitcherooLockMessage message) { LockedThisRound = message.Locked; } private static void OnClientArmed(SwitcherooArmedMessage message) { clientBusyUntil = Time.timeAsDouble + (double)message.WindUpSeconds + 1.5; SwitcherooUi.BeginCountdown(message.WindUpSeconds, message.UserName); SwitcherooAudio.PlayAnticipation(); } private static void OnClientResult(SwitcherooResultMessage message) { clientBusyUntil = 0.0; Plugin.Log.LogInfo((object)$"Switcheroo swapped {message.SwappedCount} balls."); SwitcherooAudio.PlayPayoff(); } } internal static class SwitcherooPool { private static ItemSpawnerSettings? managed; private static readonly HashSet ManagedPools = new HashSet(); public static bool FallbackActive { get; private set; } public static void Remember(ItemSpawnerSettings settings) { managed = settings; } public static bool IsManaged(ItemSpawnerSettings settings) { return (Object)(object)managed == (Object)(object)settings; } public static bool IsManagedPool(ItemPool pool) { return ManagedPools.Contains(((Object)pool).GetInstanceID()); } public static void EnsureInjected() { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.Enabled.Value || (Object)(object)managed == (Object)null || FallbackActive) { return; } try { ManagedPools.Clear(); Inject(managed.AheadOfBallItemPoolDefaults); Inject(managed.AheadOfBallItemPool); foreach (ItemPoolData itemPoolsDefault in managed.ItemPoolsDefaults) { Inject(itemPoolsDefault.pool); } foreach (ItemPoolData itemPool in managed.ItemPools) { Inject(itemPool.pool); } } catch (Exception arg) { UseFallback($"the Switcheroo could not be added to the item pools: {arg}"); } } private static void Inject(ItemPool? pool) { //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)pool == (Object)null) { return; } ManagedPools.Add(((Object)pool).GetInstanceID()); if (!pool.ContainsItemType((ItemType)200)) { float num = RankWeight(pool, warnIfMissing: true); if (num <= 0f) { Plugin.Log.LogWarning((object)("Pool '" + ((Object)pool).name + "' has no weights to rank against; leaving it alone.")); return; } ItemSpawnChance[] array = pool.spawnChances; Array.Resize(ref array, array.Length + 1); array[^1] = new ItemSpawnChance { item = (ItemType)200, spawnChanceWeight = num }; pool.spawnChances = array; pool.UpdateTotalWeight(); Plugin.Log.LogInfo((object)$"Added the Switcheroo to pool '{((Object)pool).name}' at weight {num:0.###}."); } } internal static float RankWeight(ItemPool pool, bool warnIfMissing) { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: 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_0062: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Invalid comparison between Unknown and I4 //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) float spawnChanceWeight = pool.GetSpawnChanceWeight((ItemType)10); float spawnChanceWeight2 = pool.GetSpawnChanceWeight((ItemType)15); if (spawnChanceWeight > 0f && spawnChanceWeight2 > 0f) { return (spawnChanceWeight + spawnChanceWeight2) / 2f; } if (spawnChanceWeight > 0f || spawnChanceWeight2 > 0f) { return Mathf.Max(spawnChanceWeight, spawnChanceWeight2); } float num = 0f; ItemSpawnChance[] spawnChances = pool.SpawnChances; foreach (ItemSpawnChance val in spawnChances) { if ((int)val.item != 200 && !(val.spawnChanceWeight <= 0f) && (num == 0f || val.spawnChanceWeight < num)) { num = val.spawnChanceWeight; } } if (warnIfMissing) { Plugin.Log.LogInfo((object)("Pool '" + ((Object)pool).name + "' has neither the Orbital Laser nor the Thunderstorm; " + $"ranking the Switcheroo against its rarest item at weight {num:0.###}.")); } return num; } public static void UseFallback(string reason) { if (!FallbackActive) { FallbackActive = true; Plugin.Log.LogError((object)("Switcheroo item probabilities are unavailable: " + reason + " Falling back to picking the item as items are drawn, so it still spawns, but it has no slider in the match setup.")); Withdraw(); } } private static void Withdraw() { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)managed == (Object)null) { return; } try { RemoveFrom(managed.AheadOfBallItemPoolDefaults); RemoveFrom(managed.AheadOfBallItemPool); foreach (ItemPoolData itemPoolsDefault in managed.ItemPoolsDefaults) { RemoveFrom(itemPoolsDefault.pool); } foreach (ItemPoolData itemPool in managed.ItemPools) { RemoveFrom(itemPool.pool); } } catch (Exception arg) { Plugin.Log.LogError((object)$"Could not take the Switcheroo back out of the item pools: {arg}"); } } private static void RemoveFrom(ItemPool? pool) { //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_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Invalid comparison between Unknown and I4 //IL_0046: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)pool == (Object)null || !pool.ContainsItemType((ItemType)200)) { return; } List list = new List(pool.spawnChances.Length); ItemSpawnChance[] spawnChances = pool.spawnChances; foreach (ItemSpawnChance val in spawnChances) { if ((int)val.item != 200) { list.Add(val); } } pool.spawnChances = list.ToArray(); pool.UpdateTotalWeight(); } } [HarmonyPatch(typeof(ItemSpawnerSettings), "ResetRuntimeData")] internal static class ItemSpawnerRuntimePoolPatch { private static void Postfix(ItemSpawnerSettings __instance) { if (SwitcherooPool.IsManaged(__instance)) { SwitcherooPool.EnsureInjected(); } } } [HarmonyPatch(typeof(ItemPool), "GetWeightedRandomItem")] internal static class ItemPoolDrawPatch { private static bool Prefix(ItemPool __instance, ref ItemType __result) { if (!Plugin.Enabled.Value || !SwitcherooPool.FallbackActive) { return true; } if (!SwitcherooPool.IsManagedPool(__instance)) { return true; } float num = SwitcherooPool.RankWeight(__instance, warnIfMissing: false); float totalSpawnChanceWeight = __instance.TotalSpawnChanceWeight; if (num <= 0f || totalSpawnChanceWeight <= 0f) { return true; } if (Random.value >= num / (totalSpawnChanceWeight + num)) { return true; } __result = (ItemType)200; return false; } } [HarmonyPatch(typeof(MatchSetupRules), "Initialize")] internal static class MatchSetupRuleRowPatch { private const string LabelKey = "RULE_SWITCHEROO_ONCE_PER_HOLE"; private const string LabelText = "One Switcheroo per hole"; private static DropdownOption? row; private static int ownerId; private static TableReference labelTable; private static bool labelTableKnown; private static bool languageHookInstalled; private static void Postfix(MatchSetupRules __instance) { if (!Plugin.Enabled.Value) { return; } try { Build(__instance); } catch (Exception arg) { Plugin.Log.LogError((object)($"Could not add the once-per-hole row to the match setup: {arg} The rule stays off " + "and the Switcheroo can be used as often as it is found.")); } } private static void Build(MatchSetupRules rules) { MatchSetupRules.CategoryPerRule[(Rule)200] = (RuleCategory)3; if ((!((Object)(object)row == (Object)null) && ownerId == ((Object)rules).GetInstanceID()) || Clone(rules)) { AccessTools.Method(typeof(MatchSetupRules), "InitDropdownOnOff", (Type[])null, (Type[])null).Invoke(rules, new object[3] { row, (object)(Rule)200, null }); } } private static bool Clone(MatchSetupRules rules) { DropdownOption hitOtherPlayersBalls = rules.hitOtherPlayersBalls; if ((Object)(object)hitOtherPlayersBalls == (Object)null) { Plugin.Log.LogWarning((object)"No battle row to copy; the once-per-hole rule has no control."); return false; } GameObject val = Object.Instantiate(((Component)hitOtherPlayersBalls).gameObject, ((Component)hitOtherPlayersBalls).transform.parent); ((Object)val).name = "SwitcherooOncePerHole"; val.transform.SetSiblingIndex(((Component)hitOtherPlayersBalls).transform.GetSiblingIndex() + 1); DropdownOption component = val.GetComponent(); if ((Object)(object)component == (Object)null) { Object.Destroy((Object)(object)val); Plugin.Log.LogWarning((object)"The copied battle row has no DropdownOption; leaving the rule out of the UI."); return false; } Label(val); row = component; ownerId = ((Object)rules).GetInstanceID(); Plugin.Log.LogInfo((object)"Added the once-per-hole rule to the match setup's Battle section."); return true; } private static void Label(GameObject clone) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) LocalizeStringEvent componentInChildren = clone.GetComponentInChildren(true); if ((Object)(object)componentInChildren == (Object)null) { Plugin.Log.LogWarning((object)"The copied row has no localized label; it will read as the row it was copied from."); return; } labelTable = ((LocalizedReference)componentInChildren.StringReference).TableReference; labelTableKnown = true; if (AddEntry()) { ((LocalizedReference)componentInChildren.StringReference).TableEntryReference = TableEntryReference.op_Implicit("RULE_SWITCHEROO_ONCE_PER_HOLE"); componentInChildren.RefreshString(); InstallLanguageHook(); return; } TMP_Text component = ((Component)componentInChildren).GetComponent(); Object.Destroy((Object)(object)componentInChildren); if ((Object)(object)component != (Object)null) { component.text = "One Switcheroo per hole"; Plugin.Log.LogWarning((object)"Localization was not ready; the once-per-hole row is labelled in English only."); } } private static bool AddEntry() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) if (!labelTableKnown) { return false; } try { StringTable val = ((LocalizedDatabase)(object)LocalizationSettings.StringDatabase)?.GetTable(labelTable, (Locale)null); if ((Object)(object)val == (Object)null) { return false; } ((DetailedLocalizationTable)(object)val).AddEntry("RULE_SWITCHEROO_ONCE_PER_HOLE", "One Switcheroo per hole"); return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not name the once-per-hole rule: " + ex.Message)); return false; } } private static void InstallLanguageHook() { if (!languageHookInstalled) { LocalizationManager.LanguageChanged += delegate { AddEntry(); }; languageHookInstalled = true; } } } [HarmonyPatch(typeof(MatchSetupRules), "Initialize")] internal static class MatchSetupRulesPatch { private static void Prefix(MatchSetupRules __instance) { if (Plugin.Enabled.Value && !SwitcherooPool.FallbackActive) { try { Extend(__instance); } catch (Exception arg) { SwitcherooPool.UseFallback($"the match setup screen could not be extended: {arg}"); return; } SwitcherooPool.Remember(__instance.itemSpawnerSettings); SwitcherooPool.EnsureInjected(); } } private static void Extend(MatchSetupRules rules) { ItemType[] array = rules.itemOrder; if (Array.IndexOf(array, (ItemType)200) < 0) { ItemType[] array2 = (ItemType[])(object)new ItemType[array.Length + 1]; Array.Copy(array, array2, array.Length); array2[^1] = (ItemType)200; array = array2; } int[] array3 = new int[Math.Max(200, array.Length)]; for (int i = 0; i < array3.Length; i++) { array3[i] = Array.IndexOf(array, (ItemType)(i + 1)); } rules.itemOrder = array; rules.itemOrderLookup = array3; } } internal static class SwitcherooThrownItem { public const ThrownUsedItemType Type = (ThrownUsedItemType)200; private static bool registered; private static bool failedOnce; public static void EnsureRegistered() { //IL_00a9: Unknown result type (might be due to invalid IL or missing references) if (registered || failedOnce || !Plugin.Enabled.Value || !SingletonBehaviour.HasInstance) { return; } try { ThrownUsedItem value; if (ThrownUsedItemManager.prefabPerType.ContainsKey((ThrownUsedItemType)200)) { registered = true; } else if (ThrownUsedItemManager.prefabPerType.TryGetValue((ThrownUsedItemType)6, out value) && !((Object)(object)value == (Object)null)) { GameObject gameObject = ((Component)value).gameObject; bool activeSelf = gameObject.activeSelf; gameObject.SetActive(false); GameObject val; try { val = Object.Instantiate(gameObject); } finally { gameObject.SetActive(activeSelf); } ((Object)val).name = "SwitcherooThrownItem"; Object.DontDestroyOnLoad((Object)(object)val); Switcheroo.Recolour(val); ThrownUsedItem component = val.GetComponent(); component.type = (ThrownUsedItemType)200; ThrownUsedItemManager.prefabPerType[(ThrownUsedItemType)200] = component; registered = true; Plugin.Log.LogInfo((object)$"Registered the discarded Switcheroo as ThrownUsedItemType {200}."); } } catch (Exception arg) { failedOnce = true; Plugin.Log.LogError((object)$"Could not register the discarded Switcheroo: {arg}"); } } } [HarmonyPatch(typeof(PlayerInventory), "ThrowUsedItemInternal")] internal static class ThrowUsedItemPatch { private static bool Prefix(PlayerInventory __instance, ThrownUsedItemType thrownItemType, bool forcePlayerPosition, Vector3 forcedPlayerPosition) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 //IL_0012: Unknown result type (might be due to invalid IL or missing references) if ((int)thrownItemType != 200) { return true; } try { SwitcherooThrownItem.EnsureRegistered(); Throw(__instance, forcePlayerPosition, forcedPlayerPosition); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not throw the spent Switcheroo: " + ex.Message)); } return false; } private static void Throw(PlayerInventory inventory, bool forcePlayerPosition, Vector3 forcedPlayerPosition) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0082: 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_0088: 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) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: 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) //IL_0061: Unknown result type (might be due to invalid IL or missing references) ThrownUsedItem unusedThrownItem = ThrownUsedItemManager.GetUnusedThrownItem((ThrownUsedItemType)200); if (!((Object)(object)unusedThrownItem == (Object)null)) { PlayerInfo playerInfo = inventory.PlayerInfo; Quaternion orbitalLaserThrowDirectionLocalRotation = GameManager.ItemSettings.OrbitalLaserThrowDirectionLocalRotation; Vector3 orbitalLaserThrowLocalAngularVelocity = GameManager.ItemSettings.OrbitalLaserThrowLocalAngularVelocity; Vector3 val = default(Vector3); Quaternion val2 = default(Quaternion); ((Component)playerInfo.RightHandEquipmentSwitcher).transform.GetPositionAndRotation(ref val, ref val2); if (forcePlayerPosition) { val += forcedPlayerPosition - ((Component)inventory).transform.position; } Vector3 val3 = ((Component)inventory).transform.TransformDirection(orbitalLaserThrowDirectionLocalRotation * Vector3.forward); Vector3 val4 = ((Component)inventory).transform.TransformDirection(orbitalLaserThrowDirectionLocalRotation * orbitalLaserThrowLocalAngularVelocity); Vector3 val5 = playerInfo.Rigidbody.linearVelocity + val3 * GameManager.ItemSettings.OrbitalLaserThrowSpeed; Vector3 val6 = playerInfo.Rigidbody.angularVelocity + val4; unusedThrownItem.Initialize(val, val2, val5, val6, playerInfo.GetEffectiveTeam()); PhysicsManager.TemporarilyIgnoreCollisionsBetween(playerInfo.AsEntity, unusedThrownItem.AsEntity, 0.5f, false); } } } internal static class SwitcherooUi { private static readonly Color32 GradientBottom = new Color32(byte.MaxValue, (byte)252, (byte)212, byte.MaxValue); private const float DenialSeconds = 1.6f; private const float DenialFlashHz = 4f; private const float DenialAloneY = -180f; private const float DenialBelowCountdownY = -360f; private static TextMeshProUGUI? label; private static TextMeshProUGUI? userLabel; private static TextMeshProUGUI? denialLabel; private static CanvasGroup? group; private static float countdownEndTime; private static float denialEndTime; private static string denialText = string.Empty; private static string countdownUser = string.Empty; public static void BeginCountdown(float seconds, string userName) { countdownEndTime = Time.time + seconds; countdownUser = userName ?? string.Empty; } public static void ShowDenial(string message) { denialText = message; denialEndTime = Time.time + 1.6f; } public static void Tick() { //IL_013d: Unknown result type (might be due to invalid IL or missing references) float num = countdownEndTime - Time.time; float num2 = denialEndTime - Time.time; if (num <= 0f && num2 <= 0f) { if ((Object)(object)group != (Object)null && group.alpha != 0f) { group.alpha = 0f; } } else { if (!EnsureLabel()) { return; } group.alpha = 1f; if (num > 0f) { ((Behaviour)label).enabled = true; ((TMP_Text)label).text = $"SWITCHEROO IN {Mathf.CeilToInt(num)}"; ((Behaviour)userLabel).enabled = countdownUser.Length > 0; if (((Behaviour)userLabel).enabled) { ((TMP_Text)userLabel).text = "USED BY " + countdownUser.ToUpperInvariant(); } } else { ((Behaviour)label).enabled = false; ((Behaviour)userLabel).enabled = false; } if (num2 > 0f) { ((Behaviour)denialLabel).enabled = true; ((TMP_Text)denialLabel).text = denialText; ((TMP_Text)denialLabel).rectTransform.anchoredPosition = new Vector2(0f, (num > 0f) ? (-360f) : (-180f)); bool flag = Mathf.Repeat(num2 * 4f, 1f) > 0.5f; ((TMP_Text)denialLabel).alpha = (flag ? 1f : 0.15f); } else { ((Behaviour)denialLabel).enabled = false; } } } private static bool EnsureLabel() { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown //IL_0063: 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_00bb: Expected O, but got Unknown //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Unknown result type (might be due to invalid IL or missing references) //IL_01d1: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_01fd: Unknown result type (might be due to invalid IL or missing references) //IL_0230: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)label != (Object)null) { return true; } TMP_FontAsset val = FindGameFont(); if ((Object)(object)val == (Object)null) { return false; } GameObject val2 = new GameObject("BallSwapItemCountdown"); Object.DontDestroyOnLoad((Object)(object)val2); Canvas val3 = val2.AddComponent(); val3.renderMode = (RenderMode)0; val3.sortingOrder = 500; CanvasScaler val4 = val2.AddComponent(); val4.uiScaleMode = (ScaleMode)1; val4.referenceResolution = new Vector2(1920f, 1080f); val4.screenMatchMode = (ScreenMatchMode)0; val4.matchWidthOrHeight = 1f; group = val2.AddComponent(); group.alpha = 0f; group.interactable = false; group.blocksRaycasts = false; GameObject val5 = new GameObject("Label"); val5.transform.SetParent(val2.transform, false); label = val5.AddComponent(); ((TMP_Text)label).font = val; ((TMP_Text)label).fontSize = 76f; ((TMP_Text)label).alignment = (TextAlignmentOptions)514; ((TMP_Text)label).textWrappingMode = (TextWrappingModes)0; ((Graphic)label).raycastTarget = false; ((TMP_Text)label).enableVertexGradient = true; ((TMP_Text)label).colorGradient = new VertexGradient(Color.white, Color.white, Color32.op_Implicit(GradientBottom), Color32.op_Implicit(GradientBottom)); ((TMP_Text)label).outlineWidth = 0.2f; ((TMP_Text)label).outlineColor = new Color32((byte)0, (byte)0, (byte)0, byte.MaxValue); RectTransform rectTransform = ((TMP_Text)label).rectTransform; rectTransform.anchorMin = new Vector2(0.5f, 1f); rectTransform.anchorMax = new Vector2(0.5f, 1f); rectTransform.pivot = new Vector2(0.5f, 1f); rectTransform.anchoredPosition = new Vector2(0f, -180f); rectTransform.sizeDelta = new Vector2(1400f, 120f); userLabel = BuildSecondaryLabel(val2, val, "User", 40f, GradientBottom, -292f, 60f); denialLabel = BuildSecondaryLabel(val2, val, "Denial", 58f, new Color32(byte.MaxValue, (byte)59, (byte)48, byte.MaxValue), -180f, 120f); Plugin.Log.LogInfo((object)("Countdown label built using the game font '" + ((Object)val).name + "'.")); return true; } private static TextMeshProUGUI BuildSecondaryLabel(GameObject root, TMP_FontAsset font, string name, float fontSize, Color32 color, float y, float height) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: 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) GameObject val = new GameObject(name); val.transform.SetParent(root.transform, false); TextMeshProUGUI val2 = val.AddComponent(); ((TMP_Text)val2).font = font; ((TMP_Text)val2).fontSize = fontSize; ((TMP_Text)val2).alignment = (TextAlignmentOptions)514; ((TMP_Text)val2).textWrappingMode = (TextWrappingModes)0; ((Graphic)val2).raycastTarget = false; ((Graphic)val2).color = Color32.op_Implicit(color); ((TMP_Text)val2).outlineWidth = 0.2f; ((TMP_Text)val2).outlineColor = new Color32((byte)0, (byte)0, (byte)0, byte.MaxValue); ((Behaviour)val2).enabled = false; RectTransform rectTransform = ((TMP_Text)val2).rectTransform; rectTransform.anchorMin = new Vector2(0.5f, 1f); rectTransform.anchorMax = new Vector2(0.5f, 1f); rectTransform.pivot = new Vector2(0.5f, 1f); rectTransform.anchoredPosition = new Vector2(0f, y); rectTransform.sizeDelta = new Vector2(1400f, height); return val2; } private static TMP_FontAsset? FindGameFont() { TMP_Text[] array = Resources.FindObjectsOfTypeAll(); if (array.Length == 0) { return null; } return (from text in array select text.font into font where (Object)(object)font != (Object)null group font by font into @group orderby @group.Count() descending select @group.Key).FirstOrDefault(); } } [HarmonyPatch(typeof(PlayerInventory), "TryUseItem")] internal static class SwitcherooUsePatch { private static bool Prefix(PlayerInventory __instance, bool isAirhornReaction, ref bool shouldEatInput, ref bool __result) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Invalid comparison between Unknown and I4 //IL_009d: Unknown result type (might be due to invalid IL or missing references) shouldEatInput = true; if (!((NetworkBehaviour)__instance).isLocalPlayer) { return true; } if ((int)__instance.GetEffectivelyEquippedItem(false) != 200) { return true; } if (SwitcherooNetwork.LockedThisRound) { Refuse("ONCE PER ROUND"); __result = false; return false; } if (SwitcherooNetwork.SwapInProgress) { Trace.Log("use refused: a swap is already counting down"); Refuse("SWAP IN PROGRESS"); __result = false; return false; } int num = SwapService.CountEligible(); if (num < 2) { Trace.Log($"use refused: only {num} eligible ball(s)"); Refuse("NO BALLS TO SWAP"); __result = false; return false; } InventorySlot val = default(InventorySlot); ItemData val2 = default(ItemData); bool flag = default(bool); if (!__instance.CanUseEquippedItem(false, isAirhornReaction, ref val, ref val2, ref shouldEatInput, ref flag)) { __result = false; return false; } Trace.Log($"use accepted, currentItemUse={__instance.CurrentItemUse}, slot={__instance.EquippedItemIndex}"); __instance.ItemUseTimestamp = Time.timeAsDouble; __instance.CancelItemUse(); __instance.itemUseRoutine = ((MonoBehaviour)__instance).StartCoroutine(SwitcherooRoutine(__instance)); __instance.PlayerInfo.CancelEmote(false); __instance.CancelItemFlourish(); __result = true; return false; } private static void Refuse(string message) { SwitcherooUi.ShowDenial(message); SwitcherooAudio.PlayDenial(); } private static IEnumerator SwitcherooRoutine(PlayerInventory inventory) { Trace.Log("routine: started"); inventory.SetCurrentItemUse((ItemUseType)1); yield return (object)new WaitForSeconds(GameManager.ItemSettings.OrbitalLaserActivationTime); Trace.Log("routine: activation wait done, sending request"); SwitcherooNetwork.RequestSwap(); float waited = 0f; while (SwitcherooNetwork.RequestState == SwapRequestState.Waiting) { if (waited >= 2f) { SwitcherooNetwork.TimeOutRequest(); break; } yield return null; waited += Time.deltaTime; } if (SwitcherooNetwork.RequestState != SwapRequestState.Accepted) { Trace.Log("routine: use denied (" + SwitcherooNetwork.DenialText + "), keeping the item"); Refuse(SwitcherooNetwork.DenialText); inventory.SetCurrentItemUse((ItemUseType)0); yield break; } Trace.Log("routine: host accepted, spending the item"); int index = inventory.EquippedItemIndex; inventory.DecrementUseFromSlotAt(index); bool thrown = false; for (float timeSince = BMath.GetTimeSince(inventory.ItemUseTimestamp); timeSince < GameManager.ItemSettings.OrbitalLaserActivationTotalDuration; timeSince = BMath.GetTimeSince(inventory.ItemUseTimestamp)) { if (!thrown && timeSince >= GameManager.ItemSettings.OrbitalLaserThrowTime) { try { SwitcherooThrownItem.EnsureRegistered(); inventory.ThrowUsedItemForAllClients((ThrownUsedItemType)200, false, default(Vector3), false); inventory.LocalPlayerMarkThrownItem((ThrownItemHand)2); } catch (Exception arg) { Plugin.Log.LogError((object)$"Could not throw the spent Switcheroo: {arg}"); } thrown = true; Trace.Log($"routine: threw spent device at {timeSince:0.00}s"); } yield return null; } Trace.Log("routine: finished, clearing use state"); inventory.SetCurrentItemUse((ItemUseType)0); inventory.RemoveIfOutOfUses(index, true); } } internal static class Trace { public static void Log(string message) { if (Plugin.VerboseLogging.Value) { Plugin.Log.LogInfo((object)("[trace] " + message)); } } } } namespace System.Diagnostics.CodeAnalysis { [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class ConstantExpectedAttribute : Attribute { public object? Min { get; set; } public object? Max { get; set; } } [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class ExperimentalAttribute : Attribute { public string DiagnosticId { get; } public string? UrlFormat { get; set; } public ExperimentalAttribute(string diagnosticId) { DiagnosticId = diagnosticId; } } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] [ExcludeFromCodeCoverage] internal sealed class MemberNotNullAttribute : Attribute { public string[] Members { get; } public MemberNotNullAttribute(string member) { Members = new string[1] { member }; } public MemberNotNullAttribute(params string[] members) { Members = members; } } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] [ExcludeFromCodeCoverage] internal sealed class MemberNotNullWhenAttribute : Attribute { public bool ReturnValue { get; } public string[] Members { get; } public MemberNotNullWhenAttribute(bool returnValue, string member) { ReturnValue = returnValue; Members = new string[1] { member }; } public MemberNotNullWhenAttribute(bool returnValue, params string[] members) { ReturnValue = returnValue; Members = members; } } [AttributeUsage(AttributeTargets.Constructor, AllowMultiple = false, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class SetsRequiredMembersAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class StringSyntaxAttribute : Attribute { public const string CompositeFormat = "CompositeFormat"; public const string DateOnlyFormat = "DateOnlyFormat"; public const string DateTimeFormat = "DateTimeFormat"; public const string EnumFormat = "EnumFormat"; public const string GuidFormat = "GuidFormat"; public const string Json = "Json"; public const string NumericFormat = "NumericFormat"; public const string Regex = "Regex"; public const string TimeOnlyFormat = "TimeOnlyFormat"; public const string TimeSpanFormat = "TimeSpanFormat"; public const string Uri = "Uri"; public const string Xml = "Xml"; public string Syntax { get; } public object?[] Arguments { get; } public StringSyntaxAttribute(string syntax) { Syntax = syntax; Arguments = new object[0]; } public StringSyntaxAttribute(string syntax, params object?[] arguments) { Syntax = syntax; Arguments = arguments; } } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class UnscopedRefAttribute : Attribute { } } namespace System.Runtime.Versioning { [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class RequiresPreviewFeaturesAttribute : Attribute { public string? Message { get; } public string? Url { get; set; } public RequiresPreviewFeaturesAttribute() { } public RequiresPreviewFeaturesAttribute(string? message) { Message = message; } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class CallerArgumentExpressionAttribute : Attribute { public string ParameterName { get; } public CallerArgumentExpressionAttribute(string parameterName) { ParameterName = parameterName; } } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class CollectionBuilderAttribute : Attribute { public Type BuilderType { get; } public string MethodName { get; } public CollectionBuilderAttribute(Type builderType, string methodName) { BuilderType = builderType; MethodName = methodName; } } [AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class CompilerFeatureRequiredAttribute : Attribute { public const string RefStructs = "RefStructs"; public const string RequiredMembers = "RequiredMembers"; public string FeatureName { get; } public bool IsOptional { get; set; } public CompilerFeatureRequiredAttribute(string featureName) { FeatureName = featureName; } } [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class InterpolatedStringHandlerArgumentAttribute : Attribute { public string[] Arguments { get; } public InterpolatedStringHandlerArgumentAttribute(string argument) { Arguments = new string[1] { argument }; } public InterpolatedStringHandlerArgumentAttribute(params string[] arguments) { Arguments = arguments; } } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class InterpolatedStringHandlerAttribute : Attribute { } [EditorBrowsable(EditorBrowsableState.Never)] [ExcludeFromCodeCoverage] internal static class IsExternalInit { } [AttributeUsage(AttributeTargets.Method, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class ModuleInitializerAttribute : Attribute { } [AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = false, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class OverloadResolutionPriorityAttribute : Attribute { public int Priority { get; } public OverloadResolutionPriorityAttribute(int priority) { Priority = priority; } } [AttributeUsage(AttributeTargets.Parameter, Inherited = true, AllowMultiple = false)] [ExcludeFromCodeCoverage] internal sealed class ParamCollectionAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class RequiredMemberAttribute : Attribute { } [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] [EditorBrowsable(EditorBrowsableState.Never)] [ExcludeFromCodeCoverage] internal sealed class RequiresLocationAttribute : Attribute { } [AttributeUsage(AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Event | AttributeTargets.Interface, Inherited = false)] [ExcludeFromCodeCoverage] internal sealed class SkipLocalsInitAttribute : Attribute { } }