Decompiled source of TwitchTrolling v1.2.0
com.github.zehsteam.TwitchTrolling.dll
Decompiled 2 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using MenuLib; using MenuLib.MonoBehaviors; using MenuLib.Structs; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Photon.Pun; using REPOLib.Commands; using REPOLib.Extensions; using REPOLib.Modules; using TMPro; using TwitchChatAPI; using TwitchChatAPI.Enums; using TwitchChatAPI.Objects; using UnityEngine; using UnityEngine.AI; using UnityEngine.Networking; using UnityEngine.Serialization; using UnityEngine.UI; using com.github.zehsteam.TwitchTrolling.Dependencies; using com.github.zehsteam.TwitchTrolling.Extensions; using com.github.zehsteam.TwitchTrolling.Helpers; using com.github.zehsteam.TwitchTrolling.MEvents; using com.github.zehsteam.TwitchTrolling.MEvents.BaseEvents; using com.github.zehsteam.TwitchTrolling.MonoBehaviours; using com.github.zehsteam.TwitchTrolling.Objects; using com.github.zehsteam.TwitchTrolling.Patches; using com.github.zehsteam.TwitchTrolling.Twitch; using com.github.zehsteam.TwitchTrolling.Twitch.Helpers; using com.github.zehsteam.TwitchTrolling.Twitch.Objects; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("REPOLib")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("Zehs")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright © 2025 Zehs")] [assembly: AssemblyDescription("Let Twitch chat spawn monsters and trigger events with custom bit amounts and subs. Highly configurable, easy to use, no extension or app needed.")] [assembly: AssemblyFileVersion("1.2.0.0")] [assembly: AssemblyInformationalVersion("1.2.0+5b294c9d53c10e90587f4d124787d3a75832afd4")] [assembly: AssemblyProduct("TwitchTrolling")] [assembly: AssemblyTitle("com.github.zehsteam.TwitchTrolling")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.2.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace com.github.zehsteam.TwitchTrolling { internal static class Assets { public static GameObject PluginManagerPrefab { get; private set; } public static GameObject PluginHUDPrefab { get; private set; } public static GameObject EnemyNametagWorldCanvasPrefab { get; private set; } public static GameObject TruckPropsPrefab { get; private set; } public static GameObject AudioEnemySpawnPrefab { get; private set; } public static GameObject ModCreditsPrefab { get; private set; } public static EnemyConfigEntryDefaultValuesList EnemyConfigEntryDefaultValuesList { get; private set; } public static EnemySpawnSFXList EnemySpawnSFXList { get; private set; } public static MEventSFXList MEventSFXList { get; private set; } public static void Load() { string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); string text = "twitchtrolling_assets"; string text2 = Path.Combine(directoryName, text); if (!File.Exists(text2)) { Logger.LogFatal("Failed to load assets. AssetBundle file could not be found at path \"" + text2 + "\". Make sure the \"" + text + "\" file is in the same folder as the mod's DLL file."); } else { AssetBundle val = AssetBundle.LoadFromFile(text2); if ((Object)(object)val == (Object)null) { Logger.LogFatal("Failed to load assets. AssetBundle is null."); } else { OnAssetBundleLoaded(val); } } } private static void OnAssetBundleLoaded(AssetBundle assetBundle) { PluginManagerPrefab = LoadAsset<GameObject>("PluginManager", assetBundle); PluginHUDPrefab = LoadAsset<GameObject>("PluginHUD", assetBundle); EnemyNametagWorldCanvasPrefab = LoadAsset<GameObject>("EnemyNametagWorldCanvas", assetBundle); TruckPropsPrefab = LoadAsset<GameObject>("TruckProps", assetBundle); AudioEnemySpawnPrefab = LoadAsset<GameObject>("AudioEnemySpawn", assetBundle); Utilities.FixAudioMixerGroups(AudioEnemySpawnPrefab); ModCreditsPrefab = LoadAsset<GameObject>("TwitchTrolling ModCredits", assetBundle); NetworkPrefabs.RegisterNetworkPrefab(((Object)ModCreditsPrefab).name, ModCreditsPrefab); EnemyConfigEntryDefaultValuesList = LoadAsset<EnemyConfigEntryDefaultValuesList>("EnemyConfigEntryDefaultValuesList", assetBundle); EnemySpawnSFXList = LoadAsset<EnemySpawnSFXList>("EnemySpawnSFXList", assetBundle); MEventSFXList = LoadAsset<MEventSFXList>("MEventSFXList", assetBundle); } private static T LoadAsset<T>(string name, AssetBundle assetBundle) where T : Object { if (string.IsNullOrWhiteSpace(name)) { Logger.LogError("Failed to load asset of type \"" + typeof(T).Name + "\" from AssetBundle. Name is null or whitespace."); return default(T); } if ((Object)(object)assetBundle == (Object)null) { Logger.LogError("Failed to load asset of type \"" + typeof(T).Name + "\" with name \"" + name + "\" from AssetBundle. AssetBundle is null."); return default(T); } T val = assetBundle.LoadAsset<T>(name); if ((Object)(object)val == (Object)null) { Logger.LogError("Failed to load asset of type \"" + typeof(T).Name + "\" with name \"" + name + "\" from AssetBundle. No asset found with that type and name."); return default(T); } return val; } private static bool TryLoadAsset<T>(string name, AssetBundle assetBundle, out T asset) where T : Object { asset = LoadAsset<T>(name, assetBundle); return (Object)(object)asset != (Object)null; } } internal static class ConfigManager { public static ConfigFile ConfigFile { get; private set; } public static ConfigEntry<bool> ExtendedLogging { get; private set; } public static ConfigEntry<bool> TwitchIntegration_Enabled { get; private set; } public static ConfigEntry<bool> TwitchSubEvent_Enabled { get; private set; } public static ConfigEntry<int> TwitchSubEvent_Tier2EnemySpawnCountMultiplier { get; private set; } public static ConfigEntry<int> TwitchSubEvent_Tier3EnemySpawnCountMultiplier { get; private set; } public static ConfigEntry<bool> TwitchCheerEvent_Enabled { get; private set; } public static ConfigEntry<int> TwitchCheerEvent_MinAmountForRandomEnemy { get; private set; } public static ConfigEntry<bool> TwitchRaidEvent_Enabled { get; private set; } public static ConfigEntry<int> TwitchRaidEvent_ViewersPerRandomEnemy { get; private set; } public static ConfigEntry<int> TwitchRaidEvent_MaxEnemySpawnCount { get; private set; } public static ConfigEntry<bool> Enemy_SpawnDespawned { get; private set; } public static ConfigEntry<float> Enemy_MinSpawnDistance { get; private set; } public static ConfigEntry<float> Enemy_MaxSpawnDistance { get; private set; } public static ConfigEntry<bool> Event_Enabled { get; private set; } public static ConfigEntry<int> Event_AmountForRandomEvent { get; private set; } public static ConfigEntry<bool> Message_Enabled { get; private set; } public static ConfigEntry<float> Message_Duration { get; private set; } public static ConfigEntry<int> Message_FontSize { get; private set; } public static ConfigEntry<int> Message_BackgroundTransparency { get; private set; } public static ConfigEntry<bool> DeathMessage_Enabled { get; private set; } public static ConfigEntry<bool> DeathMessage_ShowPlatformIcon { get; private set; } public static ConfigEntry<float> DeathMessage_Duration { get; private set; } public static ConfigEntry<int> DeathMessage_FontSize { get; private set; } public static ConfigEntry<int> DeathMessage_BackgroundTransparency { get; private set; } public static ConfigEntry<bool> EnemyNametag_Enabled { get; private set; } public static ConfigEntry<bool> EnemyNametag_ShowPlatformIcon { get; private set; } public static ConfigEntry<float> EnemyNametag_SizeMultiplier { get; private set; } public static ConfigEntry<int> EnemyNametag_BackgroundTransparency { get; private set; } public static void Initialize(ConfigFile configFile) { ConfigFile = configFile; BindConfigs(); } private static void BindConfigs() { ExtendedLogging = ConfigFile.Bind<bool>("- General -", "ExtendedLogging", false, "Enable extended logging."); TwitchIntegration_Enabled = ConfigFile.Bind<bool>("- Twitch Integration -", "Enabled", true, "If enabled, Twitch integration will be enabled to spawn enemies from Subs, Cheers, and Raids."); TwitchSubEvent_Enabled = ConfigFile.Bind<bool>("- Twitch Sub Event -", "Enabled", true, "If enabled, Twitch subs will be able to spawn enemies."); TwitchSubEvent_Tier2EnemySpawnCountMultiplier = ConfigFile.Bind("- Twitch Sub Event -", "Tier2EnemySpawnCountMultiplier", 5, "The amount to multiply the enemy spawn count for tier 2 subs.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 30)); TwitchSubEvent_Tier3EnemySpawnCountMultiplier = ConfigFile.Bind("- Twitch Sub Event -", "Tier3EnemySpawnCountMultiplier", 10, "The amount to multiply the enemy spawn count for tier 3 subs.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 30)); TwitchCheerEvent_Enabled = ConfigFile.Bind<bool>("- Twitch Cheer Event -", "Enabled", true, "If enabled, Twitch cheers will be able to spawn enemies."); TwitchCheerEvent_MinAmountForRandomEnemy = ConfigFile.Bind("- Twitch Cheer Event -", "MinAmountForRandomEnemy", 350, "The min amount of bits for a random enemy spawn.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 1000)); TwitchCheerEvent_MinAmountForRandomEnemy.SettingChanged += delegate { PageManager.UpdatePage(); }; TwitchRaidEvent_Enabled = ConfigFile.Bind<bool>("- Twitch Raid Event -", "Enabled", true, "If enabled, Twitch raids will be able to spawn enemies."); TwitchRaidEvent_ViewersPerRandomEnemy = ConfigFile.Bind("- Twitch Raid Event -", "ViewersPerRandomEnemy", 5, "The amount of viewers for each random enemy spawn.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100)); TwitchRaidEvent_MaxEnemySpawnCount = ConfigFile.Bind("- Twitch Raid Event -", "MaxEnemySpawnCount", 20, "The max amount of enemies that can spawn from a raid.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100)); Enemy_SpawnDespawned = ConfigFile.Bind<bool>("- Enemy -", "SpawnDespawned", false, "If enabled, enemies will spawn despawned."); Enemy_MinSpawnDistance = ConfigFile.Bind("- Enemy -", "MinSpawnDistance", 25f, "The min distance an enemy can spawn from the local player. (meters)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f)); Enemy_MaxSpawnDistance = ConfigFile.Bind("- Enemy -", "MaxSpawnDistance", 300f, "The max distance an enemy can spawn from the local player. (meters)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 500f)); Event_Enabled = ConfigFile.Bind<bool>("- Event -", "Enabled", true, "If enabled, Twitch cheers will be able to trigger events."); Event_AmountForRandomEvent = ConfigFile.Bind("- Event -", "AmountForRandomEvent", 150, "The amount of bits for a random event.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 1000)); Event_AmountForRandomEvent.SettingChanged += delegate { PageManager.UpdatePage(); }; Message_Enabled = ConfigFile.Bind<bool>("- Message -", "Enabled", true, "If enabled, will show messages in the bottom right."); Message_Duration = ConfigFile.Bind("- Message -", "Duration", 10f, "The duration of a message. (seconds)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 60f)); Message_FontSize = ConfigFile.Bind("- Message -", "FontSize", 12, "The font size of the messages.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 50)); Message_BackgroundTransparency = ConfigFile.Bind("- Message -", "BackgroundTransparency", 192, "The transparency of the message background.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 255)); Message_FontSize.SettingChanged += delegate { MessageItem.OnConfigSettingsChanged(); }; Message_BackgroundTransparency.SettingChanged += delegate { MessageItem.OnConfigSettingsChanged(); }; DeathMessage_Enabled = ConfigFile.Bind<bool>("- Death Message -", "Enabled", true, "If enabled, will show a message when you die to an enemy spawned from a viewer."); DeathMessage_ShowPlatformIcon = ConfigFile.Bind<bool>("- Death Message -", "ShowPlatformIcon", true, "If enabled, will show which platform the enemy was spawned from."); DeathMessage_Duration = ConfigFile.Bind("- Death Message -", "Duration", 5f, "The duration of the death message. (seconds)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 60f)); DeathMessage_FontSize = ConfigFile.Bind("- Death Message -", "FontSize", 16, "The font size of the death message.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 50)); DeathMessage_BackgroundTransparency = ConfigFile.Bind("- Death Message -", "BackgroundTransparency", 192, "The transparency of the death message background.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 255)); EnemyNametag_Enabled = ConfigFile.Bind<bool>("- Enemy Nametag -", "Enabled", true, "If enabled, enemies will spawn with a nametag of the viewer that spawned that enemy."); EnemyNametag_ShowPlatformIcon = ConfigFile.Bind<bool>("- Enemy Nametag -", "ShowPlatformIcon", true, "If enabled, nametags will show which platform the enemy was spawned from."); EnemyNametag_SizeMultiplier = ConfigFile.Bind("- Enemy Nametag -", "SizeMultiplier", 0.75f, "The size multiplier for enemy nametags.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.25f, 5f)); EnemyNametag_BackgroundTransparency = ConfigFile.Bind("- Enemy Nametag -", "BackgroundTransparency", 192, "The transparency of the nametag background.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 255)); EnemyNametag_ShowPlatformIcon.SettingChanged += delegate { EnemyNametag.OnConfigSettingsChanged(); }; EnemyNametag_SizeMultiplier.SettingChanged += delegate { EnemyNametag.OnConfigSettingsChanged(); }; EnemyNametag_BackgroundTransparency.SettingChanged += delegate { EnemyNametag.OnConfigSettingsChanged(); }; } } internal static class EnemyConfigManager { public static ConfigFile ConfigFile => ConfigManager.ConfigFile; public static List<EnemyConfigEntry> ConfigEntries { get; private set; } = new List<EnemyConfigEntry>(); public static void Initialize() { EnemyConfigEntryDefaultValues[] list = Assets.EnemyConfigEntryDefaultValuesList.List; foreach (EnemyConfigEntryDefaultValues defaultValues in list) { RegisterConfigEntry(defaultValues); } EnemyParent val = default(EnemyParent); foreach (EnemySetup allEnemy in Enemies.AllEnemies) { if (EnemySetupExtensions.TryGetEnemyParent(allEnemy, ref val)) { RegisterConfigEntry(val.enemyName); } } } public static void RegisterConfigEntry(string enemyName) { if (!HasConfigEntry(enemyName)) { EnemyConfigEntry item = new EnemyConfigEntry(enemyName); ConfigEntries.Add(item); } } public static void RegisterConfigEntry(EnemyConfigEntryDefaultValues defaultValues) { if (!HasConfigEntry(defaultValues.EnemyName)) { EnemyConfigEntry item = new EnemyConfigEntry(defaultValues); ConfigEntries.Add(item); } } public static bool HasEnemies() { foreach (EnemyConfigEntry configEntry in ConfigEntries) { if (configEntry.Enabled.Value) { return true; } } return false; } public static bool HasConfigEntry(string enemyName) { return ConfigEntries.Any((EnemyConfigEntry x) => x.EnemyNameEquals(enemyName)); } public static EnemyConfigEntry GetConfigEntry(string enemyName) { return ConfigEntries.FirstOrDefault((EnemyConfigEntry x) => x.EnemyNameEquals(enemyName)); } public static bool TryGetConfigEntry(string enemyName, out EnemyConfigEntry configEntry) { configEntry = GetConfigEntry(enemyName); return configEntry != null; } public static List<EnemyConfigEntry> GetConfigEntriesThatMatchBitsAmount(int bitsAmount, bool includeDisable = false) { List<EnemyConfigEntry> list = new List<EnemyConfigEntry>(); foreach (EnemyConfigEntry configEntry in ConfigEntries) { if ((includeDisable || configEntry.Enabled.Value) && configEntry.BitsToSpawn.Value == bitsAmount) { list.Add(configEntry); } } return list; } public static bool TryGetConfigEntriesThatMatchBitsAmount(int bitsAmount, out List<EnemyConfigEntry> list, bool includeDisable = false) { list = GetConfigEntriesThatMatchBitsAmount(bitsAmount, includeDisable); return list.Any(); } public static List<EnemyConfigEntry> GetConfigEntriesThatMatchSubsAmount(int subsAmount, bool includeDisable = false) { List<EnemyConfigEntry> list = new List<EnemyConfigEntry>(); foreach (EnemyConfigEntry configEntry in ConfigEntries) { if ((includeDisable || configEntry.Enabled.Value) && configEntry.SubsToSpawn.Value == subsAmount) { list.Add(configEntry); } } return list; } public static bool TryGetConfigEntriesThatMatchSubsAmount(int subsAmount, out List<EnemyConfigEntry> list, bool includeDisable = false) { list = GetConfigEntriesThatMatchSubsAmount(subsAmount, includeDisable); return list.Any(); } } internal static class EnemyManager { private static AudioPlayerFactory _audioPlayerFactory; public static Dictionary<EnemyParent, ViewerData> ViewerEnemies { get; private set; } public static bool SpawnDespawned => ConfigManager.Enemy_SpawnDespawned.Value; public static float MinSpawnDistance => ConfigManager.Enemy_MinSpawnDistance.Value; public static float MaxSpawnDistance => ConfigManager.Enemy_MaxSpawnDistance.Value; static EnemyManager() { ViewerEnemies = new Dictionary<EnemyParent, ViewerData>(); _audioPlayerFactory = new AudioPlayerFactory { Type = AudioPlayerType.EnemySpawn, MaxDistance = 30f }; } public static void ResetViewerEnemies() { ViewerEnemies.Clear(); } public static void SpawnEnemy(ViewerSpawnData viewerSpawnData) { if (viewerSpawnData == null) { Logger.LogError("EnemyManager: Failed to spawn enemies. ViewerSpawnData is null.", extended: false, showMessage: true); return; } if (!CanSpawnEnemies()) { Logger.LogError("EnemyManager: Failed to spawn enemies. You are not allowed to spawn enemies at this time.", extended: false, showMessage: true); return; } List<EnemyConfigEntry> entries = new List<EnemyConfigEntry>(); for (int i = 0; i < viewerSpawnData.SpawnCount; i++) { if (EnemyConfigManager.TryGetConfigEntry(viewerSpawnData.TargetEnemyName, out var configEntry)) { entries.Add(configEntry); } else { entries.Add(EnemyConfigManager.ConfigEntries.GetRandom()); } } if (entries.Count < 1) { Logger.LogError("EnemyManager: Failed to spawn enemies. No enemies where found to spawn.", extended: false, showMessage: true); return; } if (entries.All((EnemyConfigEntry x) => x == entries[0])) { viewerSpawnData.SetTargetEnemyName(entries[0].EnemyName); } int num = entries.Sum((EnemyConfigEntry x) => x.SpawnCount.Value); viewerSpawnData.SetTotalSpawnCount(num); if (num < 1) { Logger.LogError("EnemyManager: Failed to spawn enemies. Total spawn count is less than 1.", extended: false, showMessage: true); return; } foreach (EnemyConfigEntry item in entries) { SpawnEnemy(item, viewerSpawnData.Viewer); } MessageManager.Instance?.ShowSpawnEnemyMessage(viewerSpawnData); Logger.LogInfo($"\n\n{viewerSpawnData.Viewer.DisplayName} spawned {num}x enemies {viewerSpawnData.SpawnReason}\n", extended: true); } private static void SpawnEnemy(EnemyConfigEntry configEntry, ViewerData viewerData) { if (configEntry == null) { Logger.LogError("EnemyManager: Failed to spawn enemies. EnemyConfigEntry is null.", extended: false, showMessage: true); return; } if (viewerData == null) { Logger.LogError("EnemyManager: Failed to spawn enemies. ViewerData is null.", extended: false, showMessage: true); return; } string enemyName = configEntry.EnemyName; int value = configEntry.SpawnCount.Value; if (value < 1) { Logger.LogError("EnemyManager: Failed to spawn enemy \"" + enemyName + "\". Spawn count is less than 1.", extended: false, showMessage: true); return; } EnemySetup enemySetup = default(EnemySetup); if (!Enemies.TryGetEnemyByName(enemyName, ref enemySetup)) { Logger.LogError("EnemyManager: Failed to spawn enemy \"" + enemyName + "\". EnemySetup is null.", extended: false, showMessage: true); return; } for (int i = 0; i < value; i++) { SpawnEnemy(enemySetup, viewerData); } } private static void SpawnEnemy(EnemySetup enemySetup, ViewerData viewerData) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)enemySetup == (Object)null) { Logger.LogError("EnemyManager: Failed to spawn enemies. EnemySetup is null.", extended: false, showMessage: true); return; } if (viewerData == null) { Logger.LogError("EnemyManager: Failed to spawn enemies. ViewerData is null.", extended: false, showMessage: true); return; } EnemyParent val = default(EnemyParent); if (!EnemySetupExtensions.TryGetEnemyParent(enemySetup, ref val)) { Logger.LogError("EnemyManager: Failed to spawn enemy. EnemyParent is null.", extended: false, showMessage: true); return; } Vector3 randomSpawnPosition = GetRandomSpawnPosition(); List<EnemyParent> list = Enemies.SpawnEnemy(enemySetup, randomSpawnPosition, Quaternion.identity, SpawnDespawned); PlayerAvatar localPlayer = PlayerHelper.GetLocalPlayer(); EnemyNametag enemyNametag = default(EnemyNametag); foreach (EnemyParent item in list) { ViewerEnemies.Add(item, viewerData); GameObject val2 = Object.Instantiate<GameObject>(Assets.EnemyNametagWorldCanvasPrefab); if (val2.TryGetComponent<EnemyNametag>(ref enemyNametag)) { enemyNametag.SetData(item, viewerData); } AudioClip spawnSFX = Assets.EnemySpawnSFXList.GetSpawnSFX(item.enemyName); _audioPlayerFactory?.Play(spawnSFX, ((Component)item).transform.position); if ((Object)(object)localPlayer != (Object)null) { float num = Vector3.Distance(((Component)item).transform.position, ((Component)localPlayer).transform.position); Logger.LogInfo($"Spawned \"{item.enemyName}\" {num} meters away from you.", extended: true); } } } private static Vector3 GetRandomSpawnPosition() { //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_0019: 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_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)LevelGenerator.Instance == (Object)null) { Logger.LogError("EnemyManager: Failed to get valid spawn position. LevelGenerator instance is null.", extended: false, showMessage: true); return Vector3.zero; } Vector3 val = Vector3.zero; float minSpawnDistance = MinSpawnDistance; float maxSpawnDistance = MaxSpawnDistance; if (PlayerHelper.TryGetLocalPlayer(out var playerAvatar)) { val = ((Component)playerAvatar).transform.position; } LevelPoint val2 = SemiFunc.LevelPointGet(val, minSpawnDistance, maxSpawnDistance); if ((Object)(object)val2 == (Object)null) { val2 = SemiFunc.LevelPointGet(val, Mathf.Min(minSpawnDistance, 25f), float.MaxValue); if ((Object)(object)val2 == (Object)null) { return val; } } return ((Component)val2).transform.position; } public static bool CanSpawnEnemies() { if (!SemiFunc.IsMasterClientOrSingleplayer()) { return false; } if (!LevelHelper.CurrentLevelHasEnemies()) { return false; } if (!PlayerHelper.IsLocalPlayerAlive()) { return false; } return true; } public static ViewerData GetViewerData(EnemyParent enemyParent) { if ((Object)(object)enemyParent == (Object)null) { return null; } if (ViewerEnemies.TryGetValue(enemyParent, out var value)) { return value; } return null; } public static bool TryGetViewerData(EnemyParent enemyParent, out ViewerData viewerData) { viewerData = GetViewerData(enemyParent); return viewerData != null; } } internal static class Logger { public static ManualLogSource ManualLogSource { get; private set; } public static void Initialize(ManualLogSource manualLogSource) { ManualLogSource = manualLogSource; } public static void LogDebug(object data) { Log((LogLevel)32, data); } public static void LogInfo(object data, bool extended = false) { Log((LogLevel)16, data, extended); } public static void LogWarning(object data, bool extended = false) { Log((LogLevel)4, data, extended); } public static void LogError(object data, bool extended = false, bool showMessage = false) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) Log((LogLevel)2, data, extended); if (showMessage) { MessageManager.Instance?.ShowMessage(data.ToString(), Color.red); } } public static void LogFatal(object data, bool extended = false) { Log((LogLevel)1, data, extended); } public static void Log(LogLevel logLevel, object data, bool extended = false) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) if (!extended || IsExtendedLoggingEnabled()) { ManualLogSource manualLogSource = ManualLogSource; if (manualLogSource != null) { manualLogSource.Log(logLevel, data); } } } public static bool IsExtendedLoggingEnabled() { if (ConfigManager.ExtendedLogging == null) { return false; } return ConfigManager.ExtendedLogging.Value; } } internal static class Menu { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static BuilderDelegate <>9__5_0; public static BuilderDelegate <>9__5_1; public static BuilderDelegate <>9__5_2; public static Action <>9__5_3; internal void <Initialize>b__5_0(Transform parent) { CreateMainButtonForMainMenu(parent); } internal void <Initialize>b__5_1(Transform parent) { CreateMainButtonForLobbyAndEscapeMenu(parent); } internal void <Initialize>b__5_2(Transform parent) { CreateMainButtonForLobbyAndEscapeMenu(parent); } internal void <Initialize>b__5_3() { PageManager.OnPageStatusChanged -= HandlePageStatusChanged; PageManager.OnPageCreated -= HandlePageCreated; } } private static REPOPopupPage _popupPage; private static REPOLabel _pageStatusLabel; private static REPOLabel _pageUrlLabel; private static REPOButton _createPageButton; private static REPOButton _copyPageURLButton; public static void Initialize() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown //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: Expected O, but got Unknown //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) //IL_0067: Expected O, but got Unknown object obj = <>c.<>9__5_0; if (obj == null) { BuilderDelegate val = delegate(Transform parent) { CreateMainButtonForMainMenu(parent); }; <>c.<>9__5_0 = val; obj = (object)val; } MenuAPI.AddElementToMainMenu((BuilderDelegate)obj); object obj2 = <>c.<>9__5_1; if (obj2 == null) { BuilderDelegate val2 = delegate(Transform parent) { CreateMainButtonForLobbyAndEscapeMenu(parent); }; <>c.<>9__5_1 = val2; obj2 = (object)val2; } MenuAPI.AddElementToLobbyMenu((BuilderDelegate)obj2); object obj3 = <>c.<>9__5_2; if (obj3 == null) { BuilderDelegate val3 = delegate(Transform parent) { CreateMainButtonForLobbyAndEscapeMenu(parent); }; <>c.<>9__5_2 = val3; obj3 = (object)val3; } MenuAPI.AddElementToEscapeMenu((BuilderDelegate)obj3); PageManager.OnPageStatusChanged += HandlePageStatusChanged; PageManager.OnPageCreated += HandlePageCreated; Application.quitting += delegate { PageManager.OnPageStatusChanged -= HandlePageStatusChanged; PageManager.OnPageCreated -= HandlePageCreated; }; } private static REPOButton CreateMainButtonForMainMenu(Transform parent) { //IL_002a: 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) float num = 22f; float num2 = 0f; float num3 = 35f; num2 += num3; if (SpawnManagerProxy.Enabled) { num2 += num3; } return CreateMainButton(parent, Vector2.op_Implicit(new Vector2(550f, num + num2))); } private static REPOButton CreateMainButtonForLobbyAndEscapeMenu(Transform parent) { //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) float num = 0f; num += 145f; if (MoreHeadProxy.Enabled) { num += 100f; } return CreateMainButton(parent, Vector2.op_Implicit(new Vector2(num, 0f))); } private static REPOButton CreateMainButton(Transform parent, Vector3 localPosition) { //IL_0021: 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) return MenuAPI.CreateREPOButton("Twitch Trolling", (Action)HandleMainButtonClick, parent, Vector2.op_Implicit(localPosition)); } private static void CreateMenu() { //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_0039: 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) //IL_006b: Expected O, but got Unknown //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Expected O, but got Unknown //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Expected O, but got Unknown //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Expected O, but got Unknown //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Expected O, but got Unknown _popupPage = MenuAPI.CreateREPOPopupPage("Twitch Trolling", (PresetSide)0, false, true, 0f); REPOPopupPage popupPage = _popupPage; Padding maskPadding = _popupPage.maskPadding; maskPadding.top = 35f; popupPage.maskPadding = maskPadding; float xPosition = 75f; float yPosition = 270f; _popupPage.AddElement((BuilderDelegate)delegate(Transform parent) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) _pageStatusLabel = MenuAPI.CreateREPOLabel("Page Status:", parent, new Vector2(xPosition, yPosition)); ((Component)_pageStatusLabel).transform.localScale = Vector3.one * 0.7f; }); yPosition -= 40f; _popupPage.AddElement((BuilderDelegate)delegate(Transform parent) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) _pageUrlLabel = MenuAPI.CreateREPOLabel(string.Empty, parent, new Vector2(xPosition, yPosition)); ((Component)_pageUrlLabel).transform.localScale = Vector3.one * 0.5f; }); yPosition -= 60f; _popupPage.AddElement((BuilderDelegate)delegate(Transform parent) { //IL_002d: 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) _createPageButton = MenuAPI.CreateREPOButton("Create Page", (Action)HandleCreatePageButtonClick, parent, Vector2.op_Implicit(new Vector3(xPosition, yPosition))); }); _popupPage.AddElement((BuilderDelegate)delegate(Transform parent) { //IL_002d: 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) _copyPageURLButton = MenuAPI.CreateREPOButton("Copy Page URL", (Action)HandleCopyPageURLButtonClick, parent, Vector2.op_Implicit(new Vector3(xPosition, yPosition))); ((Component)_copyPageURLButton).gameObject.SetActive(false); }); yPosition = 35f; _popupPage.AddElement((BuilderDelegate)delegate(Transform parent) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) MenuAPI.CreateREPOButton("Close", (Action)HandleCloseButtonClick, parent, new Vector2(xPosition, yPosition)); }); _popupPage.OpenPage(false); HandlePageStatusChanged(); PageManager.CheckPageStatus(); PageManager.VerifyTwitchChannel(); } private static void HandleMainButtonClick() { CreateMenu(); } private static void HandleCloseButtonClick() { REPOPopupPage popupPage = _popupPage; if (popupPage != null) { popupPage.ClosePage(true); } } private static void HandleCreatePageButtonClick() { REPOButton createPageButton = _createPageButton; if (createPageButton != null) { ((Component)createPageButton).gameObject.SetActive(false); } PageManager.CreatePage(); } private static void HandleCopyPageURLButtonClick() { if (PageManager.HasPage) { GUIUtility.systemCopyBuffer = PageManager.PageURL; } } private static void HandlePageStatusChanged() { if ((Object)(object)_popupPage == (Object)null) { return; } bool hasPage = PageManager.HasPage; ((Component)_createPageButton).gameObject.SetActive(!hasPage); ((Component)_copyPageURLButton).gameObject.SetActive(hasPage); if (hasPage) { ((TMP_Text)_pageStatusLabel.labelTMP).text = "Page Status: <color=#00FF00>ONLINE</color>"; string text = PageManager.PageURL.Replace("?id", "\n?id"); ((TMP_Text)_pageUrlLabel.labelTMP).text = "<color=#FFFFFF>" + text + "</color>"; return; } ((TMP_Text)_pageStatusLabel.labelTMP).text = "Page Status: <color=#FF0000>OFFLINE</color>"; if (!PageManager.IsCreatePageEnabled(out var reason)) { string text2 = "<size=25><color=#FF0000>" + reason + "</color></size>"; ((TMP_Text)_pageUrlLabel.labelTMP).text = "You are unable to create a page! Reason:\n" + text2; ((Component)_createPageButton).gameObject.SetActive(false); } else { ((TMP_Text)_pageUrlLabel.labelTMP).text = "Create a page to share your prices\nwith your viewers!"; } } private static void HandlePageCreated(string pageUrl) { HandlePageStatusChanged(); } } internal static class PageManager { internal enum TwitchChannelStatus { None, Accepted, NotLive, NotAffiliateOrPartner, Banned, FailedToCheck } [CompilerGenerated] private sealed class <CheckPageStatusCoroutine>d__27 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; private UnityWebRequest <request>5__2; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <CheckPageStatusCoroutine>d__27(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 1) { try { } finally { <>m__Finally1(); } } <request>5__2 = null; <>1__state = -2; } private bool MoveNext() { //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Invalid comparison between Unknown and I4 //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Invalid comparison between Unknown and I4 bool result; try { switch (<>1__state) { default: result = false; break; case 0: { <>1__state = -1; string pageId = GetPageId(); string pageAccessToken = GetPageAccessToken(); if (string.IsNullOrEmpty(pageId) || string.IsNullOrEmpty(pageAccessToken)) { HasPage = false; PageManager.OnPageStatusChanged?.Invoke(); result = false; break; } string text = "https://twitchtrolling.up.railway.app/api/pages/" + pageId; <request>5__2 = UnityWebRequest.Get(text); <>1__state = -3; <>2__current = <request>5__2.SendWebRequest(); <>1__state = 1; result = true; break; } case 1: <>1__state = -3; if ((int)<request>5__2.result == 3 && <request>5__2.responseCode == 404) { Logger.LogWarning("Page not found (404). Clearing saved page ID and access token."); SavePageId(string.Empty); SavePageAccessToken(string.Empty); HasPage = false; PageManager.OnPageStatusChanged?.Invoke(); result = false; } else if ((int)<request>5__2.result != 1) { Logger.LogWarning("Page status check failed: " + <request>5__2.error); HasPage = false; PageManager.OnPageStatusChanged?.Invoke(); result = false; } else { HasPage = true; PageManager.OnPageStatusChanged?.Invoke(); result = false; } <>m__Finally1(); break; } } catch { //try-fault ((IDisposable)this).Dispose(); throw; } return result; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; if (<request>5__2 != null) { ((IDisposable)<request>5__2).Dispose(); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <CreatePageCoroutine>d__28 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; private UnityWebRequest <request>5__2; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <CreatePageCoroutine>d__28(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 1) { try { } finally { <>m__Finally1(); } } <request>5__2 = null; <>1__state = -2; } private bool MoveNext() { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Expected O, but got Unknown //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Expected O, but got Unknown //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Expected O, but got Unknown //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Invalid comparison between Unknown and I4 bool result; try { switch (<>1__state) { default: result = false; break; case 0: { <>1__state = -1; if (!IsCreatePageEnabled(out var reason)) { Logger.LogError("You are unable to create a page: " + reason); result = false; break; } string text4 = "https://twitchtrolling.up.railway.app/api/pages"; string s = JsonConvert.SerializeObject((object)GetDataForPage()); <request>5__2 = new UnityWebRequest(text4, "POST"); <>1__state = -3; byte[] bytes = Encoding.UTF8.GetBytes(s); <request>5__2.uploadHandler = (UploadHandler)new UploadHandlerRaw(bytes); <request>5__2.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer(); <request>5__2.SetRequestHeader("Content-Type", "application/json"); <request>5__2.SetRequestHeader("Authorization", "Bearer d74c6ba6-4584-4969-8af9-066549673563"); <>2__current = <request>5__2.SendWebRequest(); <>1__state = 1; result = true; break; } case 1: { <>1__state = -3; if ((int)<request>5__2.result != 1) { Logger.LogError("Failed to create page: " + <request>5__2.error); PageManager.OnPageStatusChanged?.Invoke(); result = false; } else { string text = <request>5__2.downloadHandler.text; JObject val; try { val = JObject.Parse(text); } catch (Exception ex) { Logger.LogError("Failed to parse create page response: " + ex.Message); PageManager.OnPageStatusChanged?.Invoke(); result = false; goto IL_0213; } string text2 = ((object)val["id"])?.ToString(); string text3 = ((object)val["accessToken"])?.ToString(); if (string.IsNullOrEmpty(text2) || string.IsNullOrEmpty(text3)) { Logger.LogError("Create page response missing required fields."); PageManager.OnPageStatusChanged?.Invoke(); result = false; } else { SavePageId(text2); SavePageAccessToken(text3); HasPage = true; _updatePageDataFirstTime = true; PageManager.OnPageCreated?.Invoke(PageURL); result = false; } } goto IL_0213; } IL_0213: <>m__Finally1(); break; } } catch { //try-fault ((IDisposable)this).Dispose(); throw; } return result; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; if (<request>5__2 != null) { ((IDisposable)<request>5__2).Dispose(); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <UpdatePageCoroutine>d__29 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public TimeSpan delay; private UnityWebRequest <request>5__2; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <UpdatePageCoroutine>d__29(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 2) { try { } finally { <>m__Finally1(); } } <request>5__2 = null; <>1__state = -2; } private bool MoveNext() { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Invalid comparison between Unknown and I4 //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Expected O, but got Unknown //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Expected O, but got Unknown //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Expected O, but got Unknown bool result; try { switch (<>1__state) { default: result = false; break; case 0: <>1__state = -1; <>2__current = (object)new WaitForSeconds((float)delay.TotalSeconds); <>1__state = 1; result = true; break; case 1: { <>1__state = -1; string pageId = GetPageId(); string pageAccessToken = GetPageAccessToken(); if (string.IsNullOrEmpty(pageId) || string.IsNullOrEmpty(pageAccessToken)) { Logger.LogWarning("UpdatePage: Missing Page ID or Access Token."); result = false; break; } string text = "https://twitchtrolling.up.railway.app/api/pages/" + pageId; string s = JsonConvert.SerializeObject((object)GetDataForPage()); <request>5__2 = new UnityWebRequest(text, "PUT"); <>1__state = -3; byte[] bytes = Encoding.UTF8.GetBytes(s); <request>5__2.uploadHandler = (UploadHandler)new UploadHandlerRaw(bytes); <request>5__2.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer(); <request>5__2.SetRequestHeader("Content-Type", "application/json"); <request>5__2.SetRequestHeader("Authorization", "Bearer " + pageAccessToken); <>2__current = <request>5__2.SendWebRequest(); <>1__state = 2; result = true; break; } case 2: <>1__state = -3; if ((int)<request>5__2.result != 1) { Logger.LogError("Failed to update page: " + <request>5__2.error); result = false; } else { Logger.LogInfo("Page updated successfully.", extended: true); result = false; } <>m__Finally1(); break; } } catch { //try-fault ((IDisposable)this).Dispose(); throw; } return result; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; if (<request>5__2 != null) { ((IDisposable)<request>5__2).Dispose(); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <VerifyTwitchChannelCoroutine>d__38 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; private UnityWebRequest <request>5__2; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <VerifyTwitchChannelCoroutine>d__38(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 1) { try { } finally { <>m__Finally1(); } } <request>5__2 = null; <>1__state = -2; } private bool MoveNext() { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Invalid comparison between Unknown and I4 //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Invalid comparison between Unknown and I4 //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Expected O, but got Unknown //IL_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_021d: Unknown result type (might be due to invalid IL or missing references) //IL_0224: Invalid comparison between Unknown and I4 bool result; try { JObject val2; switch (<>1__state) { default: result = false; break; case 0: { <>1__state = -1; _twitchChannelStatus = TwitchChannelStatus.None; if ((int)API.ConnectionState != 2) { PageManager.OnPageStatusChanged?.Invoke(); result = false; break; } string channel = API.Channel; string text2 = "https://api.ivr.fi/v2/twitch/user?login=" + channel; <request>5__2 = UnityWebRequest.Get(text2); <>1__state = -3; <request>5__2.SetRequestHeader("User-Agent", "TwitchTrolling/1.2.0"); <>2__current = <request>5__2.SendWebRequest(); <>1__state = 1; result = true; break; } case 1: { <>1__state = -3; if ((int)<request>5__2.result != 1) { Logger.LogError("VerifyTwitchChannel: Failed to verify Twitch channel."); _twitchChannelStatus = TwitchChannelStatus.FailedToCheck; PageManager.OnPageStatusChanged?.Invoke(); result = false; } else { string text = <request>5__2.downloadHandler.text; try { JArray val = JArray.Parse(text); if (((JContainer)val).Count != 0) { val2 = (JObject)val[0]; goto IL_0171; } Logger.LogWarning("VerifyTwitchChannel: No user found with the specified login."); _twitchChannelStatus = TwitchChannelStatus.FailedToCheck; PageManager.OnPageStatusChanged?.Invoke(); result = false; } catch (Exception ex) { Logger.LogError("VerifyTwitchChannel: Failed to parse Twitch user response: " + ex.Message); _twitchChannelStatus = TwitchChannelStatus.FailedToCheck; PageManager.OnPageStatusChanged?.Invoke(); result = false; } } goto IL_0258; } IL_0171: if (((JToken)val2).Value<bool>((object)"banned")) { _twitchChannelStatus = TwitchChannelStatus.Banned; PageManager.OnPageStatusChanged?.Invoke(); result = false; } else { JObject val3 = (JObject)(((object)((JToken)val2).Value<JObject>((object)"roles")) ?? ((object)new JObject())); bool valueOrDefault = ((JToken)val3).Value<bool?>((object)"isAffiliate").GetValueOrDefault(); bool valueOrDefault2 = ((JToken)val3).Value<bool?>((object)"isPartner").GetValueOrDefault(); if (!valueOrDefault && !valueOrDefault2) { _twitchChannelStatus = TwitchChannelStatus.NotAffiliateOrPartner; PageManager.OnPageStatusChanged?.Invoke(); result = false; } else if (val2["stream"] == null || (int)val2["stream"].Type == 10) { _twitchChannelStatus = TwitchChannelStatus.NotLive; PageManager.OnPageStatusChanged?.Invoke(); result = false; } else { _twitchChannelStatus = TwitchChannelStatus.Accepted; PageManager.OnPageStatusChanged?.Invoke(); result = false; } } goto IL_0258; IL_0258: <>m__Finally1(); break; } } catch { //try-fault ((IDisposable)this).Dispose(); throw; } return result; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; if (<request>5__2 != null) { ((IDisposable)<request>5__2).Dispose(); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private const string _apiOrigin = "https://twitchtrolling.up.railway.app"; private const string _createPageToken = "d74c6ba6-4584-4969-8af9-066549673563"; private static bool _updatePageDataFirstTime; private static float _lastUpdatePageTime = float.NegativeInfinity; private const float _updatePageCooldown = 0.1f; private static string _previousChannel; private static TwitchChannelStatus _twitchChannelStatus; public static bool HasPage { get; private set; } public static string PageId => GetPageId(); public static string PageURL { get { if (!HasPage) { return string.Empty; } return "https://twitchtrolling.com/page.html?id=" + PageId; } } public static event Action OnPageStatusChanged; public static event Action<string> OnPageCreated; public static void Initialize() { API.OnConnect += HandleTwitchOnConnect; Application.quitting += delegate { API.OnConnect -= HandleTwitchOnConnect; }; _previousChannel = API.Channel; CheckPageStatus(); VerifyTwitchChannel(); } public static void CheckPageStatus() { PluginManager instance = PluginManager.Instance; if (instance != null) { ((MonoBehaviour)instance).StartCoroutine(CheckPageStatusCoroutine()); } } public static void CreatePage() { PluginManager instance = PluginManager.Instance; if (instance != null) { ((MonoBehaviour)instance).StartCoroutine(CreatePageCoroutine()); } } public static void UpdatePage() { if (HasPage) { float num = 0.1f; float realtimeSinceStartup = Time.realtimeSinceStartup; if (!(realtimeSinceStartup - _lastUpdatePageTime < num)) { _lastUpdatePageTime = realtimeSinceStartup; ((MonoBehaviour)PluginManager.Instance).StartCoroutine(UpdatePageCoroutine(TimeSpan.FromSeconds(num))); } } } public static void UpdatePageFirstTime() { if (!_updatePageDataFirstTime) { _updatePageDataFirstTime = true; UpdatePage(); } } [IteratorStateMachine(typeof(<CheckPageStatusCoroutine>d__27))] private static IEnumerator CheckPageStatusCoroutine() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <CheckPageStatusCoroutine>d__27(0); } [IteratorStateMachine(typeof(<CreatePageCoroutine>d__28))] private static IEnumerator CreatePageCoroutine() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <CreatePageCoroutine>d__28(0); } [IteratorStateMachine(typeof(<UpdatePageCoroutine>d__29))] private static IEnumerator UpdatePageCoroutine(TimeSpan delay) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <UpdatePageCoroutine>d__29(0) { delay = delay }; } private static JObject GetDataForPage() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Expected O, but got Unknown //IL_0089: 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_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Expected O, but got Unknown //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Expected O, but got Unknown //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0102: 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_0133: Expected O, but got Unknown //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Expected O, but got Unknown JObject val = new JObject { ["channel"] = JToken.op_Implicit(API.Channel) }; JArray val2 = new JArray(); if (EnemyConfigManager.HasEnemies()) { JObject val3 = new JObject { ["name"] = JToken.op_Implicit("Random Enemy"), ["price"] = JToken.op_Implicit(ConfigManager.TwitchCheerEvent_MinAmountForRandomEnemy.Value) }; val2.Add((JToken)(object)val3); } foreach (EnemyConfigEntry configEntry in EnemyConfigManager.ConfigEntries) { if (configEntry.Enabled.Value) { JObject val4 = new JObject { ["name"] = JToken.op_Implicit(configEntry.EnemyName), ["price"] = JToken.op_Implicit(configEntry.BitsToSpawn.Value) }; val2.Add((JToken)(object)val4); } } val["enemies"] = (JToken)(object)val2; JArray val5 = new JArray(); if (MEventManager.HasEvents()) { JObject val6 = new JObject { ["name"] = JToken.op_Implicit("Random Event"), ["price"] = JToken.op_Implicit(ConfigManager.Event_AmountForRandomEvent.Value) }; val5.Add((JToken)(object)val6); } foreach (MEvent @event in MEventManager.Events) { if (@event.Enabled.Value) { JObject val7 = new JObject { ["name"] = JToken.op_Implicit(@event.Name), ["price"] = JToken.op_Implicit(@event.BitsPrice.Value) }; val5.Add((JToken)(object)val7); } } val["events"] = (JToken)(object)val5; return val; } private static string GetPageId() { return Plugin.GlobalSave.Load("PageId", string.Empty); } private static string GetPageAccessToken() { return Plugin.GlobalSave.Load("PageAccessToken", string.Empty); } private static void SavePageId(string pageId) { Plugin.GlobalSave.Save("PageId", pageId); } private static void SavePageAccessToken(string accessToken) { Plugin.GlobalSave.Save("PageAccessToken", accessToken); } private static void HandleTwitchOnConnect() { string channel = API.Channel; if (!(_previousChannel == channel)) { _previousChannel = channel; VerifyTwitchChannel(); UpdatePage(); } } public static bool IsCreatePageEnabled(out string reason) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Invalid comparison between Unknown and I4 reason = string.Empty; if ((int)API.ConnectionState != 2) { reason = "You are not connected to Twitch!"; return false; } if (_twitchChannelStatus == TwitchChannelStatus.None) { reason = "Your Twitch channel does not meet the requirements!"; return false; } if (_twitchChannelStatus == TwitchChannelStatus.NotLive) { reason = "You are not live!"; return false; } if (_twitchChannelStatus == TwitchChannelStatus.NotAffiliateOrPartner) { reason = "Your Twitch channel is not affiliate or partner!"; return false; } if (_twitchChannelStatus == TwitchChannelStatus.Banned) { reason = "Your Twitch channel is banned!"; return false; } return true; } public static void VerifyTwitchChannel() { PluginManager instance = PluginManager.Instance; if (instance != null) { ((MonoBehaviour)instance).StartCoroutine(VerifyTwitchChannelCoroutine()); } } [IteratorStateMachine(typeof(<VerifyTwitchChannelCoroutine>d__38))] private static IEnumerator VerifyTwitchChannelCoroutine() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <VerifyTwitchChannelCoroutine>d__38(0); } } [BepInPlugin("com.github.zehsteam.TwitchTrolling", "TwitchTrolling", "1.2.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] internal class Plugin : BaseUnityPlugin { private readonly Harmony _harmony = new Harmony("com.github.zehsteam.TwitchTrolling"); internal static Plugin Instance { get; private set; } internal static JsonSave GlobalSave { get; private set; } private void Awake() { Instance = this; Logger.Initialize(Logger.CreateLogSource("com.github.zehsteam.TwitchTrolling")); Logger.LogInfo("TwitchTrolling has awoken!"); _harmony.PatchAll(typeof(RunManagerPatch)); _harmony.PatchAll(typeof(LevelGeneratorPatch)); _harmony.PatchAll(typeof(EnemyDirectorPatch)); _harmony.PatchAll(typeof(HUDCanvasPatch)); _harmony.PatchAll(typeof(HurtColliderPatch)); _harmony.PatchAll(typeof(TruckScreenTextPatch)); _harmony.PatchAll(typeof(PlayerControllerPatch)); _harmony.PatchAll(typeof(ItemDronePatch)); _harmony.PatchAll(typeof(FanTrapPatch)); _harmony.PatchAll(typeof(ItemRubberDuckPatch)); _harmony.PatchAll(typeof(EnemyDuckPatch)); GlobalSave = new JsonSave(Utils.GetPluginPersistentDataPath(), "GlobalSave"); Assets.Load(); ConfigManager.Initialize(((BaseUnityPlugin)this).Config); PluginManager.Spawn(); TwitchIntegrationManager.Initialize(); Menu.Initialize(); PageManager.Initialize(); } } internal static class Utils { [CompilerGenerated] private sealed class <InvokeAfterDurationCoroutine>d__5 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public TimeSpan duration; public Action action; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <InvokeAfterDurationCoroutine>d__5(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForSeconds((float)duration.TotalSeconds); <>1__state = 1; return true; case 1: <>1__state = -1; action?.Invoke(); return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } public static string GetPluginPersistentDataPath() { return Path.Combine(Application.persistentDataPath, "TwitchTrolling"); } public static ConfigFile CreateConfigFile(BaseUnityPlugin plugin, string path, string name = null, bool saveOnInit = false) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown BepInPlugin metadata = MetadataHelper.GetMetadata((object)plugin); if (name == null) { name = metadata.GUID; } name += ".cfg"; return new ConfigFile(Path.Combine(path, name), saveOnInit, metadata); } public static ConfigFile CreateLocalConfigFile(BaseUnityPlugin plugin, string name = null, bool saveOnInit = false) { return CreateConfigFile(plugin, Paths.ConfigPath, name, saveOnInit); } public static ConfigFile CreateGlobalConfigFile(BaseUnityPlugin plugin, string name = null, bool saveOnInit = false) { string pluginPersistentDataPath = GetPluginPersistentDataPath(); if (name == null) { name = "global"; } return CreateConfigFile(plugin, pluginPersistentDataPath, name, saveOnInit); } public static string GetTextWithReadableColor(string text, string hexColor, string backgroundHexColor = "#000000") { if (string.IsNullOrWhiteSpace(hexColor)) { hexColor = "#FFFFFF"; } if (string.IsNullOrWhiteSpace(backgroundHexColor)) { backgroundHexColor = "#000000"; } string readableColor = ColorHelper.GetReadableColor(hexColor, backgroundHexColor); return "<color=" + readableColor + ">" + text + "</color>"; } [IteratorStateMachine(typeof(<InvokeAfterDurationCoroutine>d__5))] public static IEnumerator InvokeAfterDurationCoroutine(TimeSpan duration, Action action) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <InvokeAfterDurationCoroutine>d__5(0) { duration = duration, action = action }; } } public static class MyPluginInfo { public const string PLUGIN_GUID = "com.github.zehsteam.TwitchTrolling"; public const string PLUGIN_NAME = "TwitchTrolling"; public const string PLUGIN_VERSION = "1.2.0"; } } namespace com.github.zehsteam.TwitchTrolling.Twitch { internal static class TwitchEventHandler { public static void Initialize() { try { API.OnMessage += HandleMessage; API.OnCheer += HandleCheer; API.OnSub += HandleSub; API.OnRaid += HandleRaid; Application.quitting += delegate { API.OnMessage -= HandleMessage; API.OnCheer -= HandleCheer; API.OnSub -= HandleSub; API.OnRaid -= HandleRaid; }; } catch { Logger.LogFatal("TwitchEventHandler: Failed to initialize."); throw; } } public static void HandleMessage(TwitchMessage twitchMessage) { } public static void HandleCheer(TwitchCheerEvent cheerEvent) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) if (!TwitchIntegrationManager.IsCheerEventEnabled) { return; } if (cheerEvent == null) { Logger.LogError("TwitchEventHandler: Failed to handle cheer. TwitchCheerEvent is null."); return; } if (!CanHandleEvents()) { ShowUnableToHandleEventsMessage(); return; } if (!TwitchIntegrationManager.CanPlayEvents()) { TwitchEventQueue.Enqueue(cheerEvent); return; } ViewerData viewerData = new ViewerData(((TwitchEvent)cheerEvent).User); int cheerAmount = cheerEvent.CheerAmount; string targetEnemyName = string.Empty; int spawnCount = 1; int bitsUsed; if (EnemyConfigManager.HasEnemies() && EnemyConfigManager.TryGetConfigEntriesThatMatchBitsAmount(cheerAmount, out var list)) { if (list.TryGetRandom(out var enemyConfigEntry)) { targetEnemyName = enemyConfigEntry.EnemyName; } bitsUsed = cheerAmount; SpawnEnemyFromBits(viewerData, bitsUsed, spawnCount, targetEnemyName); return; } if (MEventManager.Enabled && MEventManager.HasEvents()) { float num = ConfigManager.Event_AmountForRandomEvent.Value; if (MEventManager.TryGetEvent(cheerAmount, out var mEvent)) { mEvent.ExecuteEvent(viewerData, PlayerAvatar.instance, cheerAmount); return; } if ((float)cheerAmount == num && MEventManager.TryGetRandomEvent(out mEvent)) { mEvent.ExecuteEvent(viewerData, PlayerAvatar.instance, cheerAmount); return; } } int accumulatedBits = TwitchIntegrationManager.GetAccumulatedBits(viewerData); cheerAmount += accumulatedBits; int value = ConfigManager.TwitchCheerEvent_MinAmountForRandomEnemy.Value; if (cheerAmount < value) { TwitchIntegrationManager.SetAccumulatedBits(viewerData, cheerAmount); return; } spawnCount = Mathf.FloorToInt((float)(cheerAmount / value)); int num2 = cheerAmount % value; bitsUsed = cheerAmount - num2; TwitchIntegrationManager.SetAccumulatedBits(viewerData, num2); SpawnEnemyFromBits(viewerData, bitsUsed, spawnCount, targetEnemyName); } public static void HandleSub(TwitchSubEvent subEvent) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Invalid comparison between Unknown and I4 //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Invalid comparison between Unknown and I4 //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Invalid comparison between Unknown and I4 //IL_009d: Unknown result type (might be due to invalid IL or missing references) if (!TwitchIntegrationManager.IsSubEventEnabled) { return; } if (subEvent == null) { Logger.LogError("Failed to handle sub. TwitchSubEvent is null."); return; } if (!CanHandleEvents()) { ShowUnableToHandleEventsMessage(); return; } if (!TwitchIntegrationManager.CanPlayEvents()) { TwitchEventQueue.Enqueue(subEvent); return; } int num = 1; if ((int)subEvent.Type == 3) { num = subEvent.GiftCount; } string targetEnemyName = string.Empty; int num2 = 1; if (EnemyConfigManager.TryGetConfigEntriesThatMatchSubsAmount(num, out var list)) { if (list.TryGetRandom(out var enemyConfigEntry)) { targetEnemyName = enemyConfigEntry.EnemyName; } } else { num2 = num; } if ((int)subEvent.Tier == 2) { num2 *= ConfigManager.TwitchSubEvent_Tier2EnemySpawnCountMultiplier.Value; } else if ((int)subEvent.Tier == 3) { num2 *= ConfigManager.TwitchSubEvent_Tier3EnemySpawnCountMultiplier.Value; } ViewerSpawnData viewerSpawnData = new ViewerSpawnData(((TwitchEvent)subEvent).User, num2, GetSpawnReason(subEvent), targetEnemyName); Logger.LogInfo("HandleSub:\n" + JsonConvert.SerializeObject((object)viewerSpawnData, (Formatting)1), extended: true); EnemyManager.SpawnEnemy(viewerSpawnData); } public static void HandleRaid(TwitchRaidEvent raidEvent) { //IL_0070: Unknown result type (might be due to invalid IL or missing references) if (TwitchIntegrationManager.IsRaidEventEnabled) { if (raidEvent == null) { Logger.LogError("TwitchEventHandler: Failed to handle raid. TwitchRaidEvent is null."); return; } if (!CanHandleEvents()) { ShowUnableToHandleEventsMessage(); return; } if (!TwitchIntegrationManager.CanPlayEvents()) { TwitchEventQueue.Enqueue(raidEvent); return; } int value = ConfigManager.TwitchRaidEvent_ViewersPerRandomEnemy.Value; int value2 = ConfigManager.TwitchRaidEvent_MaxEnemySpawnCount.Value; int spawnCount = Mathf.Clamp(raidEvent.ViewerCount / value, 1, value2); string spawnReason = $"by raiding with {raidEvent.ViewerCount} viewers"; ViewerSpawnData viewerSpawnData = new ViewerSpawnData(((TwitchEvent)raidEvent).User, spawnCount, spawnReason); Logger.LogInfo("HandleRaid:\n" + JsonConvert.SerializeObject((object)viewerSpawnData, (Formatting)1), extended: true); EnemyManager.SpawnEnemy(viewerSpawnData); } } private static string GetSpawnReason(TwitchSubEvent subEvent) { //IL_0010: 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_004c: Invalid comparison between Unknown and I4 //IL_0018: 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_0098: Invalid comparison between Unknown and I4 //IL_004f: 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_003a: Expected I4, but got Unknown //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Invalid comparison between Unknown and I4 //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Expected I4, but got Unknown //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Expected I4, but got Unknown //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Expected I4, but got Unknown if (subEvent == null) { return string.Empty; } string result = string.Empty; if ((int)subEvent.Type == 0) { result = (((int)subEvent.Tier != 0) ? $"by subbing at tier {(int)subEvent.Tier}" : "by subbing with prime"); } else if ((int)subEvent.Type == 1) { result = (((int)subEvent.Tier != 0) ? $"by resubbing at tier {(int)subEvent.Tier} for {subEvent.CumulativeMonths} months" : $"by resubbing with prime for {subEvent.CumulativeMonths} months"); } else if ((int)subEvent.Type == 2) { result = $"by gifting a tier {(int)subEvent.Tier} sub to {subEvent.RecipientUser}"; } else if ((int)subEvent.Type == 3) { result = $"by gifting {subEvent.GiftCount} tier {(int)subEvent.Tier} subs"; } return result; } private static void SpawnEnemyFromBits(ViewerData viewer, int bitsUsed, int spawnCount, string targetEnemyName) { string spawnReason = $"by cheering {bitsUsed} bits"; ViewerSpawnData viewerSpawnData = new ViewerSpawnData(viewer, spawnCount, spawnReason, targetEnemyName); EnemyManager.SpawnEnemy(viewerSpawnData); } public static void ShowUnableToHandleEventsMessage() { MessageManager.Instance?.ShowMessage("<color=#FFFF00>Warning!</color> Triggering Twitch events is not currently supported on non-host clients\nDisable Twitch Chat API to hide these warnings"); } public static bool CanHandleEvents() { if (!PhotonNetwork.InRoom) { return true; } return SemiFunc.IsMasterClientOrSingleplayer(); } } internal static class TwitchEventQueue { [CompilerGenerated] private sealed class <PlayQueuedEventsCoroutine>d__17 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public TimeSpan initialDelay; private TimeSpan <delayBetweenEvents>5__2; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <PlayQueuedEventsCoroutine>d__17(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Expected O, but got Unknown //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Expected O, but got Unknown //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Expected O, but got Unknown //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Expected O, but got Unknown TwitchCheerEvent result; TwitchSubEvent result2; switch (<>1__state) { default: return false; case 0: <>1__state = -1; Logger.LogInfo("TwitchEventQueue: Started playing queued events.", extended: true); Logger.LogInfo($"TwitchEventQueue: Playing queued events after {(float)initialDelay.TotalSeconds} seconds.", extended: true); <>2__current = (object)new WaitForSeconds((float)initialDelay.TotalSeconds); <>1__state = 1; return true; case 1: <>1__state = -1; if (!TwitchEventHandler.CanHandleEvents()) { return false; } if (!TwitchIntegrationManager.CanPlayEvents()) { Logger.LogWarning("TwitchEventQueue: Failed to play queued events. You are not allowed to play events at this time."); return false; } if (!HasQueuedEvents()) { Logger.LogInfo("TwitchEventQueue: No events in the queue to play.", extended: true); return false; } MessageManager.Instance?.ShowMessage("Playing Twitch events from the queue"); <delayBetweenEvents>5__2 = TimeSpan.FromSeconds(0.5); goto IL_00d7; case 2: <>1__state = -1; goto IL_00d7; case 3: <>1__state = -1; goto IL_011b; case 4: { <>1__state = -1; break; } IL_00d7: if (TwitchIntegrationManager.IsCheerEventEnabled && CheerQueue.TryDequeue(out result)) { TwitchEventHandler.HandleCheer(result); <>2__current = (object)new WaitForSeconds((float)<delayBetweenEvents>5__2.TotalSeconds); <>1__state = 2; return true; } goto IL_011b; IL_011b: if (TwitchIntegrationManager.IsSubEventEnabled && SubQueue.TryDequeue(out result2)) { TwitchEventHandler.HandleSub(result2); <>2__current = (object)new WaitForSeconds((float)<delayBetweenEvents>5__2.TotalSeconds); <>1__state = 3; return true; } break; } if (TwitchIntegrationManager.IsRaidEventEnabled && RaidQueue.TryDequeue(out var result3)) { TwitchEventHandler.HandleRaid(result3); <>2__current = (object)new WaitForSeconds((float)<delayBetweenEvents>5__2.TotalSeconds); <>1__state = 4; return true; } Logger.LogInfo("TwitchEventQueue: Finished plyaing queued events.", extended: true); SaveData(); _playQueuedEventsCoroutine = null; return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private static Coroutine _playQueuedEventsCoroutine; public static Queue<TwitchCheerEvent> CheerQueue { get; private set; } = new Queue<TwitchCheerEvent>(); public static Queue<TwitchSubEvent> SubQueue { get; private set; } = new Queue<TwitchSubEvent>(); public static Queue<TwitchRaidEvent> RaidQueue { get; private set; } = new Queue<TwitchRaidEvent>(); public static void LoadData() { Logger.LogInfo("TwitchEventQueue: Loading saved data..."); try { if (Plugin.GlobalSave.TryLoad<string>("CheerQueue", out var value)) { CheerQueue = JsonConvertHelper.DeserializeQueue<TwitchCheerEvent>(value); Logger.LogInfo("TwitchEventQueue: Loaded CheerQueue JSON data:\n\n" + value, extended: true); } if (Plugin.GlobalSave.TryLoad<string>("SubQueue", out value)) { SubQueue = JsonConvertHelper.DeserializeQueue<TwitchSubEvent>(value); Logger.LogInfo("TwitchEventQueue: Loaded SubQueue JSON data:\n\n" + value, extended: true); } if (Plugin.GlobalSave.TryLoad<string>("RaidQueue", out value)) { RaidQueue = JsonConvertHelper.DeserializeQueue<TwitchRaidEvent>(value); Logger.LogInfo("TwitchEventQueue: Loaded RaidQueue JSON data:\n\n" + value, extended: true); } Logger.LogInfo("TwitchEventQueue: Finished loading saved data."); } catch (Exception arg) { Logger.LogError($"TwitchEventQueue: Failed to load saved data. {arg}"); } } public static void SaveData() { Logger.LogInfo("TwitchEventQueue: Saving data...", extended: true); try { Plugin.GlobalSave.Save("CheerQueue", JsonConvertHelper.SerializeQueue(CheerQueue)); Plugin.GlobalSave.Save("SubQueue", JsonConvertHelper.SerializeQueue(SubQueue)); Plugin.GlobalSave.Save("RaidQueue", JsonConvertHelper.SerializeQueue(RaidQueue)); Logger.LogInfo("TwitchEventQueue: Saved data.", extended: true); } catch (Exception arg) { Logger.LogError($"TwitchEventQueue: Failed to save data. {arg}"); } } public static void PlayQueuedEvents() { PlayQueuedEvents(TimeSpan.Zero); } public static void PlayQueuedEvents(TimeSpan initialDelay) { if (!TwitchIntegrationManager.IsEnabled) { Logger.LogWarning("TwitchEventQueue: Failed to play queued events. Twitch integration is not enabled."); return; } if ((Object)(object)PluginManager.Instance == (Object)null) { Logger.LogError("TwitchEventQueue: Failed to play queued events. PluginManager instance is null."); return; } if (!TwitchIntegrationManager.CanPlayEvents()) { Logger.LogWarning("TwitchEventQueue: Failed to play queued events. You are not allowed to play events at this time."); return; } if (_playQueuedEventsCoroutine != null) { ((MonoBehaviour)PluginManager.Instance).StopCoroutine(_playQueuedEventsCoroutine); } _playQueuedEventsCoroutine = ((MonoBehaviour)PluginManager.Instance).StartCoroutine(PlayQueuedEventsCoroutine(initialDelay)); } [IteratorStateMachine(typeof(<PlayQueuedEventsCoroutine>d__17))] private static IEnumerator PlayQueuedEventsCoroutine(TimeSpan initialDelay) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <PlayQueuedEventsCoroutine>d__17(0) { initialDelay = initialDelay }; } public static bool HasQueuedEvents() { if (CheerQueue.Count <= 0 && SubQueue.Count <= 0) { return RaidQueue.Count > 0; } return true; } public static void Enqueue(TwitchCheerEvent cheerEvent) { if (!CheerQueue.Contains(cheerEvent)) { CheerQueue.Enqueue(cheerEvent); SaveData(); ShowEnqueueMessage((TwitchEvent)(object)cheerEvent, "cheer"); } } public static void Enqueue(TwitchSubEvent subEvent) { if (!SubQueue.Contains(subEvent)) { SubQueue.Enqueue(subEvent); SaveData(); ShowEnqueueMessage((TwitchEvent)(object)subEvent, "sub"); } } public static void Enqueue(TwitchRaidEvent raidEvent) { if (!RaidQueue.Contains(raidEvent)) { RaidQueue.Enqueue(raidEvent); SaveData(); ShowEnqueueMessage((TwitchEvent)(object)raidEvent, "raid"); } } private static void ShowEnqueueMessage(TwitchEvent twitchEvent, string eventName) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) string displayNameWithColor = twitchEvent.User.GetDisplayNameWithColor(); string message = "Added Twitch " + eventName + " event from " + displayNameWithColor + " to queue"; MessageManager.Instance?.ShowMessage(message); } } internal static class TwitchIntegrationManager { public static Dictionary<string, int> AccumulatedBits { get; private set; } = new Dictionary<string, int>(); public static bool IsEnabled => ConfigManager.TwitchIntegration_Enabled.Value; public static bool IsCheerEventEnabled { get { if (IsEnabled) { return ConfigManager.TwitchCheerEvent_Enabled.Value; } return false; } } public static bool IsSubEventEnabled { get { if (IsEnabled) { return ConfigManager.TwitchSubEvent_Enabled.Value; } return false; } } public static bool IsRaidEventEnabled { get { if (IsEnabled) { return ConfigManager.TwitchRaidEvent_Enabled.Value; } return false; } } public static void Initialize() { LoadData(); Application.quitting += SaveData; TwitchEventHandler.Initialize(); } private static void LoadData() { Logger.LogInfo("TwitchIntegrationManager: Loading saved data..."); try { TwitchEventQueue.LoadData(); LoadAccumulatedBits(); Logger.LogInfo("TwitchIntegrationManager: Finished loading saved data."); } catch (Exception arg) { Logger.LogError($"TwitchIntegrationManager: Failed to load saved data. {arg}"); } } public static void SaveData() { Logger.LogInfo("TwitchIntegrationManager: Saving data..."); try { TwitchEventQueue.SaveData(); SaveAccumulatedBits(); Logger.LogInfo("TwitchIntegrationManager: Saved data."); } catch (Exception arg) { Logger.LogError($"TwitchIntegrationManager: Failed to save data. {arg}"); } } private static void LoadAccumulatedBits() { try { if (Plugin.GlobalSave.TryLoad<string>("AccumulatedBits", out var value)) { AccumulatedBits = JsonConvert.DeserializeObject<Dictionary<string, int>>(value); Logger.LogInfo("TwitchIntegrationManager: Loaded AccumulatedBits JSON data:\n\n" + value, extended: true); } } catch (Exception arg) { Logger.LogError($"TwitchIntegrationManager: Failed to load AccumulatedBits from saved data. {arg}"); } } public static void SaveAccumulatedBits() { Plugin.GlobalSave.Save("AccumulatedBits", JsonConvert.SerializeObject((object)AccumulatedBits)); } public static int GetAccumulatedBits(ViewerData viewer) { if (viewer == null) { return 0; } return GetAccumulatedBits(viewer.Username); } public static int GetAccumulatedBits(string username) { return AccumulatedBits.GetValueOrDefault(username.ToLower(), 0); } public static void SetAccumulatedBits(ViewerData viewer, int value) { if (viewer != null) { if (value <= 0) { AccumulatedBits.Remove(viewer.Username); } else { AccumulatedBits[viewer.Username] = value; } SaveAccumulatedBits(); } } public static bool CanPlayEvents() { if (!IsEnabled) { return false; } return EnemyManager.CanSpawnEnemies(); } } } namespace com.github.zehsteam.TwitchTrolling.Twitch.Objects { [Serializable] public class ViewerData { public string UserId { get; } public string Username => DisplayName.ToLower(); public string DisplayName { get; } public string Color { get; } public ViewerData(string userId, string displayName, string color) { UserId = userId; DisplayName = displayName; Color = color; } public ViewerData(TwitchUser twitchUser) { UserId = ((TwitchUser)(ref twitchUser)).UserId; DisplayName = ((TwitchUser)(ref twitchUser)).DisplayName; Color = ((TwitchUser)(ref twitchUser)).Color; } public string GetDisplayNameWithColor() { return Utils.GetTextWithReadableColor(DisplayName, Color); } } [Serializable] public class ViewerSpawnData { public ViewerData Viewer { get; private set; } public int SpawnCount { get; private set; } public string SpawnReason { get; private set; } public string TargetEnemyName { get; private set; } public int TotalSpawnCount { get; private set; } public ViewerSpawnData(ViewerData viewer, int spawnCount, string spawnReason, string targetEnemyName = "") { Viewer = viewer; SpawnCount = spawnCount; SpawnReason = spawnReason; TargetEnemyName = targetEnemyName; } public ViewerSpawnData(TwitchUser twitchUser, int spawnCount, string spawnReason, string targetEnemyName = "") { //IL_0007: Unknown result type (might be due to invalid IL or missing references) Viewer = new ViewerData(twitchUser); SpawnCount = spawnCount; SpawnReason = spawnReason; TargetEnemyName = targetEnemyName; } public void SetTargetEnemyName(string value) { TargetEnemyName = value; } public void SetTotalSpawnCount(int value) { TotalSpawnCount = value; } } } namespace com.github.zehsteam.TwitchTrolling.Twitch.Helpers { internal static class TwitchHelper { public static TwitchUser CreateTwitchUser(bool isVIP = false, bool isSubscriber = false, bool isModerator = false, bool isBroadcaster = false) { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) if (PlayerHelper.TryGetLocalPlayer(out var playerAvatar)) { string steamID = playerAvatar.steamID; string displayName = SemiFunc.PlayerGetName(playerAvatar); string color = ColorHelper.ColorToHex(SemiFunc.PlayerGetColorFromSteamID(steamID)); return CreateTwitchUser(steamID, displayName, color, isVIP, isSubscriber, isModerator, isBroadcaster); } return CreateTwitchUser("0", "Unknown", "#FFFFFF", isVIP, isSubscriber, isModerator, isBroadcaster); } public static TwitchUser CreateTwitchUser(string userId, string displayName, string color = "#FFFFFF", bool isVIP = false, bool isSubscriber = false, bool isModerator = false, bool isBroadcaster = false) { //IL_0002: 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) TwitchUser result = default(TwitchUser); ((TwitchUser)(ref result)).UserId = userId; ((TwitchUser)(ref result)).Username = displayName.ToLower(); ((TwitchUser)(ref result)).DisplayName = displayName; ((TwitchUser)(ref result)).Color = color; ((TwitchUser)(ref result)).IsVIP = isVIP; ((TwitchUser)(ref result)).IsSubscriber = isSubscriber; ((TwitchUser)(ref result)).IsModerator = isModerator; ((TwitchUser)(ref result)).IsBroadcaster = isBroadcaster; return result; } } } namespace com.github.zehsteam.TwitchTrolling.Twitch.Commands { internal static class TwitchSimulateCheerCommand { [CommandExecution("Simulate Cheer", "Simulate Twitch cheers.", true, false)] [CommandAlias("simulatecheer")] [CommandAlias("simcheer")] [CommandAlias("scheer")] [CommandAlias("cheer")] public static void SimulateCheer(string args) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: 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_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown int cheerAmount = ConfigManager.TwitchCheerEvent_MinAmountForRandomEnemy.Value; if (int.TryParse(args, out var result)) { cheerAmount = result; } TwitchCheerEvent cheerEvent = new TwitchCheerEvent { User = TwitchHelper.CreateTwitchUser(), CheerAmount = cheerAmount }; TwitchEventHandler.HandleCheer(cheerEvent); } } internal static class TwitchSimulateSubCommand { [CommandExecution("Simulate Sub", "Simulate Twitch subscriptions.", true, false)] [CommandAlias("simulatesub")] [CommandAlias("simsub")] [CommandAlias("ssub")] [CommandAlias("sub")] public static void SimulateSubs(string args) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: 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_0029: 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_0038: Expected O, but got Unknown int giftCount = 1; if (int.TryParse(args, out var result)) { giftCount = result; } TwitchSubEvent subEvent = new TwitchSubEvent { User = TwitchHelper.CreateTwitchUser(), Type = (SubType)3, Tier = (SubTier)1, GiftCount = giftCount }; TwitchEventHandler.HandleSub(subEvent); } } } namespace com.github.zehsteam.TwitchTrolling.Patches { [HarmonyPatch(typeof(EnemyDirector))] internal static class EnemyDirectorPatch { private static bool _patchedStart; [HarmonyPatch("Start")] [HarmonyPostfix] private static void StartPatch() { if (!_patchedStart) { _patchedStart = true; EnemyConfigManager.Initialize(); PageManager.UpdatePageFirstTime(); } } } [HarmonyPatch(typeof(EnemyDuck))] internal static class EnemyDuckPatch { public static List<EnemyDuck> DucksToBypassStunAggroOnce = new List<EnemyDuck>(); public static void Reset() { DucksToBypassStunAggroOnce = new List<EnemyDuck>(); } [HarmonyPatch("StateStun")] [HarmonyTranspiler] private static IEnumerable<CodeInstruction> StateStunTranspiler(IEnumerable<CodeInstruction> instructions) { //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Expected O, but got Unknown string text = "EnemyDuckPatch: [StateStun Transpiler]"; MethodInfo methodInfo = AccessTools.Method(typeof(EnemyDuck), "UpdateState", (Type[])null, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(EnemyDuckPatch), "StateStun_UpdateState", (Type[])null, (Type[])null); if (methodInfo == null || methodInfo2 == null) { Logger.LogError(text + " Failed to create transpiler. Required methods not found."); return instructions; } List<CodeInstruction> list = new List<CodeInstruction>(); foreach (CodeInstruction instruction in instructions) { if (CodeInstructionExtensions.Calls(instruction, methodInfo)) { list.Add(new CodeInstruction(OpCodes.Call, (object)methodInfo2)); Logger.LogInfo(text + " Replaced " + methodInfo.Name + " call with " + methodInfo2.Name + ".", extended: true); } else { list.Add(instruction); } } return list.AsEnumerable(); } private static void StateStun_UpdateState(EnemyDuck enemyDuck, State state) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 //IL_002b: Unknown result type (might be due to invalid IL or missing references) if ((int)state == 11 && DucksToBypassStunAggroOnce.Contains(enemyDuck)) { DucksToBypassStunAggroOnce.Remove(enemyDuck); enemyDuck.UpdateState((State)1); } else { enemyDuck.UpdateState(state); } } } [HarmonyPatch(typeof(FanTrap))] internal static class FanTrapPatch { public static List<FanTrap> FansToBypassIdle = new List<FanTrap>(); [HarmonyPatch("SetState")] [HarmonyPrefix] private static bool SetStatePatch(FanTrap __instance, States state) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) if ((int)state != 0) { return true; } if (FansToBypassIdle.Contains(__instance)) { return false; } return true; } } [HarmonyPatch(typeof(HUDCanvas))] internal static class HUDCanvasPatch { [HarmonyPatch("Awake")] [HarmonyPostfix] private static void AwakePatch(HUDCanvas __instance) { Transform parent = ((Component)__instance).transform.Find("HUD").Find("Game Hud"); PluginHUD.Spawn(parent); } } [HarmonyPatch(typeof(HurtCollider))] internal static class HurtColliderPatch { [HarmonyPatch("PlayerHurt")] [HarmonyTranspiler] private static IEnumerable<CodeInstruction> PlayerHurtTranspiler(IEnumerable<CodeInstruction> instructions) { //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Expected O, but got Unknown //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Expected O, but got Unknown string text = "HurtColliderPatch: [PlayerHurt Transpiler]"; MethodInfo methodInfo = AccessTools.Method(typeof(PlayerHealth), "Hurt", (Type[])null, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(PlayerHelper), "HurtWithCaller", (Type[])null, (Type[])null); if (methodInfo == null || methodInfo2 == null) { Logger.LogError(text + " Failed to create transpiler. Required methods not found."); return instructions; } List<CodeInstruction> list = new List<CodeInstruction>(); foreach (CodeInstruction instruction in instructions) { if (CodeInstructionExtensions.Calls(instruction, methodInfo)) { list.Add(new CodeInstruction(OpCodes.Ldarg_0, (object)null)); list.Add(new CodeInstruction(OpCodes.Call, (object)methodInfo2)); Logger.LogInfo(text + " Replaced " + methodInfo.Name + " call with " + methodInfo2.Name + ".", extended: true); } else { list.Add(instruction); } } return list.AsEnumerable(); } } [HarmonyPatch(typeof(ItemDrone))] internal static class ItemDronePatch { public static List<PhysGrabObject> IgnorePhysGrabObjects = new List<PhysGrabObject>(); [HarmonyPatch("NewRayHitPointLogic")] [HarmonyPrefix] private static bool NewRayHitPointLogicPatch(ItemDrone __instance, int photonViewId, Transform newMagnetTarget) { PhysGrabObject physGrabObject = ((!((Object)(object)newMagnetTarget != (Object)null)) ? ((Component)PhotonView.Find(photonViewId)).gameObject.GetComponent<PhysGrabObject>() : ((Component)newMagnetTarget).GetComponent<PhysGrabObject>()); return CanAttachToPhysGrabObject(__instance, physGrabObject); } [HarmonyPatch("GetHighestParentWithRigidbody")] [HarmonyPostfix] private static void GetHighestParentWithRigidbodyPatch(ItemDrone __instance, ref Transform __result) { PhysGrabObject physGrabObject = default(PhysGrabObject); if (!((Object)(object)__result == (Object)null) && ((Component)__result).TryGetComponent<PhysGrabObject>(ref physGrabObject) && !CanAttachToPhysGrabObject(__instance, physGrabObject)) { __result = null; } } private static bool CanAttachToPhysGrabObject(ItemDrone itemDrone, PhysGrabObject physGrabObject) { if ((Object)(object)itemDrone == (Object)null || (Object)(object)physGrabObject == (Object)null) { return true; } if (IsSameDroneType(itemDrone, physGrabObject)) { return false; } if (IgnorePhysGrabObjects.Contains(physGrabObject)) { return false; } return true; } private static bool IsSameDroneType(ItemDrone itemDrone, PhysGrabObject physGrabObject) { if ((Object)(object)itemDrone == (Object)null || (Object)(object)physGrabObject == (Object)null) { return false; } ItemAttributes val = default(ItemAttributes); if (!((Component)physGrabObject).TryGetComponent<ItemAttributes>(ref val)) { return false; } return itemDrone.itemAttributes.itemName == val.itemName; } } [HarmonyPatch(typeof(ItemRubberDuck))] internal static class ItemRubberDuckPatch { public static List<ItemRubberDuck> SpecialRubberDucks = new List<ItemRubberDuck>(); [HarmonyPatch("Quack")] [HarmonyPostfix] private static void QuackPatch(ItemRubberDuck __instance) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: 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_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0071: 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) if (SpecialRubberDucks.Contains(__instance) && SemiFunc.IsMasterClientOrSingleplayer() && !(__instance.itemBattery.batteryLife <= 0f) && !__instance.physGrabObject.grabbed) { Vector3 velocity = __instance.rb.velocity; if (((Vector3)(ref velocity)).magnitude >= 20f) { Rigidbody rb = __instance.rb; rb.velocity *= 5f; __instance.rb.AddTorque(Random.insideUnitSphere * 40f); } } } } [HarmonyPatch(typeof(LevelGenerator))] internal static class LevelGeneratorPatch { [HarmonyPatch("EnemySetup")] [HarmonyPostfix] private static void EnemySetupPatch() { EnemyManager.ResetViewerEnemies(); EnemyDuckPatch.Reset(); if (TwitchIntegrationManager.CanPlayEvents()) { TwitchEventQueue.PlayQueuedEvents(TimeSpan.FromSeconds(10.0)); } } } [HarmonyPatch(typeof(PlayerController))] internal static class PlayerControllerPatch { private static bool _usingCustomSpeed; private static float _timeCustomSpeedEnd = float.NegativeInfinity;